Skip to main content

Overview

Planning enables agents to think ahead, break down complex tasks, and reason about their actions before execution. ADK-TS provides a flexible planning system through the BasePlanner interface and multiple concrete implementations.

BasePlanner

The BasePlanner abstract class defines the contract for all planning implementations:
import { BasePlanner } from '@iqai/adk';
import type { ReadonlyContext, CallbackContext } from '@iqai/adk';

export abstract class BasePlanner {
  // Build system instruction for planning
  abstract buildPlanningInstruction(
    readonlyContext: ReadonlyContext,
    llmRequest: LlmRequest,
  ): string | undefined;
  
  // Process LLM response for planning
  abstract processPlanningResponse(
    callbackContext: CallbackContext,
    responseParts: Part[],
  ): Part[] | undefined;
}
Source: packages/adk/src/planners/base-planner.ts:11

Built-in Planners

BuiltInPlanner

Leverages model-native thinking capabilities (e.g., Claude’s extended thinking, GPT-4’s reasoning).
import { BuiltInPlanner, AgentBuilder } from '@iqai/adk';

const planner = new BuiltInPlanner({
  thinkingConfig: {
    type: 'enabled',
    budget: 10000, // Token budget for thinking
  }
});

const agent = new AgentBuilder()
  .withName('ThinkingAgent')
  .withModel('claude-sonnet-4-20250514')
  .withPlanner(planner)
  .buildLoop(); // Loop agents support planning
Key Features:
  • Uses model’s native reasoning capabilities
  • No custom prompt engineering required
  • Works with models that support thinking (Claude, GPT-4o, etc.)
  • Returns undefined for both instruction and response processing
Source: packages/adk/src/planners/built-in-planner.ts:11
The BuiltInPlanner applies thinking configuration directly to the LLM request without modifying prompts or responses.

PlanReActPlanner

Implements the Plan-Reason-Act pattern, constraining the LLM to generate explicit plans before taking actions.
import { PlanReActPlanner, AgentBuilder } from '@iqai/adk';

const planner = new PlanReActPlanner();

const agent = new AgentBuilder()
  .withName('PlanningAgent')
  .withModel('gpt-4')
  .withTools([searchTool, calculatorTool])
  .withPlanner(planner)
  .buildLoop();
How It Works:
  1. Planning Phase: Agent generates a structured plan
  2. Action Phase: Executes tools based on the plan
  3. Reasoning Phase: Reflects on results and adjusts
  4. Final Answer: Provides response after plan completion
Source: packages/adk/src/planners/plan-re-act-planner.ts:20

Response Format

The planner enforces specific tags in LLM responses:
// Planning tags
/*PLANNING*/
1. Use search tool to find current price of Bitcoin
2. Use calculator to compute 5 BTC in USD
3. Format and return the result

/*ACTION*/
search("Bitcoin price USD")

/*REASONING*/
The search returned $42,000 per BTC. Now I'll calculate 5 * 42,000.

/*ACTION*/
calculate("5 * 42000")

/*FINAL_ANSWER*/
The value of 5 Bitcoin is $210,000 USD.

Replanning Support

/*PLANNING*/
1. Fetch user data from API
2. Process and analyze

/*ACTION*/
fetchUserData(userId)

/*REASONING*/
The API returned an error. I need to revise my plan.

/*REPLANNING*/
1. Check if user exists in local cache
2. Use cached data if available
3. Otherwise inform user of API issue
The PlanReActPlanner works with any model but requires more tokens than BuiltInPlanner due to explicit planning instructions.

Using Planners with Agents

With LoopAgent

Planners are designed for LoopAgent, which iterates until task completion:
import { AgentBuilder, PlanReActPlanner } from '@iqai/adk';

const agent = new AgentBuilder()
  .withName('ResearchAgent')
  .withModel('gpt-4')
  .withInstruction(`
    You are a research assistant. Use available tools to gather
    information and provide comprehensive answers.
  `)
  .withTools([webSearchTool, calculatorTool, noteTakingTool])
  .withPlanner(new PlanReActPlanner())
  .buildLoop(); // Important: Use buildLoop() for planning

const response = await agent.ask(
  'What is the current market cap of Tesla and how does it compare to Ford?'
);

Accessing Planning Output

Planning details are available in events:
for await (const event of agent.runAsync(context)) {
  // Check for planning/reasoning parts
  if (event.content?.parts) {
    for (const part of event.content.parts) {
      if (part.thought) {
        console.log('Agent thinking:', part.text);
      } else if (part.text) {
        console.log('Agent output:', part.text);
      }
    }
  }
}

Custom Planners

Implement custom planning strategies by extending BasePlanner:
import { BasePlanner } from '@iqai/adk';
import type { Part } from '@google/genai';

