ReAct vs Plan-and-Execute
ReAct interleaves thought and tools. Plan-and-execute writes the plan first. Pick based on tool cost, reversibility, and how often the world changes mid-run.
- agents
- planning
- react
You planned a 12-step itinerary. Step 2 is sold out. Steps 3–12 are now fiction you will still try to book. That is why a ReAct agent exists: think, act, look at the world, then think again. Plan-and-execute writes the checklist first. It is faster when the world sits still. It is a trap when a tool can fail or a seat can vanish.
Neither pattern is a personality. They are control flow. Pick them the way you pick a loop versus a batch job: cost of a tool call, whether you can undo it, and how often observations invalidate the rest of the list.
Key takeaways
- ReAct interleaves thought, tool, observation until a stop condition.
- Plan-and-execute drafts every step up front, then runs the list.
- Prefer ReAct when tools are fallible, expensive to reverse, or the data changes mid-run.
- Prefer a plan when steps are cheap, local, and a human should review the checklist.
- Hybrid: plan phases, ReAct inside a phase.
ReAct agent vs plan-and-execute
The think, act, observe loop is the runtime. ReAct and plan-and-execute are two ways to schedule it. ReAct, from Yao et al., keeps the next action conditional on the last observation. Plan-and-execute spends one (or a few) completions on a structured plan, then walks it.
type ToolResult = { ok: boolean; data: unknown; error?: string };
type PlanStep = { id: string; tool: string; args: Record<string, unknown> };
export async function reactLoop(opts: {
goal: string;
maxSteps: number;
think: (trace: string) => Promise<{ kind: 'act'; tool: string; args: Record<string, unknown> } | { kind: 'stop'; answer: string }>;
act: (tool: string, args: Record<string, unknown>) => Promise<ToolResult>;
}): Promise<string> {
let trace = `Goal: ${opts.goal}\n`;
for (let i = 0; i < opts.maxSteps; i++) {
const decision = await opts.think(trace);
if (decision.kind === 'stop') return decision.answer;
const observation = await opts.act(decision.tool, decision.args);
trace += `act ${decision.tool} ${JSON.stringify(decision.args)}\nobs ${JSON.stringify(observation)}\n`;
if (!observation.ok && observation.error === 'sold_out') {
trace += 'Plan is stale. Re-plan from this observation.\n';
}
}
throw new Error('max steps');
}
export async function planThenExecute(opts: {
plan: (goal: string) => Promise<PlanStep[]>;
act: (step: PlanStep) => Promise<ToolResult>;
goal: string;
}): Promise<ToolResult[]> {
const steps = await opts.plan(opts.goal);
const out: ToolResult[] = [];
for (const step of steps) {
const result = await opts.act(step);
out.push(result);
if (!result.ok) break; // failure: leftover steps are invalid
}
return out;
}
The failure comment is the product difference. ReAct can insert a new thought after sold_out. A naive plan runner keeps walking.
Which loop should you ship?
| ReAct | Plan-and-execute | |
|---|---|---|
| Control flow | Next tool depends on last observation | Checklist first, then walk |
| Tokens | More model turns; each turn is smaller | One fat plan, then cheaper steps (or no model per step) |
| Recoverability | High if you feed errors back | Low unless you re-plan on failure |
| Reviewability | Trace is a diary | Plan is a document a human can edit |
| Failure mode | Loops, “let me try again” | Stale plan, overconfident sequence |
Use ReAct when:
- Inventory, calendars, tickets, or prices can change between steps.
- A tool is costly to reverse (charge, email, deploy).
- You do not know the next lookup until you see the last one.
Use plan-and-execute when:
- The tools are local and cheap (
read_file,lint). - You want a human to approve the list before any step runs. Pair with human in the loop on the plan, not on every micro-tool.
- The task is “run these migrations in order” and step 2 cannot invalidate step 5’s meaning, only its success.
Token use is not a slogan. ReAct pays for extra completions and extra observations stuffed into context. A plan pays for a long first completion and then risks throwing it away. Cap both. See agent failure modes for the unbounded “try again” bill.
Hybrid: plan the phases, ReAct the messy one
You rarely need a religion. A travel bot can plan phases (research → book → confirm) and ReAct inside book, where seats disappear. A coding bot can plan files to touch, then ReAct while tests are red. That outer list is what a coding-agent loop should show the user before edits start.
type Phase = 'research' | 'book' | 'confirm';
export async function hybridTravel(goal: string) {
const phases: Phase[] = ['research', 'book', 'confirm'];
const notes: string[] = [];
for (const phase of phases) {
const answer = await reactLoop({
goal: `${phase}: ${goal}\nPrior notes: ${notes.join(' | ')}`,
maxSteps: phase === 'book' ? 8 : 4,
think: plannerFor(phase),
act: toolsFor(phase),
});
notes.push(`${phase}: ${answer}`);
}
return notes.at(-1);
}
Keep phase tools disjoint. Research should not charge a card. Confirm should not search flights again unless book reported a failure. Disjoint allowlists do more than a paragraph of “you are a careful travel agent.”
This is the same instinct as a frontend event loop: one turn of work, then look at the queues. If you care how JS actually schedules that, the event loop writeup is the non-AI version of the same idea.
Interview answer (out loud)
“A ReAct agent interleaves thought, tool, and observation so a failed or stale step can change the next action. Plan-and-execute writes the checklist first, which is easy to review and easy to invalidate. I pick ReAct when the world moves or tools are hard to undo. I pick a plan when steps are cheap and I want a human to edit the list. Hybrid is plan the phases, ReAct inside the noisy phase, with a max-step cap on both.”
Further reading (primary sources)
Related on this site
- The think, act, observe loop
- Harness engineering for coding agents
- Agent failure modes
- The event loop
Related guides
- The Think, Act, Observe LoopThe agent loop is: reason, pick a tool, run it, feed the result back, repeat until stop. Timeouts, max steps, and what "done" means.
- Agent Failure ModesAgents loop, lie about tool results, skip confirmation, and get stuck apologizing. Catalog the failures and the circuit breakers.
- Agent HandoffsA handoff passes the conversation to another agent with a slimmer prompt and different tools. What to copy, what to drop, and how to avoid ping-pong.
- Agent MemoryAgents forget when the context window ends. Working memory, session summaries, and long-term stores are three different systems. Do not mix them.
- Agent System Design InterviewDesign a support or coding agent in a loop: tools, approvals, traces, evals, and cost. The frontend surfaces matter as much as the model.