Skip to main content
Reasoning engines control how agents process inputs, use tools, and generate outputs. AgentLIB provides five built-in engines, each optimized for different task types.

Overview

Every agent uses a reasoning engine to orchestrate its behavior. If you don’t specify one, agents use a simple single-shot completion. For multi-step tasks with tools, you’ll want to choose an appropriate engine.
import { createAgent } from '@agentlib/core'
import { ReactEngine } from '@agentlib/reasoning'

const agent = createAgent({ name: 'agent' })
  .provider(model)
  .reasoning(new ReactEngine({ maxSteps: 8 }))
  // or shorthand: .reasoning('react')

ReAct Engine

Best for: Tool-using agents, most general-purpose tasks ReAct (Reasoning + Acting) interleaves thinking and tool calls. The agent reasons about what to do, calls a tool, observes the result, and repeats until it has enough information to answer.
1

Import and Configure

import { createAgent, defineTool } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { ReactEngine } from '@agentlib/reasoning'

const searchTool = defineTool({
  schema: {
    name: 'search',
    description: 'Search the web for information',
    parameters: {
      type: 'object',
      properties: { query: { type: 'string' } },
      required: ['query'],
    },
  },
  async execute({ query }) {
    return { results: [`Result for: ${query}`] }
  },
})
2

Create ReAct Agent

const agent = createAgent({ name: 'react-agent' })
  .provider(openai({ 
    apiKey: process.env.OPENAI_API_KEY,
    model: 'gpt-4o'
  }))
  .tool(searchTool)
  .reasoning(new ReactEngine({ maxSteps: 8 }))
3

Observe Reasoning Steps

agent.on('step:reasoning', (step) => {
  console.log(`[${step.type}]`, step.type === 'thought' ? step.content : '')
})

const result = await agent.run('What is the population of Tokyo?')
console.log(result.output)

Configuration Options

new ReactEngine({
  maxSteps: 8,  // Maximum reasoning iterations
})
ReAct is the default choice for most agents. It’s simple, effective, and works well with tools.

Chain-of-Thought Engine

Best for: Complex reasoning problems, math, logic puzzles Chain-of-Thought (CoT) forces the model to think explicitly before answering. The thinking is captured for observability but stripped from the final output.
import { ChainOfThoughtEngine } from '@agentlib/reasoning'

const agent = createAgent({ name: 'cot-agent' })
  .provider(model)
  .reasoning(new ChainOfThoughtEngine({
    useThinkingTags: true,  // Use <thinking> tags for clarity
    maxToolSteps: 3,        // Allow limited tool use
  }))

agent.on('step:reasoning', (step) => {
  if (step.type === 'thought') {
    console.log('💭 Thinking:', step.content)
  }
})

const result = await agent.run(
  'If a train travels 120km in 1.5 hours, what is its average speed? ' +
  'Is that faster or slower than a car driving at 90km/h?'
)
console.log(result.output)

Configuration Options

new ChainOfThoughtEngine({
  useThinkingTags: true,  // Wrap thinking in <thinking> tags
  maxToolSteps: 3,        // Max tool calls during reasoning
})
CoT is ideal when you need the model to show its work. The thinking process is available in reasoning steps for debugging and observability.

Planner Engine

Best for: Multi-step tasks, decomposable workflows, research tasks The Planner engine first creates a structured plan (JSON list of subtasks), executes each subtask with tool access, then synthesizes all results into a final answer.
1

Define Tools for Planning

import { PlannerEngine } from '@agentlib/reasoning'

const searchTool = defineTool({
  schema: {
    name: 'search',
    description: 'Search the web',
    parameters: {
      type: 'object',
      properties: { query: { type: 'string' } },
      required: ['query'],
    },
  },
  async execute({ query }) {
    return { results: [`Result for: ${query}`] }
  },
})

const writeFileTool = defineTool({
  schema: {
    name: 'write_file',
    description: 'Write content to a file',
    parameters: {
      type: 'object',
      properties: {
        path: { type: 'string' },
        content: { type: 'string' },
      },
      required: ['path', 'content'],
    },
  },
  async execute({ path }) {
    console.log(`Writing to ${path}...`)
    return { success: true }
  },
})
2

Create Planner Agent

const agent = createAgent({ name: 'planner-agent' })
  .provider(model)
  .tool(searchTool)
  .tool(writeFileTool)
  .reasoning(new PlannerEngine({ maxExecutionSteps: 15 }))
3

Observe Plan Execution

agent.on('step:reasoning', (step) => {
  if (step.type === 'plan') {
    console.log('📋 Plan:')
    step.tasks.forEach((t) => console.log(`  [${t.id}] ${t.description}`))
  }
  if (step.type === 'thought') {
    console.log('▶', step.content)
  }
})

const result = await agent.run(
  'Research the top 3 JavaScript frameworks in 2025 and write a comparison report to /tmp/frameworks.md'
)
console.log(result.output)

Configuration Options

new PlannerEngine({
  maxExecutionSteps: 15,  // Max steps during task execution
})

Plan Structure

The engine emits plan steps with this structure:
{
  type: 'plan',
  tasks: [
    {
      id: '1',
      description: 'Search for JavaScript frameworks 2025',
      status: 'pending',
      dependsOn: [],
    },
    {
      id: '2',
      description: 'Compare the top 3 results',
      status: 'pending',
      dependsOn: ['1'],
    },
    {
      id: '3',
      description: 'Write comparison to file',
      status: 'pending',
      dependsOn: ['2'],
    },
  ],
  engine: 'planner'
}
Use the Planner engine for complex, multi-step tasks where you want explicit task decomposition and sequential execution.

Reflect Engine

