跳到主要内容

🔄 ReAct 架构

引言

ReAct(Reasoning and Acting)架构是 LangGraph 中最重要和最常用的智能代理架构模式之一。它将推理(Reasoning)、行动(Acting)和观察(Observation)三个核心概念结合在一起,让 LLM 能够在解决复杂问题时进行多步骤的思考和行动。

对于前端开发者来说,ReAct 架构就像是一个智能的状态机,它能够根据当前状态自主决定下一步的行动,并根据行动结果更新状态,直到达到目标。

概念解释

ReAct 的三个核心组件

ReAct 架构由三个核心组件组成,它们形成一个循环的执行模式:

  1. 推理(Reasoning):LLM 分析当前状态,理解问题并制定计划
  2. 行动(Acting):基于推理结果,选择并执行具体的工具或操作
  3. 观察(Observation):获取行动的结果,并将其作为新的信息输入到下一轮推理中

执行流程

代码示例

基础 ReAct 实现

基础 ReAct 代理示例:

import '../../utils/loadEnv';
import {
StateGraph,
Annotation,
messagesStateReducer,
START,
END,
} from '@langchain/langgraph';
import {
BaseMessage,
HumanMessage,
AIMessage,
ToolMessage,
} from '@langchain/core/messages';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { z } from 'zod';

// 定义状态
const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
});

// 定义工具
const searchTool = tool(
async (input) => {
// 模拟搜索 API 调用
await new Promise((resolve) => setTimeout(resolve, 1000));
return `搜索结果:关于 "${input.query}" 的相关信息已找到。这是一个模拟的搜索结果。`;
},
{
name: 'search',
description: '搜索互联网上的信息',
schema: z.object({
query: z.string().describe('搜索查询'),
}),
}
);

const calculatorTool = tool(
async (input) => {
try {
// 简单的数学表达式计算(生产环境中应使用更安全的计算方法)
const result = Function(`"use strict"; return (${input.expression})`)();
return `计算结果:${input.expression} = ${result}`;
} catch (error) {
return `计算错误:无法计算表达式 "${input.expression}"`;
}
},
{
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z
.string()
.describe('数学表达式,如 "2 + 3" 或 "Math.sqrt(16)"'),
}),
}
);

// 初始化模型和工具
const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0,
});

const tools = [searchTool, calculatorTool];
const toolNode = new ToolNode(tools);

// 绑定工具到模型
const modelWithTools = model.bindTools(tools);

// 定义代理节点(推理阶段)
const agentNode = async (state: typeof StateAnnotation.State) => {
const response = await modelWithTools.invoke(state.messages);
return { messages: [response] };
};

// 定义路由函数(决定下一步行动)
const shouldContinue = (state: typeof StateAnnotation.State) => {
const lastMessage = state.messages[state.messages.length - 1];

// 如果最后一条消息包含工具调用,则执行工具
if (
lastMessage instanceof AIMessage &&
lastMessage.tool_calls &&
lastMessage.tool_calls.length > 0
) {
return 'tools';
}

// 否则结束对话
return END;
};

// 构建 ReAct 图
const workflow = new StateGraph(StateAnnotation)
.addNode('agent', agentNode)
.addNode('tools', toolNode)
.addEdge(START, 'agent')
.addConditionalEdges('agent', shouldContinue)
.addEdge('tools', 'agent');

// 编译图
const app = workflow.compile();

// 使用示例
async function runReActExample() {
console.log('=== ReAct 架构示例 ===\n');

const examples = [
'请帮我搜索一下 TypeScript 的最新特性',
'计算 25 * 4 + 10 的结果',
'搜索 LangGraph 的相关信息,然后计算 100 / 5 的结果',
];

for (const [index, question] of examples.entries()) {
console.log(`示例 ${index + 1}: ${question}`);
console.log('---');

try {
const result = await app.invoke({
messages: [new HumanMessage(question)],
});

// 显示对话历史
result.messages.forEach((message, i) => {
if (message instanceof HumanMessage) {
console.log(`👤 用户: ${message.content}`);
} else if (message instanceof AIMessage) {
if (message.tool_calls && message.tool_calls.length > 0) {
console.log(`🤖 代理: 我需要使用工具来帮助回答这个问题`);
message.tool_calls.forEach((toolCall) => {
console.log(
`🔧 调用工具: ${toolCall.name}(${JSON.stringify(toolCall.args)})`
);
});
} else {
console.log(`🤖 代理: ${message.content}`);
}
} else if (message instanceof ToolMessage) {
console.log(`⚙️ 工具结果: ${message.content}`);
}
});
} catch (error) {
console.error('执行错误:', error);
}

console.log('\n' + '='.repeat(50) + '\n');
}
}

// 流式执行示例
async function runStreamingExample() {
console.log('=== 流式执行示例 ===\n');

const question = '搜索人工智能的发展历史,然后计算 2024 - 1956 等于多少年';
console.log(`问题: ${question}\n`);

try {
const stream = await app.stream({
messages: [new HumanMessage(question)],
});

for await (const chunk of stream) {
console.log('步骤:', Object.keys(chunk)[0]);
console.log('状态更新:', JSON.stringify(chunk, null, 2));
console.log('---');
}
} catch (error) {
console.error('流式执行错误:', error);
}
}

// 导出供其他模块使用
export {
app as reactAgent,
StateAnnotation,
runReActExample,
runStreamingExample,
};

// 如果直接运行此文件
if (require.main === module) {
runReActExample()
.then(() => {
console.log('\n开始流式执行示例...\n');
return runStreamingExample();
})
.catch(console.error);
}

使用预构建的 ReAct 代理

LangGraph 提供了预构建的 createReactAgent 函数,可以快速创建 ReAct 架构的代理:

预构建 ReAct 代理示例:

import '../../utils/loadEnv';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { HumanMessage } from '@langchain/core/messages';
import { z } from 'zod';

// 定义工具
const searchTool = tool(
async (input) => {
// 模拟搜索 API 调用
await new Promise((resolve) => setTimeout(resolve, 800));
return `搜索结果:关于 "${input.query}" 的详细信息:
- 这是一个模拟的搜索结果
- 包含了相关的技术信息和最新动态
- 数据来源可靠,更新及时`;
},
{
name: 'search',
description: '搜索互联网上的最新信息',
schema: z.object({
query: z.string().describe('搜索查询关键词'),
}),
}
);

const calculatorTool = tool(
async (input) => {
try {
// 安全的数学表达式计算
const allowedOperations = /^[\d\s+\-*/().]+$/;
if (!allowedOperations.test(input.expression)) {
throw new Error('不支持的数学表达式');
}

const result = Function(`"use strict"; return (${input.expression})`)();
return `计算结果:${input.expression} = ${result}`;
} catch (error) {
return `计算错误:${error.message}`;
}
},
{
name: 'calculator',
description: '执行基础数学计算',
schema: z.object({
expression: z
.string()
.describe('数学表达式,支持 +、-、*、/、() 等基本运算'),
}),
}
);

const weatherTool = tool(
async (input) => {
// 模拟天气 API 调用
await new Promise((resolve) => setTimeout(resolve, 600));
const weatherData = {
北京: { temp: '22°C', condition: '晴天', humidity: '45%' },
上海: { temp: '26°C', condition: '多云', humidity: '60%' },
广州: { temp: '28°C', condition: '小雨', humidity: '75%' },
深圳: { temp: '27°C', condition: '阴天', humidity: '70%' },
};

const weather = weatherData[input.city] || {
temp: '25°C',
condition: '晴天',
humidity: '50%',
};

return `${input.city}的天气情况:
温度:${weather.temp}
天气:${weather.condition}
湿度:${weather.humidity}`;
},
{
name: 'get_weather',
description: '获取指定城市的天气信息',
schema: z.object({
city: z.string().describe('城市名称'),
}),
}
);

// 初始化模型
const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0.1,
});

// 创建预构建的 ReAct 代理
const agent = createReactAgent({
llm: model,
tools: [searchTool, calculatorTool, weatherTool],
});

// 使用示例
async function runPrebuiltReActExample() {
console.log('=== 预构建 ReAct 代理示例 ===\n');

const testCases = [
{
name: '单工具使用 - 搜索',
question: '请搜索一下 React 18 的新特性',
},
{
name: '单工具使用 - 计算',
question: '帮我计算 (25 + 15) * 3 - 20 的结果',
},
{
name: '单工具使用 - 天气',
question: '北京今天的天气怎么样?',
},
{
name: '多工具组合使用',
question:
'请搜索一下人工智能的发展历史,然后告诉我上海的天气,最后计算 2024 - 1956 等于多少年',
},
{
name: '复杂任务处理',
question:
'我想了解 TypeScript 的最新版本信息,同时帮我计算如果我每天学习2小时,30天能学习多少小时,另外告诉我深圳的天气情况',
},
];

for (const testCase of testCases) {
console.log(`📋 测试案例: ${testCase.name}`);
console.log(`❓ 问题: ${testCase.question}`);
console.log('---');

try {
const startTime = Date.now();

const result = await agent.invoke({
messages: [new HumanMessage(testCase.question)],
});

const endTime = Date.now();
const duration = endTime - startTime;

// 显示最终回答
const finalMessage = result.messages[result.messages.length - 1];
console.log(`🤖 回答: ${finalMessage.content}`);
console.log(`⏱️ 执行时间: ${duration}ms`);
} catch (error) {
console.error('❌ 执行错误:', error.message);
}

console.log('\n' + '='.repeat(60) + '\n');
}
}

// 交互式对话示例
async function runInteractiveExample() {
console.log('=== 交互式对话示例 ===\n');

const conversation = [
'你好,我想了解一下 LangGraph 的相关信息',
'那能帮我计算一下 15 * 8 + 32 的结果吗?',
'最后告诉我广州的天气情况',
];

let messages = [];

for (const [index, userInput] of conversation.entries()) {
console.log(`👤 用户 (第${index + 1}轮): ${userInput}`);

messages.push(new HumanMessage(userInput));

try {
const result = await agent.invoke({ messages });

// 更新消息历史
messages = result.messages;

const aiResponse = result.messages[result.messages.length - 1];
console.log(`🤖 助手: ${aiResponse.content}`);
} catch (error) {
console.error('❌ 对话错误:', error.message);
}

console.log('---');
}
}

// 流式响应示例
async function runStreamingExample() {
console.log('=== 流式响应示例 ===\n');

const question =
'请搜索 Vue.js 3.0 的新特性,然后计算 100 / 4 的结果,最后告诉我北京的天气';
console.log(`❓ 问题: ${question}\n`);

try {
const stream = await agent.stream({
messages: [new HumanMessage(question)],
});

let stepCount = 0;
for await (const chunk of stream) {
stepCount++;
const nodeName = Object.keys(chunk)[0];
console.log(`📍 步骤 ${stepCount}: ${nodeName}`);

if (chunk[nodeName]?.messages) {
const message = chunk[nodeName].messages[0];
if (message.content) {
console.log(`💬 内容: ${message.content.substring(0, 100)}...`);
}
if (message.tool_calls && message.tool_calls.length > 0) {
message.tool_calls.forEach((toolCall) => {
console.log(
`🔧 工具调用: ${toolCall.name}(${JSON.stringify(toolCall.args)})`
);
});
}
}
console.log('---');
}
} catch (error) {
console.error('❌ 流式执行错误:', error.message);
}
}

