Patterns

ReAct

The Observe-Think-Act loop pattern -- the most common agent architecture, built from loop + llm + tool primitives.

Overview

ReAct (Reason + Act) is the foundational agent pattern. The agent loops over an LLM call, invoking tools as needed, until the model responds without tool calls or a step/cost limit is reached.

Signature

function react(opts: {
  model: string;
  system?: string;
  tools: Tool[];
  maxSteps?: number;
  maxCost?: number;
}): StepLoop<string, string>
ParameterTypeDefaultPurpose
modelstring--Model identifier (e.g., 'gpt-4o')
systemstring--System prompt
toolsTool[]--Available tools for the agent
maxStepsnumber10Maximum loop iterations
maxCostnumber--Optional USD cost cap

Returns: StepLoop<string, string> -- a loop step that takes a string input and produces a string output.

Example

import { z } from 'zod';
import { react } from '@noetic/core';

const searchTool = {
  name: 'search',
  description: 'Search the web',
  input: z.object({ query: z.string() }),
  output: z.object({ result: z.string() }),
  execute: async (args) => {
    return { result: `Results for: ${args.query}` };
  },
};

const agent = react({
  model: 'gpt-4o',
  system: 'You are a research assistant.',
  tools: [searchTool],
  maxSteps: 5,
  maxCost: 0.50,
});

How It Works

Under the hood, react() composes three primitives:

  1. step.llm -- calls the model with the current conversation and available tools
  2. until.noToolCalls() -- stops when the model responds with plain text (no tool calls)
  3. until.maxSteps(n) -- safety limit on iterations
  4. until.maxCost(usd) -- optional cost cap

These are combined with any() (stop if any predicate fires):

function react(opts) {
  const llmStep = step.llm({
    id: 'react-step',
    model: opts.model,
    system: opts.system,
    tools: opts.tools,
  });

  return {
    kind: 'loop',
    id: 'react-loop',
    body: llmStep,
    until: any(
      until.noToolCalls(),
      until.maxSteps(opts.maxSteps ?? 10),
      ...(opts.maxCost ? [until.maxCost(opts.maxCost)] : []),
    ),
  };
}

When to Use

  • Single-agent tasks with tool access
  • Question answering with retrieval
  • Code generation with execution feedback
  • Any task where the agent decides when it is done

Customizing

Since react() returns a plain StepLoop, you can modify it or build your own variant:

// Add error handling
const myReact = react({ model: 'gpt-4o', tools });
myReact.onError = (error, ctx) => {
  if (error.kind === 'llm_rate_limit') return 'retry';
  return 'abort';
};

// Add iteration feedback
myReact.prepareNext = (output, verdict, ctx) => {
  return `Previous output: ${output}\nContinue.`;
};

On this page