Skip to main content

Overview

The PlannerEngine implements a two-phase reasoning approach: first create a structured plan as a list of subtasks, then execute each task sequentially. This is ideal for complex requests that benefit from upfront decomposition.

When to Use

Use PlannerEngine when:
  • Tasks are complex and benefit from upfront planning
  • You want to see the full plan before execution begins
  • Subtasks have dependencies (some must complete before others)
  • You need structured, observable task breakdowns
  • You want to enable replanning on task failures

Constructor

import { PlannerEngine } from '@agentlib/reasoning'

const engine = new PlannerEngine(config)

Configuration

maxExecutionSteps
number
default:20
Maximum steps allowed during plan execution across all tasks.
allowReplan
boolean
default:false
Whether to re-plan mid-execution if a task fails. When false, the engine throws an error on task failure.
plannerPrompt
string
Custom system prompt for the planning phase. Should instruct the model to output a JSON array of tasks with id, description, and dependsOn fields.
executorPrompt
string
Custom system prompt for the execution phase. Should instruct the model to complete individual subtasks.

Usage Example

import { Agent } from '@agentlib/core'
import { PlannerEngine } from '@agentlib/reasoning'
import { searchTool, emailTool, calendarTool } from './tools'

const agent = new Agent({
  name: 'assistant',
  model: myModelProvider,
  tools: [searchTool, emailTool, calendarTool],
  reasoning: new PlannerEngine({
    maxExecutionSteps: 30,
    allowReplan: true
  })
})

const result = await agent.run({
  input: 'Research the top 3 AI conferences in 2024, find their dates, and add them to my calendar'
})

How It Works

Phase 1: Planning

  1. Model receives the user request with available tools context
  2. Model produces a JSON array of subtasks:
    [
      { "id": "t1", "description": "Search for top AI conferences in 2024", "dependsOn": [] },
      { "id": "t2", "description": "Extract conference dates", "dependsOn": ["t1"] },
      { "id": "t3", "description": "Add events to calendar", "dependsOn": ["t2"] }
    ]
    
  3. Plan is validated and emitted as a PlanStep

Phase 2: Execution

  1. Tasks are executed in dependency order
  2. Each task:
    • Receives context from completed dependencies
    • Can make tool calls (with its own mini ReAct loop)
    • Marks itself as done, failed, or in_progress
  3. Results are accumulated in a task results map

Phase 3: Synthesis

  1. All task results are combined
  2. Model synthesizes a final coherent answer
  3. Result is returned to the user

Plan Task Schema

Each task in the plan has:
interface PlanTask {
  id: string                    // Unique task identifier
  description: string           // What the task should accomplish  
  dependsOn?: string[]         // Task IDs that must complete first
  status: 'pending' | 'in_progress' | 'done' | 'failed'
  result?: unknown             // Output from task execution
}

Step Emissions

  • PlanStep: Emitted after planning phase with full task list
  • ThoughtStep: Emitted at the start of each task execution
  • ToolCallStep / ToolResultStep: Emitted during task tool usage
  • ResponseStep: Emitted with the final synthesized answer

Error Handling

Max execution steps exceeded:
[PlannerEngine] Max execution steps (20) reached.
Task failure (when allowReplan is false):
[PlannerEngine] Task "t2" failed: <error message>
Task exceeds individual step limit:
[PlannerEngine] Task "t1" exceeded max steps.

Default Planner Prompt

You are a planning assistant. Break the user's request into a clear, ordered list of subtasks.

Respond with ONLY a JSON array of tasks in this exact format (no markdown, no preamble):
[
  { "id": "t1", "description": "...", "dependsOn": [] },
  { "id": "t2", "description": "...", "dependsOn": ["t1"] }
]

Rules:
- Each task must be atomic and independently executable
- dependsOn lists task ids that must complete first
- Order tasks so dependencies come first
- Be specific — the executor will act on each description

String Alias

const agent = new Agent({
  name: 'my-agent',
  reasoning: 'planner' // Uses default PlannerEngine
})

Implementation Reference

Source: packages/reasoning/src/engines/planner.ts:70

Build docs developers (and LLMs) love