1 min lesson
Where loops actually fail
Work through the cases in "Where loops actually fail", pairing each signal with the move that fits.
Step 1 of 3
Where loops actually failthe two failure modes you will demo
No progress
The model retries the same edit, re-reads the same file or oscillates between two wrong fixes.
Symptom: step count climbs, the diff stops changing in any meaningful way.
Runaway cost
The loop keeps calling the biggest model on a growing context with no ceiling.
Symptom: token spend per task has no upper bound you can name before you run it.
Learn more
Advanced table
Tool design for code tasks
Tool design for code tasksthe contracts that make a loop debuggable
The tools matter more than the prompt. A loop with sharp, observable tools recovers from a confused model; a loop with vague tools fails even when the model is right.
- Tool
- read_file
- Make it idempotent by
- Returning the same content for the same path + revision
- Make it observable by
- Including line numbers and the resolved path it actually read
- Tool
- edit_file
- Make it idempotent by
- Applying an anchored patch, not a blind overwrite
- Make it observable by
- Returning the exact diff that landed, not just “ok”
- Tool
- run_tests
- Make it idempotent by
- Running the same suite the same way every call
- Make it observable by
- Returning pass/fail counts and the first failing assertion
- Tool
- search
- Make it idempotent by
- Deterministic ordering for the same query
- Make it observable by
- Returning ranked hits with file + line, not a blob
| Tool | Make it idempotent by | Make it observable by |
|---|---|---|
| read_file | Returning the same content for the same path + revision | Including line numbers and the resolved path it actually read |
| edit_file | Applying an anchored patch, not a blind overwrite | Returning the exact diff that landed, not just “ok” |
| run_tests | Running the same suite the same way every call | Returning pass/fail counts and the first failing assertion |
| search | Deterministic ordering for the same query | Returning ranked hits with file + line, not a blob |
Idempotent tools let you retry safely. Observable tools give the model a real signal to re-plan on.
A loop skeleton with the controls that keep it from running away.
def run_agent(task, *, max_steps=12, cost_ceiling_usd=2.0):
state = init_state(task)
spent = 0.0
for step in range(max_steps):
action, usage = model.plan(state) # propose ONE tool call
spent += usage.cost_usd
if spent > cost_ceiling_usd:
return stop("cost_ceiling", state)
result = tools.invoke(action) # idempotent + observable
state = state.observe(action, result) # feed the real result back
if state.task_verified(): # tests green, types clean
return stop("verified", state)
if state.no_progress(window=3): # diff unchanged 3 steps
return escalate_to_human(state)
return escalate_to_human(state) # hit step budget