Authoring Behavior Trees
A behavior tree defines how an agent thinks and acts. You rarely write one by hand. Instead, you describe the behavior you want in Chat, let an LLM draft the tree, then run it in a sandbox, watch it execute, and iterate until it does what you expect. This page walks through that loop.
If you want the underlying concepts (node types, ticks, the blackboard), read Behavior Trees first. For the exact JSON schema, see Behavior Tree JSON.
The authoring loop
Describe the behavior in chat
Open Chat and describe what you want the agent to do in plain language. Be concrete about the steps, the order they happen in, and the failure handling you expect.
“Draft a behavior tree for an agent that fixes failing tests: first run the test suite, and only if it fails, analyze the failures, apply a fix, and re-run validation. Retry the fix up to three times before giving up.”
The model translates this into the tree’s building blocks — sequences for ordered steps, selectors for fallbacks, a retry decorator around the fix-and-validate loop, and leaf actions or llm-action nodes for the reasoning steps.
Review the generated tree
The draft lands as a .bt.json file you can inspect. Open the Agents view to see the tree rendered as a node graph rather than raw JSON. Walk the structure top to bottom and confirm:
- The control-flow nodes (sequence, selector, retry, parallel) are nested the way you intended.
- Each leaf maps to a real action your agent can perform.
llm-actionandllm-conditionnodes sit where you want the agent to reason, not where a deterministic check would do.
You almost never edit the JSON directly. If something is off, ask in chat — “make the retry wrap only the fix step, not the initial test run” — and let the model revise the tree. Hand-editing is a fallback for surgical tweaks, not the main workflow.
Test in the sandbox
Run the tree in isolation before wiring it to real tasks. From the Agents view, click + New run to open the run overlay:
| Field | What it does |
|---|---|
| Agent type | Picks which agent the run uses (a built-in type or a custom one from .agentloop/agents/). |
| Behavior tree | Selects any .bt.json for that agent. This is a per-run picker — you can swap trees without touching files. |
| Preset | Synthesizes the starting blackboard: Minimal, Clone recent run, From fixture, or Blank. |
| Task title / description | Seeds the run with a goal. |
An Advanced section lets you hand-edit the starting blackboard as raw JSON, persist the run to history, or isolate it in a worktree. Click Run and the desktop opens the visualizer.
New to the node types? Start with the bt-sandbox agent. It ships a library of single-purpose example trees — one per mechanic (sequence, selector, retry exhaustion, parallel, llm-condition, and more). Run each one to see exactly how that mechanic behaves before you build your own.
Watch it execute and iterate
The visualizer streams execution in real time:
- Node states animate through
READY → RUNNING → SUCCEEDED / FAILEDas colors change. - The execution log records every tick and state transition, indexed by tick number.
- The blackboard inspector shows custom keys mutating; changed values flash.
- Step controls let you pause, resume, and step one node at a time, with breakpoints on node names or blackboard keys.
When the tree misbehaves, go back to chat and describe the fix. Re-run and compare. Because the tree picker is per-run, iterating is just: revise, pick the new tree, run again.
Apply it
Once the tree does what you want, it lives under its agent in .agentloop/agents/<name>/ and is picked up the next time that agent runs a real task. See Custom Agents for how trees attach to an agent definition.
Inspecting and steering from chat
You do not need the desktop UI to observe or control a running tree. Chat can introspect and steer any live behavior tree:
| Ask chat to… | What you get |
|---|---|
| ”What’s the agent doing on task 12?” | The current node, recent execution log, and last failure. |
| ”Show me the blackboard at that failure tick.” | The blackboard state at any historical tick. |
| ”Pause it.” / “Step to the next node.” | Live pause, single-step, and resume. |
| ”Restart it from scratch.” | A fresh run from the root. |
| ”Rerun from this node.” | Re-execution starting at a chosen node, with optional blackboard overrides. |
You: What's the engineer doing on task 12?
Chat: Current node: FixValidationFailures (llm-action)
Recent log: RUNNING for 3 ticks
Last failure: RunValidation failed at tick 8
You: Show me the blackboard at that failure tick.
Chat: Blackboard at tick 8:
validationResults: { errors: ["linting failed"] }
You: Restart it from scratch.
Chat: Behavior tree restarted from root.Live pause, step, and resume work only for in-process trees — a sandbox run or a daemon-hosted continuous agent. A tree running in a separate worker process can be inspected but not paused from chat.
Debugging a run
When a run misbehaves, you don’t have to start over. Every tick is recorded, so you can rewind through the run, inspect any node’s state and the blackboard at any moment, set breakpoints, replay it from a saved fixture, and fork from a suspect tick to test a fix without touching the original. This is the fastest way to localize a bug in a long run — branch at the tick you suspect, change one variable, and watch the outcome diverge.
See Debugging Behavior Trees for the full time-travel, fixtures, and forking workflow.
Presets and the blackboard
Every run starts from a blackboard — the shared state the tree reads and writes. The Preset dropdown gives you a curated starting point:
| Preset | Starting state |
|---|---|
| Minimal | A small set of standard custom.* keys that works for most runs. |
| Clone recent run | The starting blackboard from a recent run. |
| From fixture | A saved blackboard captured from an earlier run. |
| Blank | An empty custom.* object. |
For finer control, the run overlay’s Advanced section accepts a raw JSON blackboard that overlays on top of the preset. Keys you set there win over both the preset and the tree’s own defaultBlackboard.