How Messages Become a Program

In AutoGen, the important question is not only what an agent says but what the message causes the system to do next. This chapter focuses on the boundary between computation and control: how an agent turns context into a response, how that response routes work, and how a conversation stops. The distinction gives the reader a way to trace a running workflow rather than describing it as a group of components that happen to exchange text.

A message carries data and a trigger

Every useful conversation has at least two layers. The first is computational: an agent interprets an input and produces something, such as a proposed answer, a program, a request for clarification, or the result of a tool call. The second is control flow: the system determines when that computation happens, who receives the result, and whether another step follows. AutoGen calls these conversation-centric computation and conversation-driven control. They are closely coupled, but they are not the same question.

Consider a simple assistant-and-executor arrangement. The assistant computes a candidate solution, perhaps by generating code. The executor receives that proposal and computes an action by running the code. Its output may be a value, an error, or another observation. The assistant then computes a revised solution from the new context. At each stage, there is a local act of computation and a control decision about the next recipient or next turn. If the executor’s output is treated only as text to display, the workflow has lost the feedback that makes the arrangement useful. If every output automatically triggers another attempt, it has lost the boundary that makes the arrangement safe and understandable.

This is why the message itself matters. It carries data across an agent boundary, but its arrival can also trigger a transition. A request can initiate a reply. A result can initiate a correction. An error can route work back to the agent that produced the failed proposal. A human response can release a paused interaction. A termination signal can prevent another computation. The conversation is functioning as a program because messages connect state to the events that change state.

The relationship can be traced without assuming that all agents share the same context. An initiating user or agent sends a task to one participant. That participant sends a proposal to another. The recipient’s reply behavior decides whether to act, ask for help, return feedback, or end. The important edge is not simply “A talks to B.” It is “A’s message causes B to perform a particular computation, whose result becomes an input to the next control decision.”

sequenceDiagram participant U as Initiator participant A as Agent A participant B as Agent B participant X as Tool or human U->>A: Task or message A->>B: Proposal or request B->>B: Receive and generate reply B->>X: Act or request input X-->>B: Result, error, or judgment B-->>A: Feedback message alt Continue A->>B: Revised request else Terminate B-->>U: Bounded result end

The sequence shows a control loop, not a promise that every run follows the same path. A tool or person may be absent in one application; an agent may call another participant directly in a different one. The stable idea is that a response is also a possible transition. AutoGen’s unified conversational interface makes sending, receiving, and reply generation available as common operations so that different agent implementations can participate in this pattern.

The default auto-reply mechanism makes that transition concrete. When an agent receives a message, it can automatically invoke its reply-generation behavior and send a response to the sender, unless a termination condition has been satisfied. A developer can therefore initialize a conversation and let the configured reply functions induce the flow. The agent that receives the message owns the immediate response; the response, in turn, supplies the next event for the conversation. For a small workflow, this avoids a separate control-plane module whose only job would be to call one agent after another.

Removing that extra controller is useful for more than brevity. It keeps a local interaction close to the capability that handles it. A proxy can decide how to handle a proposed code block; an assistant can decide how to react to execution feedback; a custom participant can speak to another agent before replying to the original sender. This makes the workflow composable: the developer can replace or extend one reply behavior without rewriting the whole coordination layer.

But local ownership does not mean the system has no global behavior. A chain of auto-replies can create a longer workflow through its accumulated messages. The developer still has to define what counts as a completed task, what should happen when an agent cannot respond, and how many attempts are acceptable. The termination condition is not a cosmetic final message. It is the rule that changes the conversation from a running process into an outcome. Without it, a valid feedback loop can become a repeated exchange even when no new information is being produced.

The same point applies to errors. An execution failure can be a productive input if the next reply changes the proposal in light of it. It becomes a wasted turn if the system sends the same proposal again or if the receiving agent cannot identify what failed. The programming model therefore places a burden on message content and reply behavior: the response must preserve enough context to make the next computation meaningful, and the control logic must distinguish a new attempt from an unchanged repetition.

Flexible control needs explicit boundaries

AutoGen allows developers to express control in both natural language and program code. Natural-language instructions can ask an LLM-backed agent to repair a result, constrain an output format, request another participant, or signal completion. This is powerful when the appropriate next step depends on the conversation’s meaning rather than on a fixed sequence. The model can infer that a prior result contains an error and propose another action without the developer enumerating every possible branch.

