---
title: The supervisor-worker pattern | Letta Docs
description: 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

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

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

The pattern uses three key components:

1. **Shared archival memory**: Workers contribute findings to a large searchable knowledge base.
2. **Shared memory blocks**: Memory blocks make the small coordination state visible to all agents (guidelines, priorities, status).
3. **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_all` and `match_some`)
- Receives responses from targeted workers
- Synthesizes results from worker contributions

The supervisor uses `match_all` and `match_some` parameters to target specific workers. For example, to send a task only to the technical worker: `match_all=["worker"]` and `match_some=["technical"]`. This enables intelligent task assignment based on worker specialization.

## Implementation

### Step 1: Define your routing convention

For this pattern, the supervisor routes work to workers using tag criteria. Define two filtering modes and use them consistently in prompts and personas:

- `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

Create the shared memory block and archive:

```
# Shared memory block for guidelines
guidelines_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 findings
findings_archive = client.archives.create(
    name="research_findings",
    description="Collaborative archive for all research findings"
)
```

### Step 3: Create the supervisor

Create the supervisor 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 tag-based routing:
- 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"]
)


client.agents.archives.attach(findings_archive.id, agent_id=supervisor.id)
```

### Step 4: Create worker agents

Create specialized workers, each with their own expertise:

```
# Technical research worker
tech_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 worker
market_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_findings` archive
- All share the `research_guidelines` memory block

### Step 5: Supervisor routes tasks intelligently

The supervisor autonomously analyzes tasks and routes them to appropriate workers:

```
# Technical task - supervisor routes to technical worker
response = 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 worker
response = 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 workers
response = 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

```
from letta_client import Letta
import os


client = Letta(api_key=os.getenv("LETTA_API_KEY"))


# Create shared resources
guidelines = client.blocks.create(
    label="guidelines",
    value="Focus on quality sources."
)
archive = client.archives.create(name="findings")


# Create supervisor with broadcast tool
supervisor = 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 tag-based routing:
- For technical questions: match_all=["worker"], match_some=["technical"]


I delegate to workers rather than answering directly."""
    }],
    block_ids=[guidelines.id],
    tags=["supervisor"]
)
client.agents.archives.attach(archive.id, agent_id=supervisor.id)


# Create technical worker
tech_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 worker
response = 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]"
    }]
)


# Cleanup
client.agents.delete(tech_worker.id)
client.agents.delete(supervisor.id)
client.archives.delete(archive.id)
client.blocks.delete(guidelines.id)
```

## 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
