This is a hands-on guide to using picobot sub-agents. For the technical reference (depth tracking, registry internals, config), see Sub-agents.
What Are Sub-agents?¶
Sub-agents are background workers. You give one a task, it runs independently using its own context window, and reports back when done. The parent agent continues working while the sub-agent runs.
Think of it like delegating to a colleague: you hand them a brief, they go away and do the work, you carry on with other things, and they come back with results.
When to Use Them¶
Good reasons to spawn a sub-agent:
- Parallel research. "Research Rust, Go, and Zig advantages" — spawn three sub-agents, each researches one language, results come back in parallel.
- Long-running tasks. "Audit all Python files in this project" — let a sub-agent grind through it while you answer other questions.
- Context isolation. The parent is mid-conversation and doesn't want to pollute its context with a deep tangent.
Bad reasons:
- Simple tool calls. Don't spawn a sub-agent to read one file. Just read it.
- Tasks that need conversation context. Sub-agents can't see the parent's chat history.
- Tasks that need coordination. Sub-agents can't talk to each other.
The Three Tools¶
spawn — Start a sub-agent¶
spawn(task="...", label="my-task")
The task string is everything the sub-agent receives. It has no other context. Write it like a brief for someone who just walked in the door.
The label is optional but makes status tracking readable.
wait_for_subagents — Block until done¶
wait_for_subagents(ids=["abc123"], timeout=120)
This is the key tool. It blocks until the specified sub-agents finish, then returns their results. It costs zero tokens while waiting — no API calls are made during the wait. The parent's ReAct loop simply pauses at this tool call.
If you omit ids, it waits for all running sub-agents. The timeout defaults to 120 seconds.
check_subagents — Status check and cancel¶
check_subagents(action="list") # overview of all sub-agents
check_subagents(action="check", id="abc123") # detailed live state for one
check_subagents(action="cancel", id="abc123") # kill a stuck sub-agent
Use list for a quick glance at what's running, check for detailed inspection (iteration progress, tool call log, files written), and cancel to kill a stuck or off-track sub-agent. But don't use this in a polling loop — that burns tokens on every check. Use wait_for_subagents instead.
Writing Good Task Descriptions¶
The most common mistake is writing vague sub-agent tasks. The sub-agent has no context from your conversation — the task string is all it gets.
Bad:
spawn(task="Research Python web frameworks")
What should it research? Where should it put the results? What format? The sub-agent will make something up.
Good:
spawn(task="""Search the web for Flask, FastAPI, and Django.
For each framework, find: year created, async support (yes/no), ORM included (yes/no), latest stable version.
Write a markdown comparison table to ~/workspace/frameworks.md with columns: Framework, Year, Async, ORM, Version.
Include a 2-3 sentence summary of each framework's main use case below the table.""")
The checklist for a good sub-agent task:
- What to do — specific actions, not vague goals
- What to produce — exact file paths and formats
- What success looks like — how to verify it's done correctly
The Typical Pattern¶
Here's how a well-structured sub-agent workflow looks:
1. spawn(task="detailed instructions...", label="my-task")
→ Sub-agent starts in background
2. [Parent does its own work — writes files, runs commands, etc.]
3. wait_for_subagents(ids=["abc123"])
→ Parent blocks (zero cost) until sub-agent finishes
→ Returns the sub-agent's results
4. [Parent reads sub-agent output files, integrates results]
5. [Parent writes final deliverable combining its work + sub-agent results]
The parent should always have something useful to do between spawn and wait. If it has nothing to do, that's a sign the task shouldn't be delegated — just do it directly.
Parallel Sub-agents¶
Spawn multiple sub-agents for parallel work:
spawn(task="Research Rust advantages...", label="rust-research")
spawn(task="Research Go advantages...", label="go-research")
spawn(task="Research Zig advantages...", label="zig-research")
[Do your own work while they run]
wait_for_subagents() # waits for all three
All three run concurrently. wait_for_subagents() with no ids waits for all of them.
What Sub-agents Can and Can't Do¶
Always available (core tools):
- Read, write, edit files and list directories
- Run shell commands
Available on request (pass tools=["web_search", "kb"] etc. to spawn):
- Search the web and fetch URLs (
web_search,web_fetch) - Read and write to the knowledge base (
kb) - Discover and load additional tools (
tool_search) - Use the browser (
browser, requires playwright) - Spawn their own sub-agents (
spawn, up to max depth)
They can't:
- See the parent's conversation history
- Talk to other sub-agents
- Schedule cron jobs
- Access the parent's tool registry (they get their own)
Keeping the tool set lean matters — each extra tool adds schema tokens to every LLM call in the sub-agent's loop. A monitoring task that only needs exec and write_file doesn't need web_search or kb.
What Happens When Things Go Wrong¶
Sub-agent fails: The error is captured in the registry. When the parent calls wait_for_subagents, it sees the failure message. The parent can then handle it — retry, do the work itself, or report the error.
Sub-agent times out: If a sub-agent exceeds the wait_for_subagents timeout, the parent gets a note saying it's still running. The sub-agent continues in the background — it doesn't get killed. The parent can wait again later or move on.
Sub-agent writes garbage: This is usually a task description problem. If the sub-agent produces poor output, improve the task instructions — be more specific about expected format and content.
Depth and Nesting¶
Sub-agents can spawn their own sub-agents, up to a configurable limit (default: 2 levels).
Parent (depth 0)
└── Sub-agent A (depth 1) — can spawn
└── Sub-agent B (depth 2) — cannot spawn (at max depth)
At max depth, the spawn tool isn't available. The sub-agent knows this — its system prompt says "do not spawn further sub-agents."
If you find yourself wanting depth 3+, rethink the task decomposition. Two levels is usually enough.
Monitoring¶
Sub-agents are tracked in ~/.picobot/data/subagents/registry.json. Each entry records:
- Task ID and label
- Status (running, completed, failed)
- Spawn and completion timestamps
- The full result or error message
- Parent session key (for delivery routing)
The dashboard (picobot dashboard) shows running sub-agents in real time.
In interactive mode, you'll see a console notification when a sub-agent finishes:
[Sub-agent "rust-research" completed]
Quick Reference¶
| Tool | Purpose | Token cost |
|---|---|---|
spawn |
Start a background sub-agent | N/A (returns immediately) |
wait_for_subagents |
Block until sub-agents finish | Zero while waiting |
check_subagents(list) |
Overview of all sub-agents | One API round-trip |
check_subagents(check) |
Detailed live state for one | One API round-trip |
check_subagents(cancel) |
Kill a stuck sub-agent | One API round-trip |