Skip to main content
Status: In Development · Playground demo coming soon. The recipe below is complete and runnable today; only the hosted playground showcase is pending.
A two-agent support system where a fast Tier-1 triage agent handles common issues and hands off to a specialist Tier-2 agent for harder cases. Both agents share memory, so the Tier-2 specialist doesn’t make the customer re-explain anything. The full context, plus T1’s triage summary, is already loaded.

What you’ll build

A multi-agent support cluster where:
  • Tier-1 triages: answers common questions, takes safe actions, escalates clean
  • Tier-2 specializes: picks up with full T1 context already in memory, runs deeper diagnostics
  • Memory is shared across both agents: same user_id, same customer_id
  • Handoffs are explicit: the customer is told they’re being moved, T1 writes a summary, T2 reads it
Est. build time: 60-75 minutes (multi-agent orchestration takes longer to get right).

When to use this recipe

Build this if:
  • Your support has a meaningful skill split (general vs specialist, billing vs technical, etc.)
  • A high % of tickets resolve at T1 and you want to keep T2 capacity for the hard ones
  • Customer continuity across the handoff matters: no “please explain again” moments
  • You can describe the escalation rule clearly (this is the bit that breaks if vague)

Architecture at a glance

Tier-1 to Tier-2 support escalation architecture: customer chat hits Tier-1 agent for triage, easy cases reply directly, hard cases escalate through a shared Synap memory pool to the Tier-2 specialist agent which writes its resolution back to memory
The handoff happens via memory, not state. T2 doesn’t need a routing payload; it pulls everything it needs from Synap on first call.

Stack

Multi-agent orchestration is a great fit for LangGraph (Python) and Mastra (TypeScript) if you want graph-shaped routing with retries and persistence baked in. The recipe below uses OpenAI Agents / Vercel AI SDK to stay consistent with the rest of the Cookbook; port to LangGraph/Mastra once your routing graph grows.

Prerequisites

  • A Synap API key. See Authentication
  • Python: Python 3.11+
  • TypeScript: Node 18+ and Python 3.11+ on the host

Install

Build it

1. Shared scoping

Both agents use the same scopes. That’s the whole trick.
  • customer_id = "<your-product>": single tenant or per-customer org
  • user_id = <ticket requester ID>
  • conversation_id: one per ticket, shared across both agents
conversation_id, user_id, and customer_id must be valid UUIDs. Generate the per-ticket id with str(uuid.uuid4()) (Python) or crypto.randomUUID() (JS), as shown below.

2. The escalation policy

This belongs in your code, not the LLM’s head. T1 calls a tool to escalate; the tool decides what counts.

3. The Tier-1 agent

Fast model, common-issue tools, escalate when out of depth.

4. The Tier-2 agent

Bigger model, deeper tools, picks up with full T1 context already in memory.

5. The router

One small function picks which agent gets the next message based on routing state.

Run & verify

Turn 1 (routes to T1)
Turn 2 (still T1, but escalates)
Turn 3 (now T2)
The customer didn’t repeat anything. T2 read the escalation from memory and dove in.

Customize / extend

  • Three or more tiers → add tools and routing entries. Memory model is unchanged.
  • Human at the end → the final tier can be a human queue. See WhatsApp + Human Handoff for the pattern.
  • LangGraph / Mastra port → if routing grows into a real graph, port to LangGraph (Python) or Mastra (TypeScript).
  • Slack as the channel for T2 → some teams have T2 specialists working out of Slack. Same agent, different I/O. See Patterns → Slack Bot.

Troubleshooting

T2 re-asks the customer to explain
  • Sharpen the T2 system prompt’s “don’t make them re-explain” rule.
  • Confirm the T1 escalation memory is being written before the customer’s next turn (no race).
  • Check synap_search actually fires on the first T2 turn: log tool calls during development.
T1 escalates too eagerly
  • The escalation rule is too vague in the prompt. Tighten the criteria, and consider gating with a tool-side check: require synap_search to have been called first.
Customer pings the same number after T2 resolves; gets T1 again
  • ROUTING is in-process memory. Use Redis with TTL. After a resolution, clear the routing entry so the next ticket starts at T1.
The handoff feels abrupt
  • The bridge message helps. Customize it per escalation reason (“connecting you with billing” vs “connecting you with engineering”).