Files
mfowler e4453dcfdd docs(examples): add the "snake pit" worker-pool example
Based on @ponder.ooo's "snake pit agent orchestrator" idea (bsky 2026-05-28) and
Claude's metaphor-mapping elaboration: agents are snakes, tasks are food tossed
into a shared pit; snakes devour/digest/regurgitate/excrete.

A worker-pool-over-a-shared-queue topology (contrast the builder-adversary phase
machine):
- pit/ is a filesystem queue; snakes claim by atomic mv (no two eat the same food)
- species = specialized agents: keeper (zookeeper), planner (regurgitation IS
  task decomposition), snake-1..3 (worker pool), cleanup (scavenger + coprophagy)
- no [loop] phase machine; persistent agents self-pace via /loop
- README carries the full bio→compute mapping table from the thread image

Verified: `agents.py status --config agents.toml` lists all 6 agents + service.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:50:42 +00:00

127 lines
7.7 KiB
TOML

# examples/snakepit — the "snake pit" agent orchestrator.
#
# Based on @ponder.ooo's idea (bsky, 2026-05-28): "each agent is a snake. you toss food (tasks) into
# the pit. agents can devour tasks, gradually digest them, regurgitate them whole or in broken /
# digested parts, excrete waste (chat logs, debug traces, &c). obviously some specialist agents are
# on cleanup duty."
#
# Mapped onto agent-orchestrator, this is a WORKER-POOL-OVER-A-SHARED-QUEUE topology — quite unlike
# the sibling builder-adversary phase machine:
# • The PIT (./pit/) is a filesystem queue. Snakes claim work by ATOMIC `mv` (mv within one
# filesystem is atomic, so two snakes never devour the same food).
# • SNAKES (snake-1..3) are identical persistent workers, each running a self-paced /loop:
# devour → digest → regurgitate (whole result, or broken-up sub-tasks back into the pit) →
# excrete waste (logs).
# • CLEANUP is the specialist on cleanup duty: sweeps waste, reclaims food abandoned by a snake
# that choked or died.
# • KEEPER (the zookeeper) tosses food in and keeps the pit healthy.
# There is no [loop] phase machine here — no kind="loop" agents. See pit/README.md for the protocol.
#
# Run it by hand (status starts nothing):
# python3 ../../agents.py status --config agents.toml
# python3 ../../agents.py up --config agents.toml # needs `claude` on PATH
# python3 ../../agents.py down --config agents.toml
# Mechanics-only (no agent CLI): set defaults.backend = "demo".
# ─────────────────────────── global watchdog cadence ───────────────────────────
[watchdog]
signal_interval = 30
heavy_interval = 300
limit_probe_fallback = 300
limit_reset_slack = 45
stall_grace = 180
# ─────────────────────────── defaults inherited by every agent ───────────────────────────
[defaults]
session_prefix = "snakepit-" # REQUIRED — sessions: snakepit-snake-1, snakepit-keeper, …
log_dir = ".ao-state" # REQUIRED — logs + state/, resolved relative to this file
backend = "claude" # set to "demo" for a dependency-free mechanics-only run
model = "claude-sonnet-4-6"
watch = "heal" # keep every snake alive/healed; they self-pace and nap when the pit is empty
# ─────────────────────────── backends (declared as data) ───────────────────────────
[backend.claude]
bin = "claude"
flags = "--dangerously-skip-permissions"
remote_control = true
supports_resume = true
prompt_delivery = "arg"
process_name = "claude"
submit_key = "Enter"
stall_idle = 300
active_re = "esc to interrupt|Running tool|⠇|⠙|· \\d+"
limit_re = "spend limit|usage limit|limit reached|reached your .*limit|out of (credits|tokens)"
fatal_re = "redacted_thinking|blocks cannot be modified|cannot be modified"
[backend.demo] # dependency-free: a shell that just idles
bin = "echo '[demo] {session} up (kickoff: {kickoff})'; exec sleep 1000000"
prompt_delivery = "exec"
# ─────────────────────────── the keeper (zookeeper / supervisor) ───────────────────────────
[[agent]]
name = "keeper" # tmux session: snakepit-keeper
kind = "persistent"
model = "claude-opus-4-8"
resume = true
watch = "heal"
prompt = """
You are the KEEPER of the snake pit (its zookeeper). On startup: read pit/README.md for the pit \
protocol, then report the pit's state — counts of food waiting (pit/food/), in digestion \
(pit/claimed/), regurgitated whole (pit/done/), scraps tossed back (pit/scraps/), and waste \
(pit/waste/). Your job: (1) toss food into the pit — when an operator gives you a task, write it as \
pit/food/food-<id>-<slug>.md per the schema in pit/README.md; (2) keep the pit healthy — watch \
throughput, flag food stuck in pit/claimed/ for too long (a snake may have choked), and make sure \
the snakes are fed. Stay available; report when asked."""
# Optional periodic survey of the pit (uncomment to have the watchdog wake the keeper on a timer):
# wake = { interval = 1800, prompt_file = "prompts/keeper-survey.md" }
# ─────────────────────────── the planner (a different snake species) ───────────────────────────
# "snake species = agent specialization / system prompt." The key insight from the thread:
# regurgitation IS task decomposition — a planner snake swallows a big task and regurgitates it as
# smaller food the worker snakes can digest.
[[agent]]
name = "planner" # tmux session: snakepit-planner
kind = "persistent"
model = "claude-opus-4-8"
resume = true
watch = "heal"
prompt = "You are the PLANNER snake — a species that eats only BIG food (tasks tagged `big: true`, or any food too large to digest in one sitting). Read prompts/planner.md and pit/README.md, then loop: devour big food from pit/food/, and regurgitate it IN PARTS — a set of smaller, self-contained food-* items tossed back into pit/food/ for the worker snakes — then remove the big item. Regurgitation IS task decomposition."
# ─────────────────────────── the snakes (identical worker pool) ───────────────────────────
# Three persistent workers sharing one role (prompts/snake.md); each knows its own snake-id from its
# inline prompt and uses it to claim food. Add more snakes by copying a block and bumping the id.
[[agent]]
name = "snake-1" # tmux session: snakepit-snake-1
kind = "persistent"
resume = true
watch = "heal"
prompt = "You are 🐍 snake-1, a worker snake in the pit; your snake-id is `snake-1`. Read prompts/snake.md for your full role and the pit protocol, then begin your self-paced loop — devour food from pit/food/, digest it, regurgitate the result, excrete your waste."
[[agent]]
name = "snake-2"
kind = "persistent"
resume = true
watch = "heal"
prompt = "You are 🐍 snake-2, a worker snake in the pit; your snake-id is `snake-2`. Read prompts/snake.md for your full role and the pit protocol, then begin your self-paced loop — devour food from pit/food/, digest it, regurgitate the result, excrete your waste."
[[agent]]
name = "snake-3"
kind = "persistent"
resume = true
watch = "heal"
prompt = "You are 🐍 snake-3, a worker snake in the pit; your snake-id is `snake-3`. Read prompts/snake.md for your full role and the pit protocol, then begin your self-paced loop — devour food from pit/food/, digest it, regurgitate the result, excrete your waste."
# ─────────────────────────── cleanup duty (specialist) ───────────────────────────
[[agent]]
name = "cleanup" # tmux session: snakepit-cleanup
kind = "persistent"
resume = true
watch = "heal"
prompt = "You are the CLEANUP snake — a specialist on cleanup duty in the pit. Read prompts/cleanup.md for your full role, then begin your self-paced loop: sweep waste from pit/waste/, and reclaim food abandoned in pit/claimed/ by a snake that choked or died (toss it back to pit/food/)."
# Non-AI helper: render the snakes' tmux transcripts into clean logs.
[[service]]
name = "cleanlogs" # tmux session: snakepit-cleanlogs
command = "python3 ../../agent-log.py follow-all"
dir = "."