Advanced 1 - Sub-agent isolation
In tutorial 4 you spawned specialists in parallel. Each specialist got the full module set the parent app declares. That's fine for trusted teams of agents but wrong when you want a coordinator who can do everything and a worker who can only read.
This tutorial uses per-agent module restriction to give each
specialist a different slice of the toolbox. The reader sees
[read, glob, grep] only; the writer sees the full filesystem.
The coordinator sees both, plus the Agent tool to dispatch.
The pattern
Each entry in agents[].modules can be one of two shapes:
modules:
- filesystem # full module access
- {filesystem: [read, glob, grep]} # ONLY these three actions
- {memory: [remember]} # one action only
The simple form (a string) grants the whole module. The dict form restricts to a named action subset. The coordinator's modules act as the superset - a specialist cannot see a module the coordinator doesn't have.
The YAML
Save this as isolation-bot.yaml. Three agents share the same
brain (DeepSeek) and dispatch through agent_spawn.
app:
app_id: isolation-bot
name: Isolation Bot
version: "1.0"
runtime:
mode: conversation
workdir_mode: auto
max_turns: 8
timeout: 120
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: 400
system_prompt: |
You are the coordinator. You have two specialists:
- `reader` (read-only file access)
- `writer` (write access)
Use Agent(prompt="...", specialist=<id>, wait=true) to dispatch.
For tasks like "summarise this file", call reader. For tasks
like "write a new file", call writer. Be concise.
- id: reader
role: specialist
brain: *deepseek
modules:
- {filesystem: [read, glob, grep]} # read-only slice
- memory # full memory module
system_prompt: |
You read files. You CANNOT write or edit anything. If asked
to write, reply: "I cannot write files; I only have read access."
- id: writer
role: specialist
brain: *deepseek
modules:
- filesystem # full filesystem access
- memory
system_prompt: |
You write files. Use Write to create them.
tools:
modules:
filesystem: {}
memory: {}
agent_spawn: {}
capabilities:
default_policy: auto
The &deepseek / *deepseek YAML anchor is just to avoid copying
the brain block three times. The interesting part is the
agents[].modules declaration.
What the system actually does
When the coordinator calls Agent(prompt="...", specialist="reader", wait=true), the daemon does three things:
- Look up reader's
moduleslist. It computes the action filter{"filesystem": {"read", "glob", "grep"}, "memory": ALL}. - Build the reader's tool index with only the matching actions.
The LLM running as reader sees
Read,Glob,Grep, plus all memory actions.Write,Edit,Deleteare not in the schema - the LLM does not know they exist. - Run the agent loop. Even if the reader's LLM hallucinates a "Write" call, the dispatcher rejects it at gate 1 because the action is not in the agent's profile.
This is not just a system-prompt instruction. It's a hard schema restriction.