export class TreeOfThoughtsPlanner extends BasePlanner {
  buildPlanningInstruction(readonlyContext, llmRequest): string {
    return `
      Generate multiple potential approaches to solve this problem.
      For each approach:
      1. Outline the steps
      2. Evaluate feasibility
      3. Score the approach (1-10)
      
      Then, select the highest-scoring approach and execute it.
      
      Use the tag /*APPROACHES*/ for listing options.
      Use the tag /*SELECTED_APPROACH*/ for your choice.
      Use the tag /*EXECUTION*/ for implementation.
    `;
  }
  
  processPlanningResponse(callbackContext, responseParts): Part[] {
    const processedParts: Part[] = [];
    
    for (const part of responseParts) {
      if (part.text?.includes('/*APPROACHES*/') || 
          part.text?.includes('/*SELECTED_APPROACH*/')) {
        // Mark as thought/reasoning
        part.thought = true;
      }
      
      if (part.functionCall) {
        // Allow function calls through
        processedParts.push(part);
      } else if (part.text?.includes('/*EXECUTION*/')) {
        // Include execution text
        processedParts.push(part);
      }
    }
    
    return processedParts;
  }
}

Using Custom Planners

const customPlanner = new TreeOfThoughtsPlanner();

const agent = new AgentBuilder()
  .withName('ComplexProblemSolver')
  .withModel('gpt-4')
  .withPlanner(customPlanner)
  .buildLoop();

Planning Patterns

Break tasks into ordered steps:
const instruction = `
  /*PLANNING*/
  1. First step
  2. Second step (depends on 1)
  3. Third step (depends on 2)
`;
Identify independent tasks:
const instruction = `
  /*PLANNING*/
  Parallel tasks (can run simultaneously):
  - Task A: Fetch user data
  - Task B: Fetch product catalog
  - Task C: Fetch pricing info
  
  Sequential tasks (after parallel):
  - Task D: Merge all data
  - Task E: Generate recommendation
`;
Branching based on results:
const instruction = `
  /*PLANNING*/
  1. Check user authentication status
  2a. If authenticated: Load user preferences
  2b. If not authenticated: Use default settings
  3. Proceed with request
`;

Planning with Tools

Planners work seamlessly with tool-equipped agents:
import { AgentBuilder, PlanReActPlanner, BaseTool } from '@iqai/adk';

class DatabaseTool extends BaseTool {
  name = 'database_query';
  description = 'Execute SQL queries against the database';
  
  async execute(context, args) {
    return await this.db.query(args.sql);
  }
}

const agent = new AgentBuilder()
  .withModel('gpt-4')
  .withTools([
    new DatabaseTool(),
    new APITool(),
    new VisualizationTool(),
  ])
  .withPlanner(new PlanReActPlanner())
  .withInstruction(`
    Plan your approach before using tools.
    Consider which tools to use and in what order.
  `)
  .buildLoop();

Configuration

Thinking Config (BuiltInPlanner)

interface ThinkingConfig {
  type: 'enabled' | 'disabled';
  budget?: number;        // Token budget for thinking
  allowPartial?: boolean; // Allow incomplete thinking
}

const planner = new BuiltInPlanner({
  thinkingConfig: {
    type: 'enabled',
    budget: 5000,
    allowPartial: false,
  }
});

Custom Instruction Context

class ContextAwarePlanner extends BasePlanner {
  buildPlanningInstruction(readonlyContext, llmRequest): string {
    const availableTools = llmRequest.config?.tools || [];
    const toolNames = availableTools.map(t => t.name).join(', ');
    
    return `
      You have access to these tools: ${toolNames}
      
      Plan your approach using only these tools.
      Break down complex tasks into tool invocations.
    `;
  }
}

Best Practices

  1. Choose the Right Planner
    • Use BuiltInPlanner for models with native thinking
    • Use PlanReActPlanner for explicit planning steps
    • Build custom planners for domain-specific needs
  2. Provide Clear Instructions
    • Explain planning expectations in agent instructions
    • Give examples of good plans
    • Specify tool usage patterns
  3. Monitor Planning Overhead
    • Planning consumes additional tokens
    • Set appropriate thinking budgets
    • Balance planning depth vs. cost
  4. Test Planning Quality
    • Review generated plans in development
    • Validate that plans match execution
    • Adjust instructions based on results
  5. Handle Replanning
    • Expect plans to change based on results
    • Allow agents to adapt strategies
    • Don’t over-constrain the planning process
Planners are most effective for multi-step tasks requiring tool orchestration. Simple queries may not benefit from explicit planning.

Next Steps

Code Execution

Execute code safely in sandboxed environments

Flows & Processors

Control request/response lifecycle

Build docs developers (and LLMs) love