Agentic AITypeScriptverifiedVerified
ReAct Agent Pattern in TypeScript
Interleaves chain-of-thought Reasoning with Action execution, enabling LLMs to dynamically plan, act, and observe in a loop.
How to Implement the ReAct Agent Pattern in TypeScript
1Step 1: Define the Tool and AgentStep interfaces
interface Tool {
name: string;
description: string;
execute(input: string): Promise<string>;
}
interface AgentStep {
thought: string;
action: string;
actionInput: string;
observation: string;
}2Step 2: Implement the ReAct reasoning loop
const MAX_STEPS = 10;
async function reactLoop(
query: string,
tools: Tool[],
llm: (prompt: string) => Promise<string>
): Promise<string> {
const steps: AgentStep[] = [];
for (let i = 0; i < MAX_STEPS; i++) {
// Reason about what to do next
const prompt = buildPrompt(query, tools, steps);
const response = await llm(prompt);
// Parse thought and action from response
const { thought, action, actionInput, isFinal, finalAnswer } =
parseResponse(response);
if (isFinal) return finalAnswer;
// Execute the chosen tool
const tool = tools.find((t) => t.name === action);
if (!tool) throw new Error(`Unknown tool: ${action}`);
const observation = await tool.execute(actionInput);
steps.push({ thought, action, actionInput, observation });
}
return "Max steps reached without final answer.";
}3Step 3: Build the prompt and parse LLM responses
function buildPrompt(
query: string,
tools: Tool[],
steps: AgentStep[]
): string {
// Build prompt with tools, history, and instructions
return `Query: ${query}\nTools: ${tools.map(t => t.name).join(", ")}`;
}
function parseResponse(response: string) {
// Parse LLM output into structured action
return {
thought: "",
action: "",
actionInput: "",
isFinal: false,
finalAnswer: "",
};
}import { z } from "zod";
// ── Types ────────────────────────────────────────────────────────
const ToolResultSchema = z.object({
success: z.boolean(),
data: z.string(),
metadata: z.record(z.unknown()).optional(),
});
type ToolResult = z.infer<typeof ToolResultSchema>;
interface Tool {
name: string;
description: string;
parameters: Record<string, string>;
execute(input: Record<string, string>): Promise<ToolResult>;
}
interface AgentStep {
thought: string;
action: string;
actionInput: Record<string, string>;
observation: ToolResult;
timestamp: number;
}
interface AgentConfig {
maxSteps: number;
model: string;
temperature: number;
tools: Tool[];
systemPrompt: string;
}
interface AgentResult {
answer: string;
steps: AgentStep[];
totalTokens: number;
durationMs: number;
}
// ── ReAct Agent ──────────────────────────────────────────────────
class ReActAgent {
private steps: AgentStep[] = [];
constructor(private config: AgentConfig) {}
async run(
query: string,
signal?: AbortSignal
): Promise<AgentResult> {
const startTime = Date.now();
this.steps = [];
let totalTokens = 0;
for (let i = 0; i < this.config.maxSteps; i++) {
if (signal?.aborted) {
throw new Error("Agent execution aborted");
}
// Build messages with full history
const messages = this.buildMessages(query);
// Get LLM response
const response = await this.callLLM(messages);
totalTokens += response.tokens;
// Check for final answer
if (response.isFinalAnswer) {
return {
answer: response.finalAnswer,
steps: this.steps,
totalTokens,
durationMs: Date.now() - startTime,
};
}
// Execute tool
const tool = this.config.tools.find(
(t) => t.name === response.action
);
if (!tool) {
throw new Error(
`Tool "${response.action}" not found. Available: ${
this.config.tools.map((t) => t.name).join(", ")
}`
);
}
const observation = await tool.execute(response.actionInput);
this.steps.push({
thought: response.thought,
action: response.action,
actionInput: response.actionInput,
observation,
timestamp: Date.now(),
});
}
return {
answer: "Reached maximum steps without a final answer.",
steps: this.steps,
totalTokens,
durationMs: Date.now() - startTime,
};
}
private buildMessages(query: string) {
return {
system: this.config.systemPrompt,
query,
history: this.steps,
};
}
private async callLLM(_messages: unknown) {
// Replace with actual LLM API call
return {
thought: "",
action: "",
actionInput: {} as Record<string, string>,
isFinalAnswer: true,
finalAnswer: "Mock response",
tokens: 150,
};
}
}
export { ReActAgent, type AgentConfig, type AgentResult, type Tool };ReAct Agent Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
ReAct Agent Pattern in the Real World
“Like a detective investigating a case: they form a hypothesis (Thought), gather evidence by interviewing witnesses or examining clues (Action), analyze what they found (Observation), and then refine their theory. They keep investigating until they solve the case.”