Skip to content

Planner โ€‹

The planner module separates goal decomposition from execution. LLMPlanner uses a language model to break a goal into a structured Plan of Task objects. ClassicalPlanner uses deterministic rules. PlanValidator checks a plan before execution starts.

ts
import {
  LLMPlanner,
  ClassicalPlanner,
  PlanValidator,
  TaskPriority,
  TaskStatus,
} from 'confused-ai';

LLMPlanner โ€” LLM-driven decomposition โ€‹

ts
import { createAgent, OpenAIProvider } from 'confused-ai';
import { LLMPlanner, TaskPriority } from 'confused-ai';

const llm = new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o' });

const planner = new LLMPlanner(
  {
    maxIterations: 10,
    allowParallelExecution: true,
    model: 'gpt-4o',
    temperature: 0.3,
    maxTokens: 2_000,
  },
  {
    generateText: async (prompt) => {
      const result = await llm.generate([{ role: 'user', content: prompt }]);
      return result.text;
    },
  },
);

// Generate a plan
const plan = await planner.plan('Launch a new product blog post', {
  availableTools: ['search_web', 'write_content', 'publish_post'],
  constraints: ['Must be done in 2 hours', 'Use SEO best practices'],
});

console.log(plan.tasks.map(t => ({
  id:       t.id,
  name:     t.name,
  priority: t.priority,
  deps:     t.dependencies,
})));
// [
//   { id: 'task-1', name: 'Research keywords', priority: TaskPriority.HIGH, deps: [] },
//   { id: 'task-2', name: 'Write draft',       priority: TaskPriority.MEDIUM, deps: ['task-1'] },
//   { id: 'task-3', name: 'SEO review',        priority: TaskPriority.MEDIUM, deps: ['task-2'] },
//   { id: 'task-4', name: 'Publish post',      priority: TaskPriority.LOW,    deps: ['task-3'] },
// ]

Task shape โ€‹

ts
interface Task {
  readonly id:                  string;
  readonly name:                string;
  readonly description:         string;
  readonly dependencies:        string[];         // task IDs this depends on
  readonly priority:            TaskPriority;     // CRITICAL=0 HIGH=1 MEDIUM=2 LOW=3
  readonly estimatedDurationMs: number | undefined;
  readonly metadata: {
    toolIds?:         string[];   // which tools this task needs
    requiredMemory?:  string[];   // memory keys needed
    outputKey?:       string;     // key to store result under
    maxRetries?:      number;
    timeoutMs?:       number;
  };
}

PlanValidator โ€‹

Validate a plan before executing it:

ts
import { PlanValidator } from 'confused-ai';

const validator = new PlanValidator();

const validation = await validator.validate(plan, {
  availableTools: ['search_web', 'write_content', 'publish_post'],
});

if (!validation.valid) {
  console.error('Plan is invalid:', validation.errors);
  // ['Task task-3 depends on task-99 which does not exist']
} else {
  console.log('Plan is valid. Executing...');
}

Execute a plan โ€‹

Execute tasks in dependency order with agents:

ts
import { createAgent } from 'confused-ai';

// Map task names to agents
const executors: Record<string, ReturnType<typeof createAgent>> = {
  'Research keywords': createAgent({ name: 'researcher', instructions: 'Research SEO keywords.', model: 'gpt-4o-mini', apiKey: '...' }),
  'Write draft':       createAgent({ name: 'writer',     instructions: 'Write blog content.',     model: 'gpt-4o',     apiKey: '...' }),
  'SEO review':        createAgent({ name: 'seo',        instructions: 'Review for SEO.',          model: 'gpt-4o-mini', apiKey: '...' }),
  'Publish post':      createAgent({ name: 'publisher',  instructions: 'Publish the post.',        model: 'gpt-4o-mini', apiKey: '...' }),
};

const taskResults: Record<string, string> = {};

for (const task of plan.tasks) {
  const executor = executors[task.name];
  if (!executor) continue;

  // Build context from upstream results
  const context = task.dependencies.map(depId => {
    const depTask = plan.tasks.find(t => t.id === depId);
    return depTask ? `${depTask.name}: ${taskResults[depId]}` : '';
  }).join('\n');

  const result = await executor.run(`${task.description}\n\nContext:\n${context}`);
  taskResults[task.id] = result.text;
  console.log(`โœ“ ${task.name}`);
}

console.log('Done!', taskResults);

ClassicalPlanner โ€” deterministic rules โ€‹

ts
import { ClassicalPlanner } from 'confused-ai';

const planner = new ClassicalPlanner({
  // Register rule-based decompositions
  rules: [
    {
      match: (goal) => goal.includes('report'),
      decompose: (goal) => [
        { name: 'Gather data',     description: 'Collect raw data.',  priority: TaskPriority.HIGH },
        { name: 'Analyse data',    description: 'Run analysis.',       priority: TaskPriority.MEDIUM },
        { name: 'Write report',    description: 'Write the report.',   priority: TaskPriority.LOW },
      ],
    },
  ],
});

const plan = await planner.plan('Generate quarterly sales report');

Where to go next โ€‹

  • Workflows โ€” execute a plan as a structured DAG.
  • Reasoning โ€” step-by-step chain-of-thought before planning.
  • Orchestration โ€” multi-agent teams to execute each plan task.

Released under the MIT License.