// 导出供其他模块使用
export {
agent as prebuiltReactAgent,
runPrebuiltReActExample,
runInteractiveExample,
runStreamingExample,
};

// 如果直接运行此文件
if (require.main === module) {
runPrebuiltReActExample()
.then(() => {
console.log('\n开始交互式对话示例...\n');
return runInteractiveExample();
})
.then(() => {
console.log('\n开始流式响应示例...\n');
return runStreamingExample();
})
.catch(console.error);
}

可视化说明

ReAct 架构流程图

状态变化示例

实践指导

步骤 1:定义工具

首先定义代理可以使用的工具:

import { tool } from '@langchain/core/tools';
import { z } from 'zod';

// 搜索工具
const searchTool = tool(
async (input) => {
// 模拟搜索 API 调用
return `搜索结果:关于 ${input.query} 的相关信息...`;
},
{
name: 'search',
description: '搜索互联网上的信息',
schema: z.object({
query: z.string().describe('搜索查询'),
}),
}
);

// 计算工具
const calculatorTool = tool(
async (input) => {
try {
// 简单的数学表达式计算
const result = eval(input.expression);
return `计算结果:${input.expression} = ${result}`;
} catch (error) {
return `计算错误:${error.message}`;
}
},
{
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z.string().describe('数学表达式'),
}),
}
);

步骤 2:创建 ReAct 代理

import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0,
});

const tools = [searchTool, calculatorTool];

// 创建 ReAct 代理
const agent = createReactAgent({
llm: model,
tools: tools,
systemMessage: `你是一个有用的助手。你可以使用提供的工具来帮助回答问题。

在回答问题时,请遵循以下步骤:
1. 仔细分析问题
2. 确定是否需要使用工具获取信息
3. 如果需要,选择合适的工具并提供正确的参数
4. 基于工具结果提供准确的答案

如果不需要工具,请直接回答问题。`,
});

步骤 3:使用代理

// 使用代理回答问题
const response = await agent.invoke({
messages: [
{
role: 'user',
content: '请帮我搜索一下 TypeScript 的最新特性,然后计算 25 * 4 的结果',
},
],
});

console.log(response.messages[response.messages.length - 1].content);

自定义 ReAct 实现

如果需要更多控制,可以手动实现 ReAct 架构:

自定义 ReAct 实现示例:

import '../../utils/loadEnv';
import {
StateGraph,
Annotation,
messagesStateReducer,
START,
END,
} from '@langchain/langgraph';
import {
BaseMessage,
HumanMessage,
AIMessage,
ToolMessage,
SystemMessage,
} from '@langchain/core/messages';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { z } from 'zod';

// 扩展状态定义,包含更多控制信息
const CustomStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
// 添加自定义状态字段
toolCallCount: Annotation<number>(),
maxToolCalls: Annotation<number>(),
currentStep: Annotation<string>(),
reasoning: Annotation<string[]>({
reducer: (state: string[], update: string[]) => [...state, ...update],
default: () => [],
}),
});

// 定义工具
const searchTool = tool(
async (input) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return `🔍 搜索结果:关于 "${input.query}" 的信息:
- 相关技术文档和教程
- 最新版本更新和特性
- 社区讨论和最佳实践
- 官方文档链接`;
},
{
name: 'search',
description: '搜索技术相关信息',
schema: z.object({
query: z.string().describe('搜索关键词'),
}),
}
);

const calculatorTool = tool(
async (input) => {
try {
const result = Function(`"use strict"; return (${input.expression})`)();
return `🧮 计算结果:${input.expression} = ${result}`;
} catch (error) {
return `❌ 计算错误:${error.message}`;
}
},
{
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z.string().describe('数学表达式'),
}),
}
);

const analysisTool = tool(
async (input) => {
await new Promise((resolve) => setTimeout(resolve, 800));
return `📊 分析结果:
主题:${input.topic}
分析维度:
- 技术复杂度:中等
- 学习难度:适中
- 应用场景:广泛
- 发展趋势:积极向上
建议:建议深入学习和实践`;
},
{
name: 'analyze',
description: '分析技术主题',
schema: z.object({
topic: z.string().describe('要分析的主题'),
}),
}
);

// 初始化模型和工具
const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0.2,
});

const tools = [searchTool, calculatorTool, analysisTool];
const toolNode = new ToolNode(tools);
const modelWithTools = model.bindTools(tools);

// 自定义推理节点
const reasoningNode = async (state: typeof CustomStateAnnotation.State) => {
console.log(`🤔 推理阶段 - 当前步骤: ${state.currentStep}`);

// 添加系统消息来指导推理
const systemMessage =
new SystemMessage(`你是一个专业的AI助手,使用ReAct架构来解决问题。

当前状态:
- 工具调用次数:${state.toolCallCount}/${state.maxToolCalls}
- 当前步骤:${state.currentStep}

请按照以下步骤进行推理:
1. 分析用户的问题和需求
2. 确定需要使用的工具和顺序
3. 如果需要工具,选择合适的工具并提供参数
4. 如果信息足够,直接提供答案

可用工具:
- search: 搜索技术信息
- calculator: 数学计算
- analyze: 技术分析

请基于当前对话历史进行推理和决策。`);

const messagesWithSystem = [systemMessage, ...state.messages];
const response = await modelWithTools.invoke(messagesWithSystem);

return {
messages: [response],
currentStep: 'reasoning_complete',
reasoning: [
`推理完成:${response.content ? String(response.content).substring(0, 100) : '工具调用'}`,
],
};
};