Best for: High-stakes tasks, accuracy-critical outputs, writing The Reflect engine generates an answer, self-critiques it (score 0-10), and revises if the score falls below the threshold. Slower but produces more reliable output.
import { ReflectEngine } from '@agentlib/reasoning'

const agent = createAgent({ name: 'reflect-agent' })
  .provider(model)
  .reasoning(new ReflectEngine({
    maxReflections: 2,        // Maximum revision cycles
    acceptanceThreshold: 8,   // Minimum score to accept (0-10)
  }))

agent.on('step:reasoning', (step) => {
  if (step.type === 'reflection') {
    console.log(`🔍 Reflection: ${step.assessment}`)
    console.log(`   Needs revision: ${step.needsRevision}`)
  }
})

const result = await agent.run(
  'Explain the CAP theorem and its implications for distributed systems design.'
)
console.log(result.output)
console.log('\nSteps taken:', result.state.steps.length)

Configuration Options

new ReflectEngine({
  maxReflections: 2,        // How many times to revise
  acceptanceThreshold: 8,   // Score threshold (0-10)
})

Reflection Steps

The engine emits reflection steps during self-critique:
{
  type: 'reflection',
  assessment: 'The explanation is clear but lacks concrete examples. Score: 6/10',
  needsRevision: true,
  engine: 'reflect'
}
Reflection adds latency and cost (multiple LLM calls). Use it when output quality is more important than speed.

Autonomous Engine

Best for: Long-horizon tasks, open-ended exploration, research agents The Autonomous engine runs indefinitely until it explicitly calls a built-in finish tool. Suitable for agents that need to explore before they know when they’re done.
import { AutonomousEngine } from '@agentlib/reasoning'

const agent = createAgent({
  name: 'autonomous-agent',
  systemPrompt: 'You are an autonomous research assistant. Work step by step. When you have a complete answer, call the finish tool.',
})
  .provider(model)
  .tool(searchTool)
  .tool(readFileTool)
  .reasoning(new AutonomousEngine({ maxSteps: 25 }))
  .policy({ maxSteps: 25, tokenBudget: 50_000 })

agent.on('step:reasoning', (step) => {
  if (step.type === 'thought') console.log('🤖', step.content.slice(0, 120))
  if (step.type === 'tool_call') console.log(`🔧 ${step.toolName}(${JSON.stringify(step.args).slice(0, 60)})`)
})

const result = await agent.run(
  'Find and summarize recent developments in quantum computing from the last 6 months.'
)
console.log('\n✅ Final answer:')
console.log(result.output)

Configuration Options

new AutonomousEngine({
  maxSteps: 25,  // Safety limit to prevent infinite loops
})
The agent automatically gets a finish tool. When it calls finish(answer), the engine completes and returns the answer.

Custom Reasoning Engine

Build your own reasoning strategy by implementing the ReasoningEngine interface:
import { ReasoningEngine, ReasoningContext } from '@agentlib/core'

const myEngine: ReasoningEngine = {
  name: 'my-engine',
  async execute(rCtx: ReasoningContext) {
    console.log('--- Custom engine executing ---')
    
    // Access the model
    const response = await rCtx.model.complete({
      messages: rCtx.ctx.state.messages
    })

    // Record a step for observability
    rCtx.pushStep({
      type: 'response',
      content: response.message.content,
      engine: 'my-engine'
    })

    // Return final output
    return response.message.content
  },
}

const agent = createAgent({ name: 'custom-agent' })
  .provider(model)
  .reasoning(myEngine)

const result = await agent.run('Hello!')
console.log(result.output)

Engine Context

The ReasoningContext provides:
  • ctx: Execution context with input, data, state
  • model: Model provider for completions
  • tools: Tool registry for tool access
  • policy: Agent policies (maxSteps, etc.)
  • systemPrompt: System prompt if configured
  • pushStep(step): Record reasoning steps
  • callTool(name, args, callId): Execute tools
Custom engines are powerful for domain-specific reasoning patterns. Study the built-in engines for implementation patterns.

Choosing the Right Engine

EngineBest ForTool SupportComplexitySpeed
ReActGeneral tool use✅ FullLowFast
Chain-of-ThoughtReasoning tasks⚠️ LimitedMediumFast
PlannerMulti-step tasks✅ FullHighMedium
ReflectHigh accuracy⚠️ LimitedHighSlow
AutonomousOpen-ended✅ FullMediumVariable
CustomDomain-specificCustomVariableVariable

Complete Example

import 'dotenv/config'
import { createAgent, defineTool } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { ReactEngine } from '@agentlib/reasoning'

const model = openai({
  apiKey: process.env.OPENAI_API_KEY,
  model: 'gpt-4o',
})

const searchTool = defineTool({
  schema: {
    name: 'search',
    description: 'Search the web',
    parameters: {
      type: 'object',
      properties: { query: { type: 'string' } },
      required: ['query'],
    },
  },
  async execute({ query }) {
    return { results: [`Result for: ${query}`] }
  },
})

const calculatorTool = defineTool({
  schema: {
    name: 'calculator',
    description: 'Evaluate a math expression',
    parameters: {
      type: 'object',
      properties: { expression: { type: 'string' } },
      required: ['expression'],
    },
  },
  async execute({ expression }) {
    return { result: eval(String(expression)) }
  },
})

const agent = createAgent({ name: 'react-agent' })
  .provider(model)
  .tool(searchTool)
  .tool(calculatorTool)
  .reasoning(new ReactEngine({ maxSteps: 8 }))

agent.on('step:reasoning', (step) => {
  console.log(`[${step.type}]`, step.type === 'thought' ? step.content : '')
})

const result = await agent.run('What is the population of Tokyo multiplied by 2?')
console.log('Final output:', result.output)

Next Steps

Build docs developers (and LLMs) love