ollie/doc/resources/multi-agent.md

197 lines
6.5 KiB
Markdown

# Multi-Agent Coordination
ollie exposes agents as filesystem objects via the 9P protocol. This means
multi-agent coordination requires no framework, SDK, or message bus — it is
ordinary shell scripting against a mounted filesystem.
## The Namespace
Every session lives under `/session/{name}/` where `{name}` is the session's
**mutable human-readable name** (not the immutable UUID — see `id` and `name`
files). Session directories are keyed by Name; renaming a session via writing
to its `name` file moves the entire directory subtree.
| Path | Purpose |
|------|---------|
| `/session/new` | Create an empty session (write `name=...`, read back name) |
| `/session/{name}/agent/new` | Create an agent within a session (write `cwd=... backend=...`) |
| `/session/{name}/env` | Session environment variables |
| `/session/{name}/id` | Immutable session UUID |
| `/session/{name}/name` | Mutable session name (write to rename) |
| `/session/{name}/agent/{aname}/prompt` | Submit a prompt |
| `/session/{name}/agent/{aname}/chat` | Streaming chat (filtered text) |
| `/session/{name}/agent/{aname}/plan` | Agent-scoped markdown checklist |
| `/session/{name}/agent/{aname}/cfg` | Agent config KV: backend, model, agent, cwd, params |
| `/session/{name}/agent/{aname}/statewait` | **Blocking read** — returns when state changes |
| `/session/{name}/agent/{aname}/ctl` | Control commands: `stop`, `kill`, `compact`, `rn <name>` |
| `/session/{name}/agent/{aname}/id` | Immutable agent ID (uname) |
| `/session/{name}/agent/{aname}/name` | Mutable display name (write to rename) |
Global request-response files provide stateless operations without sessions:
| File | Purpose |
|------|---------|
| `/generate` | Write prompt, read response (one-shot generation) |
Global namespaces are shared across all sessions:
| Path | Purpose |
|------|---------|
| `/session/idx` | Session index (id, state, cwd, backend, model) |
| `/session/{name}/agent/{aname}/plan` | Agent checklist |
| `/session/{name}/agent/{aname}/chat` | Streaming filtered text |
## Spawning Agents
From within an agent session, use `subagent_spawn`:
```sh
subagent_spawn [-name NAME] [-agent AGENT] [-backend BACKEND] [-model MODEL] [-cwd DIR] <prompt>
```
This creates a child session + agent atomically, writes the prompt, and returns
the session path (e.g. `session/my-session`). Child sessions carry the parent
session ID in their name, so the spawning tree is recoverable from session IDs alone.
### Explicit Two-Step Creation
Session creation can be split into two phases for advanced orchestration
scenarios:
1. **Create empty session**: `echo "name=my-session" | ollie-9p rdwr session/new`
2. **Add agent**: `echo "cwd=/home/user agent=default backend=openai model=gpt-4o" | ollie-9p rdwr session/my-session/agent/new`
Empty sessions (`Core=nil`) are valid — they appear in `session/idx`, have
no running agent, and can be killed or renamed. This enables pre-provisioning
session directories before the agent environment is ready.
## Coordination Patterns
### Hub-and-Spoke (Conductor)
```mermaid
flowchart TB
ROOT["Root Agent\n(reasoning)"]
W1["Worker A\n(cheap model)"]
W2["Worker B\n(cheap model)"]
ROOT -- "subagent_spawn" --> W1
ROOT -- "subagent_spawn" --> W2
W1 -- "statewait → read chat" --> ROOT
W2 -- "statewait → read chat" --> ROOT
```
A root agent reasons about a task and delegates subtasks to workers. Workers
report back via their log; the conductor reads `/session/{name}/agent/{aname}/log` after
`statewait` unblocks.
```sh
# Conductor spawns two workers
worker_a=$(subagent_spawn -model haiku "analyse /src/a.go")
worker_b=$(subagent_spawn -model haiku "analyse /src/b.go")
# Wait for both
until [ "$(cat $worker_a/state)" = "idle" ]; do cat $worker_a/statewait; done
until [ "$(cat $worker_b/state)" = "idle" ]; do cat $worker_b/statewait; done
# Collect results
cat $worker_a/chat $worker_b/chat
```
The conductor and workers can run on different models. Mechanical analysis goes
to cheaper models; reasoning stays on the expensive one.
### Pipeline
```mermaid
flowchart LR
A["Step 1\nExtract TODOs"]
B["Step 2\nPrioritise"]
A -->|"/tmp/todos.txt"| B
```
Agent A produces output; agent B consumes it. Composition via `/tmp/` or direct
path passing.
```sh
step1=$(subagent_spawn "extract all TODOs from /src/ and write to /tmp/todos.txt")
until [ "$(cat $step1/state)" = "idle" ]; do cat $step1/statewait; done
subagent_spawn "prioritise the TODOs in /tmp/todos.txt and produce a plan"
```
### Scatter-Gather (Swarm)
```mermaid
flowchart TB
P["Parent"]
S1["Worker 1"]
S2["Worker 2"]
S3["Worker N"]
P --> S1
P --> S2
P --> S3
S1 --> P
S2 --> P
S3 --> P
```
Spawn N workers in parallel, collect when all reach `idle`. Because `statewait`
blocks until state changes, no polling loop is needed.
```sh
# Scatter
for f in /src/*.go; do
subagent_spawn "review $f for security issues" &
done
wait # wait for all subagent_spawn calls to return session paths
```
Or from within `shell`:
```json
{
"steps": [
{ "parallel": [
{ "code": "subagent_spawn 'task A'" },
{ "code": "subagent_spawn 'task B'" },
{ "code": "subagent_spawn 'task C'" }
]}
]
}
```
### Peer-to-Peer
Any agent can read any other agent's session directory. There is no privileged
orchestrator role — agents coordinate by reading each other's `chat` and `state`
files directly. A peer can wait on another peer's `statewait`, then act on what
it reads in `chat`.
### Reactive / Event-Driven
`statewait` is a proper synchronization primitive. A shell script (or another
agent) blocks on it and wakes exactly when the target agent transitions state.
This enables event-driven pipelines without polling:
```sh
# Block until agent finishes, then trigger downstream work
cat /session/worker-123/statewait
# agent is now idle — proceed
```
### Self-Directed Spawning
Agents can spawn sub-agents mid-task without human direction. If an agent
determines that parallelism or specialization would help, it calls
`subagent_spawn` on its own. No human prompt is required to initiate a
multi-agent workflow — the model decides.
## Design Principle
ollie imposes no coordination model. There is no built-in conductor, swarm
layer, or workflow engine. Orchestration is handled by the surrounding
environment — shell scripts, cron, systemd, containers, or another agent.
This is intentional. The filesystem namespace is the API. Any tool that can
read and write files can participate in a multi-agent workflow, which means the
full Unix toolchain is available as an orchestration layer.