Planning Patterns for AI Agents
Ever watched an agent do the right things in the wrong order? The ai agent planning pattern fixes that. Copy the starter, tune the variables, and ship an agent that plans before it acts.
import json
def plan(goal, model, tools_desc):
raw = model.chat([
{"role": "system", "content":
"Break the goal into an ordered list of concrete steps. "
f"Available tools: {tools_desc}. "
"Return ONLY JSON: [{\"step\": 1, \"action\": \"...\", \"tool\": \"name-or-null\"}]"},
{"role": "user", "content": goal},
]).content
return json.loads(raw)
def execute(steps, tools, model, state=None):
state = state or {}
for s in steps:
if s["tool"]:
state[s["step"]] = tools[s["tool"]](step=s["action"], state=state)
else:
state[s["step"]] = model.chat(
[{"role": "user", "content": f"{s['action']}\nContext: {state}"}]
).content
return stateEver watched an agent confidently do all the right things in exactly the wrong order? It calls the API before it has the ID it needs, or writes the summary before it's finished reading, then improvises a fix that makes things worse. That's not a knowledge gap. It's a missing plan.
The ai agent planning pattern separates thinking from doing: the agent lays out an ordered plan first, then executes it step by step. This guide is copy-first — grab the starter, understand the few variables that matter, and adapt it to your task.
Planning is the pattern that most changes how an agent feels to use. An agent that acts the instant you ask feels impulsive and hard to trust; an agent that shows its plan first feels deliberate, and deliberate is exactly what earns it access to anything that matters. That shift — from "just do something" to "here's what I intend to do" — is worth more than it sounds.
Quick-Start (Copy This Right Now)
[object Object], json
,[object Object], ,[object Object],(,[object Object],):
raw = model.chat([
{,[object Object],: ,[object Object],, ,[object Object],:
,[object Object],
,[object Object],
,[object Object],},
{,[object Object],: ,[object Object],, ,[object Object],: goal},
]).content
,[object Object], json.loads(raw)
,[object Object], ,[object Object],(,[object Object],):
state = state ,[object Object], {}
,[object Object], s ,[object Object], steps:
,[object Object], s[,[object Object],]:
state[s[,[object Object],]] = tools[s[,[object Object],]](step=s[,[object Object],], state=state)
,[object Object],:
state[s[,[object Object],]] = model.chat(
[{,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}]
).content
,[object Object], stateWhat this does: it asks the model for a JSON plan up front, then walks the steps in order, feeding each step's result into a shared state so later steps can use what earlier ones produced.
Run
plan()execute()Understanding the Variables
Three inputs decide whether this works, and all three are worth tuning deliberately.
The goal framing matters more than it looks. A goal like "handle the refund" produces a vague plan. "Verify the order exists, check it's within the refund window, issue the refund, and send a confirmation" produces a plan you can trust. The more the goal implies structure, the better the plan.
The tools description is the plan's vocabulary. The planner can only sequence tools it knows about, described in words it understands. If your tool is called
qw_lookupThe state object is the connective tissue. It carries results between steps. Whether you pass the full state or a filtered slice into each step changes both accuracy and cost — more context helps reasoning but inflates token count fast.
There's a fourth variable people forget until it bites them: the failure policy. What should happen when a step fails — halt, skip, retry, or re-plan? Baking a default policy into your executor, instead of deciding case by case under pressure, keeps behavior predictable. A retry-twice-then-escalate policy covers most transient failures without letting the agent silently push past a step that actually mattered.
⚡ Pro tip: Have the planner emit a
depends_onStep-by-Step: Building an AI Agent Planning Pattern
Here's how to grow the starter into something production-ready without over-building it.
First, add plan validation. Before executing, check that every referenced tool exists and every step has an action. A malformed plan should fail loudly at planning time, not halfway through execution when you've already spent money and made side-effecting calls.
[object Object], ,[object Object],(,[object Object],):
,[object Object], s ,[object Object], steps:
,[object Object], s[,[object Object],] ,[object Object], s[,[object Object],] ,[object Object], ,[object Object], tools:
,[object Object], ValueError(,[object Object],)
,[object Object], ,[object Object], s.get(,[object Object],):
,[object Object], ValueError(,[object Object],)
,[object Object], ,[object Object],What this does: it rejects a broken plan before any step runs, converting a mid-execution crash into a clean, early error you can retry cheaply.
Second, make execution resumable. Persist the state after each step. If step four crashes, you restart from four with steps one through three already done — critical when steps have real side effects like sending an email you don't want sent twice.
Third, add a re-plan escape hatch, but use it sparingly. When a step fails in a way the plan didn't anticipate, let the agent re-plan from the current state. The trap is re-planning after every hiccup, which turns a tidy plan-then-execute loop into an expensive free-for-all.
Fourth, decide how much of the plan to show the user. For internal tools, surfacing the raw plan builds trust — people relax when they can see the agent intends to verify before it acts. For customer-facing agents, a raw step list often reads as robotic, so you summarize the intent instead. A well-built ai agent planning pattern separates the plan it reasons over from the plan it presents, and treats those as two different artifacts aimed at two different audiences.
⚡ Pro tip: Log the plan and the outcome of every step as one record. A financial-ops analyst reviewing why an agent double-charged a customer needs to see the plan it committed to and where reality diverged, side by side, not two disconnected log streams.
Pro-Level Variations
Different jobs bend the pattern in useful directions.
A hierarchical planner suits big tasks. The top-level plan has coarse steps ("gather data," "analyze," "report"), and each expands into its own sub-plan at execution time. A market-research agent for a strategy consultant might plan three phases up top, then plan the specific searches only when it reaches the gather phase and knows what it's looking for.
A least-commitment planner suits uncertain tasks. Instead of a full plan, the agent plans only the next step or two, executes, then plans again with fresh information. It's slower and chattier but far more resilient when the environment keeps changing — think an agent navigating a flaky third-party API.
A plan-and-solve prompt suits simpler tasks where a full framework is overkill. You ask the model, in a single prompt, to first write the plan and then carry it out. It's the lightest version of the pattern and often enough for a marketing team automating routine content briefs.
A reactive-plus-planning hybrid suits agents that live in messy, shifting environments. The agent commits to a full path but re-checks the world before each step, adjusting only when reality has drifted from the plan's assumptions. An operations agent monitoring a fulfillment pipeline works this way: it commits to a plan for the day but re-validates inventory before each action, because the plan it made at 9 a.m. is already working from data that went stale by 9:05. The cost is the extra checks; the payoff is an agent that never blindly executes a plan the world has quietly invalidated underneath it.
⚡ Pro tip: Match plan granularity to task risk. Low-stakes internal tasks run fine on a coarse plan. Anything touching money, customer data, or irreversible actions deserves fine-grained steps so a human can approve the plan before it executes.
Troubleshooting Common Issues
When the JSON won't parse, the planner is wrapping it in prose or code fences. Add "Return ONLY raw JSON, no markdown" to the system prompt and strip stray fences defensively before parsing.
When plans are too vague, your goal or tool descriptions are too vague. Feed the planner a one-shot example of a good plan for a similar goal; a single example usually sharpens the output dramatically.
When the agent ignores its own plan mid-execution, you're probably handing the executor too much freedom. Keep the executor narrowly scoped to the current step and its state, not the whole open-ended goal.
When execution is slow, look at how much state you're passing into each step. It's tempting to hand every step the full history so it has maximum context, but that inflates every call's token count and latency at once. Pass each step the smallest slice of state it actually needs — often just the outputs of the two or three steps it depends on. A logistics team cut their planning agent's per-run cost by a third simply by trimming what each step received, with no measurable drop in accuracy.
⚡ Pro tip: Version your plan schema, not just your prompts. When you change what a plan looks like — adding
depends_on⚠️ Common mistake: Letting the planner and executor share one giant prompt with full autonomy. When the same call plans and acts with no boundary between them, you lose the plan's biggest benefit — a readable, approvable list of intentions you can inspect before anything with real consequences happens.
Your Turn
Start with the quick-start loop, point it at a real task with three or four dependent steps, and read the plan before you let it run. You'll learn more from one honest look at a generated plan than from any amount of theory.
A good rule as you iterate: if you can't read a generated plan and predict roughly what the agent will do next, the plan is too vague or the executor has too much freedom. Tighten one or the other until the plan reads like a contract you'd be comfortable approving before it runs.
As you tune the planner prompt, the tool descriptions, and the example plans, save the versions that work. Teams that keep their planning prompts in a shared library like PromptABCD stop rewriting the same planner for every new agent and start building on what already works. A good plan is reusable — so is the prompt that produces it.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
