Planningplanningreactplan-then-executereplanningarchitecture

Planning Strategies

Four ways to turn a goal into actions — direct execution, plan-then-execute, replanning, and the ReAct loop — each with a different profile of token cost, latency and robustness to surprises.

▶ InteractiveInterview question
Progress

Direct execution

The baseline: one model call, possibly with tools, no explicit plan. The model reads the request and either answers or calls a tool, and the orchestrator returns the result. It is the right choice more often than agent enthusiasts admit — for lookups, classification, single-tool actions and short transformations it is the cheapest, fastest and most debuggable option (Choosing the Right Abstraction).

Cost: 1 call, input ≈ context, output ≈ answer. Latency: one round trip plus one tool. It fails when the task has hidden dependencies (you must find the tenant id before you can query usage), and the model tries to do everything in one shot.

Direct execution
RequestModel (+ optional tool)ToolAnswer
UserLLMAgentToolDataDecisionHumanGuardrail

Plan-then-execute

One planning call produces an explicit list of steps as structured output (Structured Outputs); an executor then runs each step — often as direct execution with a narrow context — and a final call composes the result. The plan is data: you can validate it (unknown tools? more than 10 steps? a destructive action?), show it to a human (Approval Gates and Risk Classes), and run independent steps in parallel (Parallel vs Sequential Tool Calls).

Cost: 1 planning call + N small execution calls + 1 synthesis. Total tokens are often *lower* than ReAct because each execution call carries only its step, not the whole history. Latency: planning adds one round trip up front, but parallel steps can make the total shorter. Weakness: the plan is made before any evidence arrives, so a wrong assumption in step 1 sails through unchallenged.

Plan-then-execute
RequestPlanner → steps[]Plan valid?Step 1Step 2 (parallel)Step 3SynthesiseAnswer
UserLLMAgentToolDataDecisionHumanGuardrail

Replanning

Plan-then-execute with a feedback edge: after each step (or after a failure), a replanner sees the original goal, the plan so far, and the results, and decides whether to continue, amend the remaining steps, or stop. This recovers the robustness that pure planning lacks while keeping the plan explicit and inspectable.

Cost: planning call + N execution calls + up to N replanning calls, each carrying a summary of progress. In practice you replan only on failure or on a step whose output was flagged as surprising, so the typical overhead is 1–2 extra calls. Latency grows with every replan; cap them (Budgets, Limits and Termination) — an agent that replans ten times is an agent that does not understand the task.

Replanning
yesnoyesnoPlanExecute next stepResult as expected?Replan remaining stepsSteps left?Synthesise + return
UserLLMAgentToolDataDecisionHumanGuardrail

The ReAct loop

ReAct (Reason + Act) interleaves a short reasoning step, an action (tool call), and an observation, repeating until the model emits a final answer. There is no upfront plan; every step sees all previous thought/action/observation triples. It is the most adaptive strategy and the default inside most agent frameworks (The Agent Loop).

Cost is the catch: each iteration re-sends the growing transcript, so total input tokens grow roughly quadratically with the number of steps — 10 steps with 1k tokens added per step costs ~55k input tokens, not 10k. Latency is strictly sequential: one round trip per step. Mitigate with per-step context assembly and prefix caching (Dynamic Context Assembly) and a hard step limit.

ReAct loop
noyesGoalThoughtFinal answer?Action (tool call)AnswerObservation
UserLLMAgentToolDataDecisionHumanGuardrail
A minimal ReAct loop with a step budget. `llm.step` returns either a tool call or a final answer.
1def react(goal: str, tools: dict, llm, max_steps: int = 8) -> str:
2 transcript = [f"Goal: {goal}"]
3 for step in range(max_steps):
4 out = llm.step("\n".join(transcript), tool_schemas=list(tools.values()))
5 if out.final: # model chose to answer
6 return out.text
7 transcript.append(f"Thought: {out.thought}")
8 transcript.append(f"Action: {out.tool}({out.args})")
9 try:
10 obs = tools[out.tool].call(**out.args) # validated args, timeouts inside
11 except ToolError as e:
12 obs = f"ERROR: {e}" # let the model react to failure
13 transcript.append(f"Observation: {truncate(str(obs), 800)}")
14 return "Stopped: step budget exhausted" # never loop forever

Comparing the four

The strategies differ on when reasoning happens and how much context each call carries. That determines cost and latency more than the model does.

  • Direct: 1 call; cheapest and fastest; brittle when steps depend on each other.
  • Plan-then-execute: 2 + N calls with small contexts; parallelisable; blind to surprises.
  • Replanning: plan-then-execute + a few adaptive calls; robust and inspectable; must be capped.
  • ReAct: N calls with a growing context; most adaptive; quadratic tokens, sequential latency, hardest to audit.
  • Hybrid in practice: plan at the top level, ReAct inside a single step with a tight budget.

Key points

  • Direct execution is the right default for single-step tasks; escalate only with evidence.
  • Plan-then-execute makes the plan data: validate it, approve it, parallelise it.
  • Replanning restores adaptivity to explicit plans; cap the number of replans.
  • ReAct is adaptive but re-sends a growing transcript — token cost grows roughly quadratically with steps.
  • Every loop needs a hard step budget and a terminal branch that returns without success.
  • Choose by dependency structure and surprise rate, not by which sounds most "agentic".

Planning strategies compared

Planning strategies on the same task
Same goal, four strategies. Compare model calls, tokens and latency — and where each breaks.
GoalActionDone
Goal: "Find the cheapest flight BER→LIS next Friday and book it."
Model calls0
Tokens0
Latency0 ms

Best for one- or two-step tasks. Fails when steps depend on each other in ways the model must think about first.

1/3

When to use — and when not to

Use it when
  • Direct: lookups, single-tool actions, transformations.
  • Plan-then-execute: multi-step tasks with known structure and parallelisable subtasks.
  • Replanning: multi-step tasks where tool results frequently change what should happen next.
  • ReAct: exploratory tasks with unknown structure and a small, bounded number of steps.
Avoid it when
  • Do not use ReAct for tasks with a known fixed sequence — a workflow is cheaper and deterministic (Workflow State Graph).
  • Do not add a planner to a one-step task; it doubles latency for nothing (When Planning Helps).
  • Do not let a replanner run unbounded; it will rediscover the same dead end repeatedly.

Failure modes

  • ReAct loop without a step cap cycles between two tools forever (agent-loop-not-terminating).
  • Plan assumes an entity exists; step 1 finds it does not; steps 2–6 execute on a null anyway.
  • Planner emits a step referencing a tool that does not exist and the executor crashes.
  • Replanner loses the original goal after three amendments and optimises a sub-goal.
  • ReAct transcript grows past the window and early observations are truncated away.

Tradeoffs

Complexity
low → high
Latency
low → high
Cost
low → high
Reliability
poor → strong
Debuggability
hard → easy

Ratings are the midpoint across strategies: direct is 1/1/1, ReAct is 4/4/4 with debuggability 2.