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·Eruo Fredoline
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. /Custom Agent Frameworks
  6. /Ch. 10
Custom Agent Frameworks

10. Planning Systems

Chapter 10 of 24 · 15 min
KEY INSIGHT

Planning is explicit commitment to a sequence of actions. Deliberate planning trades flexibility for reliability—use it when tasks are complex enough that zero-shot execution fails.

Planning transforms a vague goal into a sequence of executable steps. Without a planner, agents flail—making random tool calls, repeating actions, or giving up when the path isn't obvious.

Two planning modes:

  1. Zero-shot: The LLM plans on the fly, deciding each step based on current state.
  2. Deliberate: The agent generates a full plan upfront, then executes step by step.

Zero-shot is simpler but less reliable for complex tasks. Deliberate planning is more reliable but requires the agent to commit to a sequence before execution.

Deliberate planner implementation:

@dataclass
class PlanStep:
    step_id: int
    description: str
    tool_name: Optional[str]
    tool_args: Optional[dict[str, Any]]
    status: str = "pending"  # pending, executing, completed, failed
    result: Optional[Any] = None

@dataclass
class Plan:
    goal: str
    steps: list[PlanStep]
    current_step: int = 0
    
    def is_complete(self) -> bool:
        return all(s.status == "completed" for s in self.steps)
    
    def next_step(self) -> Optional[PlanStep]:
        if self.current_step < len(self.steps):
            step = self.steps[self.current_step]
            step.status = "executing"
            return step
        return None

class DeliberatePlanner:
    def __init__(self, llm: LLMInterface):
        self.llm = llm
    
    async def create_plan(self, goal: str, tools: list[dict]) -> Plan:
        prompt = f"""You are a task planner. Given the goal and available tools, create a step-by-step plan.
        
Goal: {goal}

Available tools:
{json.dumps(tools, indent=2)}

Respond with a JSON plan:
{{
  "steps": [
    {{"description": "Step 1 description", "tool_name": "tool_name", "tool_args": {{}}}},
    ...
  ]
}}"""

        response = await self.llm.generate(prompt)
        plan_data = json.loads(response)
        
        steps = [
            PlanStep(
                step_id=i,
                description=s["description"],
                tool_name=s.get("tool_name"),
                tool_args=s.get("tool_args", {})
            )
            for i, s in enumerate(plan_data["steps"])
        ]
        
        return Plan(goal=goal, steps=steps)

Failure mode: plan-step mismatch. The plan assumes a tool exists with specific arguments, but the actual tool has different parameters. Always validate the plan against the tool registry before execution.

async def validate_plan(self, plan: Plan, registry: ToolRegistry) -> list[str]:
    errors = []
    for step in plan.steps:
        if step.tool_name:
            try:
                tool = registry.get(step.tool_name)
                # Check required parameters
                for param in tool.parameters.get("required", []):
                    if param not in step.tool_args:
                        errors.append(
                            f"Step {step.step_id}: Missing required parameter '{param}' "
                            f"for tool '{step.tool_name}'"
                        )
            except KeyError:
                errors.append(f"Step {step.step_id}: Unknown tool '{step.tool_name}'")
    return errors
EXERCISE

Design a planning prompt for your agent's domain. Test it with five different goals. Identify where the planner hallucinates unavailable tools or invents impossible actions.

← Chapter 9
Memory Persistence
Chapter 11 →
Task Planner