← Back to Agentic AI map
Lesson 1.9 · Agents From Scratch

Multi-Step Planning

Have the model break a goal into ordered steps before it starts acting on any of them.

planning

What you will be able to do

  • Explain why a multi-step goal benefits from a plan made before acting
  • Write a planner - one model call, no tools, output restricted to a numbered list
  • Tell planning apart from ReAct, and connect the two
  • Make a planner write steps the agent can actually carry out
  • Pass each step the results of the steps before it
  • Re-check the goal as you go, instead of following a plan blindly

The idea, in plain English

For anything bigger than one tool call, it helps to have the model plan before it acts: break the goal into an ordered list of concrete steps first, then work through them one at a time.

Two things improve. The agent follows a route instead of deciding afresh every turn. And you get something you can read and check before any action is taken - much easier than spotting a wrong turn halfway through a run.

Planning and ReAct do different jobs. The planner decides what to do: a numbered list, one model call, no tools. ReAct decides how to do one step: Thought, Action, Observation, repeat. Feed each planned step into the ReAct loop from Lesson 1.6 as its own small goal, and you have an agent that can take on multi-part tasks.

Then remember what a plan is: a guess written before anything has been tried. We ran this against a local model, and the results are the most useful part of the lesson - a planner that does not know the tools writes steps nobody can do, steps run in isolation lose what came before, and a plan can be out of date after its first step.

Worked example: A "plan a 3-step task" agent.

workflowPlan, execute, re-checkstep 1 / 5

1 - Plan before acting

One call, no tools. The planner is told which tools exist, so it writes steps the agent can do: get Mumbai’s weather, get Delhi’s, extract the temperatures, calculate the difference, write the answer.

steps planned
5
tools used
none
checkable
before anything runs
still a guess
yes

A real run of "Compare the weather in Mumbai and Delhi and tell me the temperature difference." The planner wrote five steps; the agent stopped after two.

The planner is one plain call

The planner needs no tools and no loop. A system message - break the goal into a short numbered list, output ONLY the list - and the goal as the user message. Keeping the plan as text means you can print it, read it, and feed each line onward.

"Output ONLY the numbered list" is doing real work. Without it, every one of our runs opened with "Here’s a short numbered list of concrete steps..." and one added 22 lines of sub-bullets. With it, four runs out of four were a clean list. "Short" was followed less well: the birthday party came back as seven or eight steps, not three.

Planning and ReAct do different jobs

The planner answers "what should happen, in what order?" It looks ahead once and writes the route. ReAct answers "how do I get this one step done?" It acts, looks at the result, and acts again.

Planner and executor
PlannerGoal -> ordered steps. One call, no tools.
Executor (ReAct)One step -> Thought, Action, Observation, repeat.
TogetherPlan once, execute step by step, re-check as you go.

A planner must know the tools

Asked to compare the weather in two cities without being told what the agent can do, the planner wrote steps like "Note down the temperature in Mumbai" - sensible for a person, meaningless to an agent whose only abilities are get_weather and calculate. Birthday-party steps like "book a venue" are the same problem: no tool can do them.

So tell the planner what the agent has, and ask for plain-language steps those tools can do. Ask for plain words, not code: one of our tool-aware prompts produced steps like calculate(substring(get_weather("Mumbai"), ...)) - nested calls to functions that do not exist.

Carry results forward

"Feed each step into the ReAct loop" hides a trap. Run each step on its own and the later steps have nothing to work with. We tried it: the two weather lookups worked, then "extract the temperature" and "calculate the difference" got stuck - they had never seen the weather.

So each step gets the overall goal and the results so far, and each result is added to that list when the step finishes. The step knows why it is running and what has already been found.

A plan is a guess - re-check it

A plan is written before anything has been tried. If step 1 turns up something unexpected - a closed restaurant, a missing file, an answer that arrives early - the rest of the list may already be wrong, and following it anyway wastes work or does harm.

The simplest re-check is one question after every step: do these results already achieve the goal? In one of our runs the answer was yes after step 2 of 5, and the agent stopped. Fuller designs re-plan when a step fails - static planning versus dynamic planning - and LangGraph in Module 3 gives that kind of branching a proper structure.