// 自定义工具执行节点
const actionNode = async (state: typeof CustomStateAnnotation.State) => {
console.log(`🔧 行动阶段 - 执行工具调用`);

const lastMessage = state.messages[state.messages.length - 1];
if (!(lastMessage instanceof AIMessage) || !lastMessage.tool_calls) {
return { currentStep: 'action_error' };
}

// 执行工具调用
const toolResults = await toolNode.invoke(state);

return {
...toolResults,
toolCallCount: state.toolCallCount + lastMessage.tool_calls.length,
currentStep: 'action_complete',
reasoning: [`执行了 ${lastMessage.tool_calls.length} 个工具调用`],
};
};

// 观察节点
const observationNode = async (state: typeof CustomStateAnnotation.State) => {
console.log(`👁️ 观察阶段 - 分析工具结果`);

const lastMessage = state.messages[state.messages.length - 1];
let observation = '';

if (lastMessage instanceof ToolMessage) {
observation = `观察到工具 ${lastMessage.name} 的执行结果`;
}

return {
currentStep: 'observation_complete',
reasoning: [observation],
};
};

// 决策函数
const shouldContinue = (state: typeof CustomStateAnnotation.State) => {
const lastMessage = state.messages[state.messages.length - 1];

// 检查是否达到最大工具调用次数
if (state.toolCallCount >= state.maxToolCalls) {
console.log('⚠️ 达到最大工具调用次数限制');
return END;
}

// 如果有工具调用,执行行动
if (
lastMessage instanceof AIMessage &&
lastMessage.tool_calls &&
lastMessage.tool_calls.length > 0
) {
return 'action';
}

// 否则结束
return END;
};

// 行动后的路由
const afterAction = (state: typeof CustomStateAnnotation.State) => {
return 'observation';
};

// 观察后的路由
const afterObservation = (state: typeof CustomStateAnnotation.State) => {
// 继续推理循环
return 'reasoning';
};

// 构建自定义 ReAct 图
const customReactWorkflow = new StateGraph(CustomStateAnnotation)
.addNode('reasoning', reasoningNode)
.addNode('action', actionNode)
.addNode('observation', observationNode)
.addEdge(START, 'reasoning')
.addConditionalEdges('reasoning', shouldContinue)
.addEdge('action', 'observation')
.addEdge('observation', 'reasoning');

// 编译图
const customReactAgent = customReactWorkflow.compile();

// 使用示例
async function runCustomReActExample() {
console.log('=== 自定义 ReAct 架构示例 ===\n');

const testCases = [
{
name: '简单查询',
question: '请搜索一下 React Hooks 的相关信息',
maxToolCalls: 3,
},
{
name: '计算任务',
question: '帮我计算 (100 + 50) * 2 - 75 的结果',
maxToolCalls: 2,
},
{
name: '复合任务',
question:
'搜索 TypeScript 的信息,然后分析它的技术特点,最后计算如果学习30天,每天2小时,总共多少小时',
maxToolCalls: 5,
},
];

for (const testCase of testCases) {
console.log(`📋 测试案例: ${testCase.name}`);
console.log(`❓ 问题: ${testCase.question}`);
console.log(`🔢 最大工具调用次数: ${testCase.maxToolCalls}`);
console.log('---');

try {
const result = await customReactAgent.invoke({
messages: [new HumanMessage(testCase.question)],
maxToolCalls: testCase.maxToolCalls,
});

console.log('\n📊 执行结果:');
console.log(`- 工具调用次数: ${result.toolCallCount}`);
console.log(`- 最终步骤: ${result.currentStep}`);
console.log(`- 推理过程: ${result.reasoning.join(' → ')}`);

const finalMessage = result.messages[result.messages.length - 1];
if (finalMessage instanceof AIMessage && finalMessage.content) {
console.log(`🤖 最终回答: ${finalMessage.content}`);
}
} catch (error) {
console.error('❌ 执行错误:', error.message);
}

console.log('\n' + '='.repeat(60) + '\n');
}
}

// 详细执行跟踪示例
async function runDetailedTrackingExample() {
console.log('=== 详细执行跟踪示例 ===\n');

const question =
'搜索 Vue.js 的信息,分析其特点,然后计算学习100小时需要多少天(每天学习3小时)';
console.log(`❓ 问题: ${question}\n`);

try {
const stream = await customReactAgent.stream({
messages: [new HumanMessage(question)],
maxToolCalls: 6,
});

let stepCount = 0;
for await (const chunk of stream) {
stepCount++;
const nodeName = Object.keys(chunk)[0];
const nodeData = chunk[nodeName];

console.log(`📍 步骤 ${stepCount}: ${nodeName}`);

if (nodeData.currentStep) {
console.log(` 当前阶段: ${nodeData.currentStep}`);
}

if (nodeData.toolCallCount !== undefined) {
console.log(` 工具调用次数: ${nodeData.toolCallCount}`);
}

if (nodeData.reasoning && nodeData.reasoning.length > 0) {
console.log(
` 推理信息: ${nodeData.reasoning[nodeData.reasoning.length - 1]}`
);
}

if (nodeData.messages && nodeData.messages.length > 0) {
const message = nodeData.messages[nodeData.messages.length - 1];
if (message instanceof AIMessage && message.tool_calls) {
message.tool_calls.forEach((toolCall) => {
console.log(
` 🔧 工具调用: ${toolCall.name}(${JSON.stringify(toolCall.args)})`
);
});
} else if (message instanceof ToolMessage) {
console.log(
` ⚙️ 工具结果: ${String(message.content).substring(0, 50)}...`
);
}
}

console.log('---');
}
} catch (error) {
console.error('❌ 详细跟踪错误:', error.message);
}
}

// 导出供其他模块使用
export {
customReactAgent,
CustomStateAnnotation,
runCustomReActExample,
runDetailedTrackingExample,
};

// 如果直接运行此文件
if (require.main === module) {
runCustomReActExample()
.then(() => {
console.log('\n开始详细执行跟踪示例...\n');
return runDetailedTrackingExample();
})
.catch(console.error);
}

