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

Phase 01 — Principal Deep Dive: The Agent Loop — ReAct, ReWOO & Plan-Execute-Replan

The Deep Dive treated the loop as a mechanism. This document treats it as a component in a platform. The question a principal engineer asks is not "how does ReAct parse a step" but "given a fleet of agents serving heterogeneous tasks under a cost and latency budget, how do I choose and compose orchestration strategies, what breaks at scale, and what is the blast radius when it does." Orchestration is not a framework you adopt; it is a set of decisions you own.

Orchestration strategy is a dial, chosen per task

The three modes are one axis — adaptivity versus cost — and the central design claim of this phase is that you set the dial per task, not per system. ReAct at one end (maximally adaptive, one model call per step, quadratic tokens), ReWOO at the other (two calls, linear tokens, blind to surprises), plan-execute-replan in between (plan once, pay per recovery). A serious platform does not standardize on one; it routes. A task whose next action depends on the last observation — an interactive debugging agent, an exploratory research query where each result reshapes the query — wants ReAct's interleaving. A task whose shape is known up front — extract five independent fields from a document, run a fixed pipeline of enrichments — wants ReWOO's fan-out. A task that is mostly known but has a flaky dependency wants bounded replanning.

The mistake this reframing prevents is defaulting. Defaulting to ReAct is the common one because it is what the tutorials show, and it is the most expensive possible choice for any task whose path is actually predictable. The dial exists so that the cost you calculated in Phase 00 becomes a lever you pull, not a bill you receive.

Composing modes in a production graph

Real platforms do not pick one mode for a whole workflow; they mix modes per node. A production planner-agent is frequently a ReWOO-shaped planning node that emits a plan, feeding a set of worker nodes some of which are themselves small ReAct loops when a sub-task is genuinely uncertain. LangGraph expresses this as a StateGraph where a planning node writes state consumed by interleaved worker nodes; Google ADK composes SequentialAgent, ParallelAgent, and LoopAgent around LlmAgents to get the same shape declaratively. The lab's run facade — one entry point, a mode argument, the right loop underneath — is the miniature of that composition surface. The architectural skill is recognizing that a single user request may cross a planning node (ReWOO), a fan-out of parallel workers, and one adaptive sub-loop (ReAct), and that the cost of the whole is the sum of the parts you chose, not a property of "the agent."

The scaling and performance envelope

The numbers are the argument. ReAct's token cost over (n) steps is (\sum_{k=1}^{n} k = \tfrac{n(n+1)}{2}), so a 20-step research agent resending its scratchpad pays on the order of (210) block-units of input — an order of magnitude more than the 20 you might naively budget. Worse, ReAct's latency is a sum: every step is a serial round-trip to the model, so wall-clock time is (\sum_i (t_{\text{model},i} + t_{\text{tool},i})), and there is no parallelism to buy because step (k+1)'s prompt does not exist until step (k)'s observation lands.

ReWOO changes the shape twice. Tokens become linear in tool count because the model is out of the worker loop. And latency turns a sum into a max: independent plan steps (no #E dependency) are data-parallel, so a group of (m) tool calls costs (\max_i t_{\text{tool},i}) instead of (\sum_i t_{\text{tool},i}). For a research task gathering five independent facts, ReAct is roughly five serial model+tool round-trips; ReWOO is two model calls plus one parallel tool fan-out. On a fleet serving thousands of such tasks, that is the difference between a cost curve that bends up with task complexity and one that stays flat — and the difference is why the planning-then-fan-out shape dominates production planner-agents.

The injected-policy seam is the test architecture

The single most important architectural decision in the runtime is invisible in production and everything in test: the model is injected as a Policy: str -> str. This is the record/replay seam. In tests the policy is a scripted pure function, so the entire loop is deterministic and you can assert the exact trace — llm_calls, tool_calls, stopped_reason, the sequence of steps. This is not a testing convenience bolted on; it encodes the platform's deepest correctness requirement: the loop's correctness must not depend on the model being right. The loop must survive a wrong tool call, a malformed step, and an infinite-loop policy. Production teams build this same seam as FakeLLM stubs, VCR-style cassettes, and recorded fixtures for exactly this reason. If your agent only passes tests when the model behaves, you have tested the model, not your system — and you will find that out during an incident, not a code review.

Failure modes and blast radius

Three failure modes define the blast radius, and each maps to a guard.

Unbounded loop. Without max_steps, a confused oracle proposing the same action forever burns a CPU and the token budget until a human kills it. The blast radius is one runaway task consuming shared quota — on a multi-tenant platform, one tenant's confused agent degrading everyone. max_steps caps it to a partial trace.

Flaky-tool amplification. If a tool raises instead of returning a structured error, the run's success probability is the product of its tools' — roughly (0.95^n) for (n) independent tool calls at 95% each, so a 10-tool plan is already below 60% end-to-end. Errors-as-observations breaks that coupling: a recoverable failure is retried or routed around, and the ceiling is set by your design, not your flakiest dependency. The blast radius of a bad design here is a fleet-wide reliability ceiling you cannot lift without rewriting the loop.

Plan invalidation in ReWOO. ReWOO's planner commits before any observation, so if step 2's result invalidates the plan, ReWOO barrels on — the workers keep executing a stale plan and the Solver inherits the mess. The blast radius is a confidently-wrong answer, which is worse than a visible failure because it passes silently downstream. Plan-execute-replan is the mitigation: it detects the failed step and rebuilds the plan with the failure in context.

Observability: the trace is the cost signal

The Trace counting llm_calls and tool_calls is the phase's observability primitive, and it is deliberately the cost signal. In production these become the spans and counters you alert on: a ReAct agent whose llm_calls climbs toward max_steps is a task about to fail expensively; a stopped_reason distribution skewing toward max_steps or max_replans is a fleet-level signal that tasks are mismatched to their mode or that a dependency is degraded. You do not need token-exact accounting to see the shape — the call counts are the leading indicator, which is why the lab asserts them.

Cross-cutting concerns, and what is deliberately deferred

A principal reads a design as much for what it omits as what it includes. This runtime deliberately defers: retries and idempotency (a recoverable observation lets the agent retry, but safe retry needs idempotency keys and backoff — that is durability, Phase 08); native tool-call JSON (models emit structured tool calls in a dedicated field; the free-text ReAct format here teaches the boundary discipline the native path still needs — Phase 02); the sandbox and authorization on execute (Phases 09/13); and memory management of the growing scratchpad (Phase 04). Cost and latency tie directly back to Phase 00's numbers — this phase makes them runnable rather than calculated. The omissions are not gaps; they are seams where later phases plug in, and the loop is designed so they can.

The "looks wrong but is intentional" decisions

Two decisions look like bugs to a reviewer who has not thought at the system level. First, the ReWOO planner is deliberately blind to observations — it plans "WithOut Observation" by design, and that blindness is the entire source of the two-call cost win. A reviewer's instinct to "let the planner see results" would recreate ReAct and delete the advantage. Second, plan-execute-replan bounds recovery instead of retrying unboundedly. An unbounded retry loop feels more robust but is strictly worse: it removes the termination guarantee and turns a persistently-failing task into an infinite one. Bounded replan over unbounded retry is the principled choice — the same reason max_steps exists, applied to recovery. Both decisions trade a little apparent capability for a hard reliability guarantee, and that trade is what separates a runtime you can run a fleet on from a demo.