The supervisor-worker pattern
Coordinate multiple specialized agents under a single supervisor agent with shared memory and archival storage.
Coordinate multiple specialized agents under a single supervisor. Workers maintain memory across sessions and share knowledge through common resources. The supervisor autonomously routes tasks to workers based on their expertise and tags.
When to use this pattern
Section titled “When to use this pattern”The supervisor-worker pattern works well when you need to:
- Distribute work across specialized agents (technical research, market analysis, content creation)
- Keep workers coordinated with shared guidelines or priorities
- Scale task execution with parallel worker agents
- Maintain shared context and knowledge across a team
Example use case
Section titled “Example use case”A research supervisor coordinates specialized workers (technical, market, and academic) to gather comprehensive information on a topic. The supervisor intelligently routes tasks to specific workers based on their expertise - sending technical questions to the technical worker, market analysis to the market worker. Each worker maintains context across research sessions, stores findings in a shared archive, and follows guidelines set by the supervisor.
How it works
Section titled “How it works”The pattern uses three key components:
- Shared archival memory: Workers contribute findings to a large searchable knowledge base.
- Shared memory blocks: Memory blocks make the small coordination state visible to all agents (guidelines, priorities, status).
- Agent tagging: Workers are tagged (for example,
["worker", "research", "technical"]) for organization and discovery by the supervisor.
The supervisor autonomously:
- Analyzes tasks and decides which workers to use
- Routes tasks to specific workers using tag-based filtering (
match_allandmatch_some) - Receives responses from targeted workers
- Synthesizes results from worker contributions
Implementation
Section titled “Implementation”Step 1: Get the supervisor coordination tool
Section titled “Step 1: Get the supervisor coordination tool”The supervisor needs the send_message_to_agents_matching_tags tool to coordinate workers:
from letta_client import Lettaimport os
client = Letta(api_key=os.getenv("LETTA_API_KEY"))
# Get the broadcast coordination tooltools = client.tools.list(name="send_message_to_agents_matching_tags")broadcast_tool = tools.items[0]This tool allows the supervisor to send messages to agents matching specific tag criteria. It supports two filtering modes:
match_all: Tags that agents MUST have (required tags)match_some: Tags where agents need at least one (optional specialization tags)
Step 2: Create shared resources
Section titled “Step 2: Create shared resources”Create the shared memory block and archive:
# Shared memory block for guidelinesguidelines_block = client.blocks.create( label="research_guidelines", description="Research guidelines and priorities set by supervisor", value="Focus on accurate, well-sourced information from 2024-2025.")
# Shared archive for research findingsfindings_archive = client.archives.create( name="research_findings", description="Collaborative archive for all research findings")Step 3: Create the supervisor
Section titled “Step 3: Create the supervisor”Create the supervisor with the broadcast tool and shared resources:
supervisor = client.agents.create( name="research_supervisor", model="anthropic/claude-sonnet-4-5-20250929", memory_blocks=[{ "label": "persona", "value": """I am a research supervisor that coordinates specialized workers.
Available workers and their tags:- technical worker (tags: worker, research, technical) - Reviews code, APIs, implementation details- market worker (tags: worker, research, market) - Analyzes market trends, competitors, industry
I route tasks to appropriate workers using send_message_to_agents_matching_tags:- For technical questions: match_all=["worker"], match_some=["technical"]- For market questions: match_all=["worker"], match_some=["market"]- For broad questions: match_all=["worker", "research"]
I delegate to workers rather than answering directly.""" }], block_ids=[guidelines_block.id], tags=["supervisor", "research"], tool_ids=[broadcast_tool.id])
client.agents.archives.attach(findings_archive.id, agent_id=supervisor.id)Step 4: Create worker agents
Section titled “Step 4: Create worker agents”Create specialized workers, each with their own expertise:
# Technical research workertech_worker = client.agents.create( name="technical_worker", model="anthropic/claude-sonnet-4-5-20250929", memory_blocks=[{ "label": "persona", "value": "I analyze technical documentation, APIs, and implementation details." }], block_ids=[guidelines_block.id], tags=["worker", "research", "technical"], tools=["archival_memory_insert", "archival_memory_search"])client.agents.archives.attach(findings_archive.id, agent_id=tech_worker.id)
# Market research workermarket_worker = client.agents.create( name="market_worker", model="anthropic/claude-sonnet-4-5-20250929", memory_blocks=[{ "label": "persona", "value": "I gather market trends, competitor information, and industry insights." }], block_ids=[guidelines_block.id], tags=["worker", "research", "market"], tools=["archival_memory_insert", "archival_memory_search"])client.agents.archives.attach(findings_archive.id, agent_id=market_worker.id)Key features:
- Workers automatically persist memory across sessions
- Workers are tagged with
["worker", "research"]plus their specialty - All share the
research_findingsarchive - All share the
research_guidelinesmemory block
Step 5: Supervisor routes tasks intelligently
Section titled “Step 5: Supervisor routes tasks intelligently”The supervisor autonomously analyzes tasks and routes them to appropriate workers:
# Technical task - supervisor routes to technical workerresponse = client.agents.messages.create( agent_id=supervisor.id, messages=[{ "role": "user", "content": "Analyze the API documentation for Letta's agent creation endpoint." }])# Supervisor autonomously uses: match_all=["worker"], match_some=["technical"]
# Market task - supervisor routes to market workerresponse = client.agents.messages.create( agent_id=supervisor.id, messages=[{ "role": "user", "content": "Research current trends in the AI agent framework market." }])# Supervisor autonomously uses: match_all=["worker"], match_some=["market"]
# Comprehensive task - supervisor broadcasts to all workersresponse = client.agents.messages.create( agent_id=supervisor.id, messages=[{ "role": "user", "content": "Conduct a comprehensive analysis of Letta from all perspectives." }])# Supervisor autonomously uses: match_all=["worker", "research"]Minimal example
Section titled “Minimal example”from letta_client import Lettaimport os
client = Letta(api_key=os.getenv("LETTA_API_KEY"))
# Get broadcast tooltools = client.tools.list(name="send_message_to_agents_matching_tags")broadcast_tool = tools.items[0]
# Create shared resourcesguidelines = client.blocks.create( label="guidelines", value="Focus on quality sources.")archive = client.archives.create(name="findings")
# Create supervisor with broadcast toolsupervisor = client.agents.create( model="anthropic/claude-sonnet-4-5-20250929", memory_blocks=[{ "label": "persona", "value": """I am a research supervisor that coordinates specialized workers.
Available workers and their tags:- technical worker (tags: worker, technical) - Reviews code, APIs
I route tasks using send_message_to_agents_matching_tags:- For technical questions: match_all=["worker"], match_some=["technical"]
I delegate to workers rather than answering directly.""" }], block_ids=[guidelines.id], tags=["supervisor"], tool_ids=[broadcast_tool.id])client.agents.archives.attach(archive.id, agent_id=supervisor.id)
# Create technical workertech_worker = client.agents.create( model="anthropic/claude-sonnet-4-5-20250929", memory_blocks=[{ "label": "persona", "value": "I am a technical researcher." }], block_ids=[guidelines.id], tags=["worker", "technical"], tools=["archival_memory_insert", "archival_memory_search"])client.agents.archives.attach(archive.id, agent_id=tech_worker.id)
# Supervisor autonomously routes to technical workerresponse = client.agents.messages.create( agent_id=supervisor.id, messages=[{ "role": "user", "content": "Review this Python code for potential issues:\n\ndef process_data(items):\n return [item * 2 for item in items]" }])
# Cleanupclient.agents.delete(tech_worker.id)client.agents.delete(supervisor.id)client.archives.delete(archive.id)client.blocks.delete(guidelines.id)Best practices
Section titled “Best practices”- Use descriptive tags: Tag workers by role, specialty, and team for flexible coordination
- Keep shared memory focused: Use shared memory blocks for guidelines and coordination, not large data
- Use archival memory for data: Store research findings, data, and historical context in archives
- Design clear personas: Give each worker a specific specialty and role definition
- Monitor worker state: Check worker memory and archive contributions to track progress