高级特性

工具调用错误处理

错误处理示例:

import '../../utils/loadEnv';
import {
StateGraph,
Annotation,
messagesStateReducer,
START,
END,
} from '@langchain/langgraph';
import {
BaseMessage,
HumanMessage,
AIMessage,
ToolMessage,
} from '@langchain/core/messages';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { z } from 'zod';

// 扩展状态定义,包含错误处理信息
const ErrorHandlingStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
errors: Annotation<string[]>({
reducer: (state: string[], update: string[]) => [...state, ...update],
default: () => [],
}),
retryCount: Annotation<number>(),
maxRetries: Annotation<number>(),
});

// 定义可能失败的工具
const unreliableSearchTool = tool(
async (input) => {
// 模拟网络不稳定,30% 概率失败
if (Math.random() < 0.3) {
throw new Error('网络连接超时,搜索服务暂时不可用');
}

await new Promise((resolve) => setTimeout(resolve, 1000));
return `🔍 搜索结果:关于 "${input.query}" 的信息已找到`;
},
{
name: 'unreliable_search',
description: '不稳定的搜索工具(可能失败)',
schema: z.object({
query: z.string().describe('搜索查询'),
}),
}
);

const faultyCalculatorTool = tool(
async (input) => {
// 模拟计算错误,对于某些表达式会失败
if (
input.expression.includes('undefined') ||
input.expression.includes('null')
) {
throw new Error('计算表达式包含无效值');
}

if (input.expression.includes('/0')) {
throw new Error('除零错误:不能除以零');
}

try {
const result = Function(`"use strict"; return (${input.expression})`)();
if (!isFinite(result)) {
throw new Error('计算结果不是有限数值');
}
return `🧮 计算结果:${input.expression} = ${result}`;
} catch (error) {
throw new Error(`计算错误:${error.message}`);
}
},
{
name: 'faulty_calculator',
description: '可能出错的计算工具',
schema: z.object({
expression: z.string().describe('数学表达式'),
}),
}
);

const timeoutTool = tool(
async (input) => {
// 模拟超时,50% 概率超时
const timeout = Math.random() < 0.5;
if (timeout) {
throw new Error('操作超时:服务响应时间过长');
}

await new Promise((resolve) => setTimeout(resolve, 2000));
return `⏰ 处理完成:${input.task}`;
},
{
name: 'timeout_tool',
description: '可能超时的处理工具',
schema: z.object({
task: z.string().describe('要处理的任务'),
}),
}
);

// 初始化模型和工具
const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0,
});

const tools = [unreliableSearchTool, faultyCalculatorTool, timeoutTool];
const modelWithTools = model.bindTools(tools);

// 自定义工具节点,包含错误处理
class ErrorHandlingToolNode {
private tools: any[];

constructor(tools: any[]) {
this.tools = tools;
}

async invoke(state: typeof ErrorHandlingStateAnnotation.State) {
const lastMessage = state.messages[state.messages.length - 1];

if (!(lastMessage instanceof AIMessage) || !lastMessage.tool_calls) {
return {
messages: [
new ToolMessage({
content: '错误:没有找到工具调用',
tool_call_id: 'error',
}),
],
errors: ['没有找到工具调用'],
};
}

const toolResults: ToolMessage[] = [];
const errors: string[] = [];

for (const toolCall of lastMessage.tool_calls) {
try {
console.log(`🔧 执行工具: ${toolCall.name}`);

// 查找对应的工具
const tool = this.tools.find((t) => t.name === toolCall.name);
if (!tool) {
throw new Error(`未找到工具: ${toolCall.name}`);
}

// 执行工具
const result = await tool.invoke(toolCall.args);

toolResults.push(
new ToolMessage({
content: result,
tool_call_id: toolCall.id,
})
);

console.log(`✅ 工具执行成功: ${toolCall.name}`);
} catch (error) {
const errorMessage = `工具 ${toolCall.name} 执行失败: ${error.message}`;
console.log(`${errorMessage}`);

errors.push(errorMessage);

// 创建错误消息
toolResults.push(
new ToolMessage({
content: `错误:${error.message}`,
tool_call_id: toolCall.id,
})
);
}
}

return {
messages: toolResults,
errors: errors,
};
}
}

const errorHandlingToolNode = new ErrorHandlingToolNode(tools);

// 代理节点
const agentNode = async (state: typeof ErrorHandlingStateAnnotation.State) => {
// 如果有错误且重试次数未达到上限,添加重试指导
let systemPrompt = '';
if (state.errors.length > 0 && state.retryCount < state.maxRetries) {
systemPrompt = `
之前的操作遇到了以下错误:
${state.errors.slice(-3).join('\n')}

当前重试次数:${state.retryCount}/${state.maxRetries}

请根据错误信息调整策略:
1. 如果是网络错误,可以重试相同的操作
2. 如果是参数错误,请修正参数后重试
3. 如果是计算错误,请检查表达式的正确性
4. 如果多次失败,考虑使用替代方案或简化任务

请继续尝试完成用户的请求。
`;
}

const messages = systemPrompt
? [new HumanMessage(systemPrompt), ...state.messages]
: state.messages;

const response = await modelWithTools.invoke(messages);
return { messages: [response] };
};

// 重试逻辑节点
const retryNode = async (state: typeof ErrorHandlingStateAnnotation.State) => {
console.log(`🔄 重试节点 - 当前重试次数: ${state.retryCount}`);

return {
retryCount: state.retryCount + 1,
};
};

// 路由函数
const shouldContinue = (state: typeof ErrorHandlingStateAnnotation.State) => {
const lastMessage = state.messages[state.messages.length - 1];

// 如果有工具调用,执行工具
if (
lastMessage instanceof AIMessage &&
lastMessage.tool_calls &&
lastMessage.tool_calls.length > 0
) {
return 'tools';
}

return END;
};

