ReAct vs Plan-and-Execute: When to Use Which Agent Pattern
Two patterns I use for tool-using agents—tight think-act-observe loops versus upfront planning with replanning on failure.
Single-shot chat cannot run tools, recover from errors, or coordinate multi-step work. Agent design patterns standardize how the model thinks, acts, and revises. The two I reach for most are ReAct and Plan-and-Execute—they solve different time horizons.
This pairs with the agent loop and six pillars; here the focus is when to pick each pattern.
Why patterns exist
| Gap | Agent response |
|---|---|
| No tools | Register functions + loop |
| Long tasks | Decompose steps |
| Wrong path | Observe results and revise |
Patterns are reusable shapes for that loop—not framework marketing.
ReAct: think → act → observe (repeat)
Core idea: at each turn the model reasons about state, optionally calls a tool, reads the observation, and continues.
for (let i = 0; i < maxLoops; i++) {
const step = await llmStep(context); // thought + optional tool call
if (step.final) break;
const observation = await runTool(step.action, step.args);
context.push({ role: "tool", content: JSON.stringify(observation) });
}
Many implementations force structured output (JSON with thought, action, args, final) so the host can parse reliably—free-form prose breaks automation.
Example task: count the word “error” in report.txt.
Turn 1: read_file → contents observed
Turn 2: count in text → 0
Turn 3: final answer to user
Strengths: responsive, good for exploration, tool-heavy workflows, quick course corrections.
Tradeoffs: no global plan—can wander on large projects; each turn spends tokens re-deriving context.
Use ReAct when:
- Steps are unclear upfront
- Few tools, fast feedback
- Interactive coding agents (mini harness)
Plan-and-Execute: plan → run steps → replan
Core idea: separate planning from execution.
- Planner — 3–6 outcome-oriented steps (what, not how).
- Executor — run each step (often a nested ReAct loop per step).
- Monitor — on failure, replan with completed work + error context.
const plan = await generatePlan(userTask);
for (const step of plan.steps) {
const result = await executeStep(step); // may be ReAct inside
if (result.failed) {
const newPlan = await replan({
originalTask: userTask,
completed: resultsSoFar,
failedStep: step,
error: result.error,
});
return runPlan(newPlan);
}
resultsSoFar.push(result);
}
Example: “Analyze logs, find errors, write a report.”
Plan: collect logs → normalize formats → analyze patterns → write report
Execute step 2 fails (mixed log formats)
Replan: insert “normalize formats” before analysis, keep completed steps
Resume
Strengths: global ordering, explicit progress, structured recovery.
Tradeoffs: more LLM calls upfront; planner can be wrong until grounded by execution.
Use Plan-and-Execute when:
- Clear phases with dependencies
- Failures require strategy changes, not instant retries
- Progress UI (“step 2 of 5”) matters
Comparison
| ReAct | Plan-and-Execute | |
|---|---|---|
| Planning | Local, each turn | Up front + replan |
| Error handling | Immediate retry/alternate tool | New plan |
| Best task size | Small–medium | Medium–large |
| Token cost | Lower per start | Higher (plan passes) |
| Mental model | Horizontal: “what’s next?” | Vertical: “what are the stages?” |
Choosing in practice
- IDE agent fixing a bug → ReAct (tight loop, unknown path).
- Generate migration across 20 files → Plan-and-Execute with per-step ReAct.
- Customer support with one lookup → neither; single RAG call may suffice (RAG posts).
Summary
Agents connect models to the real world; patterns connect ad-hoc prompts to maintainable systems. I default to ReAct for implementation-heavy, interactive work and add Plan-and-Execute when dependencies, failure modes, and progress tracking dominate—often both in one product (planner orchestrator, ReAct worker).