Use Case: Behavior Graphs
In ArcFlow, a behavior tree is not a separate engine layered on top of the world model — the tree IS the graph. Nodes are graph nodes. Transitions are edges. The blackboard is the property store. The agent's decision history is queryable with AS OF seq N.
This matters for autonomous agents because behavior is inseparable from world state. An agent that traverses the same graph it lives in — seeing its own history, querying its environment, following relationships to nearby entities — makes decisions grounded in the full world model rather than a shallow key-value blackboard. And in a multi-agent system, multiple trees live in the same graph: one agent's action is visible to another agent's condition check without any message passing.
The pattern#
A behavior tree is a directed acyclic graph where:
- Sequence nodes execute children left-to-right, failing on first failure
- Selector nodes try children until one succeeds
- Action nodes perform actual work
- Condition nodes check state
In ArcFlow, the tree IS the graph. Nodes are graph nodes. Execution order is edge traversal.
Why a graph database for BTs#
- Runtime modification — add/remove/rewire behaviors with MERGE/DELETE
- Shared blackboard — node properties ARE the blackboard
- Persistent state — behavior state survives restarts (WAL-backed)
- Algorithms — use PageRank to find bottleneck behaviors, community detection for behavior clustering
Implementation#
import { open } from 'arcflow'
const db = open('./behavior-graph')
// Define a behavior tree
db.batchMutate([
"CREATE (root:BT_Sequence {name: 'patrol', status: 'idle'})",
"CREATE (check:BT_Condition {name: 'is_enemy_visible', status: 'idle'})",
"CREATE (move:BT_Action {name: 'move_to_waypoint', status: 'idle'})",
"CREATE (attack:BT_Action {name: 'attack_enemy', status: 'idle'})",
])
db.batchMutate([
"MATCH (root:BT_Sequence {name: 'patrol'}) MATCH (check:BT_Condition {name: 'is_enemy_visible'}) MERGE (root)-[:CHILD {order: 0}]->(check)",
"MATCH (root:BT_Sequence {name: 'patrol'}) MATCH (move:BT_Action {name: 'move_to_waypoint'}) MERGE (root)-[:CHILD {order: 1}]->(move)",
"MATCH (check:BT_Condition {name: 'is_enemy_visible'}) MATCH (attack:BT_Action {name: 'attack_enemy'}) MERGE (check)-[:ON_SUCCESS]->(attack)",
])
// Tick the behavior tree
const tick = db.query("CALL behavior.tick()")
// Check status
const status = db.query("CALL behavior.status()")Dynamic behavior modification#
// Add a new behavior at runtime
db.mutate("CREATE (heal:BT_Action {name: 'heal_self', status: 'idle'})")
db.mutate("MATCH (root:BT_Sequence {name: 'patrol'}) MATCH (heal:BT_Action {name: 'heal_self'}) MERGE (root)-[:CHILD {order: 2}]->(heal)")
// Remove a behavior
db.mutate("MATCH (n:BT_Action {name: 'attack_enemy'}) DETACH DELETE n")See Also#
- Behavior Graph — behavior trees in depth: confidence-scored transitions, execution provenance
- Building a World Model — the world model the behavior tree reads from
- Live Queries —
LIVE MATCHfor condition nodes that fire on state changes - Autonomous Systems — multi-agent fleet coordination with behavior trees
- Spatial Queries — spatial condition checks in behavior tree conditions