RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /Introduction to AI Agents
  6. /Ch. 12
Introduction to AI Agents

12. Agent Planning

Chapter 12 of 16 · 20 min
KEY INSIGHT

Planning adds an initial reasoning overhead but prevents the agent from making impulsive tool calls in complex multi-step tasks. Combining planning with revision makes agents resilient to mid-execution surprises.

Planning allows the agent to think before acting. Rather than blindly following the first tool call suggestion, a planner breaks the task into steps and maps them to tool calls before executing anything.

Simple planning prompt

def plan_task(task: str, model) -> list:
    """Decompose a task into steps before execution"""
    planning_prompt = (
        "You are a planner. Break down the following task into a numbered "
        "list of steps. Each step should be achievable with a single tool call.\n\n"
        f"Task: {task}\n\n"
        "Output format:\n1. [Step description]\n2. [Step description]\netc."
    )
    
    plan_response = model.chat([{"role": "user", "content": planning_prompt}])
    
    steps = []
    for line in plan_response.content.split("\n"):
        if line.strip() and line[0].isdigit():
            steps.append(line.split(".", 1)[1].strip())
    
    return steps

def execute_with_plan(task: str, tools: list, model):
    steps = plan_task(task, model)
    print(f"Planned {len(steps)} steps")
    
    for i, step in enumerate(steps):
        print(f"Step {i+1}: {step}")
        # Route step to appropriate tool based on description
        response = model.chat([
            {"role": "system", "content": f"Execute this step: {step}"},
            {"role": "user", "content": step}
        ], tools=[t.to_openai_schema() for t in tools])
        
        if response.message.tool_calls:
            for call in response.message.tool_calls:
                result = tools[call.function.name].invoke(**call.function.arguments)
                print(f"Result: {result[:200]}")

Planning vs. ReAct

ReAct reasons step-by-step while executing, using each observation to inform the next step. Planning reasons once upfront and produces a fixed roadmap before action. The two approaches have different strengths:

Aspect ReAct Planning
Adaptability High—reacts to observations Low—follows a fixed plan
Overhead Low—never plans ahead Medium—extra LLM call upfront
Best for Open-ended exploration Structured, multi-step tasks
Failure mode Wanders off topic Plan becomes stale mid-execution

Plan revision

Plans should be revisable. After each step, check whether the result matches the expected outcome and update the remaining plan:

def execute_with_revisions(task: str, tools: list, model, max_revisions: int = 3):
    remaining_steps = plan_task(task, model)
    executed = 0
    
    for revision in range(max_revisions):
        for step in remaining_steps[:]:
            response = execute_step(step, tools, model)
            if is_outcome_unacceptable(response):
                # Revise remaining steps based on failure
                new_steps = replan_from_failure(task, remaining_steps, model)
                remaining_steps = new_steps
            else:
                remaining_steps.remove(step)
                executed += 1
        
        if not remaining_steps:
            break
    
    return executed
EXERCISE

Input a complex research task like "Compare the GDP per capita of Japan and Germany for the last 5 years." Run the planner separately and compare its output to a ReAct agent handling the same task. Count the number of tool calls and total turns for each approach.

← Chapter 11
Conversation History
Chapter 13 →
Task Decomposition