windlass.agent.supervisor¶
supervisor
¶
Multi-agent supervision.
One agent with thirty tools reasons badly: the tool list alone crowds the context, and the model has to hold every domain in its head at once. The fix is specialisation — a researcher, a coder, a writer, each with a handful of relevant tools — plus a supervisor that routes work between them.
:class:Supervisor implements the standard pattern: a coordinating model reads
the request and each worker's description, picks one, runs it, reads the result,
and either delegates again or answers.
Example
from windlass import Windlass from windlass.core.types import ToolCall researcher = Windlass.agent().llm("fake", responses=["Paris is in France."]) writer_llm = Windlass.llm("fake", responses=["Paris, France."]) writer = Windlass.agent().llm(writer_llm) boss = Supervisor( ... llm=Windlass.llm("fake", responses=["", "Paris, France."], ... tool_calls=[[ToolCall(name="researcher", ... arguments={"task": "where is Paris"})], []]), ... agents={"researcher": researcher, "writer": writer}, ... ) boss.run("Where is Paris?").output 'Paris, France.'
AgentTool
¶
Bases: Tool
Exposes an agent as a tool the supervisor can call.
This is what makes delegation work with ordinary tool calling: the supervisor does not need a special routing mechanism, because a worker is a tool from its point of view.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
Any
|
The worker agent, or anything with an |
required |
name
|
str
|
Tool name the supervisor sees. Should say what the worker is for. |
required |
description
|
str
|
What this specialist is good at. The supervisor routes on this text, so it deserves the same care as a prompt. |
''
|
**config
|
Any
|
Forwarded to :class: |
{}
|
Example
from windlass import Windlass worker = Windlass.agent().llm("fake", responses=["done"]) t = AgentTool(worker, name="helper", description="Does small jobs.") t.run(task="tidy up").content 'done'
Source code in src\windlass\agent\supervisor.py
acall
async
¶
Run the worker agent on the delegated task.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Must contain |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The worker's output text. |
Raises:
| Type | Description |
|---|---|
AgentError
|
When the worker fails. |
Source code in src\windlass\agent\supervisor.py
Supervisor
¶
Supervisor(
*,
llm: LLM,
agents: dict[str, Any] | None = None,
descriptions: dict[str, str] | None = None,
system_prompt: str | None = None,
max_iterations: int = 10,
tools: list[Any] | None = None,
tracer: Tracer | None = None,
**kwargs: Any
)
Bases: AgentRuntime
An agent that delegates to specialist agents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
llm
|
LLM
|
The coordinating model. It only routes and summarises, so a mid-sized model is usually the right call. |
required |
agents
|
dict[str, Any] | None
|
Workers, keyed by the name the supervisor will use. Values may
be :class: |
None
|
descriptions
|
dict[str, str] | None
|
What each worker is for, keyed by the same names. Falls back to the worker's own description. |
None
|
system_prompt
|
str | None
|
Override for the routing instructions. |
None
|
max_iterations
|
int
|
Ceiling on routing rounds. |
10
|
tools
|
list[Any] | None
|
Tools the supervisor may call itself, alongside its workers. |
None
|
tracer
|
Tracer | None
|
Observability backend. |
None
|
**kwargs
|
Any
|
Forwarded to :class: |
{}
|
Raises:
| Type | Description |
|---|---|
AgentError
|
When no workers are supplied. |
Performance
Workers requested in the same turn run concurrently, so a supervisor that fans out to three specialists waits for the slowest, not the sum.
Source code in src\windlass\agent\supervisor.py
add_agent
¶
Add a specialist after construction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name the supervisor will use. |
required |
agent
|
Any
|
The worker. |
required |
description
|
str
|
What it is good at. |
''
|
Returns:
| Type | Description |
|---|---|
Supervisor
|
|
Source code in src\windlass\agent\supervisor.py
abroadcast
async
¶
Run every specialist on the same task, concurrently.
Useful for ensembling — ask three specialists the same question and compare — and for fan-out work where every worker has something to contribute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
str
|
The task to send to every worker. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, AgentResponse]
|
A |
dict[str, AgentResponse]
|
reported as an error response rather than raising, so one failure |
dict[str, AgentResponse]
|
does not lose the others' work. |
Example
import asyncio from windlass import Windlass boss = Supervisor( ... llm=Windlass.llm("fake"), ... agents={"a": Windlass.agent().llm("fake", responses=["A"])}, ... ) asyncio.run(boss.abroadcast("go"))["a"].output 'A'
Source code in src\windlass\agent\supervisor.py
broadcast
¶
apipeline
async
¶
Chain specialists in sequence, each seeing the previous output.
The other multi-agent shape: research → draft → edit, where each stage genuinely depends on the last.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
task
|
str
|
The initial task. |
required |
order
|
list[str]
|
Worker names, in execution order. |
required |
Returns:
| Type | Description |
|---|---|
AgentResponse
|
The final worker's response, with every stage recorded in |
AgentResponse
|
attr: |
Raises:
| Type | Description |
|---|---|
AgentError
|
When a named worker does not exist. |
Example
import asyncio from windlass import Windlass boss = Supervisor( ... llm=Windlass.llm("fake"), ... agents={ ... "draft": Windlass.agent().llm("fake", responses=["a draft"]), ... "edit": Windlass.agent().llm("fake", responses=["an edit"]), ... }, ... ) asyncio.run(boss.apipeline("write", ["draft", "edit"])).output 'an edit'
Source code in src\windlass\agent\supervisor.py
pipeline
¶
describe
¶
Return a JSON-safe description including the roster.