After writing my post about shoe making elves, I built a version of it based on a behaviour tree. Or rather, it built itself, based on a spec and a few blog posts. Behaviour trees are now my favourite mechanism for getting LLM agents to work on my projects.
What is a behaviour tree?
The idea of a behaviour tree is taken from video games. AI non-player characters often follow a pattern similar to the following:
- Patrol, if you spot an enemy, go into chase mode
- Chase mode, move to enemy until close enough, then…
- Attack until your target is dead. Then …
- Go back to patrolling.
This works for my current thinking about how agents can be most helpful in my development process. I don’t want to be telling agents what to do, and then checking they did it, and checking for side effects. This can be improved with skills and harnesses, but it would be better if they autonomously identified work to do themselves and just got on with it.
I’m trying to shift to thinking about projects declaratively - describing the end state of what the system should do, and what it shouldn’t do. I keep this store of truth in a wiki. It includes invariants about the system’s behaviour — the aim is to derive code and tests from them directly. It also potentially unlocks autonomous building without instructing agents at the task level.
How behaviour trees work in the Shoe Maker project
The agent is run on a loop. Their first prompt is to run setup, read your inbox and next action. This is what tells them their mission.
There are two main zones of the behaviour tree that can lead to different activities being done. At the top are the urgent activities which must be done as a reaction to the state of the project (failing tests, adversarial critique of previous loops, inbox messages). If these have been cleared, the bottom of the tree can be accessed. This is a more proactive zone with three phases that progressively narrow focus: explore, prioritise execute. The analogy I used to describe this was Maslow’s hierarchy of needs (hygiene → implementation → innovation).
Explore reads everything including deterministic checks on code health and writes candidates. Another loop prioritises picks, selects one and writes a detailed work item. A new loop for execution follows it. The principle is that by separating the stages you don’t get agents skipping steps in the name of efficiency.
Explore phase takes a number of deterministic inputs:
- Octoclean - highlights code smells that could be addressed
- Invariant checking — what is described but not implemented? What is implemented, but no longer a part of the spec? What is implemented but not tested?
- Inspiration — pulls a random wikipedia page and looks at the system through that lens (another agent will evaluate it critically and might amend or kill the idea)
Selector
├── [tests failing?] → Fix tests
├── [unresolved critiques?] → Fix critiques
├── [unreviewed commits?] → Review adversarially
├── [inbox messages?] → Handle inbox
├── [work-item.md exists?] → Execute it
├── [candidates.md exists?] → Prioritise: pick one, write work-item
├── [insights exist?] → Evaluate insight
├── [innovation tier?] → Innovate: creative brief from Wikipedia
└── [always] → Explore: write candidates
They are run in serial in a daily branch (that is intended to work overnight). This gets around some of the trust issues. You can always delete the branch if you don’t like what they’ve done.
Ideally, there would be one traversal of the tree per loop for the narrowest possible agent context. In reality I’ve been experimenting with this on the Anthropic scheduled agent platform that runs every hour. A single agent goes round the loop multiple times, but they deterministically get their instructions at every pass. They are envisioned as pure functions with no side effects, they take their instructions and make modifications to the repo. Whether that applies in practice is arguable.
It’s early days, and it’s only really been run on itself. It was given a wiki, and a basic bootstrap and with a little guiding I’ve ended up with something quite interesting that regularly surprises me.
I’ve extended the idea of the behaviour tree in another project as a way of doing automated darwinian experimentation on my city generator. That’s a different tree altogether, and is at slightly earlier stage. The general idea can be applied in all sorts of ways.