从零手写一个简单的AI编程助手Agent:深入理解ReAct工作流

发布时间:2026/8/19 9:34:56
从零手写一个简单的AI编程助手Agent:深入理解ReAct工作流 前言最近Claude Code、Cursor等AI编程助手火遍全网它们能自动理解需求、编写代码、运行命令甚至帮你调试bug。你有没有好奇过这些工具背后的核心原理是什么今天我们就来手写一个简化版的Claude Code Agent通过实践深入理解AI Agent的工作机制。相信我这比你想象的要简单得多整体思路我们的目标是创建一个能自动执行编程任务的Agent比如让它“创建一个React Vite的TodoList应用”。整体架构分为三层LLM大语言模型负责理解和规划任务Tools工具集让LLM能够操作文件系统、执行命令Agent循环协调LLM和Tools完成复杂任务用户需求 → LLM思考 → 调用工具 → 获取结果 → LLM再思考 → ... → 完成任务技术选型TypeScript类型安全开发体验好LangChainLLM应用开发框架统一各家模型接口OpenAI使用GPT-4作为推理引擎Node.js运行环境核心概念解析1. Message体系在LangChain中对话由不同类型的消息组成// SystemMessage: 设定AI的角色和能力边界 new SystemMessage(你是一个编程助手可以读写文件、执行命令...) // HumanMessage: 用户输入 new HumanMessage(创建一个React Vite的TodoList) // AIMessage: AI的思考和回答 new AIMessage(我来帮你创建项目首先使用Vite初始化...) // ToolMessage: 工具执行结果 new ToolMessage({ content: 项目创建成功, toolCallId: xxx })2. Tool机制工具是LLM能力的延伸让AI能够“动手干活”const writeFileTool { name: write_file, description: 写入文件到磁盘, schema: z.object({ path: z.string(), content: z.string() }), async execute({ path, content }) { await fs.writeFile(path, content); return 文件写入成功; } }3. ReAct工作流ReAct Reason Act即“推理 行动”Reason推理LLM分析当前状态决定下一步行动Act行动执行选定的工具Observe观察获取执行结果循环上述步骤直到任务完成代码实现第一步环境配置import { ChatOpenAI } from langchain/openai; import { SystemMessage, HumanMessage, AIMessage, ToolMessage } from langchain/core/messages; import { tool } from langchain/core/tools; import { z } from zod; import fs from fs/promises; import { exec } from child_process; import util from util; const execPromise util.promisify(exec); // 初始化LLM const model new ChatOpenAI({ modelName: gpt-4, temperature: 0, apiKey: process.env.OPENAI_API_KEY });第二步定义工具集// 1. 写入文件工具 const writeFileTool tool( async ({ path, content }) { await fs.writeFile(path, content, utf-8); return ✅ 文件 ${path} 写入成功; }, { name: write_file, description: 将内容写入指定路径的文件, schema: z.object({ path: z.string().describe(文件路径), content: z.string().describe(要写入的内容) }) } ); // 2. 读取文件工具 const readFileTool tool( async ({ path }) { const content await fs.readFile(path, utf-8); return content; }, { name: read_file, description: 读取指定路径的文件内容, schema: z.object({ path: z.string().describe(文件路径) }) } ); // 3. 执行命令工具 const execCommandTool tool( async ({ command }) { try { const { stdout, stderr } await execPromise(command); return stdout || stderr || 命令执行完成; } catch (error) { return ❌ 命令执行失败: ${error.message}; } }, { name: execute_command, description: 在终端执行shell命令, schema: z.object({ command: z.string().describe(要执行的shell命令) }) } ); // 工具列表 const tools [writeFileTool, readFileTool, execCommandTool]; const toolsByName Object.fromEntries( tools.map(t [t.name, t]) );第三步实现Agent主循环async function runAgent(userInput: string) { // 消息历史 const messages [ new SystemMessage(你是一个智能编程助手能够通过工具完成各种编程任务。 可用工具 - write_file: 写入文件 - read_file: 读取文件 - execute_command: 执行shell命令 请按以下步骤思考 1. 理解用户的完整需求 2. 规划实现步骤 3. 逐步执行每次只调用一个工具 4. 遇到错误要分析原因并尝试修复), new HumanMessage(userInput) ]; // 绑定工具到模型 const modelWithTools model.bindTools(tools); let maxIterations 20; while (maxIterations-- 0) { console.log(\n 第 ${20 - maxIterations} 轮思考...); // 调用LLM const response await modelWithTools.invoke(messages); messages.push(response); // 检查是否有工具调用 const toolCalls response.additional_kwargs?.tool_calls || []; if (toolCalls.length 0) { // 没有工具调用任务完成 console.log(✅ 任务完成); console.log(response.content); return response.content; } // 执行工具调用 for (const toolCall of toolCalls) { const toolName toolCall.function.name; const toolArgs JSON.parse(toolCall.function.arguments); const toolId toolCall.id; console.log( 调用工具: ${toolName}); console.log( 参数:, toolArgs); try { // 执行工具 const tool toolsByName[toolName]; if (!tool) { throw new Error(未知工具: ${toolName}); } const result await tool.invoke(toolArgs); // 添加工具执行结果到消息历史 messages.push(new ToolMessage({ content: result, toolCallId: toolId })); console.log(✅ 工具执行成功); console.log( 结果:, result.slice(0, 200) ...); } catch (error) { // 错误处理 messages.push(new ToolMessage({ content: ❌ 工具执行失败: ${error.message}, toolCallId: toolId })); console.log(❌ 工具执行失败:, error.message); } } } return 任务执行超时请检查是否陷入死循环; }第四步启动Agent// 入口函数 async function main() { const userRequest process.argv[2] || 创建一个React Vite的TodoList应用; console.log( 启动AI编程助手...); console.log( 任务: ${userRequest}\n); try { const result await runAgent(userRequest); console.log(\n 最终结果:); console.log(result); } catch (error) { console.error( 发生错误:, error); } } main();运行演示假设我们让Agent创建TodoList应用它会这样工作 启动AI编程助手... 任务: 创建一个React Vite的TodoList应用 第 1 轮思考... 调用工具: execute_command 参数: { command: npm create vitelatest todo-app -- --template react } ✅ 工具执行成功 第 2 轮思考... 调用工具: execute_command 参数: { command: cd todo-app npm install } ✅ 工具执行成功 第 3 轮思考... 调用工具: write_file 参数: { path: todo-app/src/App.jsx, content: ... } ✅ 工具执行成功 ... (继续编写代码、运行项目) ✅ 任务完成 你的TodoList应用已创建完成运行 npm run dev 即可启动核心要点总结1. LLM的局限性大语言模型本身是无状态的不能直接操作外部世界。它只能理解文本生成文本规划步骤通过Tool机制我们赋予LLM“手脚”让它能真正干活。2. 消息历史的重要性Agent的“记忆”就是messages数组。每次交互都追加新消息LLM的思考和工具调用 → AIMessage工具执行结果 → ToolMessage这保证了Agent能“记住”之前做过的所有事情。3. 错误处理机制Agent必须能处理失败命令执行失败 → 分析错误 → 尝试修复文件写入冲突 → 调整策略 → 重新执行4. 工具设计原则单一职责每个工具只做一件事清晰描述让LLM理解工具的用途和参数错误返回返回详细的错误信息帮助LLM调试进阶优化方向并行工具调用使用Promise.all同时执行多个独立操作Token优化压缩工具返回结果避免上下文过长安全控制限制危险命令添加用户确认环节多模态支持集成图像生成、代码可视化等能力记忆持久化保存对话历史支持断点续传结语通过这篇文章我们亲手实现了一个简化版的AI编程助手揭开了Claude Code、Cursor等工具的神秘面纱。核心原理并不复杂LLM负责思考和规划Tools负责执行具体操作Agent循环协调两者完成复杂任务这只是一个开始。有了这个基础框架你可以添加更多工具如Git操作、API调用、数据库查询等构建出更强大的AI助手。技术改变世界而AI正在改变技术本身。希望这篇文章能帮助你更好地理解AI Agent的原理甚至开发出自己的AI工具