4. Multi-agent team
In step 3 a single agent did everything: think, call tools, reply.
In this step you split the work across specialists that the
coordinator delegates to. The coordinator is the agent the user
talks to; each specialist is a fresh agent loop spawned on demand
through the Agent tool.
You add one module (agent_spawn) and declare more than one entry
under agents:. The coordinator picks who to dispatch to and runs
them in parallel.
Prerequisites
Same as the previous steps (running daemon, authenticated user,
DeepSeek credential deepseek_main provisioned).
The YAML
Save this as multi-agent.yaml. Three agents share the same brain
config (a per-user DeepSeek credential), but each has its own role
and system prompt.
app:
app_id: multi-agent
name: Multi-Agent Team
version: "1.0"
runtime:
mode: conversation
workdir_mode: auto
max_turns: 8
timeout: 180
agents:
- id: coordinator
role: coordinator
delegate_to:
- summarizer # simple form: just the id
- id: translator # rich form: id + instructions
instructions: "Delegate here whenever the user's message needs a French translation."
brain: &deepseek
provider: deepseek
model: deepseek-chat
backend: openai_compat
credential:
ref: deepseek_main
scope: per_user
provider: deepseek
config:
api_key: "{{env.DEEPSEEK_API_KEY}}"
base_url: https://api.deepseek.com/v1
temperature: 0
max_tokens: 512
system_prompt: |
You are a coordinator. For any user question that needs both a
summary and a French translation, spawn both specialists IN
PARALLEL with Agent(prompt="...", specialist="summarizer",
wait=true) and Agent(prompt="...", specialist="translator",
wait=true). Then combine their results into a single answer.
Keep the final answer short.
- id: summarizer
role: specialist
specialty: "Summarises the input in ONE sentence"
brain: *deepseek
system_prompt: |
You summarise the input in ONE sentence. No preamble, no
explanation, just the one-sentence summary.
- id: translator
role: specialist
specialty: "Translates the input to French"
brain: *deepseek
system_prompt: |
You translate the user's input to French. Output only the
translation, no preamble.
tools:
modules:
agent_spawn: {}
capabilities:
default_policy: auto
ui:
greeting: "Coordinator + 2 specialists. Try: 'Summarise and translate this paragraph...'"
The YAML anchor (&deepseek / *deepseek) reuses the same brain
across the three agents to avoid copy-paste. Anything is allowed
here: a different model per specialist, a cheaper one for the
coordinator, etc. Read more on agent shapes in the
Agents reference.
Deploy and chat
digitorn install multi-agent.yaml
digitorn chat multi-agent
Live transcript
Sample transcript. The user paragraph asks the coordinator to summarise and translate at once.
> Summarise and translate this paragraph: Solar panels in Europe
produced more electricity than coal in 2023, marking the first
time renewables outpaced any single fossil fuel across the
continent.
Here are the results:
**Summary:** Solar panels in Europe generated more electricity
than coal in 2023, a historic first where renewables exceeded a
single fossil fuel continent-wide.
**French translation:** Les panneaux solaires en Europe ont
produit plus d'électricité que le charbon en 2023, marquant la
première fois que les énergies renouvelables surpassaient un
seul combustible fossile sur le continent.
Behind the scene the coordinator fired exactly two tool calls
(tool_calls_count: 2 on the result event). The live event
stream shows both specialists spawned in parallel:
[agent_event] specialist=summarizer status=spawned
[agent_event] specialist=summarizer status=running duration=2.1s
[agent_event] specialist=summarizer status=completed
preview="Solar panels in Europe generated more
electricity than coal in 2023, a historic
first where renewables exceeded a single
fossil fuel continent-wide."
[agent_event] specialist=translator status=spawned
[agent_event] specialist=translator status=running duration=2.1s
[agent_event] specialist=translator status=completed
preview="Les panneaux solaires en Europe ont produit
plus d'électricité que le charbon en 2023,
marquant la première fois que les énergies
renouvelables surpassaient un seul
combustible fossile sur le continent."
Both specialists finished in roughly two seconds because they ran concurrently through concurrent execution. The coordinator received both results back in the same turn and produced the combined reply.
What changed vs step 3
Three things. First, agents: now lists three entries with distinct
role and system_prompt blocks. The coordinator's role is
coordinator; the workers' role is specialist. The coordinator
is the entry_agent by default - it's the one the user talks to.
Second, tools.modules.agent_spawn: {} is loaded. That module
exposes the Agent tool to the coordinator. Its modes - spawn one
specialist or a batch, wait for one or several previously-spawned
runs, poll status, cancel (with or without its sub-tree), list -
are documented in the
agent_spawn module reference.
tools:
modules:
agent_spawn: {} # adds the Agent tool
capabilities:
default_policy: auto
Third, the coordinator declares delegate_to: [summarizer, translator]
(mixing the simple id form and the {id, instructions} form above).
This isn't optional decoration - it does two real things at once:
- Prompt: because
role: coordinatoranddelegate_tois non-empty, the daemon automatically appends an "Available specialists" block to the coordinator's own prompt, one line per entry (id+ itsspecialty:, plus itsinstructions:when set) - you don't have to hand-write that roster intosystem_promptyourself. - Authorization: at runtime, a coordinator can only spawn an id
that's actually in its own
delegate_to. Leavingsummarizerout of the list wouldn't just skip a prompt hint - everyAgent(specialist="summarizer", ...)call would be rejected outright, even thoughagent_spawnis granted andsummarizeris a real agent in the file. See Multi-agent reference for the full mechanics.
When to use this pattern
A flat single-agent setup is enough when the work is sequential. Multi-agent shines when you can fan out:
- Parallel reads - search across N data sources, merge results
- Specialised tools - one agent owns the database tools, another owns the file system, another owns web search
- Isolation - spawn a writer agent with
default_policy: blockon filesystem so it can't touch disk while the coordinator can
The coordinator stays in charge of the conversation. Specialists return raw output and exit. They don't carry their own user session - their context dies with the spawn.
Going further
- The
Agenttool's full surface (wait=true,run_id=.../run_ids=[...],cancel=true/cancel_tree=true,list=true, batch spawn viaagents: [...], parallelAgent()calls in one turn) is in the agent_spawn reference. - Granular per-specialist tool restriction:
agents[].modules: [{filesystem: [read]}]. The specialist sees only the listed actions, not the coordinator's full toolbox. See Multi-agent. - Modules are shared across the whole app, not just some of
them: a module is instantiated once per app, so the
coordinator and every specialist talk to the same
memory,filesystem, and any other declared module instance - there's no per-specialist fresh copy. The same workdir is shared too, so files written by one specialist are visible to the next.
Next: explore the Agent tool reference or pick a module from the index for your next experiment.