Planning Patterns
0% read · 7 min left
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
- Plan First, or Decide as You Go
- Plan-and-Execute
- Re-planning
- The Hybrid That Usually Wins
- A Note on Classical Planning
- Choosing a Pattern
- 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 planPreserve 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
- What specifically does Plan-and-Execute fix about the context degradation described in Module 2, Chapter 1?
- Why must preserving completed work across a re-plan be handled in code as well as in the prompt?
- 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