Skip to content

Overview

Side-by-side translations of the canonical multi-agent orchestration patterns from classic (non-beta) AG2 (autogen.agentchat.group) onto the beta WorkflowAdapter. Each pattern lists the classic primitives it used, a beta translation, the full runnable source, and any gaps that don't yet have a clean equivalent.

For the classic (non-beta) versions and their conceptual background, see the Pattern Cookbook in the user guide.

Status Legend#

Status Meaning
Clean Maps one-to-one onto existing beta primitives. Code below works as written.
Partial Translates with one or more named gaps. Workaround included; gap tracked on roadmap.

Quick Reference#

# Pattern Status Beta primitives Notable gap
1 Pipeline Clean TransitionGraph.sequence or FromSpeaker AgentTarget chain
2 Hierarchical Partial Handoff returns from delegate tools + ContextEquals for terminate No NestedChatTarget; sub-flows via separate channels
3 Star Clean ToolCalled graph rule for spoke selection + WAL-reading synthesis tool
4 Coordinator Clean One generic handoff(to) tool returning Handoff + finish tool returning Finish; empty transitions list + RevertToInitiatorTarget default
5 Escalation Clean Handoff returns from escalate tools + ToolCalled("resolve") for terminate
6 Redundant Partial Sequential fan-out via TransitionGraph.sequence No parallel dispatch; one specialist at a time
7 Feedback Loop Clean ContextEquals on a done flag with max_turns cap
8 Context-Aware Routing Partial Router agent's tool sets categoryContextEquals per branch No LLMCondition; routing reasoning lives inside the coordinator's LLM, not the framework
9 Triage with Tasks Clean TransitionGraph.sequence over a triage-produced plan; knobs["context_vars"] seeds the queue

The "Organic" pattern from classic AG2 (LLM-driven AutoPattern group-manager auto-selection) translates to the Coordinator pattern: one generic parameterised handoff tool on the coordinator, plus a Finish tool to terminate. No graph rewrite when specialists are added or removed. See Roadmap for the remaining gaps.

Routing idioms#

Every cookbook page below uses one of three composable routing idioms. Picking the right one is the most common decision when porting a classic pattern.

1. Static routing, graph-centralised. Plain @tool returning a string; the graph holds the routing edge.

1
2
3
4
5
6
7
async def delegate_researcher(reason: str) -> str:
    """Send the work to the research specialist."""
    return f"routing: {reason}"

# in the graph:
Transition(when=ToolCalled("delegate_researcher"),
           then=AgentTarget(researcher.agent_id))

When to use: many routing edges across multiple agents and you want one place (the graph) to read the topology. Used by 03_star.py.

2. Static routing, tool-local. @tool returning a fixed-target Handoff. No graph rule needed — the framework reads Handoff.target from the agent's local ToolResultEvent stream and stamps it onto the packet's routing field directly.

1
2
3
async def delegate_researcher(reason: str) -> Handoff:
    """Send the work to the research specialist."""
    return Handoff(target="researcher", reason=reason)

When to use: simple workflows where the routing target is obvious from the tool's name and you'd rather not maintain a parallel graph rule. Used by 02_hierarchical.py and 04_escalation.py.

3. Dynamic routing. @tool returning a computed-target Handoff. The case that wasn't cleanly expressible before the typed return shape.

1
2
3
async def smart_route(query: str, state: ChannelStateInject) -> Handoff:
    target = pick_best_specialist(query, state.context_vars)
    return Handoff(target=target, reason="routed by load")

When to use: target depends on runtime state, load balancing, or any condition the graph can't express declaratively.

State updates#

State mutations are independent of routing and use the workflow-scoped helpers set_context(channel, key, value) and delete_context(channel, key). They live in autogen.beta.network.workflow_helpers rather than the top-level autogen.beta.network namespace — they only operate on WorkflowState.context_vars and raise RuntimeError on a non-workflow channel, so the import path itself signals the adapter scope.

1
2
3
4
5
6
from autogen.beta.network.workflow_helpers import set_context

async def classify_as_billing(reason: str, channel: ChannelInject) -> str:
    """Classify the request as billing. Sets context_vars['category']."""
    await set_context(channel, "category", "billing")
    return f"classified as billing: {reason}"

Knowing when the workflow is finished#

Every cookbook page below uses the same close-detection primitive in its demo main:

1
2
3
4
5
6
close_env = await intake.wait_for_channel_event(
    channel_id=channel.channel_id,
    predicate=lambda e: e.event_type == EV_CHANNEL_CLOSED,
    timeout=180.0,
)
print(f"closed: reason={close_env.event_data.get('reason')!r}")

AgentClient.wait_for_channel_event blocks on the per-channel inbox until an envelope matching the predicate arrives — here, the EV_CHANNEL_CLOSED envelope every termination route emits. The reason string distinguishes the close (e.g. 'sequence_complete', 'approved', 'resolved', 'fall_through', 'max_iterations', 'ttl_expired').

To print the transcript, dump the WAL once after close — no polling helper needed (this is the pattern used in each cookbook page below).

For the full picture of the five close routes (application close, agent-side tool, adapter sentinel, workflow TerminateTarget, TTL / expectations), see Closing Channels → Watching for Close.

Roadmap#

The gaps surfaced in each pattern page are tracked. None block the patterns; each either has a working workaround or is currently deferred:

  • LLMCondition — declarative routing where the framework asks an LLM whether a transition fires. Unblocks the cleanest version of Context-Aware Routing.
  • NestedChatTarget — first-class sub-flow target. Unblocks Hierarchical and Redundant fully.
  • Parallel dispatch — fan out to multiple speakers within a single workflow turn, gather their replies, advance once all are in. Unblocks parallel Redundant.
  • Auto-merge of tool-return fields into context — the ReplyResult.context_variables analogue. Cuts the boilerplate for Escalation and Triage-with-Tasks.
  • AllOf / AnyOf condition composers — useful sugar across most patterns; today the conjunction is encoded via transition order.

For the broader scope of what the workflow adapter does and doesn't yet ship versus classic, see Migrating from Group Chat.

See Also#