AutoGen: expressing multi-agent applications as programmable conversations
Original source
Qingyun Wu et al., 2023: AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation. This guide uses arXiv v2.
This is an original learning guide, not a reproduction. Copyright and the complete paper remain with the original source.
What you should be able to do after this guide
By the end, you should be able to:
- Define a Conversable Agent in terms of role, internal context, capabilities, and reply rules—not merely give a model a role name.
- Distinguish computation from control flow in Conversation Programming.
- Trace how one message triggers an LLM, tool, code executor, or human and then produces the next message.
- Specify speaker selection, termination, maximum turns, error handling, and human takeover for two-agent and multi-agent workflows.
- Decide whether a task genuinely benefits from multiple agents or needs only one agent with tools.
The problem: why a complex application is more than one prompt
“Analyze this stock data and draw a chart” looks like one instruction, but it contains many kinds of work: understand the request, write code, install or call dependencies, execute the program, read errors, revise the code, inspect the chart, and decide whether to stop. Putting everything into one model call creates several problems:
- A model can generate code but cannot, by itself, prove that the code was executed.
- Tool results and human feedback must return to the model, but responsibility for relaying them is unclear.
- Different tasks require different degrees of human involvement and different stop conditions.
- Once roles multiply, choosing the next speaker, routing messages, and avoiding endless conversation become control problems of their own.
AutoGen’s answer is to wrap LLM, human, and tool capabilities in agents that can exchange messages, then express the application as conversational computation and control flow among those agents. “Conversation” here is not casual chat. It is an application-orchestration interface.
Establish the vocabulary first:
- A Conversable Agent is an entity with a role, internal context, and configured capabilities that can receive, process, and reply to messages.
- A capability is something an agent can invoke while processing a message, such as LLM inference, human input, code execution, or a function call.
- A conversation is the sequence of messages among agents. Messages carry content and advance the application state machine.
- Auto-reply means that receiving a message automatically invokes
generate_replyand sends the result onward until a termination condition is met. - Conversation Programming defines the participating agents and capabilities, then defines their conversational computations and control flow.
An agent is not the same thing as one model instance
An agent can be backed by an LLM, tools, a human, or a combination. Multiple agents may also share the same model. Real differentiation comes from context, capabilities, permissions, reply functions, and control rules—not names.
The mechanism contract: a message-driven application state machine
| Part | Meaning in the AutoGen paper | What a designer must specify |
|---|---|---|
| Input | A message from a user or another agent | Message type, sender, and trust level |
| Agent state | Role, internal context formed by sent/received messages, and capability configuration | Visible history, available tools, and whether human input is allowed |
| Computation | Actions taken to produce a reply | Invoke an LLM, call a function, execute code, or ask a human |
| Control flow | The order and conditions under which computations occur | Next speaker, recipient, entry into sub-conversations, and stopping |
| Output | A new message, tool result, code result, or human feedback | How the next agent consumes it and whether it is structured |
| Stop condition | Termination message, programmed predicate, maximum auto-replies, or human stop | Who may stop and whether stopping means the external task passed acceptance |
flowchart TD M["Receive a message"] --> R["Update the agent's conversation context"] R --> D{"Select a reply capability"} D -->|"LLM"| L["Reason or generate content"] D -->|"Tool / Code"| T["Execute a function or code"] D -->|"Human"| H["Request or receive human input"] L --> G["generate_reply creates a new message"] T --> G H --> G G --> E{"Termination condition met?"} E -->|"Yes"| X["End conversation and return output"] E -->|"No"| C["Control flow selects a recipient or next speaker"] C --> M
This mechanism separates an implicit workflow into two questions: what computation happens at each node, and where the resulting message goes next.
Core mechanism 1: how a Conversable Agent works
The paper’s Conversable Agent has four important properties.
1. A role defines processing responsibility
The role says what the agent is responsible for—for example, proposing code, executing code, or reviewing a result. It influences prompts and response behavior, but the name alone creates no isolation.
2. Message history forms internal context
An agent maintains context from messages it has sent and received. Different agents can see different histories, which can create useful information separation. An executor may need only code and execution parameters rather than a planner’s entire reasoning trace. A reviewer can receive requirements, the candidate artifact, and independent evidence without receiving the generator’s self-defense.
3. Capabilities are composable
The paper groups common backends into three classes:
- LLMs understand messages, generate replies and code, and adapt to feedback.
- Humans can be asked for input at configured turns or conditions and may be allowed to skip a request.
- Tools execute code or functions and return real results to the conversation.
An agent may have only one of these or a mixture. In the paper’s implementation, the preconfigured AssistantAgent is primarily LLM-backed. UserProxyAgent can represent human input and execute model-suggested code or function calls. Those are historical abstractions from the paper’s version, not a promise that the current AutoGen package will preserve the same API forever.
4. A unified message interface triggers replies
Agents collaborate through unified operations such as send, receive, and generate_reply. Under the default auto-reply pattern, receipt of a message invokes reply logic and sends a new message unless a termination condition is already satisfied. A reply function may use LLM inference, code or function execution, human input, or custom developer logic.
A message is therefore not static text. It behaves like an event in an event loop: it updates state, triggers computation, and produces the next event.
Core mechanism 2: Conversation Programming separates computation and control
The paper defines two concepts:
- Computation is the action an agent takes to answer the current message. It may call an LLM, run code, invoke a tool, or ask for review; the result is represented as another message.
- Control flow is the order and conditions under which those computations occur. It decides recipients, the next active agent, entry into a sub-conversation, and termination.
Control can be expressed in three ways:
- Natural-language control, such as asking an agent to reply with
TERMINATEwhen all work is complete or to propose a plan before execution. - Programming-language control, such as Python predicates for termination, human-input modes, limits on automatic replies, and custom reply functions.
- Transitions between the two, where code invokes an LLM carrying control instructions or an LLM proposes a function call that enters a programmed path.
This mixture provides flexibility, but the two forms do not have equal reliability. A code-level maximum-turn limit is deterministic. “Stop when appropriate” may be misjudged by a model. Safety-critical conditions should be moved into executable control wherever possible.
Two-agent example: closing the loop between writing and running code
Consider: “Read a local sales CSV, calculate monthly growth, and save a chart.” Here is a simplified but mechanism-complete trace.
| Step | Sender → receiver | Message or action | System state |
|---|---|---|---|
| 1 | UserProxy → Assistant | Send task, allowed file path, and acceptance requirements | Goal and permission boundary are established |
| 2 | Assistant → UserProxy | Generate Python and request execution | Candidate code exists, but there is no execution evidence |
| 3 | UserProxy | Run code in an isolated environment | Real execution occurs |
| 4 | UserProxy → Assistant | Return KeyError: revenue and actual column names | Environment feedback enters conversation context |
| 5 | Assistant → UserProxy | Revise column mapping and request another run | A second code candidate is produced |
| 6 | UserProxy | Run successfully and check that the output file exists | Execution output and a minimum assertion are available |
| 7 | UserProxy → Assistant | Return summary statistics, chart path, and assertion result | The model can summarize facts rather than pretend it ran code |
| 8 | Assistant → UserProxy | Return final explanation and termination marker | Conversation meets its stop rule |
| 9 | UserProxy / human | Verify the business definition, accept, or request revision | Conversation termination maps to task acceptance only here |
The value of this pattern does not depend on using two models. UserProxy may have no LLM at all and exist only for human interaction and controlled execution. The separation keeps “propose code” and “possess execution permission” on different sides of a trust boundary.
Multi-agent example: speaker selection is itself an algorithm
For a more complex task, add Engineer, Critic, Executor, and User Proxy, with a Manager controlling the group conversation. The dynamic group-chat manager in the paper performs three operations: select one speaker, ask that speaker to respond, and broadcast the response to the other members.
sequenceDiagram participant U as User Proxy participant M as Manager participant E as Engineer participant C as Critic participant X as Executor U->>M: Task, constraints, and acceptance criteria M->>E: Select Engineer to propose a solution E-->>M: Code and assumptions M->>C: Select Critic for independent review C-->>M: Risks and requested changes M->>E: Return review findings E-->>M: Revised code M->>X: Request isolated execution X-->>M: Logs, tests, and artifacts M->>U: Summarize evidence and request final acceptance
The Manager is not decorative. Strict round-robin speaking might run the Executor before code is ready. Unconstrained model-selected speakers may cause Engineer and Critic to repeat each other. Speaker selection should depend on structured task state such as DRAFTED → REVIEWED → EXECUTED → ACCEPTED, not just on the last paragraph of chat.
Multiple agents add real value when:
- roles have different tools or permissions—for example, only Executor may run code;
- roles receive different information—a Critic sees requirements and evidence rather than the generator’s self-assessment;
- work can run in parallel, such as independent retrieval over different sources;
- an independent rules component is useful, such as a chess-board agent that validates legal moves.
If every role sees the same context, calls the same model, and lacks independent tools and acceptance criteria, adding roles often duplicates tokens and errors.
Expressing conversation control as pseudocode
agents = {
"assistant": AssistantAgent(llm=True, tools=[]),
"executor": UserProxyAgent(
human_input="on_demand",
code_execution="sandbox",
),
}
message = user_task
sender, receiver = "executor", "assistant"
for turn in range(MAX_TURNS):
receiver_state = update_context(receiver, sender, message)
reply = agents[receiver].generate_reply(receiver_state)
if violates_policy(reply):
request_human_review(reply)
break
if accepted_result(reply):
finish(reply)
break
sender, receiver = receiver, choose_next_agent(reply)
message = reply
else:
stop_as_incomplete("maximum conversation length reached")A production implementation must also handle exceptions, timeouts, retries, idempotency, and permissions in the control layer. The key distinction remains: generating a reply is computation; choose_next_agent, policy gates, and termination checks are control flow.
How to read the evidence
The paper presents applications in mathematics, programming, question answering, operations research, online decision-making, and entertainment, with selected benchmark and pilot results. Its main evidentiary contribution is that a shared abstraction can express many conversation patterns and produce useful application results. It is not a large controlled study proving that multiple agents universally outperform one.
The dynamic group-chat pilot illustrates the boundary particularly well. Across 12 manually crafted complex tasks, the authors compare a two-agent setup, a four-agent group chat with role-play speaker selection, and the same group members with task-style speaker selection. With GPT-4, the success counts are 9, 11, and 8 respectively. In other words, four agents with the weaker selection policy underperform two agents. The sample is small and does not establish a universal ranking, but it clearly shows that orchestration and termination matter more than agent count alone.
In the conversational-chess example, removing the Board Agent and merely prompting players to keep moves legal leads to illegal moves and game disruption. That ablation supports an engineering lesson: when a rule is executable, a separate validator is usually more reliable than asking a generator to guarantee its own compliance.
Failure modes and a useful debugging order
| Symptom | Common cause | Repair direction |
|---|---|---|
| Agents repeat each other and never stop | Vague termination and state represented only in prose | Add structured stages, maximum turns, and programmed stopping |
| Reviewer always agrees with the generator | No independence in context, model, or criteria | Supply independent evidence, different tools, or deterministic rules |
| Tool result is mistaken for model prose | Messages lack type and provenance | Distinguish tool_result, human_input, and assistant_message |
| Multi-agent system is worse than one agent | Communication overhead, duplicated context, or wrong speaking order | Establish a single-agent baseline and justify each added role |
| Code execution crosses a permission boundary | Executor has excessive permission or no isolation | Apply least privilege, sandboxing, resource limits, and approval |
| Conversation stops but task is incomplete | TERMINATE was treated as business acceptance | Separate termination from tests, artifact checks, and human acceptance |
Historical contribution versus today’s engineering interpretation
The paper’s historical contribution is the proposal and implementation of Conversable Agents and Conversation Programming: a unified messaging interface that combines LLM, human, and tool capabilities, with natural language and code defining static or dynamic multi-agent conversations.
The safer engineering interpretation today is that multi-agent design is a modular workflow representation, not a button that automatically increases intelligence. Add a role only after showing that it needs different context, tools, permissions, parallel work, or independent verification—and that those benefits justify extra latency, cost, and failure surface.
The paper describes AutoGen in 2023 and its then-current built-in agents and examples. Later package structure and APIs may change. This guide teaches the mechanism rather than serving as a copy-and-paste manual for a current release.
How it differs from MCP
AutoGen focuses on participants, messages, and control flow inside an application. MCP focuses on how a Host and an external Server expose resources, prompts, and tools through a shared protocol. AutoGen asks who collaborates and who acts next. MCP asks how an external capability is discovered and invoked. An AutoGen agent may use an MCP Client, but the two abstractions occupy different layers.
Check your understanding
- Define a Conversable Agent with four fields: role, context, capabilities, and reply rules. Why is a role name alone insufficient?
- In the two-agent “write, run, repair” example, which operations are computation and which are control flow?
- Why can
TERMINATEstop a conversation without proving task success? - Under what conditions can a four-agent group chat perform worse than two agents?
- Design a tool call that requires human confirmation. Between which message and permission boundary should confirmation occur?
Answer key and reasoning cues
- The role states responsibility, context controls visible information, capabilities determine possible actions, and reply rules determine response behavior. Renaming identical configurations does not create real separation.
- LLM generation, code execution, and human review are computations. Recipient selection, execution timing, failure loops, and stopping decisions are control flow.
- A model may emit the token too early. Success requires tests, state assertions, artifact inspection, or human acceptance.
- It can be worse when roles add no independent value, messages copy context, speaker policy mismatches task state, termination is unstable, or communication costs exceed specialization benefits.
- After the model proposes a structured invocation, the execution-capable agent should display parameters and side effects before crossing the execution boundary. Only human approval should authorize the call.
Previous: Voyager · Next: MCP launch article · Back to the landmark reading index