Coordinator (~AutoPattern)
A coordinator agent at the centre with several specialists. The coordinator's LLM decides which specialist to consult next via a single generic handoff(to) tool, and ends the conversation via a finish(summary) tool. No per-specialist routing rules, no graph rewiring when a specialist is added or removed.
This is the closest beta equivalent to classic AG2's AutoPattern group chat with LLM-driven manager handoffs.
Classic (non-beta) primitives: AutoPattern with GroupChat, GroupChatManager, LLM-selected next_agent, and per-specialist handoff registration.
Key Characteristics#
- One generic handoff tool. The coordinator's LLM picks the next specialist by passing the name as a parameter (
handoff(to="researcher")). Adding a fifth specialist doesn't add a new tool — just a new participant on the channel. - One finish tool. The coordinator ends the discussion by returning
Finish(summary=...). The framework closes the channel cleanly via the typed-return routing path. - Both routing tools are terminal.
handoffandfinisheach return aToolResult(..., final=True)— calling either ends the coordinator's round immediately, so each turn produces exactly one routing decision. (Without this the round runs a multi-step tool loop and emits several routing intents; see the admonition below.) - Empty transitions list. The graph has no per-specialist rules. Dynamic
Handoffresolution routes coordinator → specialist;default_target=AgentTarget(coordinator)routes every other turn (the user's kickoff, each specialist's reply) back to the coordinator.
The whole topology fits in four lines of TransitionGraph:
Tools the coordinator carries#
Both tools are user-authored on the coordinator agent. Each returns a ToolResult(..., final=True) wrapping the typed routing object:
Why the routing tools must be terminal (final=True)
An Agent.ask round runs a multi-step tool loop: the LLM calls a tool, sees the result, and may call another — repeating until it stops. A coordinator with plain handoff / finish tools will call handoff and finish within a single round.
The workflow resolves a round's routing first-emit-wins — it acts on the first routing tool and silently drops the rest. So a round that calls handoff then finish routes the handoff and loses the finish — the channel never closes and the coordinator loops.
Returning ToolResult(..., final=True) makes each routing tool terminal: the first call ends the round, so every coordinator turn carries exactly one routing intent. Pair it with disabling parallel tool calls on the coordinator's config (disable_parallel_tool_use — see the Star pattern's "Parallel-call defence" note) so the model also commits to one tool per response.
Constraining to to known names
Typing to as a Literal[...] over the participant names lets the LLM's tool schema constrain its picks at the model layer — the LLM can't invent a name. If you'd rather keep targets fully dynamic (e.g. the participant set isn't known at decoration time), use to: str and list the valid names in the docstring; the framework's hub.name_for resolves unknown names to themselves, so a hallucinated target surfaces as a validate_send mismatch on the next envelope.
How the coordinator knows the roster#
For the coordinator to pick the right specialist it needs to know what each one is good at. That knowledge comes from two author-supplied sources:
- The
handofftool'sLiteral[...]— constrains which names are valid, surfaced to the LLM as a tool-schema enum. It says nothing about what each specialist does. - The system prompt's roster block — describes what each specialist is for. The code below builds it from each specialist's
Resume.summary:
Each Resume is built once and used twice — folded into the coordinator's prompt, and passed to register(...) so the hub registry carries the same description. Because the prompt is fixed when the coordinator Agent is constructed, the resumes must exist first.
Static snapshot vs. dynamic discovery
Baking the roster into the prompt captures it at construction time — fine for a fixed-roster channel. If specialists join or leave mid-run, the prompt goes stale. For a churning roster, register the coordinator with attach_plugin=True (the default) and let it call the peers tool to read peers' Resume / skill_md from the hub at runtime. You can also fetch a resume directly with await hub.get_resume(agent_id).
Agent Flow#
sequenceDiagram
participant User as user
participant C as coordinator
participant R as researcher
participant K as critic
participant Co as cost_analyst
participant O as operator
User->>C: kickoff (default_target → coordinator)
C->>R: handoff(to="researcher") — final=True ends the round
R->>C: text reply (default_target → coordinator)
C->>K: handoff(to="critic")
K->>C: text reply (default_target → coordinator)
C->>Co: handoff(to="cost_analyst")
Co->>C: text reply (default_target → coordinator)
C->>O: handoff(to="operator")
O->>C: text reply (default_target → coordinator)
C->>C: finish(summary=...)
Note over C: routing.kind="finish" → channel closes Migrating from Classic to Beta?#
| Classic | Beta |
|---|---|
AutoPattern builds the group chat from agents=[...] + an LLM-driven group_manager | TransitionGraph(initial_speaker=user, transitions=[], default_target=AgentTarget(coordinator)) plus the coordinator's two tools |
GroupChatManager runs the manager's LLM each turn and parses next_agent from its output | Coordinator is a regular Agent whose LLM calls handoff(to=...); the framework reads the typed Handoff return and routes |
One pseudo-tool per agent in classic, generated by AutoPattern | One generic handoff(to) tool the user writes once |
Termination via is_termination_msg predicate or max_round | Termination via the coordinator's finish(summary) tool returning Finish, or max_turns on the graph |
Code#
Tip
Every LLM-driven agent uses AnthropicConfig(model="claude-sonnet-4-6") — set ANTHROPIC_API_KEY in your environment (the script calls load_dotenv()), or swap in OpenAIConfig / GeminiConfig to run on another provider. The user agent uses TestConfig() because it only opens the channel and sends the kickoff — it never takes an LLM turn. All agents register with attach_plugin=False: none of them need the NetworkPlugin tools (say / delegate / peers / …), and leaving those off keeps the coordinator's tool surface to just handoff and finish.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
Output#
A real claude-sonnet-4-6 run. The coordinator's turn-by-turn selection is LLM-driven, so wording varies between runs; the specialist replies and the final summary below are abridged for length.
channel: e1d5855c...
user: Should we adopt Kubernetes for an 8-person startup?
coordinator: [handoff → researcher] Need evidence-based analysis on Kubernetes adoption for small startups, including benchmarks and industry data.
researcher: A CNCF survey (2022) found Kubernetes complexity is the #1 challenge, and
teams without dedicated infra staff spend disproportionate time on operations.
Managed alternatives (Railway, Render, ECS) handle startup-scale workloads
adequately; ROI on Kubernetes typically appears at 50+ engineers or 20+
services.
coordinator: [handoff → critic] Researcher has weighed in. Surface failure modes and hidden risks on both sides.
critic: The "managed platform" advice glosses over vendor lock-in: Railway and
Render have repriced and had outages that leave small teams stranded. There's
also a knowledge-concentration risk — if the one engineer who understands
your cluster leaves, you can't safely modify production. And both sides
assume you know your scaling trajectory, which most 8-person startups don't.
coordinator: [handoff → cost_analyst] Need TCO modelling comparing Kubernetes vs. managed alternatives.
cost_analyst: Kubernetes Year 1 TCO is 3–5x higher than managed platforms: ~$75–100K/year
in engineering labor (0.5 FTE at startup salaries) plus tooling, vs. $5–20K
on Render/ECS. The cost is front-loaded and fixed regardless of traction —
dangerous burn-rate structure pre-PMF.
coordinator: [handoff → operator] Need day-2 operations perspective before finalising.
operator: Week to week, Kubernetes means cert rotations, node upgrades, etcd health,
and CrashLoopBackOffs landing on whoever knows the cluster — often at 2am
before a product sprint. Managed platforms push that entire maintenance
surface to the vendor, which at your size is almost always the right trade.
coordinator: [finish] ## Recommendation: Skip Kubernetes — For Now
Evidence, cost, risk, and ops all point the same way. Use a managed platform
(Render, Fly.io, AWS ECS) until you have a dedicated platform hire and
genuine multi-service complexity. Containerise now with Docker to keep the
future migration tractable.
closed: reason='finished'
When to reach for this pattern#
This pattern works when:
- The coordinator's LLM is the only decision-maker — specialists answer when asked but don't pick the next turn.
- The set of specialists is small enough that the coordinator can reason about which one to consult (typically < 10).
- You want to add or remove specialists by editing the participant list, not by rewriting graph rules.
- Termination is a deliberate LLM call (
finish), not a side-effect of state or a turn cap.
When it's not a fit:
- If specialists hand off to each other (specialist → specialist without going through the coordinator), you need explicit
FromSpeaker → AgentTargetrules — the default-to-coordinator target no longer covers it. See Hierarchical for nested coordination. - If the LLM needs to decide whether to continue rather than who to ask, you may want a
ContextEqualsgate. See Feedback Loop. - If you need parallel fan-out + synthesis (ask all specialists, then summarise), see Star — same idea, different control flow.
See Also#
- Workflow Adapter — the underlying graph machinery, dynamic
Handoff, andFinishtyped return. - Star — fan-out variant with WAL-gated synthesis instead of LLM-driven turn-by-turn selection.
- Hierarchical — when specialists need their own sub-coordinators.
- Migrating from Group Chat — the broader translation table for classic patterns.