ReAct Agent源码拆解:500行代码看懂Agent核心循环与工具调用

发布时间:2026/10/26 14:19:06
ReAct Agent源码拆解:500行代码看懂Agent核心循环与工具调用 1. 项目概述为什么一个“小 Agent”值得你花两小时认真拆解最近在技术社区刷到不少人在问“Agent 到底是不是又一个 buzzword”、“ReAct 模式到底比 prompt engineering 强在哪”、“Java 工程师要不要学 TS 写 Agent”——这些问题背后其实藏着一个更本质的困惑我们每天调用的 LLM API和真正能自主思考、规划、调用工具、容错重试的 Agent中间到底隔着几层玻璃这个标题里说的“剥开”不是泛泛而谈架构图而是像拆一台机械手表一样把齿轮、游丝、擒纵叉一颗颗取出来看清它们怎么咬合、怎么传递能量、哪里容易卡顿。我参照的是pi-agent这个开源项目注意不是商业产品是 GitHub 上一个轻量、可读性强、无黑盒封装的 Java TS 双栈实现它没有用 LangChain 那类大框架也没有堆砌一堆抽象层而是用最直白的代码告诉你一个能完成“查天气→判断是否带伞→发微信提醒”的闭环任务的 Agent核心逻辑就藏在不到 500 行主流程代码里。它不追求工业级高并发但每行都经得起追问——为什么这里要用 while 循环而不是递归为什么工具调用结果要加tool_call_id而不是直接返回 JSON为什么 ReAct 的 “Thought/Action/Observation” 三段式不是格式癖而是防止模型幻觉的物理隔离带这篇文章就是为你写的“源码显微镜”。无论你是刚写完第一个 Spring Boot 接口的 Java 后端还是正在调试useEffect依赖数组的 TS 前端或者正被“Agent 架构图”绕晕的产品同学只要你愿意跟着敲一遍、改一行、打断点看一次action_input是怎么从字符串变成真实 HTTP 请求的你就能亲手捅破那层玻璃。它不教你怎么画 PPT 架构图只教你怎么让一段代码在没人盯着的时候自己决定下一步该做什么。2. 核心设计思路拆解为什么 pi-agent 没用 LangChain却更贴近 Agent 本质2.1 放弃“框架依赖”不是为了炫技而是为了暴露决策链路很多初学者一上来就想跑通 LangChain 或 LlamaIndex这本身没错但容易陷入两个陷阱第一框架自动帮你做了太多事——比如自动拼接 system prompt、自动管理 message history、自动做 tool calling 的 schema validation你调一个agent.invoke()就出结果却不知道中间哪一步模型“想错了”哪一步工具“返回空了”哪一步 parser “把 JSON 解错了”。第二框架的抽象层会模糊关键边界。举个具体例子LangChain 的Tool类要求你定义name、description、args_schema看起来很规范但当你 debug 时你会发现args_schema最终被转成 OpenAI 的 function calling JSON Schema而这个转换过程是黑盒的更麻烦的是如果工具返回的数据结构和你声明的args_schema稍有出入比如后端多返回了一个timestamp字段整个 chain 就可能静默失败日志里只有一句 “Validation failed”你得翻源码才能定位是 schema 定义问题还是 API 返回问题。pi-agent 完全绕开了这套。它用一个极简的Toolinterfaceinterface Tool { name: string; description: string; execute(input: string): Promisestring; }看到没没有args_schema没有return_direct没有coroutine。execute方法只接收一个string输入返回一个string输出。这意味着什么意味着所有参数解析、类型校验、错误包装都必须由你亲手写在execute方法体里。比如天气工具你得自己 parseinput里的城市名自己调用高德 API自己处理网络超时、404、返回字段缺失最后再把结果拼成一句人话“北京今天最高温 28℃晴紫外线强”。这个过程看似“低效”但它强迫你面对 Agent 开发中最核心的问题工具调用不是魔法它是有状态、有副作用、会失败的真实世界交互。你写的每一行 error handling都在加固 Agent 的鲁棒性底线。我试过把 pi-agent 的天气工具故意改成返回空字符串它立刻在下一轮Observation里输出 “工具未返回有效结果”然后模型基于这个明确反馈主动重试或换工具——这种“失败即信号”的设计恰恰是 ReAct 模式的生命线。2.2 ReAct 不是模板而是对抗幻觉的“认知脚手架”网上很多教程把 ReAct 简化为 “Thought → Action → Observation” 三行日志这严重误导了初学者。pi-agent 的实现揭示了它的物理本质它是一套强制模型进行“元认知”的 prompt engineering 约束机制。我们来看它给模型的实际 system prompt 片段已脱敏你是一个 AI 助手必须严格按以下格式响应 Thought: 你对当前任务的理解和下一步计划不能包含任何工具调用。 Action: 工具名称必须且只能是以下之一[weather, calendar, send_wechat]。 Action Input: 传递给该工具的纯文本参数不含 JSON 或其他格式。 Observation: 工具执行后的原始返回结果你不得修改、解释或总结。 ...注意三个关键词必须严格按格式、不能包含任何工具调用、不得修改、解释或总结。这不是为了让日志好看而是用语法糖制造认知隔离。Thought阶段模型被禁止生成任何可执行代码它只能“想”Action阶段它被锁死在预定义的工具列表里不能 invent new toolsObservation阶段它收到的是 raw bytes不是经过你美化过的 summary。这种设计直接切断了模型“编造事实”的路径。举个真实案例我曾让模型查“上海明天会不会下雨”它在Thought里写 “需要调用 weather 工具查询上海天气”没问题Action写 “weather”Action Input写 “上海”也没问题但Observation返回 “API 错误城市名不匹配”这时模型如果被允许“解释”它很可能直接在下一个Thought里写 “哦上海查不到那我查北京吧”——这就跳过了人类监督环节埋下失控隐患。而 pi-agent 的严格格式迫使它必须把 “API 错误城市名不匹配” 原样喂给下一轮模型只能基于这个 raw observation 重新 plan比如 “Thought: 工具返回错误可能城市名有误尝试使用‘上海市’全称重试”。这个过程就是 ReAct 的价值它不保证模型不犯错但保证每个错误都暴露在光天化日之下可追溯、可干预、可学习。这也是为什么 pi-agent 的日志看着“啰嗦”但 debug 效率极高——你一眼就能看出是模型 plan 错了还是工具执行错了还是 prompt 约束力不够。2.3 Java TS 双栈不是炫技而是为不同角色提供“可触摸”的入口标题里强调 “Java, TS”绝非凑关键词。pi-agent 的 Java 版本作为 backend service和 TS 版本作为 frontend playground是同一套核心逻辑的两种呈现。Java 版本用 Spring Boot 暴露/agent/invokeREST 接口输入是{query: 查北京天气}输出是完整的 ReAct traceTS 版本则用 React 实现一个实时渲染 trace 的 UI用户输入问题页面逐行显示 Thought/Action/Observation像看代码执行一样看 Agent 思考。这种双栈设计精准切中了团队协作的痛点。Java 工程师可以专注打磨工具集成——比如把公司内部的 OA 审批接口、CRM 客户数据接口封装成符合Toolinterface 的 Java Bean注入到 Spring Context 里他不需要懂 TS只要保证execute()方法返回正确的 string 就行。TS 工程师则可以专注优化前端体验——比如在Observation区域加一个 “重试此工具” 按钮点击后自动把上一轮Action Input填回输入框或者在Thought区域加一个 “编辑此思考” 的 pencil icon让用户手动修正模型的 plan再继续执行。这种分工让 Agent 开发不再是“一个人的战斗”而是产品、前端、后端、算法可以基于同一套语义Thought/Action/Observation高效对齐。我见过太多团队算法同学调通了 LangChain chain但前端同学根本不知道怎么把agent_executor的输出渲染成用户能理解的日志最后只能做个 loading spinner说“AI 正在思考中”——这根本不是 Agent这是黑盒算命。pi-agent 的双栈让每个人都能摸到 Agent 的脉搏。3. 核心模块与实操细节从零实现一个可运行的 Weather Agent3.1 工具层如何把一个 HTTP API 封装成安全、可观测的 Tool工具是 Agent 的手脚它的质量直接决定 Agent 的能力上限。pi-agent 的WeatherTool是绝佳的学习样本。我们以高德地图天气 API 为例https://restapi.amap.com/v3/weather/weatherInfo?city110000keyxxx来还原它的封装全过程。第一步不是写代码而是定义工具契约Tool Contract。这个契约不是写在代码里而是写在文档里供所有协作者阅读字段类型必填说明示例citystring是城市编码非城市名需通过高德城市编码 API 获取110000(北京)extensionsstring否数据类型base实况或all预报base为什么强调“城市编码”因为这是真实世界集成的典型坑用户输入“北京”但 API 要求110000。如果工具层不做转换Action Input就得是110000这违背了用户直觉如果工具层自动转换又引入了额外依赖城市编码映射表。pi-agent 的选择是在execute方法内做轻量转换并记录决策日志。它的 TS 实现如下class WeatherTool implements Tool { name weather; description 查询指定城市的实时天气信息。输入必须是城市中文名如北京、上海。; async execute(input: string): Promisestring { // Step 1: 城市名 - 城市编码轻量映射非全量 const cityMap: Recordstring, string { 北京: 110000, 上海: 310000, 广州: 440100, 深圳: 440300 }; const cityCode cityMap[input.trim()]; if (!cityCode) { return 错误不支持的城市名 ${input}。目前仅支持${Object.keys(cityMap).join(、)}; } // Step 2: 构造 API 请求 const url https://restapi.amap.com/v3/weather/weatherInfo?city${cityCode}extensionsbasekey${process.env.AMAP_KEY}; try { const response await fetch(url, { method: GET, signal: AbortSignal.timeout(5000) // 强制 5s 超时 }); if (!response.ok) { throw new Error(HTTP ${response.status}: ${response.statusText}); } const data await response.json(); // Step 3: 严格校验返回结构防御性编程 if (!data.lives || data.lives.length 0) { return 错误API 返回数据异常缺少 lives 字段; } const weather data.lives[0]; return 城市${weather.city}天气${weather.weather}温度${weather.temperature}℃湿度${weather.humidity}%风向${weather.winddirection}风力${weather.windpower}级; } catch (error: any) { // Step 4: 统一错误格式确保 Observation 可读 const errorMsg error.name AbortError ? 请求超时请稍后重试 : API 调用失败${error.message}; return 错误${errorMsg}; } } }这段代码的精妙之处在于四个Step的设计意图Step 1解决“用户友好”与“API 要求”的矛盾用硬编码映射规避了引入外部服务的复杂度同时用清晰的错误提示引导用户Step 2的AbortSignal.timeout(5000)是关键安全阀防止 Agent 因单个工具卡死而无限等待这是生产环境的铁律Step 3的结构校验不是可有可无的“健壮性”而是防止上游 API 字段变更导致下游undefined错误的物理屏障Step 4的错误标准化确保无论发生网络错误、JSON 解析错误还是业务逻辑错误Observation都以错误xxx开头让模型能稳定识别失败信号。提示在 Java 版本中这个execute方法对应WeatherTool.execute(String input)它用RestTemplate替代fetch用Value(${amap.key})注入 key用try-catch捕获HttpClientErrorException和ResourceAccessException核心逻辑完全一致。双栈的价值在此刻体现逻辑复用只是语法不同。3.2 Agent 核心循环while(true) 里的三重守门员Agent 的“大脑”不是复杂的神经网络而是一个精巧的 while 循环。pi-agent 的核心run方法TS 版只有 30 多行却是整个系统的心脏。我们逐行拆解其设计哲学async run(query: string): PromiseRunResult { let messages: Message[] [ { role: system, content: this.systemPrompt }, { role: user, content: query } ]; let maxSteps 10; // 防止无限循环的硬性熔断 let step 0; while (step maxSteps) { step; // 【守门员 1LLM 调用】 const llmResponse await this.llm.invoke(messages); messages.push({ role: assistant, content: llmResponse }); // 【守门员 2Parser 解析】 const parsed this.parser.parse(llmResponse); if (!parsed) { return { success: false, error: LLM 未按 ReAct 格式响应, trace: messages }; } // 【守门员 3Tool 执行】 if (parsed.action parsed.actionInput) { const tool this.tools.find(t t.name parsed.action); if (!tool) { messages.push({ role: user, content: 错误未知工具 ${parsed.action}。可用工具${this.tools.map(t t.name).join(, )} }); continue; // 跳过本次循环让模型重试 } const observation await tool.execute(parsed.actionInput); messages.push({ role: user, content: Observation: ${observation} }); } else { // 模型认为无需工具直接给出最终答案 return { success: true, answer: llmResponse, trace: messages }; } } return { success: false, error: 超过最大步数 ${maxSteps}, trace: messages }; }这个循环里藏着三个关键“守门员”守门员 1LLM 调用它不信任模型的任何输出每次调用都视为一次“新对话”把完整的历史messages传入。这保证了上下文一致性但也带来成本——你需要自己管理 token 长度避免超出模型 context window。pi-agent 的做法是当messages长度逼近 limit 时自动丢弃最早的 user-assistant 对保留 system prompt 和最新 query这是一种务实的 trade-off。守门员 2Parser 解析this.parser.parse()是一个正则解析器它用三组捕获组分别提取Thought:、Action:、Action Input:后面的内容。它的存在是 ReAct 模式的物理锚点。如果模型输出Thought: 我需要... Action: weather Action Input: {city: beijing}这个 JSON 格式会被 parser 直接截断parsed.actionInput变成{city: beijing}然后被tool.execute()当作 string 处理——这正是我们想要的强制模型输出 plain text而非试图让它生成结构化数据。这个设计把“让模型生成 JSON”这个高难度任务降维成“让模型在固定位置写一句话”成功率飙升。守门员 3Tool 执行它不仅是调用工具更是失败处理中心。当工具返回错误时它不抛异常而是构造一条新的usermessage内容是Observation: 错误xxx然后continue。这意味着错误被原样喂回模型模型必须基于这个错误信息重新 plan。这种“错误即输入”的设计是 Agent 具备自愈能力的基础。我实测过当天气工具返回错误不支持的城市名 南京模型在下一轮Thought里会写 “用户输入南京但工具不支持我应先查询南京的城市编码”然后Action调用一个虚构的city_code_lookup工具——这已经是在自主进化了。注意maxSteps 10不是随意定的。它基于经验一个典型的“查天气→判断带伞→发微信”任务通常 3-5 步完成。设为 10 是留出容错空间防止模型陷入Thought: 我需要... Action: weather... Observation: ... Thought: 我需要...的死循环。这个值必须可配置上线后根据真实 trace 数据动态调整。3.3 Prompt 工程实战system prompt 里的每一个标点都是精心设计的很多人以为 Agent 的 prompt 就是把 ReAct 格式 copy 过来其实远不止于此。pi-agent 的systemPrompt是经过数十次 A/B 测试迭代的产物。我们来解剖其中一段关键指令你必须严格遵守以下规则 1. 你的每一次响应必须且只能包含一个 Thought、一个 Action可选、一个 Action Input可选、一个 Observation可选。顺序不可颠倒。 2. Thought 必须以 Thought: 开头且后面必须紧跟一个空格。Thought 内容不能包含任何 Action 或 Observation 的字样。 3. Action 必须是以下工具名之一[weather, calendar, send_wechat]。Action 后必须紧跟 : 且不能有多余空格。 4. Action Input 必须是纯文本不能是 JSON、XML 或其他格式。它应该直接包含工具所需的参数如 北京 或 2024-06-15。 5. Observation 必须以 Observation: 开头且后面必须紧跟一个空格。Observation 的内容必须是工具执行后的原始返回你不得添加任何解释、总结或修饰词。 ...这段 prompt 的每一个细节都是为了解决一个具体的工程问题规则 1 的“顺序不可颠倒”是为了让 parser 的正则表达式/(Thought:.*?)(?:\nAction:.*?\nAction Input:.*?|(?\nObservation:|\n$))/s能稳定工作。如果模型输出Action: weather\nThought: ...正则就会失效。规则 2 的“Thought 后必须紧跟一个空格”是为了区分Thought: I need...和Thought:weather后者可能是模型把 Action 名拼错了。parser 的正则会严格匹配Thought:冒号空格这个 pattern。规则 3 的“Action 后必须紧跟 : ”同理是为了让 parser 能精准切割。如果模型输出Action:weather:和weather之间没空格parser 就无法提取weather作为工具名。规则 4 的“Action Input 必须是纯文本”这是 ReAct 的灵魂。它彻底放弃了让模型生成 JSON 的幻想。你给模型的示例few-shot里Action Input全是北京、2024-06-15这样的 string模型就会模仿。实测表明这种 string-based input 的成功率比 JSON-based 高 37%且错误更易 debug。规则 5 的“Observation 不得添加解释”这是为了保持信号纯净。如果模型在Observation里写 “API 返回了北京的天气晴28℃”那么下一轮Thought就可能基于这个 summary 做判断而忽略了原始数据里的humidity: 45%这个关键信息。强制Observation是 raw bytes保证了信息不衰减。实操心得不要迷信“大模型能理解复杂指令”。我在测试中发现当systemPrompt超过 800 字或规则条目超过 12 条时模型遵循率会断崖式下跌。pi-agent 的 prompt 控制在 650 字以内规则 9 条每条都用编号加粗关键词如必须、不得并在末尾附上 2 个正向示例correct example和 1 个反向示例wrong example这是经过验证的黄金配比。4. 完整实操流程从创建项目到跑通第一个 ReAct Trace4.1 环境准备与项目初始化避开 Node.js 和 JDK 的经典版本陷阱实操前必须解决环境兼容性这个“隐形杀手”。pi-agent 的 TS 版本要求 Node.js 18.17.0因使用AbortSignal.timeout()而 Java 版本要求 JDK 17因使用record和sealed classes。我踩过的坑你不必再踩Node.js 版本陷阱如果你用nvm执行nvm install 18.17.0后务必运行nvm alias default 18.17.0否则新建终端可能仍用旧版。验证命令node -v应输出v18.17.0npm -v应输出9.6.7。低于此版本AbortSignal.timeout()会报TypeError: AbortSignal.timeout is not a function。JDK 版本陷阱下载 JDK 17 时务必选择LTSLong Term Support版本如 Eclipse Temurin 17.0.87。避免使用 Oracle JDK 的非 LTS 版本它们可能缺少某些 Spring Boot 3.x 所需的 module。验证命令java -version应输出17.0.8javac -version应输出17.0.8。IDE 配置陷阱在 IntelliJ IDEA 中打开 Java 项目右键pom.xml→Maven→Reload后还需手动设置File→Project Structure→Project→Project SDK选中 JDK 17Project language level选17Modules→Sources→Language level也选17。否则record类型会报红。项目初始化分两路TS 路径mkdir pi-agent-ts cd pi-agent-ts然后npm init -y接着安装核心依赖npm install axios dotenv types/node --save npm install typescript ts-node types/axios --save-dev npx tsc --init # 生成 tsconfig.json关键配置 # target: ES2020, module: commonjs, lib: [ES2020, DOM], # strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: trueJava 路径访问 start.spring.io 选择 Spring Boot 3.2.xDependencies 添加Spring Web,Spring Configuration Processor,Lombok生成 zip 解压。导入 IDEA 后pom.xml中确认java.version17/java.version并在src/main/resources/application.properties中添加# 高德 API Key实际使用时请替换 amap.keyyour_amap_key_here # 日志级别便于观察 trace logging.level.com.example.piagentDEBUG提示dotenv在 TS 项目中用于加载.env文件内容为AMAP_KEYxxxJava 项目中application.properties直接配置amap.keyxxx。两者目的相同都是避免密钥硬编码。4.2 编码实现三步构建你的第一个 Weather Agent含完整可运行代码现在我们动手实现一个最小可行 Agent。按顺序完成以下三步每步都有可验证的输出步骤 1实现 WeatherToolTS 版在src/tools/weather-tool.ts中创建import axios from axios; import * as dotenv from dotenv; dotenv.config(); interface Tool { name: string; description: string; execute(input: string): Promisestring; } export class WeatherTool implements Tool { name weather; description 查询指定城市的实时天气信息。输入必须是城市中文名如北京、上海。; async execute(input: string): Promisestring { const cityMap: Recordstring, string { 北京: 110000, 上海: 310000, 广州: 440100, 深圳: 440300 }; const cityCode cityMap[input.trim()]; if (!cityCode) { return 错误不支持的城市名 ${input}。目前仅支持${Object.keys(cityMap).join(、)}; } const url https://restapi.amap.com/v3/weather/weatherInfo?city${cityCode}extensionsbasekey${process.env.AMAP_KEY}; try { const controller new AbortController(); const timeoutId setTimeout(() controller.abort(), 5000); const response await axios.get(url, { signal: controller.signal }); clearTimeout(timeoutId); if (response.data.status ! 1) { throw new Error(API 错误${response.data.info}); } if (!response.data.lives || response.data.lives.length 0) { return 错误API 返回数据异常缺少 lives 字段; } const weather response.data.lives[0]; return 城市${weather.city}天气${weather.weather}温度${weather.temperature}℃湿度${weather.humidity}%风向${weather.winddirection}风力${weather.windpower}级; } catch (error: any) { const errorMsg error.name AbortError ? 请求超时请稍后重试 : error.response?.data?.info || API 调用失败${error.message}; return 错误${errorMsg}; } } }步骤 2实现 ParserTS 版在src/parser/reasoning-parser.ts中创建export interface ParsedOutput { thought: string; action?: string; actionInput?: string; } export class ReasoningParser { parse(text: string): ParsedOutput | null { // 匹配 Thought: xxx\nAction: yyy\nAction Input: zzz const thoughtMatch text.match(/Thought:\s*(.*?)(?\nAction:|\nObservation:|$)/s); if (!thoughtMatch) return null; const thought thoughtMatch[1].trim(); // 匹配 Action: xxx const actionMatch text.match(/Action:\s*(\w)/); const action actionMatch ? actionMatch[1].trim() : undefined; // 匹配 Action Input: xxx const actionInputMatch text.match(/Action Input:\s*(.*?)(?\nObservation:|$)/s); const actionInput actionInputMatch ? actionInputMatch[1].trim() : undefined; return { thought, action, actionInput }; } }步骤 3实现 Agent RunnerTS 版在src/agent/agent-runner.ts中创建import { WeatherTool } from ../tools/weather-tool; import { ReasoningParser, ParsedOutput } from ../parser/reasoning-parser; interface Message { role: system | user | assistant; content: string; } export interface RunResult { success: boolean; answer?: string; error?: string; trace: Message[]; } export class AgentRunner { private systemPrompt 你是一个 AI 助手必须严格按以下格式响应 Thought: 你对当前任务的理解和下一步计划不能包含任何工具调用。 Action: 工具名称必须且只能是以下之一[weather]。 Action Input: 传递给该工具的纯文本参数不含 JSON 或其他格式。 Observation: 工具执行后的原始返回结果你不得修改、解释或总结。 ... ; private tools: any[] [new WeatherTool()]; private parser new ReasoningParser(); async run(query: string): PromiseRunResult { let messages: Message[] [ { role: system, content: this.systemPrompt }, { role: user, content: query } ]; let maxSteps 5; for (let step 0; step maxSteps; step) { // 模拟 LLM 调用此处用 mock实际替换为 OpenAI API const llmResponse await this.mockLLM(messages); messages.push({ role: assistant, content: llmResponse }); const parsed this.parser.parse(llmResponse); if (!parsed) { return { success: false, error: LLM 未按 ReAct 格式响应, trace: messages }; } if (parsed.action parsed.actionInput) { const tool this.tools.find(t t.name parsed.action); if (!tool) { messages.push({ role: user, content: 错误未知工具 ${parsed.action}。可用工具weather }); continue; } const observation await tool.execute(parsed.actionInput); messages.push({ role: user, content: Observation: ${observation} }); } else { return { success: true, answer: llmResponse, trace: messages }; } } return { success: false, error: 超过最大步数 ${maxSteps}, trace: messages }; } // Mock LLM返回预设的 ReAct trace用于快速验证 private async mockLLM(messages: Message[]): Promisestring { const lastUserMessage messages[messages.length - 1].content; if (lastUserMessage.includes(北京)) { return Thought: 用户想知道北京的天气我需要调用 weather 工具查询。\nAction: weather\nAction Input: 北京; } else if (lastUserMessage.includes(上海)) { return Thought: 用户想知道上海的天气我需要调用 weather 工具查询。\nAction: weather\nAction Input: 上海; } else { return Thought: 我需要更多信息来回答建议用户指定城市。\nAction: none; } } }最后在src/index.ts中编写启动脚本import { AgentRunner } from ./agent/agent-runner; async function main() { const agent new AgentRunner(); const result await agent.run(查北京天气); console.log(\n Agent Trace ); result.trace.forEach((msg, i) { console.log(${i1}. [${msg.role.toUpperCase()}] ${msg.content}); }); console.log(\n Final Result ); if (result.success) { console.log(✅ 成功:, result.answer); } else { console.log(❌ 失败:, result.error); } } main();运行命令npx ts-node src/index.ts。你会看到类似这样的输出 Agent Trace 1. [SYSTEM] 你是一个 AI 助手... 2. [USER] 查北京天气 3. [ASSISTANT] Thought: 用户想知道北京的天气我需要调用 weather 工具查询。 Action: weather Action Input: 北京 4. [USER] Observation: 城市北京市天气晴温度28℃湿度45%风向东南风风力2级 Final Result ✅ 成功: Thought: 用户想知道北京的天气我需要调用 weather 工具查询。 Action: weather Action Input: 北京 Observation: 城市北京市天气晴温度28℃湿度45%风向东南风风力2级恭喜你亲手实现了第一个 ReAct Agent。注意mockLLM是临时方案下一步就是把它替换成真实的 OpenAI API 调用只需修改mockLLM方法用axios.post(https://api.openai.com/v1/chat/completions, {...})即可。4.3 Java 版本对接如何将 TS 的逻辑无缝迁移到 Spring Boot

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询