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
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. You have two specialists you can spawn
in parallel using the Agent tool: `summarizer` and `translator`.
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
brain: *deepseek
system_prompt: |
You summarise the input in ONE sentence. No preamble, no
explanation, just the one-sentence summary.
- id: translator
role: specialist
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 dev deploy multi-agent.yaml
digitorn dev 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 asyncio.gather. The coordinator received
both results back in the same turn and produced the combined reply.
What changed vs step 3
Two 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. The eight modes of
the Agent tool (background, blocking, status, wait, cancel,
reassign, list) are documented in the
agent_spawn module reference.
tools:
modules:
agent_spawn: {} # adds the Agent tool
capabilities:
default_policy: auto
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 has eight modes (wait=true,agent_id=...,cancel=true,reassign=...,list=true, parallelAgent()calls in one turn). See 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. - Shared modules across spawn:
memory,web,filesystem,shellare shared by default; everything else gets a fresh instance per specialist. The sameWORKSPACEdir is shared 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.