How to Write YAML Configs That AI Agents Actually Understand
AI agent configuration YAML is where most multi-agent setups go wrong. Not because YAML is hard, but because the config is doing three jobs at once: defining who the agents are, what they're allowed to say to each other, and how the supervisor manages them. Get any of those wrong and your agents either can't communicate, step on each other's work, or burn tokens in circles.
Here's how to write configs that work, using Batty's team YAML as the reference.
The three building blocks
Every agent team config needs three things: roles, communication rules, and backend assignments.
1. Roles: who does what
Each role needs a name, a type, and an agent backend:
roles:
- name: architect
role_type: architect
agent: claude
instances: 1
- name: engineer
role_type: engineer
agent: codex
instances: 3
use_worktrees: true
The role_type determines behavior. Batty supports four:
| Type | Purpose | Typical count |
architect | Plans work, decomposes tasks | 1 |
manager | Dispatches tasks, reviews output | 1-3 |
engineer | Writes code in isolated worktrees | 1-15 |
user | Human operator (no agent, uses channels) | 0-1 |
The name is your choice — architect, tech-lead, principal, whatever fits your mental model. The role_type is what Batty uses to decide behavior.
2. Communication: who talks to whom
The talks_to field is the most important line in the config:
roles:
- name: architect
role_type: architect
agent: claude
talks_to: [manager]
- name: manager
role_type: manager
agent: claude
talks_to: [architect, engineer]
- name: engineer
role_type: engineer
agent: codex
instances: 5
talks_to: [manager]
Notice: engineers talk to the manager, not to each other and not to the architect. This is deliberate.
Why engineers shouldn't message each other: Five engineers with open communication create 20 channels (n × (n-1)). Every message burns tokens on both sender and receiver. Engineer-to-engineer chatter is almost always coordination noise — "are you working on file X?" — that a manager should handle. Restricting talks_to keeps communication linear instead of quadratic.
Why engineers shouldn't message the architect: The architect thinks in terms of features and task decomposition. Engineers think in terms of files and functions. Mixing these abstraction levels creates confusion. The manager translates between them.
3. Backends: which AI runs where
Three agent backends are supported:
# Team-level default (applies to all roles without explicit agent)
agent: claude
roles:
- name: architect
agent: claude # Best reasoning for planning
- name: engineer
agent: codex # Fast execution for scoped tasks
| Backend | Best for | Notes |
claude | Planning, review, complex reasoning | Claude Code CLI |
codex | Fast code execution, scoped tasks | Codex CLI |
kiro-cli | Spec-driven development | Kiro CLI |
You can set a team-level default with the top-level agent field, then override per-role. If neither is set, Batty defaults to claude.
Cost tip: Use claude for the architect (runs once, needs deep reasoning) and codex for engineers (runs many times, needs speed on scoped tasks). This is the most common pattern in Batty templates.
Real configs at three scales
Solo — one agent, no hierarchy:
name: my-project
roles:
- name: engineer
role_type: engineer
agent: claude
instances: 1
Trio — architect + manager + engineer:
name: my-project
roles:
- name: architect
role_type: architect
agent: claude
talks_to: [manager]
- name: manager
role_type: manager
agent: claude
talks_to: [architect, engineer]
- name: engineer
role_type: engineer
agent: codex
instances: 3
talks_to: [manager]
use_worktrees: true
workflow_policy:
test_command: cargo test
Full team — with layout and test gating:
name: my-project
roles:
- name: architect
role_type: architect
agent: claude
talks_to: [manager]
- name: manager
role_type: manager
agent: claude
talks_to: [architect, engineer]
- name: engineer
role_type: engineer
agent: codex
instances: 5
talks_to: [manager]
use_worktrees: true
layout:
zones:
- name: architect
width_pct: 15
- name: managers
width_pct: 20
- name: engineers
width_pct: 65
workflow_policy:
test_command: ./run-tests.sh
Common mistakes
These are the configs that look right but break at runtime:
Missing agent on a non-user role:
- name: worker
role_type: engineer
# no agent field, no team-level default
Batty catches this: role 'worker' has no agent configured. Fix: add agent: codex to the role or set a team-level agent default.
Agent on a user role:
- name: human
role_type: user
agent: claude # wrong — users use channels, not agents
Error: role 'human' is a user but has an agent configured. Users communicate via channel (e.g., Telegram), not agent backends.
talks_to referencing a role that doesn't exist:
- name: engineer
talks_to: [lead] # but there's no role named 'lead'
Error: role 'engineer' references unknown role 'lead' in talks_to. Check your spelling — talks_to values must match role name fields exactly.
Forgetting the test command:
workflow_policy:
# test_command is missing
No error — but no test gating either. Tasks merge without verification. If you want quality gates, set test_command explicitly.
Layout zones that exceed 100%:
layout:
zones:
- { name: left, width_pct: 60 }
- { name: right, width_pct: 50 }
Error: layout zone widths sum to 110%, exceeds 100%.
Validate before you start
Always run batty validate before launching:
$ batty validate --show-checks
[PASS] team_name: 'my-project'
[PASS] roles_present: 3 role(s) defined
[PASS] role_agent:architect: agent: claude
[PASS] role_agent:engineer: agent: codex
[PASS] talks_to:engineer→manager: valid
[PASS] backend_health:claude: available
Every check shows pass/fail. Fix the failures before running batty start. A validated config means your agents start clean — no cryptic runtime errors, no agents that can't find each other, no silent misconfigurations that waste tokens.