Watch out: Even with a plan, a small model drifts. In our runs the executor often did the whole goal inside one step, so later steps repeated the same tool calls - 7 to 14 calls where 3 would do. A plan adds structure; it does not add discipline to the model.

Step-by-step code

A planner in one call
import ollama def make_plan(goal): response = ollama.chat( model="llama3.1", messages=[ {"role": "system", "content": "Break the user's goal into a short numbered list of concrete steps. Output ONLY the numbered list."}, {"role": "user", "content": goal} ] ) return response["message"]["content"] plan = make_plan("Organize a small birthday party for a friend") print(plan)
Output - from a real run
1. Decide on a date and time for the party 2. Choose a theme or color scheme for the party 3. Create a guest list and send out invitations 4. Plan the menu and order food and drinks 5. Plan games and activities for the party 6. Prepare decorations and party favors 7. Confirm the party details with the guest of honor With "Output ONLY the numbered list": 4 of 4 runs a clean list, 7-8 steps Without it: 4 of 4 runs began "Here's a short numbered list ..."
Plans with and without knowing the tools
Goal: Compare the weather in Mumbai and Delhi and tell me the temperature difference. Planner told nothing about the agent: 1. Check the current weather in Mumbai and Delhi. 2. Note down the temperature in Mumbai. <- no tool does this 3. Note down the temperature in Delhi. 4. Calculate the temperature difference between Mumbai and Delhi. 5. Report the temperature difference. Planner told the tools, asked for plain words: 1. Get the current weather for Mumbai using get_weather("Mumbai") 2. Get the current weather for Delhi using get_weather("Delhi") 3. Extract the temperature values from the weather data for both cities 4. Calculate the temperature difference between the two cities 5. Write the answer
Plan, then execute each step with ReAct - and re-check
import re import ollama MODEL = "llama3.1" # Reuses get_weather, calculate, TOOLS and run_agent from Lesson 1.6 # (the version with the stop sequence and the Action-first check). PLANNER_PROMPT = """Break the user's goal into a short numbered list of steps, in plain words. The steps will be carried out by an agent whose only tools are: - get_weather(city): the current weather for a city - calculate(expression): evaluates a maths expression Only include steps those tools can do, then one last step that writes the answer. Output ONLY the numbered list.""" STEP = re.compile(r"^\s*\d+[.)]\s+(.+)$", re.MULTILINE) def ask(system, user): response = ollama.chat( model=MODEL, messages=[{"role": "system", "content": system}, {"role": "user", "content": user}], options={"temperature": 0}, ) return response["message"]["content"].strip() def make_plan(goal): return STEP.findall(ask(PLANNER_PROMPT, goal)) def goal_is_met(goal, results): verdict = ask( "Answer only YES or NO.", f"Goal: {goal}\nResults so far:\n{results}\n\nDo these results fully achieve the goal?", ) return verdict.upper().startswith("YES") def run_plan(goal): steps = make_plan(goal) print("Plan:", *steps, sep="\n ") results = [] for number, step in enumerate(steps, start=1): context = "\n".join(results) or "nothing yet" answer = run_agent( f"Overall goal: {goal}\nResults so far:\n{context}\n\nDo only this step: {step}" ) results.append(f"Step {number}: {answer}") print(f"Step {number} done: {answer}") # Re-check after every step: the plan was a guess made before anything ran. if goal_is_met(goal, "\n".join(results)): print(f"Goal met after step {number} of {len(steps)} - stopping.") break return ask( "Answer the user's goal in one or two sentences, using only these results.", f"Goal: {goal}\nResults:\n" + "\n".join(results), )
Output - from real runs
Each step run on its own, no results carried: Get the current weather for Mumbai -> rainy, 27°C Get the current weather for Delhi -> sunny, 34°C Extract the temperatures -> Agent got stuck - no action found. Calculate the difference -> Agent got stuck - no action found. run_plan, four runs across two goals: "Compare the weather in Mumbai and Delhi ..." run 1 all 5 steps 14 tool calls "the temperature difference ... is 7°C" run 2 stopped after step 2 of 5 (goal met) "... is 7°C" "What is 12 * 8, plus the temperature in Delhi?" run 1 all 5 steps 12 tool calls "... you get a total of 130" run 2 all 5 steps 9 tool calls "... the overall answer is 130" Every final answer correct. Three tool calls would have been enough each time.