// 错误处理路由
const handleErrors = (state: typeof ErrorHandlingStateAnnotation.State) => {
// 检查最近的工具消息是否包含错误
const recentMessages = state.messages.slice(-5);
const hasErrors = recentMessages.some(
(msg) =>
msg instanceof ToolMessage && String(msg.content).startsWith('错误:')
);

if (hasErrors && state.retryCount < state.maxRetries) {
console.log(
`⚠️ 检测到错误,准备重试 (${state.retryCount + 1}/${state.maxRetries})`
);
return 'retry';
}

// 继续正常流程
return 'agent';
};

// 构建错误处理图
const errorHandlingWorkflow = new StateGraph(ErrorHandlingStateAnnotation)
.addNode('agent', agentNode)
.addNode('tools', errorHandlingToolNode.invoke.bind(errorHandlingToolNode))
.addNode('retry', retryNode)
.addEdge(START, 'agent')
.addConditionalEdges('agent', shouldContinue)
.addConditionalEdges('tools', handleErrors)
.addEdge('retry', 'agent');

// 编译图
const errorHandlingAgent = errorHandlingWorkflow.compile();

// 使用示例
async function runErrorHandlingExample() {
console.log('=== 错误处理示例 ===\n');

const testCases = [
{
name: '网络不稳定搜索',
question: '请搜索一下 React 的相关信息',
maxRetries: 3,
},
{
name: '计算错误处理',
question: '帮我计算 10/0 的结果',
maxRetries: 2,
},
{
name: '超时处理',
question: '请处理一个复杂的数据分析任务',
maxRetries: 2,
},
{
name: '多工具组合错误',
question:
'搜索 TypeScript 信息,然后计算 undefined + 5,最后处理一个任务',
maxRetries: 3,
},
];

for (const testCase of testCases) {
console.log(`📋 测试案例: ${testCase.name}`);
console.log(`❓ 问题: ${testCase.question}`);
console.log(`🔄 最大重试次数: ${testCase.maxRetries}`);
console.log('---');

try {
const result = await errorHandlingAgent.invoke({
messages: [new HumanMessage(testCase.question)],
maxRetries: testCase.maxRetries,
retryCount: 0,
});

console.log('\n📊 执行结果:');
console.log(`- 重试次数: ${result.retryCount}`);
console.log(`- 错误数量: ${result.errors.length}`);

if (result.errors.length > 0) {
console.log('- 遇到的错误:');
result.errors.forEach((error, index) => {
console.log(` ${index + 1}. ${error}`);
});
}

const finalMessage = result.messages[result.messages.length - 1];
if (finalMessage instanceof AIMessage && finalMessage.content) {
console.log(`🤖 最终回答: ${finalMessage.content}`);
}
} catch (error) {
console.error('❌ 执行错误:', error.message);
}

console.log('\n' + '='.repeat(60) + '\n');
}
}

// 详细错误跟踪示例
async function runDetailedErrorTrackingExample() {
console.log('=== 详细错误跟踪示例 ===\n');

const question = '搜索 Vue.js 信息,计算 100/0,然后处理一个复杂任务';
console.log(`❓ 问题: ${question}\n`);

try {
const stream = await errorHandlingAgent.stream({
messages: [new HumanMessage(question)],
maxRetries: 3,
retryCount: 0,
});

let stepCount = 0;
for await (const chunk of stream) {
stepCount++;
const nodeName = Object.keys(chunk)[0];
const nodeData = chunk[nodeName];

console.log(`📍 步骤 ${stepCount}: ${nodeName}`);

if (nodeData.retryCount !== undefined) {
console.log(` 重试次数: ${nodeData.retryCount}`);
}

if (nodeData.errors && nodeData.errors.length > 0) {
console.log(
` 新增错误: ${nodeData.errors[nodeData.errors.length - 1]}`
);
}

if (nodeData.messages && nodeData.messages.length > 0) {
const message = nodeData.messages[nodeData.messages.length - 1];
if (message instanceof ToolMessage) {
const isError = String(message.content).startsWith('错误:');
console.log(
` ${isError ? '❌' : '✅'} 工具结果: ${String(message.content).substring(0, 50)}...`
);
}
}

console.log('---');
}
} catch (error) {
console.error('❌ 详细跟踪错误:', error.message);
}
}

// 导出供其他模块使用
export {
errorHandlingAgent,
ErrorHandlingStateAnnotation,
runErrorHandlingExample,
runDetailedErrorTrackingExample,
};

// 如果直接运行此文件
if (require.main === module) {
runErrorHandlingExample()
.then(() => {
console.log('\n开始详细错误跟踪示例...\n');
return runDetailedErrorTrackingExample();
})
.catch(console.error);
}

多轮对话支持

多轮对话示例:

import '../../utils/loadEnv';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { ChatOpenAI } from '@langchain/openai';
import { tool } from '@langchain/core/tools';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
import { z } from 'zod';

// 定义工具
const searchTool = tool(
async (input) => {
// 模拟搜索 API 调用
await new Promise((resolve) => setTimeout(resolve, 1000));

const searchResults = {
React:
'🔍 React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发。最新版本包含了 Hooks、Concurrent Features 等特性。',
TypeScript:
'🔍 TypeScript 是 JavaScript 的超集,添加了静态类型检查。最新版本支持更好的类型推断和新的语法特性。',
Vue: '🔍 Vue.js 是一个渐进式 JavaScript 框架,以其简单易学和灵活性著称。Vue 3 引入了 Composition API 和更好的性能。',
Angular:
'🔍 Angular 是一个完整的前端框架,提供了丰富的功能和工具。最新版本专注于性能优化和开发体验改进。',
};

const query = input.query.toLowerCase();
for (const [key, value] of Object.entries(searchResults)) {
if (query.includes(key.toLowerCase())) {
return value;
}
}

return `🔍 搜索结果:关于 "${input.query}" 的相关信息已找到,包含最新的技术动态和发展趋势。`;
},
{
name: 'search',
description: '搜索技术相关信息',
schema: z.object({
query: z.string().describe('搜索查询关键词'),
}),
}
);

