Day 12: ReAct vs Plan-Then-Act vs Compiler Agents

Day 12: ReAct vs Plan-Then-Act vs Compiler Agents

Agent patterns are control-flow choices with cost and reliability consequences. Day 12 compares ReAct (reason+act loops), plan-then-act, and compiler-style agents that emit plans executed by deterministic machinery — and shows how to cap iterations before token fires.

⚡ TL;DR: Use ReAct for short exploratory tasks with tight caps. Use plan-then-act when the steps are knowable up front. Prefer compiler/codemod paths for mechanical edits. Always enforce max steps, max tool calls, and wall-clock budgets.

ReAct: flexible and hungry

ReAct interleaves thoughts and tool calls. It shines when the next action depends on live observations (search → read → patch). It fails when the model thrash-loops on the same error.

# ✅ Hard caps in the orchestrator — not suggestions in the prompt
MAX_STEPS = 8
MAX_TOOL_CALLS = 12
DEADLINE = time.time() + 120

while step < MAX_STEPS and time.time() < DEADLINE:
    turn = model.act(state)
    if turn.done:
        break
    result = tools.run(turn.tool, turn.args)
    state = state.with_tool(result)
    step += 1
else:
    return fail("agent_budget_exceeded")

❌ Unbounded while True until the model says “final answer.”

Plan-then-act: cheaper when the map is clear

Ask once for a structured plan (steps[]), validate it, then execute without re-planning every hop. Better for “add logging to these three handlers” than for open-ended debugging.

  • Pros: fewer tokens, auditable plan for humans.
  • Cons: brittle if early steps invalidate later ones — allow a single replan on typed failure.

Compiler / structured intent agents

The model emits intents (rename_symbol, add_iam_statement) and a deterministic engine applies them. This is the highest reliability pattern for mechanical work (see AST-guided edits culture). Use when the transformation vocabulary is closed.

{"intent": "add_retry", "file": "src/http.ts", "fn": "fetchUser", "maxRetries": 3}

The compiler rejects unknown intents — structured outputs from Day 8 apply directly.

Choosing under budget

Task shape Prefer Cap
Explore unfamiliar repo ReAct low steps, high refuse
Known multi-file change Plan-then-act replan ≤1
Mechanical refactor Compiler intents no freeform shell
Long tests / builds Async tools + HITL wall clock

Measure tokens per successful task and loop rate (repeated identical tool args). Loop rate is your early warning metric.

Closing checklist

  • [ ] Max steps, tool calls, and wall-clock in orchestrator code
  • [ ] Detect repeated identical tool calls and abort
  • [ ] Prefer plan-then-act or compilers when the vocabulary is closed
  • [ ] Log tokens per successful task by pattern
  • [ ] One controlled replan policy — not infinite
  • [ ] Ban unbounded shell-ReAct in production paths

Worked example: stop the flake loop

An agent calls run_tests, gets a flake, patches randomly, re-runs, patches again. Without caps you pay for entropy. With caps: after two identical failures, return retryable=false with “escalate to human” and open a draft PR with notes instead of burning 200k tokens.

Failure modes to watch

  • Thought spam without tool progress.
  • Replanning every step (plan-then-act in name only).
  • Compiler intents that still shell out unbounded.
  • No metric for repeated tool args.

Field notes from production

Detect loops by hashing tool name+canonical args. On third identical hash, abort with LOOP_DETECTED. This single rule saves more money than most prompt tweaks. Also budget tokens separately from steps — a step can still be huge.

Implementation sketch

# Implementation sketch: loop detector
seen = {}
def guard(tool, args):
    h = hash((tool, canonicalize(args)))
    seen[h] = seen.get(h, 0) + 1
    if seen[h] >= 3: raise LoopDetected()

Operator addendum

Expose the agent pattern choice in traces (pattern=react|plan|compiler). When cost spikes, you want to know which control flow to fix, not just which model ID billed you.

Tokens per successful task

Define success oracles per workflow (tests green, PR opened, answer cited). Track median tokens and step counts for successes only — failures skew averages. When migrating from ReAct to plan-then-act, require the new pattern to win on both quality evals and tokens/success. Watch for “false success” where the agent stops early with a polite message; count those as failures in the oracle. Cap configurations belong in config files reviewed like code, not in a prompt that says “try not to loop.”

Extended discussion

Return to the core angle for Day 12: When loops help, when they burn tokens, and how to cap iterations. That sentence is the acceptance lens for every design review this week. If a proposed change does not make this angle easier to measure or enforce, it is a distraction.

Write down three metrics you will look at after shipping Day 12 ideas, schedule a 45-minute readout, and archive the notes next to the eval artifacts. Architecture without a readout becomes slideshow archaeology.

Pair this day with the adjacent lessons in the series navigation below. Forward links exist so you can keep momentum; backward links exist so you can repair foundations when a later lab fails for boring earlier reasons.

Practically, allocate half a day to implement the smallest vertical slice, half a day to wire measurement, and refuse to polish UI until both are done. This ordering is how bootcamp projects stay honest under time pressure.

Revisit assumptions whenever the model ID, embedding ID, or index alias changes — treat those as breaking changes for Day 12 behaviors, with the same seriousness as a database migration. Canary first, then promote.

Series navigation

← Day 11 · Day 13 →

Last updated September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply