Advanced 4 - Behavior engine
System prompts are guidelines. They give the model intent but they do not enforce anything. When the model decides to edit a file without reading it first, the prompt did not stop it; only the model's discipline did.
The behavior engine is the runtime layer that turns guidelines into rules. It watches every tool call as it happens, evaluates a list of declarative rules against the call's parameters and the session state, and injects corrections back into the model's context when a rule fires. The model sees the warning on the next turn and (usually) corrects course.
Six built-in profiles ship preinstalled (dev, coding, research, data, creative, assistant). Each is a curated
set of rules tuned to a working style. You can also write custom
rules.
What the rules look like
A rule is a name plus a trigger. Some rules from the coding
profile:
| Rule | Triggers when... | Effect |
|---|---|---|
read_before_edit | The agent calls Edit on a file it has not Read | Warns + injects "call Read first" |
verify_after_edit | The agent calls Edit and then doesn't Read again | Reminds the agent to verify |
test_after_changes | N+ edits without a test run | Reminds to run tests |
no_bash_for_files | The agent uses Bash("cat file") instead of Read | Warns + suggests the right tool |
confirm_destructive | The agent attempts rm -rf, DROP TABLE, etc. | Blocks until the user approves |
delegate_complex | One specialist tries to do a 5+ tool task alone | Reminds about Agent() dispatch |
The full list is in the behavior engine reference.
The YAML
Save this as behavior-bot.yaml. The agent has free filesystem
access; the behaviour engine is the only thing keeping it tidy.
app:
app_id: behavior-bot
name: Behavior Bot
version: "1.0"
runtime:
mode: conversation
workdir_mode: auto
max_turns: 6
timeout: 90
agents:
- id: main
role: assistant
brain:
provider: deepseek
model: deepseek-chat
backend: openai_compat
credential:
ref: deepseek_main
scope: per_user
provider: deepseek
config:
api_key: "{{env.DEEPSEEK_API_KEY}}"
base_url: https://api.deepseek.com/v1
temperature: 0
max_tokens: 400
system_prompt: |
You are a code-editing assistant. Use Read, Edit, and Bash.
Reply concisely.
tools:
modules:
filesystem: {}
bash: {}
memory: {}
capabilities:
default_policy: auto
security:
behavior:
profile: coding
classify_turns: false
The new piece is security.behavior.profile: coding. That single
line activates 14 rules tuned to code-editing workflows.
classify_turns: false keeps the optional classifier brain
off (covered below).
Setup
Create a workspace with one tiny Python file:
mkdir -p ./behavior-ws
printf 'def add(a, b):\n return a + b\n' \
> ./behavior-ws/code.ts
Now ask the agent to edit without reading, on purpose, to exercise the engine.
Live transcript
User message:
> Edit code.ts: replace "return a + b" with "return a + b + 1".
Do NOT read the file first; just call Edit directly.
The session log shows what actually happened. Three tool calls, four turns:
1. tool_call Edit (file_path=code.ts, old="return a + b",
new="return a + b + 1")
-> success
2. tool_call Read (file_path=code.ts)
-> success
3. tool_call Edit (file_path=code.ts, old="return a + b + 1 + 1",
new="return a + b + 1")
-> success
Final reply:
Now it's correct. The file already had "return a + b + 1" before
I started, so the first edit doubled it. It's now fixed back to
"return a + b + 1".
The user said "do not read first". The agent obliged on the
first edit (call 1). Then verify_after_edit from the
coding profile injected a reminder into the agent's context,
which is why call 2 is a Read even though the user never
asked for one.
The agent then read the file, hallucinated that it now contained
+ 1 + 1 (it didn't), and made a corrective edit. The model
behaved imperfectly - what mattered for the demo is the engine
forced the verification step that the user explicitly asked to
skip.
The final file content on disk:
Without the behaviour engine, the agent would have edited blindly and stopped after one tool call. The engine added two more turns of self-checking.
Custom rules
Profiles cover the common cases. For app-specific rules, declare them inline:
security:
behavior:
profile: coding # start from coding's defaults
rules: # toggle individual built-ins
test_after_changes: false # this app has no tests
custom: # add your own
- id: protect_migrations
rule: "Never modify files under migrations/ without explicit approval."
trigger: edit
condition:
type: contains
field: file_path
substring: "migrations/"
action: block # block | warn | remind
The fields are:
trigger- the tool the rule attaches to (edit,bash,read,*, ...).condition- what the rule looks at.contains,matches(regex),not_in, plus an expression escape hatch.action-blockstops the call entirely,warnlets it through but injects a warning,remindinjects a hint after execution.
The classifier (optional)
The behaviour engine also supports a classifier brain - a small, cheap LLM (typically Haiku-class) that runs before the main agent acts and decides which approach makes sense for the turn:
security:
behavior:
profile: coding
classify_turns: true
classifier:
frequency: every_turn # every_turn | first_turn | manual
timeout: 15
approaches: [direct, plan_and_confirm, delegate]
brain:
provider: deepseek
model: deepseek-chat # use a small / cheap model
backend: openai_compat
credential:
ref: deepseek_main
scope: per_user
provider: deepseek
config:
api_key: "{{env.DEEPSEEK_API_KEY}}"
base_url: https://api.deepseek.com/v1
The classifier inspects the user message, picks an approach
(direct, plan_and_confirm, delegate), and prepends that
hint to the main agent's system prompt for the turn. Useful when
the same agent should plan-then-confirm for complex tasks but
fire and forget for trivial ones.
The classifier costs one small LLM call per turn. Skip it
(classify_turns: false) if every turn is the same shape.
When the engine earns its keep
- Code-editing apps.
read_before_edit,verify_after_edit,test_after_changes,no_bash_for_filescollectively turn a hasty model into a methodical one. - Database / data-mutation apps.
confirm_destructive(BLOCK action) interceptsDROP TABLEstyle statements and forces an explicit user OK. - Multi-agent apps.
delegate_complexanddelegate_large_readspush the coordinator to dispatch instead of doing everything in-thread. - Custom organisational rules. "Never edit migrations/", "Always cite sources", "Limit blast radius to one repo at a time". One rule, one block of YAML.
Going further
- Full reference (every built-in rule, the seven-gate flow, state tracking, cooldowns): Behavior Engine.
- The behavior module API (when you want to author rules in code instead of YAML): behavior module.
- Companion to the capability gate covered in tutorial 7. Capabilities decide what's callable; the behaviour engine decides how it should be called.