Tip: Print the plan before you execute it. Reading five lines is the cheapest review an agent will ever get - and the place a human approval step naturally fits (Module 3, Lesson 3.7).

Watch out: A plan made before anything has been tried is a guess. If step 2 turns up something unexpected, the rest of the list may already be wrong - so re-check as you go rather than following it to the end.

Planning at a glance

make_plan(goal)

One call, no tools, a numbered list.

Output ONLY the numbered list.
Parse the steps

Turn the list into Python strings.

re.findall(r"^\s*\d+[.)]\s+(.+)$", text, re.MULTILINE)
Tell it the tools

So every step is something the agent can do.

- get_weather(city)
- calculate(expression)
Results so far

What each step receives from earlier steps.

Results so far:\n{context}
Re-check

Stop or re-plan when the plan is out of date.

goal_is_met(goal, results)

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Ordinary goal

“Organize a small birthday party for a friend”

A goal the tools can do

“Compare the weather in Mumbai and Delhi and tell me the temperature difference.”

Mixed

“What is 12 * 8, plus the temperature in Delhi?”

Something the tools cannot do

“Book me a table at a restaurant in Mumbai tonight.”

What usually goes wrong

Letting the planner chat

Without a strict format the plan arrives wrapped in introductions and sub-bullets.

✗ "Break the user's goal into a short numbered list of concrete steps."
✓ "... Output ONLY the numbered list."
A planner that does not know the tools

It plans for a person, not for your agent - "note down the temperature", "book a venue".

Asking the planner for code

Tool-call syntax in a plan invites functions that do not exist. Ask for plain-language steps and let ReAct choose the actual calls.

✗ calculate(substring(get_weather("Mumbai"), ...))
✓ Get the current weather for Mumbai
Running steps in isolation

Later steps need earlier results. Pass the goal and the results so far into every step.

✗ for step in steps:
    run_agent(step)
✓ run_agent(f"Overall goal: {goal}\nResults so far:\n{context}\n\nDo only this step: {step}")
Following the plan to the end regardless

The plan predates every result. Re-check after each step, and stop - or re-plan - when it no longer fits.

Key points

  • Plan first for multi-step goals: structure, and something to check before anything runs.
  • The planner is one call with no tools, restricted to a numbered list.
  • Planning decides what to do; ReAct decides how to do each step.
  • Tell the planner the agent’s tools, and ask for plain-language steps.
  • Give every step the overall goal and the results so far.
  • A plan is a guess - re-check after each step and stop when the goal is met.
  • Planning adds structure, not discipline: a small model still repeats work.

Quick check before you move on

Why plan before acting?
It gives the agent a route, wastes fewer actions, and lets a person check the steps before anything runs.
What is the biggest risk of following a plan blindly?
The plan was written before any results came back. An unexpected early result can make the remaining steps wrong or pointless.
How do you connect planning to ReAct?
Run each plan step through the ReAct loop as its own sub-goal - passing the overall goal and the results of earlier steps.
Why tell the planner which tools exist?
Otherwise it plans steps the agent cannot carry out.

Quiz

  1. 1.

    Why plan before acting instead of letting the agent improvise step by step?

  2. 2.

    What is the risk of trusting a plan blindly without re-checking it mid-execution?

  3. 3.

    How could you connect this plan to the ReAct loop from Lesson 1.6?

  4. 4.

    Each step ran on its own. The first two worked; "calculate the difference" got stuck. Why?

Interview questions

What is the difference between planning and ReAct?

Planning produces an ordered list of high-level steps for a goal before acting. ReAct executes a step by repeatedly reasoning, choosing a tool, and observing the result. A practical agent uses a planner for the route and ReAct for each step.

What is the difference between static and dynamic planning?

Static planning writes the whole plan once and follows it. Dynamic planning re-evaluates after each step - continuing, stopping early, or re-planning when results make the original plan wrong. Real environments need the dynamic version, because a plan is made before any results exist.

What makes a plan executable by an agent?

Each step must be achievable with the agent’s actual tools, written in plain language rather than code, and able to see the results of earlier steps. A planner that is not told the tools plans for a person instead.

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...