« Phase 01 README · Warmup · Hitchhiker's · Deep Dive · Principal Deep Dive · Core Contributor

Phase 01 — Staff Engineer Notes: The Agent Loop — ReAct, ReWOO & Plan-Execute-Replan

There are two kinds of engineer on an agent team. One calls Runner.run and, when it loops or picks the wrong tool, opens a ticket and waits. The other opens the trace, sees llm_calls climbing toward the cap while tool_calls repeats the same failing call, and knows in ten seconds that the guard fired because a tool is raising where it should be observing. This phase builds the second engineer. The loop is small; the judgment about how to run it in production is the entire job, and it is what an interview is actually probing.

The decisions a staff engineer owns

Three decisions are yours, and none of them are the framework's default.

Which mode per task. Not per system — per task. The framework will happily let you run everything as ReAct, and it is the most expensive possible choice for any task whose path is knowable. Owning this means being able to look at a task and name its shape: does the next action depend on the last observation, or is the plan knowable up front? That single question picks the mode.

Where to bound replans. max_replans is a reliability-versus-cost decision, not a default to accept. Set it too low and a task that would have recovered on the second replan fails; set it high and a permanently-broken dependency burns budget replanning against a wall. The right number comes from asking how many distinct recovery paths a task plausibly has — usually one or two — not from optimism.

How to set max_steps. This is the one most people get wrong. max_steps is derived from a reliability budget, not guessed. If your per-step reliability is roughly (p) and a task needs (k) genuine steps, your success probability is about (p^{k}); you set the cap to (k) plus a small margin, and if the task needs more steps than the budget tolerates you add verification or decomposition — you do not raise the cap. A higher cap does not make a confused agent smarter; it makes it fail more expensively. max_steps is a guard, not a capability dial, and treating it as a dial is the tell of someone who has not run one of these in production.

The decision framework

State it in one breath so you can deploy it in a design review:

  • Uncertain path — each result decides the next queryReAct. You are paying for adaptivity because you genuinely need it. A debugging agent, an exploratory research query, anything where the observation reshapes the plan.
  • Predictable path where cost or latency matterReWOO. The plan is knowable up front, the steps are independent enough to fan out, and you want two model calls and a parallel tool phase instead of N serial round-trips. Extraction pipelines, fixed enrichment chains, multi-source lookups.
  • Mostly-known path with occasional failureplan-execute-replan. You want ReWOO's cost with a recovery lever. This is where most serious production planner-agents actually live, because "mostly known with a flaky step" describes most real work.

The senior move is choosing, with the cost numbers from Phase 00 in hand. Defaulting is the junior move regardless of which default you pick.

Code-review red flags

When you review agent code, these stop you cold:

  • A tool that raises instead of returning a structured error. This caps the whole run's reliability at that tool's reliability. dispatch must never raise; a failure is an observation the agent recovers from. If you see a bare tool exception propagating into the loop, that is a reliability ceiling nobody signed off on.
  • max_steps set high "just in case." A high cap is not a safety margin; it is permission for a confused agent to burn more tokens before it fails. Ask what reliability budget produced the number. If the answer is "it seemed safe," reject it.
  • ReAct where plan-execute would be cheaper. A knowable-path task running interleaved is paying a model call per step for adaptivity it never uses. In the trace, llm_calls == steps on a task that had a fixed plan is money set on fire.
  • Substitution that stringifies an exact reference. If #E1 produced an int and the substitution passes "6" to the next tool, you have a type bug that either throws or silently computes the wrong thing. An exact reference preserves type; an embedded one interpolates str(...). Getting this wrong is the single most common ReWOO bug.
  • A ReWOO planner "improved" to see observations. That is not an improvement; it recreates ReAct and deletes the entire cost advantage. The planner is blind by design.

Production war stories

The 30-step research agent. A pure-ReAct research agent gathering many independent facts, resending its growing scratchpad every step. The bill was an order of magnitude over budget because tokens scale as (\tfrac{n(n+1)}{2}), not (n), and latency was the sum of thirty serial round-trips. The fix was not a bigger budget; it was recognizing the lookups were independent and switching that phase to a ReWOO fan-out — two model calls and a parallel tool phase. Cost and latency both collapsed. Nobody had chosen ReAct; it was the default, and the default was the bug.

The agent that hung. No max_steps. A confused policy proposed the same search forever, the process pinned a CPU, and the token budget drained until a human noticed and killed it. On a multi-tenant platform that is one tenant's runaway degrading everyone. The guard is one line and its absence is an incident.

The substitution type bug. A plan wired a word-count into a multiply via an exact #E2 reference, but the substitution stringified it. multiply received "8" and "8" * 2 produced "88". The answer was confidently wrong and passed silently downstream — the worst failure mode, because there was no error to alert on. The fix was four lines: preserve type on an exact reference. Finding it took a day because the trace looked successful.

The exact interview signal

When an interviewer asks you to walk through ReAct versus ReWOO, they are not listening for definitions. They are listening for whether you can hand-trace the call counts: why run_react records llm_calls == steps (one model call per step, because the loop calls the model once per iteration and each step is one iteration) versus why run_rewoo records llm_calls == 2 (planner plus solver, with the tool loop holding no model call at all). If you can trace a concrete three-tool task through both — four LLM calls for ReAct (three acting steps plus the finalizer) against two for ReWOO — and then connect that to Phase 00's quadratic-versus-linear tokens, you have shown you understand the mechanism, not the vocabulary. The follow-up — "a tool starts failing intermittently, what happens and what should happen" — is probing whether you know errors-as-observations lifts reliability instead of an exception capping it. That pair of answers is the whole signal.

Closing takeaways

  • The mode is a per-task cost decision, not a default. ReAct for uncertain paths, ReWOO for predictable ones, plan-execute-replan for mostly-known-with-failure. Choosing is the seniority; defaulting is not.
  • The guards are the engineering. max_steps and max_replans come from a reliability budget, not a guess, and a tool that raises instead of observing is a reliability ceiling in disguise. The loop is five lines; the guards are the job.
  • Preserve type across the wire. An exact #E1 reference keeps its type or the next tool silently breaks. The quietest bugs are the ones where the trace looks successful.
  • Correctness must not depend on the model being right. Inject the model, script the policy, assert the trace. If it only works when the model behaves, it is a demo.
  • The person who built the loop from scratch is the one who fixes the framework when it loops, blows its recursion limit, or picks the wrong tool at 2 a.m. That is why you build it once by hand, and it is exactly the person an agent team needs to trust with production.