const calculatorTool = tool(
async (input) => {
try {
// 安全的数学表达式计算
const allowedOperations = /^[\d\s+\-*/().]+$/;
if (!allowedOperations.test(input.expression)) {
throw new Error('不支持的数学表达式');
}

const result = Function(`"use strict"; return (${input.expression})`)();
return `🧮 计算结果:${input.expression} = ${result}`;
} catch (error) {
return `❌ 计算错误:${error.message}`;
}
},
{
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z.string().describe('数学表达式'),
}),
}
);

const notesTool = tool(
async (input) => {
// 模拟笔记存储
const notes = new Map();

if (input.action === 'save') {
notes.set(input.key, input.content);
return `📝 笔记已保存:${input.key}`;
} else if (input.action === 'get') {
const note = notes.get(input.key) || '未找到相关笔记';
return `📖 笔记内容:${note}`;
} else if (input.action === 'list') {
const notesList = Array.from(notes.keys()).join(', ') || '暂无笔记';
return `📋 所有笔记:${notesList}`;
}

return '❓ 不支持的笔记操作';
},
{
name: 'notes',
description: '管理笔记和记录',
schema: z.object({
action: z.enum(['save', 'get', 'list']).describe('操作类型'),
key: z.string().optional().describe('笔记标识'),
content: z.string().optional().describe('笔记内容'),
}),
}
);

// 初始化模型
const model = new ChatOpenAI({
model: process.env.OPENAI_MODEL_NAME,
temperature: 0.1,
});

// 创建多轮对话代理
const multiTurnAgent = createReactAgent({
llm: model,
tools: [searchTool, calculatorTool, notesTool],
});

// 对话会话管理类
class ConversationSession {
public messages: any[] = [];
private sessionId: string;
private context: Map<string, any> = new Map();

constructor(sessionId: string) {
this.sessionId = sessionId;
}

// 添加用户消息
addUserMessage(content: string) {
this.messages.push(new HumanMessage(content));
}

// 获取对话历史
getMessages() {
return [...this.messages];
}

// 设置上下文信息
setContext(key: string, value: any) {
this.context.set(key, value);
}

// 获取上下文信息
getContext(key: string) {
return this.context.get(key);
}

// 清除对话历史
clearHistory() {
this.messages = [];
}

// 获取会话摘要
getSummary() {
return {
sessionId: this.sessionId,
messageCount: this.messages.length,
context: Object.fromEntries(this.context),
};
}
}

// 多轮对话示例
async function runMultiTurnExample() {
console.log('=== 多轮对话示例 ===\n');

const session = new ConversationSession('demo-session-001');

const conversation = [
{
turn: 1,
user: '你好,我想了解一下 React 的相关信息',
context: '开始新的技术咨询对话',
},
{
turn: 2,
user: '那 TypeScript 和 React 结合使用有什么优势?',
context: '继续讨论技术栈组合',
},
{
turn: 3,
user: '帮我计算一下,如果我每天学习 2 小时,30 天能学习多少小时?',
context: '学习时间规划计算',
},
{
turn: 4,
user: '请帮我记录一下:React + TypeScript 学习计划 - 30天共60小时',
context: '保存学习计划',
},
{
turn: 5,
user: '现在搜索一下 Vue.js 的信息,我想对比一下不同框架',
context: '技术对比分析',
},
{
turn: 6,
user: '总结一下我们今天讨论的内容',
context: '对话总结',
},
];

for (const { turn, user, context } of conversation) {
console.log(`👤 第${turn}轮对话 - 用户: ${user}`);
console.log(`📝 上下文: ${context}`);
console.log('---');

// 添加用户消息到会话
session.addUserMessage(user);
session.setContext(`turn_${turn}_context`, context);

try {
// 调用代理处理消息
const result = await multiTurnAgent.invoke({
messages: session.getMessages(),
});

// 更新会话消息历史
session.messages = result.messages;

// 获取最新的 AI 回复
const aiResponse = result.messages[result.messages.length - 1];
if (aiResponse instanceof AIMessage) {
console.log(`🤖 助手: ${aiResponse.content}`);
}
} catch (error) {
console.error(`❌ 第${turn}轮对话错误:`, error.message);
}

console.log('\n' + '='.repeat(50) + '\n');
}

// 显示会话摘要
console.log('📊 会话摘要:');
console.log(JSON.stringify(session.getSummary(), null, 2));
}

// 上下文感知对话示例
async function runContextAwareExample() {
console.log('=== 上下文感知对话示例 ===\n');

const session = new ConversationSession('context-demo-001');

// 设置初始上下文
session.setContext('user_role', 'frontend_developer');
session.setContext('experience_level', 'intermediate');
session.setContext('current_project', 'React应用开发');

const contextualConversation = [
'我正在开发一个 React 应用,遇到了状态管理的问题',
'具体来说,我有多个组件需要共享用户信息',
'你觉得应该用 Context API 还是 Redux?',
'如果用 Context API,性能会有问题吗?',
'帮我计算一下,如果有 50 个组件订阅同一个 Context,重新渲染的开销',
'请记录一下我们讨论的状态管理方案对比',
];

for (const [index, userInput] of contextualConversation.entries()) {
console.log(`👤 用户 (第${index + 1}轮): ${userInput}`);

// 构建包含上下文的消息
const contextInfo = `
用户背景:
- 角色:${session.getContext('user_role')}
- 经验水平:${session.getContext('experience_level')}
- 当前项目:${session.getContext('current_project')}

请基于用户背景提供针对性的建议。
`;

session.addUserMessage(contextInfo + '\n用户问题:' + userInput);

try {
const result = await multiTurnAgent.invoke({
messages: session.getMessages(),
});

session.messages = result.messages;

const aiResponse = result.messages[result.messages.length - 1];
if (aiResponse instanceof AIMessage) {
console.log(`🤖 助手: ${aiResponse.content}`);
}
} catch (error) {
console.error(`❌ 对话错误:`, error.message);
}

console.log('---');
}
}