Program code contributes a different kind of control. It can specify termination conditions, decide when human input is requested, configure tool execution, bound the number of automatic replies, and register custom reply functions. These are useful places for rules that should not depend entirely on an LLM interpreting an instruction correctly. A program can enforce a maximum attempt count even when the conversation has not reached a satisfying answer. It can place a tool call behind a condition. It can make a human pause explicit rather than hoping the model remembers to ask.

The two forms are not sealed compartments. AutoGen supports transitions between them. A custom programmed reply function can invoke an LLM whose response contains part of the control decision. Conversely, a natural-language response can lead to a function call that invokes code or another agent. This is best understood as a boundary crossing: code delegates an interpretation to a model, or model output requests an operation governed by code. The boundary should be visible because each side has a different failure mode.

A model-mediated decision can be context-sensitive and adaptable, but it is probabilistic. The early AutoGen paper notes that language models do not follow every instruction perfectly and that ambiguous directions may need to be made more precise or handled by other mechanisms. A programmatic decision is easier to state and test, but it can be rigid when the task’s next step depends on information that was not known in advance. The design question is not which language is universally superior. It is which part of the workflow needs interpretation and which part needs an invariant.

Static and dynamic conversation patterns make that choice visible. A static pattern has a predetermined order of interaction. It is easier to explain to an operator and often easier to estimate for tests and model calls. A dynamic pattern lets the current message determine whether another agent should be involved. A custom reply function can hold one conversation while consulting another. A function call can let the model choose a capability based on the current state. A GroupChatManager can select the next speaker and broadcast that speaker’s response to the group. These are different ways to make routing responsive to context.

Dynamic routing is not simply a larger version of a two-agent chat. It changes who owns the next transition. In a fixed assistant-and-executor exchange, the roles imply the next handoff. In a group, a manager or selection policy must decide which participant should act, and the selected participant must produce a response that the rest of the group can use. Shared context can help the selection decision, but it also increases the amount of information that must be interpreted. A route that is flexible enough to avoid unnecessary work can also become difficult to reproduce when small differences in a message change the chosen speaker.

The AutoGen paper’s group-chat pilot makes the trade-off concrete without settling it for every task. Across 12 manually crafted complex tasks, a four-agent group chat using a role-play-based speaker-selection prompt succeeded on 11 tasks with GPT-4, compared with 9 for a two-agent system and 8 for a group chat using a task-based selection prompt. It averaged 4.5 LLM calls with no termination failures, while the two-agent comparison averaged 6.8 calls and had 3 termination failures. The authors attribute the advantage to better consideration of conversation context and role alignment, but the small, manually designed sample makes this a demonstration of a mechanism rather than a general ranking of topologies.

The numbers also illustrate why control flow cannot be judged by success rate alone. A group may solve more of the selected tasks while consuming more context or requiring a more sophisticated routing prompt. A two-agent system may be preferable when the workflow is predictable and the extra choices add no useful capability. Conversely, a dynamic group may be justified when the task branches, when no fixed order captures the work, or when a stalled conversation needs a different participant. The right comparison is between the control gap the topology closes and the coordination overhead it introduces.

The most important boundary is that natural language does not automatically become a deterministic policy. Auto-reply can remove boilerplate and make a small workflow elegant, but it can also hide a chain of decisions inside message callbacks. Dynamic functions can create a useful route, but they can also send an agent into an interaction whose context or output format it cannot handle. If the system cannot record which message caused a transition, which capability produced the response, and why the conversation terminated, the flexibility is difficult to maintain.

Observability is therefore part of the programming model. A useful trace should let an operator distinguish a proposal from an observation, an ordinary next turn from a recovery attempt, and a genuine completion from a reply that merely sounds final. Exception handling matters for the same reason. If a tool fails, the system needs a defined route for the error; if a human does not respond, it needs a bounded alternative; if an agent violates an expected format, another component must either repair it or stop. These are not separate administrative concerns added after the conversation is written. They determine whether the conversation is actually controlling the task.

AutoGen’s flexibility is most valuable when the developer can state these boundaries clearly. Use model-mediated control where interpretation of the current context is the point. Use programmatic control where termination, permissions, execution, or attempt limits must hold regardless of what a model proposes. Use dynamic routing when the task’s information can justify a different participant, not simply because a group looks more capable. Once messages are understood as both computations and triggers, the next question is what kind of feedback can change those triggers: new evidence, an execution result, a grounding fact, or a person’s judgment.

References