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:

  1. Define a Conversable Agent in terms of role, internal context, capabilities, and reply rules—not merely give a model a role name.
  2. Distinguish computation from control flow in Conversation Programming.
  3. Trace how one message triggers an LLM, tool, code executor, or human and then produces the next message.
  4. Specify speaker selection, termination, maximum turns, error handling, and human takeover for two-agent and multi-agent workflows.
  5. 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_reply and 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

PartMeaning in the AutoGen paperWhat a designer must specify
InputA message from a user or another agentMessage type, sender, and trust level
Agent stateRole, internal context formed by sent/received messages, and capability configurationVisible history, available tools, and whether human input is allowed
ComputationActions taken to produce a replyInvoke an LLM, call a function, execute code, or ask a human
Control flowThe order and conditions under which computations occurNext speaker, recipient, entry into sub-conversations, and stopping
OutputA new message, tool result, code result, or human feedbackHow the next agent consumes it and whether it is structured
Stop conditionTermination message, programmed predicate, maximum auto-replies, or human stopWho 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:

  1. 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.
  2. 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 TERMINATE when 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.

StepSender → receiverMessage or actionSystem state
1UserProxy → AssistantSend task, allowed file path, and acceptance requirementsGoal and permission boundary are established
2Assistant → UserProxyGenerate Python and request executionCandidate code exists, but there is no execution evidence
3UserProxyRun code in an isolated environmentReal execution occurs
4UserProxy → AssistantReturn KeyError: revenue and actual column namesEnvironment feedback enters conversation context
5Assistant → UserProxyRevise column mapping and request another runA second code candidate is produced
6UserProxyRun successfully and check that the output file existsExecution output and a minimum assertion are available
7UserProxy → AssistantReturn summary statistics, chart path, and assertion resultThe model can summarize facts rather than pretend it ran code
8Assistant → UserProxyReturn final explanation and termination markerConversation meets its stop rule
9UserProxy / humanVerify the business definition, accept, or request revisionConversation 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

SymptomCommon causeRepair direction
Agents repeat each other and never stopVague termination and state represented only in proseAdd structured stages, maximum turns, and programmed stopping
Reviewer always agrees with the generatorNo independence in context, model, or criteriaSupply independent evidence, different tools, or deterministic rules
Tool result is mistaken for model proseMessages lack type and provenanceDistinguish tool_result, human_input, and assistant_message
Multi-agent system is worse than one agentCommunication overhead, duplicated context, or wrong speaking orderEstablish a single-agent baseline and justify each added role
Code execution crosses a permission boundaryExecutor has excessive permission or no isolationApply least privilege, sandboxing, resource limits, and approval
Conversation stops but task is incompleteTERMINATE was treated as business acceptanceSeparate 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

  1. Define a Conversable Agent with four fields: role, context, capabilities, and reply rules. Why is a role name alone insufficient?
  2. In the two-agent “write, run, repair” example, which operations are computation and which are control flow?
  3. Why can TERMINATE stop a conversation without proving task success?
  4. Under what conditions can a four-agent group chat perform worse than two agents?
  5. Design a tool call that requires human confirmation. Between which message and permission boundary should confirmation occur?

Previous: Voyager · Next: MCP launch article · Back to the landmark reading index