A multi-agent teams implementation using LangGraph (Blackboard & Mailbox architecture)
Project description
๐ค LangGraph Agent Teams
A Python library for building agent teams with LangGraph, inspired by the Claude Code Agent Teams feature. One agent acts as the team lead โ coordinating work, assigning tasks, and synthesizing results โ while teammates work independently, each in its own context window, claiming tasks from a shared task list and messaging each other through mailboxes.
Unlike supervisor/subagent patterns where workers only report back to a caller, agent teams coordinate through two shared primitives:
- Task board โ a shared list of work items with dependencies (
blocks/blocked_by). The lead creates tasks; teammates claim and complete them. Claiming uses file locking, so concurrent teammates never grab the same task. - Mailbox โ asynchronous, direct agent-to-agent messaging. Any member can message any other member by name โ teammate-to-teammate debate is a first-class capability, not just teammate-to-lead reporting. Idle notifications flow to the lead automatically, and the team shuts down gracefully via a shutdown request/response protocol.
As in Claude Code, the coordination tools (SendMessage plus the task tools) are always available to every team member โ define members with TeamMemberSpec and the package attaches them automatically.
All coordination state (task board, mailboxes, roster) is persisted as JSON files on disk โ you can cat a mailbox or ls the task list at any time while the team is running, and state survives crashes.
Features
- ๐ Lead / teammate architecture โ a coordinator agent delegates to independent worker agents
- ๐ Shared task board โ dependency-aware tasks with atomic, lock-protected claiming
- ๐ฌ Inter-agent mailboxes โ every member can message every other member (or broadcast), with idle notifications and graceful shutdown
- ๐งฐ Coordination tools always on โ
TeamMemberSpecmembers automatically getSendMessage+ task tools, like Claude Code teammates - โก True parallelism โ teammates run concurrently as async LangGraph nodes
- ๐๏ธ File-system-as-a-bus โ inspect and debug the team's state live on disk
- ๐ ๏ธ LangChain tools included โ
SendMessage,TaskCreate,TaskUpdate,TaskList,TaskGet
When to Use Agent Teams
Agent teams shine when parallel exploration adds real value:
- Research and review โ teammates investigate different aspects of a problem simultaneously
- New modules or features โ each teammate owns a separate piece without stepping on the others
- Competing hypotheses โ teammates test different theories in parallel and converge faster
- Cross-layer work โ frontend, backend, and tests each owned by a different teammate
For sequential tasks or work with many dependencies, a single agent or a supervisor/subagent pattern is more effective โ teams add coordination overhead and use more tokens.
Installation
pip install langgraph-agent-teams
Quickstart
pip install langgraph-agent-teams langchain-openai
export OPENAI_API_KEY=<your_api_key>
import asyncio
from langgraph_agent_teams import TeamMemberSpec, create_agent_team
# Every member automatically gets the coordination tools:
# SendMessage, TaskCreate, TaskUpdate, TaskList, TaskGet
lead = TeamMemberSpec(
name="team-lead",
model="openai:gpt-5.5",
system_prompt="You are a team lead. Delegate work to your teammates.",
)
researcher = TeamMemberSpec(
name="researcher",
model="openai:gpt-5.5",
system_prompt="You are a thorough researcher.",
agent_type="researcher",
)
critic = TeamMemberSpec(
name="critic",
model="openai:gpt-5.5",
system_prompt="You are a devil's advocate who challenges findings.",
agent_type="critic",
)
app = create_agent_team(
lead, [researcher, critic], team_name="research-team"
).compile()
result = asyncio.run(
app.ainvoke(
{
"messages": [
{
"role": "user",
"content": "Evaluate whether we should migrate from REST to GraphQL. "
"Have one teammate research the benefits and another challenge them.",
}
]
}
)
)
print(result["messages"][-1].content)
[!IMPORTANT] Agent teams rely on asyncio concurrency โ the lead and all teammates run at the same time. Always invoke the compiled graph with
await app.ainvoke(...)(orasyncio.run(...)); synchronousinvokeis not supported.
How It Works
โโโโโโโโโโโโโโโ
user โโโโโบ โ team lead โ โโโโโ idle notifications, reports, replies
โโโโโโโโฌโโโโโโโ
TaskCreate / โ SendMessage
TaskUpdate โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ shared task board โ {base_dir}/tasks/{team}/
โ 1.json 2.json .lock ... โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโ
โ claim (locked) โ claim (locked)
โโโโโโโโผโโโโโโโ โโโโโโโโผโโโโโโโ
โ teammate A โ โ teammate B โ each with its own context window
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
โฒ โฒ
โโโโโโ mailboxes โโ {base_dir}/teams/{team}/inboxes/
- A
setupnode resets the team's on-disk state and registers every member in the roster. - The lead node runs the lead agent with a coordination briefing. The lead breaks the request into tasks (
TaskCreate), optionally assigns them (TaskUpdatewithowner), and messages teammates (SendMessage). - Each teammate node runs a poll loop: read unread mail โ claim the next available task โ run its own agent turn โ mark the task complete โ report the result and an idle notification back to the lead.
- Teammate messages are delivered to the lead automatically; the lead responds, reassigns, or creates follow-up work.
- When every task is complete and all teammates are idle, the lead broadcasts a shutdown request; each teammate acknowledges with a shutdown response and exits. The lead's final message synthesizes the team's results.
Task dependencies
Tasks can depend on each other. A pending task with unresolved dependencies cannot be claimed until those dependencies are completed โ blocked tasks unblock automatically:
# Inside the lead's reasoning, via the TaskCreate / TaskUpdate tools:
# TaskCreate(subject="Research API options", description="...") -> task 1
# TaskCreate(subject="Write recommendation", description="...", blocked_by=["1"]) -> task 2
Lead-assigned vs. self-claimed tasks
- Lead assigns: the lead sets
owneron a task withTaskUpdateโ only that teammate can claim it. - Self-claim: unassigned tasks are claimed by idle teammates in ID order.
Teammate-to-teammate communication
Every TeamMemberSpec member can message any other member by name with SendMessage โ the classic "teammates debate each other" pattern from the Claude Code docs works out of the box:
result = await app.ainvoke({"messages": [{
"role": "user",
"content": "Investigate why the app disconnects after one message. "
"Have teammates pursue different hypotheses and message each other "
"to try to disprove each other's theories, like a scientific debate.",
}]})
Communication is agent-driven with a harness fallback: if a teammate finishes a turn without calling SendMessage, its final reply is forwarded automatically โ task reports go to the lead, message replies go back to the sender โ so no work is silently lost even with models that under-use tools. Fallback replies are marked internally and never trigger another automatic reply, so two teammates can't ping-pong forever.
Using prebuilt agents (advanced)
You can also pass prebuilt agents (e.g. from langchain.agents.create_agent) instead of specs โ useful when you need middleware or custom agent construction. In that case, attach the coordination tools yourself so the agent can communicate:
from langchain.agents import create_agent
from langgraph_agent_teams import create_team_tools
coder = create_agent(
model="openai:gpt-5.5",
tools=[*my_tools, *create_team_tools("coder", team_name="research-team")],
name="coder",
)
Prebuilt agents without team tools still work โ the harness claims tasks, completes them, and forwards their replies for them.
Inspecting a Running Team
Team state lives under ~/.langgraph-agent-teams by default (override with base_dir):
~/.langgraph-agent-teams/
โโโ teams/{team_name}/
โ โโโ config.json # Roster: members, roles, live status
โ โโโ inboxes/
โ โโโ team-lead.json # The lead's mailbox
โ โโโ researcher.json # A teammate's mailbox
โโโ tasks/{team_name}/
โโโ .lock # Claim lock
โโโ .highwatermark # Highest task ID ever created
โโโ 1.json # Task #1
# Watch the task board while the team works
watch -n1 'cat ~/.langgraph-agent-teams/tasks/research-team/*.json | jq -r "\(.id) [\(.status)] \(.owner // \"unassigned\") โ \(.subject)"'
You can also use the store classes directly:
from langgraph_agent_teams import get_team_resources
resources = get_team_resources("research-team")
for task in resources.task_board().list_tasks():
print(task.id, task.status, task.owner, task.subject)
for message in resources.mailbox().read_all("team-lead"):
print(message.from_agent, "->", message.summary or message.text[:80])
API Reference
TeamMemberSpec(name, model, system_prompt=None, tools=[], agent_type=None)
Declarative definition of one team member. The package builds the agent with langchain.agents.create_agent and automatically attaches the five coordination tools bound to the member's identity. tools adds extra domain tools on top; agent_type is a role label shown in the roster and briefings.
create_agent_team(lead, teammates, team_name, base_dir=None, *, poll_interval=0.5, shutdown_timeout=60.0, max_agent_turns=100)
Builds the team StateGraph. The lead and every teammate run as concurrent async nodes; compile and ainvoke it like any LangGraph graph.
| Parameter | Description |
|---|---|
lead |
TeamMemberSpec (recommended) or a prebuilt agent equipped with create_team_tools. |
teammates |
TeamMemberSpecs (recommended) or prebuilt agents, each with a unique name. |
team_name |
Unique team name; also names the on-disk state directories. |
base_dir |
Root directory for team state (default ~/.langgraph-agent-teams). |
poll_interval |
Seconds between mailbox/task-board polls. |
shutdown_timeout |
Max seconds the lead waits for shutdown acknowledgements. |
max_agent_turns |
Safety cap on agent invocations per member per run. |
create_team_tools(agent_name, team_name, base_dir=None)
Returns the five coordination tools bound to one agent's identity:
| Tool | Purpose |
|---|---|
SendMessage |
DM another agent by name, or "*" to broadcast |
TaskCreate |
Add a task to the shared board (optionally blocked_by) |
TaskUpdate |
Change status/owner/description, add dependency edges |
TaskList |
List every task with status, owner, and blockers |
TaskGet |
Fetch one task's full details |
Store classes
SwarmTaskBoard, AgentMailbox, and TeamRoster are exported for direct use โ every operation is atomic and protected by portalocker file locks, so they're safe across threads, asyncio tasks, and even separate processes.
Comparing Agent Teams with Other Multi-Agent Patterns
| Pattern | Coordination | Communication | Best for |
|---|---|---|---|
| Agent Teams | Shared task list + lead | Teammates message each other directly | Parallel work requiring discussion and collaboration |
| Group Chat | Centralized selector | Broadcast (shared history) | Moderated turn-taking discussions |
| Swarm | Decentralized handoffs | Control transfer via tools | Fluid handoffs, direct user interaction |
| Subagents | Main agent delegates | Results report back to caller only | Focused tasks where only the result matters |
Use subagents when you need quick, focused workers that report back. Use agent teams when workers benefit from a shared work queue, independent context windows, and inter-agent communication.
Best Practices
- Give teammates enough context โ teammates don't see the lead's conversation history, so the lead's task descriptions must be self-contained (the built-in briefing tells it so)
- Size tasks appropriately โ self-contained units with a clear deliverable; 5โ6 tasks per teammate keeps everyone productive
- Start with 3โ5 teammates โ token costs scale linearly with team size, and coordination overhead grows with it
- Avoid file conflicts โ if teammates edit files, break work so each owns a different set
- Use dependencies for ordering โ
blocked_byedges let the board sequence work without the lead micromanaging
Examples
See the examples/ directory:
software_teamโ a lead coordinating researcher/coder/reviewer teammates
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
MIT License โ see LICENSE for details.
Acknowledgments
- Inspired by the Claude Code Agent Teams feature and its blackboard/mailbox architecture
- Built on LangGraph and LangChain agents
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langgraph_agent_teams-0.1.0.tar.gz.
File metadata
- Download URL: langgraph_agent_teams-0.1.0.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9cb9913e8ff2b7f15373c29dc0e220792cd09140f231097efab7d61330266a6
|
|
| MD5 |
a7034014c1429c4748cf8ddea7848ee7
|
|
| BLAKE2b-256 |
af02bf52f3fe1d3e3faf1e7ce1b83b450a9259c443fbe363e7d94975726592eb
|
File details
Details for the file langgraph_agent_teams-0.1.0-py3-none-any.whl.
File metadata
- Download URL: langgraph_agent_teams-0.1.0-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5c4a5d72af66599f1cdc9b88c88938dfd33c8a6096b38c67605db62ec5b71cf
|
|
| MD5 |
baf2475aab510c5fcfad5cc7a4e6ba26
|
|
| BLAKE2b-256 |
2b30c298902bb3d01ee93cfef0a8387be9e8a3d1432072b7ced74ac7e53c9e53
|