Mingqi Hou

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

GapAgent response
No toolsRegister functions + loop
Long tasksDecompose steps
Wrong pathObserve 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:

Plan-and-Execute: plan → run steps → replan

Core idea: separate planning from execution.

  1. Planner — 3–6 outcome-oriented steps (what, not how).
  2. Executor — run each step (often a nested ReAct loop per step).
  3. 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:

Comparison

ReActPlan-and-Execute
PlanningLocal, each turnUp front + replan
Error handlingImmediate retry/alternate toolNew plan
Best task sizeSmall–mediumMedium–large
Token costLower per startHigher (plan passes)
Mental modelHorizontal: “what’s next?”Vertical: “what are the stages?”

Choosing in practice

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).