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>| Parameter | Type | Default | Purpose |
|---|---|---|---|
model | string | -- | Model identifier (e.g., 'gpt-4o') |
system | string | -- | System prompt |
tools | Tool[] | -- | Available tools for the agent |
maxSteps | number | 10 | Maximum loop iterations |
maxCost | number | -- | 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:
step.llm-- calls the model with the current conversation and available toolsuntil.noToolCalls()-- stops when the model responds with plain text (no tool calls)until.maxSteps(n)-- safety limit on iterationsuntil.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.`;
};