Agentic AI · Planning and Self Correction

Planning Patterns

JrCodex7 min readChapter 13 of 36

Jr Codex Agentic AI Notes

Level: Intermediate–Advanced Prerequisites: Chapter 1: Task Decomposition Time to complete: ~25 minutes


Table of Contents

  1. Plan First, or Decide as You Go
  2. Plan-and-Execute
  3. Re-planning
  4. The Hybrid That Usually Wins
  5. A Note on Classical Planning
  6. Choosing a Pattern
  7. Summary & Next Steps

1. Plan First, or Decide as You Go

The Two Poles
─────────────────────────────────────────
  ReAct              plan at every step, one step
                     ahead. Maximum adaptivity, no
                     global view.

  PLAN-AND-EXECUTE   plan everything up front, then
                     execute. Global view, but the
                     plan was written before any
                     evidence arrived.
─────────────────────────────────────────
The Trade, Stated Honestly
─────────────────────────────────────────
  ReAct ADAPTS but WANDERS.
    Every observation can redirect it — which is
    right when the situation changed, and wrong when
    it is merely distracted by an interesting tool
    result.

  PLAN-AND-EXECUTE is COHERENT but BRITTLE.
    It cannot be distracted. It also cannot notice
    that step 2 invalidated the entire plan.
─────────────────────────────────────────

Section 4 resolves this. But the pure forms are worth understanding first, because each is correct for some tasks.


2. Plan-and-Execute

def plan_and_execute(agent, goal, tools, step_budget=6):
    plan = make_plan(agent.client, goal, tools)          # ONE planning call, up front
 
    for step in topological_order(plan):
        if step.status != "pending":
            continue
        step.status = "running"
        try:                                             # each step is its own SHORT loop
            step.result = agent.react(
                task=step.description,
                context=completed_results(plan, step.depends_on),
                max_steps=step_budget,                   # per-step budget, not global
            )
            step.status = "done"
        except StepFailed as e:
            step.status = "failed"
            step.result = str(e)
            if blocks_everything(plan, step):
                break
 
    return synthesise(agent.client, goal, plan)
Two Structural Advantages
─────────────────────────────────────────
  A SEPARATE CONTEXT PER STEP
    Each sub-task gets a clean, short context
    containing only its own description and its
    dependencies' results — never the accumulated
    debris of nine unrelated steps. This alone
    solves much of Module 2, Chapter 1's degradation
    curve.

  PER-STEP BUDGETS
    A runaway sub-task consumes its own 6 steps and
    stops, rather than the whole run's 30. Cost
    becomes predictable: roughly steps x budget.
─────────────────────────────────────────
The Weakness
─────────────────────────────────────────
  The plan was written with zero evidence.

  If step 1 reveals the premise was wrong — the
  competitor has no public pricing, the database
  does not have that table — steps 2 through 5 are
  now nonsense, and a pure Plan-and-Execute agent
  will dutifully attempt all of them.
─────────────────────────────────────────

3. Re-planning

The fix is not to plan better up front. It is to plan again when the evidence warrants it.

REPLAN_PROMPT = """The current plan is no longer valid. Revise it.
 
GOAL: {goal}
 
COMPLETED (keep these results — do not repeat this work):
{completed}
 
WHAT WENT WRONG:
{problem}
 
Produce a revised plan for the REMAINING work only."""
 
def should_replan(plan, step, result) -> bool:
    if plan.is_stuck():                                  # nothing can proceed
        return True
    if step.attempts >= 2 and step.status == "failed":   # retrying is not working
        return True
    if result and result.invalidates_assumption:         # evidence broke the premise
        return True
    return False
 
def execute_with_replanning(agent, goal, plan, max_replans=2):
    replans = 0
    while not plan.is_complete():
        for step in plan.ready():
            result = agent.run_step(step, plan)
 
            if should_replan(plan, step, result):
                if replans >= max_replans:               # BOUND IT — Chapter 4
                    return escalate(plan, "replan limit reached")
                plan = revise(agent.client, goal, plan, problem=result.problem)
                replans += 1
                break                                    # restart the loop on the new plan
    return plan
Preserve Completed Work Across a Re-plan
─────────────────────────────────────────
  The single most important detail. A re-plan that
  discards finished steps will redo them — and
  possibly re-trigger their side effects.

  The prompt says "keep these results, do not repeat
  this work" AND your code carries the results
  forward. Both, because either alone fails.
─────────────────────────────────────────
Bound the Re-planning
─────────────────────────────────────────
  An agent that re-plans on every setback never
  executes anything. Two or three re-plans, then
  escalate to a human.

  Unbounded re-planning is thrash, covered in
  Chapter 4.
─────────────────────────────────────────