// 对话分支管理示例
async function runConversationBranchingExample() {
console.log('=== 对话分支管理示例 ===\n');

const mainSession = new ConversationSession('main-session');

// 主对话线
console.log('🌟 主对话线开始');
mainSession.addUserMessage('我想学习前端开发,应该从哪里开始?');

let result = await multiTurnAgent.invoke({
messages: mainSession.getMessages(),
});
mainSession.messages = result.messages;

const mainResponse = result.messages[result.messages.length - 1];
console.log(`🤖 主线回答: ${mainResponse.content}`);

// 创建分支对话 - 深入 HTML/CSS
console.log('\n🌿 分支1: 深入 HTML/CSS');
const branch1 = new ConversationSession('branch-html-css');
branch1.messages = [...mainSession.getMessages()]; // 继承主对话历史
branch1.addUserMessage('我想深入了解 HTML 和 CSS 的高级特性');

result = await multiTurnAgent.invoke({
messages: branch1.getMessages(),
});

const branch1Response = result.messages[result.messages.length - 1];
console.log(`🤖 分支1回答: ${branch1Response.content}`);

// 创建分支对话 - 深入 JavaScript
console.log('\n🌿 分支2: 深入 JavaScript');
const branch2 = new ConversationSession('branch-javascript');
branch2.messages = [...mainSession.getMessages()]; // 继承主对话历史
branch2.addUserMessage('我更想专注于 JavaScript 和现代框架');

result = await multiTurnAgent.invoke({
messages: branch2.getMessages(),
});

const branch2Response = result.messages[result.messages.length - 1];
console.log(`🤖 分支2回答: ${branch2Response.content}`);

// 合并分支见解
console.log('\n🔄 合并分支见解');
mainSession.addUserMessage(`
基于我们的讨论,我了解了:
1. HTML/CSS 方面:${String(branch1Response.content).substring(0, 100)}...
2. JavaScript 方面:${String(branch2Response.content).substring(0, 100)}...

请帮我制定一个综合的学习计划。
`);

result = await multiTurnAgent.invoke({
messages: mainSession.getMessages(),
});

const finalResponse = result.messages[result.messages.length - 1];
console.log(`🤖 综合建议: ${finalResponse.content}`);
}

// 导出供其他模块使用
export {
multiTurnAgent,
ConversationSession,
runMultiTurnExample,
runContextAwareExample,
runConversationBranchingExample,
};

// 如果直接运行此文件
if (require.main === module) {
runMultiTurnExample()
.then(() => {
console.log('\n开始上下文感知对话示例...\n');
return runContextAwareExample();
})
.then(() => {
console.log('\n开始对话分支管理示例...\n');
return runConversationBranchingExample();
})
.catch(console.error);
}

最佳实践

1. 工具设计原则

  • 单一职责:每个工具应该专注于一个特定的任务
  • 清晰描述:提供准确的工具描述和参数说明
  • 错误处理:优雅地处理工具执行错误
  • 幂等性:确保工具调用的一致性

2. 提示词优化

const systemMessage = `你是一个专业的助手,能够使用各种工具来解决问题。

工作流程:
1. 仔细分析用户的问题
2. 确定解决问题所需的信息和步骤
3. 选择合适的工具获取信息或执行操作
4. 基于工具结果进行推理和分析
5. 提供清晰、准确的最终答案

注意事项:
- 如果信息不足,使用搜索工具获取更多信息
- 如果需要计算,使用计算工具确保准确性
- 始终基于事实回答,避免猜测
- 如果无法完成任务,请说明原因`;

3. 状态管理

const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
// 添加自定义状态字段
toolCallCount: Annotation<number>({
default: () => 0,
}),
lastToolUsed: Annotation<string>({
default: () => '',
}),
});

性能优化

1. 工具调用限制

const MAX_TOOL_CALLS = 10;

const shouldContinue = (state: typeof StateAnnotation.State) => {
const lastMessage = state.messages[state.messages.length - 1];

// 检查工具调用次数
if (state.toolCallCount >= MAX_TOOL_CALLS) {
return END;
}

if (lastMessage.tool_calls?.length) {
return 'tools';
}
return END;
};

2. 并行工具调用

// ReAct 架构支持并行调用多个工具
const parallelTools = [searchTool, calculatorTool, weatherTool];

const agent = createReactAgent({
llm: model,
tools: parallelTools,
// 启用并行工具调用
systemMessage: '你可以同时使用多个工具来提高效率。',
});

小结与延伸

ReAct 架构是构建智能代理的强大模式,它通过推理-行动-观察的循环让 LLM 能够处理复杂的多步骤任务。主要优势包括:

  • 灵活性:能够根据情况动态选择工具和策略
  • 可解释性:每一步的推理过程都是透明的
  • 可扩展性:容易添加新的工具和能力
  • 鲁棒性:能够处理工具调用失败等异常情况

在下一节多代理系统中,我们将学习如何将多个 ReAct 代理组合起来,构建更复杂的协作系统。

实践建议
  • 从简单的工具开始,逐步增加复杂性
  • 仔细设计工具的输入输出格式
  • 使用日志和调试工具跟踪代理的推理过程
  • 定期测试和优化工具的性能