4. The Hybrid That Usually Wins

The Structure
─────────────────────────────────────────
  PLAN a coarse outline (3-8 steps)
        │
        ▼
  For each step: run a SHORT ReAct loop
        │        (adaptive within the step)
        ▼
  After each step: check whether the plan still
        │          holds
        ▼
  Re-plan only if it does not — bounded
─────────────────────────────────────────
Why This Is the Right Default
─────────────────────────────────────────
  It applies each pattern where the model is
  actually competent:

  LONG HORIZON  handled by an explicit plan, which
                the model is bad at holding in
                context

  SHORT HORIZON handled by ReAct, which the model is
                good at (Module 2, Chapter 1)

  CHANGE        handled by bounded re-planning, so
                new evidence is used without
                destroying coherence
─────────────────────────────────────────
def hybrid_agent(agent, goal, tools, max_replans=2):
    plan = make_plan(agent.client, goal, tools)          # coarse, 3-8 steps
    replans = 0
 
    while not plan.is_complete():
        batch = plan.ready()
        if not batch:
            if plan.is_stuck() and replans < max_replans:
                plan, replans = revise(agent.client, goal, plan), replans + 1
                continue
            break
 
        for step in batch:
            step.result = agent.react(step.description,          # SHORT adaptive loop
                                      context=deps(plan, step),
                                      max_steps=5)
            step.status = "done"
 
            if invalidates(step.result, plan) and replans < max_replans:
                plan, replans = revise(agent.client, goal, plan,
                                       problem=step.result), replans + 1
                break
 
    return synthesise(agent.client, goal, plan)

5. A Note on Classical Planning

The AI Notes cover automated planning — STRIPS-style representations with preconditions, effects, and search for a valid action sequence. It is worth knowing where that fits.

Classical vs LLM Planning
─────────────────────────────────────────
  CLASSICAL (STRIPS and successors)
    Actions have formal preconditions and effects.
    A search algorithm finds a PROVABLY valid plan.

    Requires: a complete, formal world model.
    Gives:    guarantees — the plan is correct if
              the model is.

  LLM PLANNING
    The model proposes a plausible plan from
    natural-language descriptions.

    Requires: nothing formal.
    Gives:    no guarantees at all.
─────────────────────────────────────────
Why LLM Planning Won in Practice
─────────────────────────────────────────
  Real domains resist formalisation. Writing
  complete preconditions and effects for "research a
  competitor" is not merely hard — the action's
  effects are not knowable in advance.

  LLM planning trades correctness guarantees for the
  ability to plan in domains nobody can formalise.
  That is usually the right trade, and it is exactly
  why verification (Chapter 3) and bounding
  (Chapter 4) become necessary.
─────────────────────────────────────────
Where Classical Ideas Still Apply
─────────────────────────────────────────
  PRECONDITIONS as guards — check before executing a
  step whether its inputs actually exist. Cheap, and
  catches a broken plan before it does damage.

  EFFECTS as postconditions — verify a step achieved
  what it claimed (Chapter 3).

  GOAL STATE as a completion test — an explicit,
  checkable definition of done beats asking the
  agent whether it is finished.
─────────────────────────────────────────

6. Choosing a Pattern

Decision Guide
─────────────────────────────────────────
  Short task, under ~5 steps, path depends on
  findings
      ──► pure ReAct. A plan is overhead.

  Task has clear independent parts, and you want
  parallelism
      ──► Plan-and-Execute.

  Long task in a domain where surprises are normal
      ──► the HYBRID. This is most real work.

  The environment is formal and fully specified
  (a game, a solver, a build system)
      ──► classical planning, genuinely. Do not use
          an LLM to plan something a solver solves
          exactly.
─────────────────────────────────────────

7. Summary & Next Steps

Key Takeaways

  • ReAct adapts but wanders; Plan-and-Execute is coherent but was written before any evidence arrived — the pure forms trade the same property in opposite directions.
  • Plan-and-Execute gives each sub-task a clean context and its own step budget, which addresses most context degradation and makes cost predictable.
  • Re-planning must preserve completed results in both the prompt and the code, and must be bounded or the agent thrashes instead of executing.
  • The hybrid — coarse plan, short ReAct loops per step, bounded re-planning — applies each mechanism where the model is actually competent.

Concept Check

  1. What specifically does Plan-and-Execute fix about the context degradation described in Module 2, Chapter 1?
  2. Why must preserving completed work across a re-plan be handled in code as well as in the prompt?
  3. Classical planning offers correctness guarantees that LLM planning cannot. Why did LLM planning still displace it for most agent work?

Next Chapter

→ Chapter 3: Reflection & Self-Correction


Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to Agentic AI Index