news 2026/9/25 6:46:09

OpenClaw 工具调用完整链路拆解:从 AgentEvent 到 tool_result 的配置与验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenClaw 工具调用完整链路拆解:从 AgentEvent 到 tool_result 的配置与验证

1. 一次工具调用卡住时,先看这三个节点

OpenClaw 的工具调用链路,说到底是三个节点在接力:AgentEvent、tool_call、tool_result。你如果正在接入 OpenClaw,或者线上突然出现“模型说要调工具但没动静”“工具执行了但模型像没看见结果”这类问题,八成是这三个节点里有一个断了。OpenClaw 是一个事件驱动的 Agent 运行时,它和 OpenAI 那种流式delta.tool_calls的玩法不一样——模型一次性吐出结构化的tool_callsJSON,OpenClaw 的适配层解析后创建tool_call类型的 AgentEvent,工具执行完再封装成tool_result事件回灌给模型。整条链路是“事件总线”串起来的,不是增量流。

这篇文章面向正在接入或排查 OpenClaw 工具调用问题的开发者。我会把从模型生成工具调用、到 OpenClaw 解析、到执行、到结果回传的完整路径拆开,给出可复制的配置骨架和逐步验证动作。你跟着做,能定位到链路到底断在哪一环。适合谁:已经跑通基础对话、准备接工具或正在被工具调用问题卡住的同学。下面所有配置和验证都基于本地源码路径C:\work\openclaw的目录结构来写,你换成自己的路径即可。

2. 前置准备:TaoToken 接入与工具注册骨架

在拆链路之前,先把“模型从哪来”和“工具从哪注册”这两件事定下来。OpenClaw 本身不绑定模型供应商,它通过适配层对接。我这边习惯用 TaoToken 做统一接入,原因是它的 API 兼容 OpenAI 与 Anthropic 两种协议,OpenClaw 的openai-transport-stream.ts和anthropic-transport-stream.ts都能直接对上,省得为不同模型改适配代码。

先拿 Key。打开控制台创建 API Key,地址是 https://taotoken.net/console ,创建完复制保存。如果你还没决定用哪个模型,可以先去模型对话页面试一下工具调用能力,地址 https://taotoken.net/chat ,选一个支持 function calling 的模型发一句“帮我列出当前目录文件”,看它会不会生成结构化的 tool_calls。这一步能提前排除“模型本身不支持工具调用”这个最容易被忽略的原因。

拿到 Key 后,在 OpenClaw 的模型配置里填上。OpenClaw 的模型适配层读取的是环境变量或配置文件,我一般写成环境变量,避免硬编码:

# Windows PowerShell $env:OPENCLAW_MODEL_BASE_URL="https://taotoken.net/api" $env:OPENCLAW_MODEL_API_KEY="sk-你的Key" $env:OPENCLAW_MODEL_NAME="claude-sonnet-4-5" # macOS / Linux export OPENCLAW_MODEL_BASE_URL="https://taotoken.net/api" export OPENCLAW_MODEL_API_KEY="sk-你的Key" export OPENCLAW_MODEL_NAME="claude-sonnet-4-5"

注意 base URL 用https://taotoken.net/api,不要带多余路径,适配层会自己拼/v1/messages或/v1/chat/completions。填错这里最常见的表现是 404,而不是 401,别被误导。

工具注册这一侧,OpenClaw 的注册中心是src/agents/openclaw-tools.ts里的createOpenClawTools()。所有工具在这里被创建并注册,name是调用的唯一标识。你新增一个工具,本质就是往这个注册表里加一项,包含name、description、parameters三要素。系统提示注入则由src/agents/skills/workspace.ts的buildWorkspaceSkillsPrompt()完成,它把可用技能和底层工具格式化成 XML 文本塞进 system prompt。也就是说,模型能“看见”哪些工具,取决于这一步注入了什么。

3. 可复制配置:把 tool_call 到 tool_result 串起来

这一节是核心,我把链路拆成四段,每段给你可复制的配置或代码骨架。

3.1 阶段一:模型生成 tool_calls

模型收到 system prompt(含工具描述)和用户请求后,推理并生成工具调用。关键点:OpenClaw 里模型的tool_calls是模型输出的文本内容的一部分,不是框架事件。它被包在模型的content字段里,一次性生成,不是流式增量。这跟 OpenAI 的delta.tool_calls有本质区别,排查时别拿 OpenAI 的经验套。

你可以在src/agents/skills/skill-contract.ts的formatSkillsForPrompt()里确认 XML 格式是否正确。如果工具描述没注入进去,模型根本不知道有exec这个工具,自然不会生成 tool_calls。验证方法:打印发给模型的完整 system prompt,搜<available_skills>和工具名,搜不到就是注入断了。

3.2 阶段二:OpenClaw 解析并创建 AgentEvent

模型响应回来后,适配层(openai-transport-stream.ts或anthropic-transport-stream.ts)接收完整响应,再由openai-ws-message-conversion.ts这类转换器从响应文本里提取tool_calls对象。解析成功后,系统创建一个tool_call类型的 AgentEvent,结构如下:

{ "type": "tool_call", "toolName": "exec", "toolCallId": "call_abc123", "arguments": { "command": "dir" }, "runId": "run_xyz789" }

这个事件被投递到 OpenClaw 内部事件总线。toolCallId是后续把结果对回去的关键,runId用来串同一次运行的所有事件。排查时如果事件总线里没有tool_call,说明解析环节失败,重点看转换器有没有正确识别模型返回的 JSON 结构。

3.3 阶段三:执行工具

事件分发由src/agents/pi-embedded-subscribe.handlers.tools.ts的handleToolExecutionStart()负责。它监听tool_call事件,根据toolName在createOpenClawTools()生成的注册表里查找工具实现,然后调用其执行函数。以exec为例,最终落到bash-tools.exec.ts:

// 模拟执行逻辑 const result = await exec({ command: "dir" });

工具返回结果有三种形态,这个区分很重要,因为不同形态后续处理路径不同:

返回状态结构后续动作
success{"status":"success","output":"..."}直接封装为 tool_result
error{"status":"error","error":"..."}封装为 tool_result,模型据此纠错
approval-pending{"status":"approval-pending","approvalId":"req_456"}挂起,等待审批后再继续

参数标准化由src/agents/pi-embedded-runner/run/attempt.tool-call-normalization.ts处理,核心调度在attempt.ts。如果你遇到“工具收到了参数但格式不对”,先看这个标准化文件。

3.4 阶段四:结果回传模型

handleToolExecutionEnd()接收工具最终结果,封装成tool_result类型的 AgentEvent:

{ "type": "tool_result", "toolCallId": "call_abc123", "result": { "status": "success", "output": "文件1.txt\n文件2.txt" }, "runId": "run_xyz789" }

然后它把tool_result连同原始tool_call一起,作为新的对话历史重新发给模型。模型基于这个新信息生成最终回复。注意toolCallId必须和tool_call事件里的一致,否则模型对不上号,会表现为“工具执行了但模型装没看见”。

4. 验证请求:逐步确认链路没断

配置完别急着上复杂任务,用最小请求逐段验证。我一般分四步走。

第一步,验证模型能生成 tool_calls。直接发一个明确需要工具的请求,比如“列出当前目录下的文件”。在适配层加一行日志,打印模型原始响应,确认content里含tool_calls字段。没有就回到 3.1 检查 system prompt 注入。

第二步,验证 AgentEvent 创建。在事件总线上订阅tool_call类型,打印事件体。用 curl 直接打模型接口做对照,确认解析器没漏字段:

curl https://taotoken.net/api/v1/messages \ -H "x-api-key: sk-你的Key" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{"role":"user","content":"列出当前目录文件"}], "tools": [{"name":"exec","description":"执行命令","input_schema":{"type":"object","properties":{"command":{"type":"string"}}}}] }'

返回里如果stop_reason是tool_use,说明模型侧没问题,问题在 OpenClaw 解析。

第三步,验证工具执行。在handleToolExecutionStart()里打断点或加日志,确认toolName能查到实现、执行函数被调用、返回值符合预期。这一步最常见的坑是工具名大小写不一致,注册表里是exec,事件里传成Exec,查不到就静默失败。

第四步,验证结果回传。订阅tool_result事件,确认toolCallId与tool_call匹配,然后看模型最终回复是否用上了工具输出。如果模型回复里没有工具结果,检查回传的历史里tool_result有没有正确挂到对应tool_call下。

5. 本篇常见错排查

报错一:模型不生成 tool_calls。先确认模型支持 function calling,去 https://taotoken.net/chat 用同样 prompt 试一次。再检查buildWorkspaceSkillsPrompt()是否把工具描述注入了 system prompt。最后看formatSkillsForPrompt()的 XML 有没有格式错误导致模型解析不了。

报错二:有 tool_call 事件但工具没执行。九成是toolName在注册表里查不到。打印注册表所有name,和事件里的toolName逐字对比,注意大小写和连字符。另一个可能是handleToolExecutionStart()没订阅到事件,检查事件总线订阅是否在工具注册之后才建立。

报错三:工具执行了但模型没反应。检查tool_result的toolCallId是否和tool_call一致。不一致的常见原因是转换器在解析时重新生成了 ID,而不是复用模型给的。还要确认回传历史时tool_result和tool_call的配对顺序正确,顺序错了模型会忽略。

报错四:approval-pending 卡住不继续。这是审批流没走完,不是链路断了。检查approvalId对应的审批请求有没有被处理,处理完要主动触发继续,否则事件总线一直在等。

报错五:参数格式不对。看attempt.tool-call-normalization.ts的标准化逻辑,模型给的参数类型可能和工具 schema 不匹配,比如数字给成了字符串。标准化层负责兜底转换,如果它没覆盖你的场景,就得手动补规则。

6. 把链路跑通之后

链路跑通后,你会发现 OpenClaw 这套事件驱动设计的好处:每个节点都是可观测的,断在哪一环一目了然。我建议你在接入阶段就把tool_call和tool_result两个事件都打上日志,带上runId和toolCallId,线上出问题直接按 runId 捞全链路。长期做编码类 Agent 或需要多轮工具调用的场景,可以考虑用 Coding Plan 来管理额度和并发,地址是 https://taotoken.net/coding-plan ,接入方式不变,还是那套 base URL 和 Key。工具注册和适配层的细节文档在 https://taotoken.net/doc ,遇到协议对不上的时候翻一下比猜快。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 6:43:54

AfKayAs.2远控木马深度解析:从样本结构到检测规则

拿到这个样本的时候&#xff0c;我习惯性地先看了一眼文件哈希&#xff0c;然后在沙箱里丢了一把。AfKayAs.2这个名字&#xff0c;在威胁情报社区里其实不算陌生&#xff0c;它是某个远控木马家族的升级变种&#xff0c;前一代AfKayAs.1曾经在不少攻防演练和真实攻击场景里出现…

作者头像 李华
网站建设 2026/9/25 6:43:28

STM32驱动AD5522 SMU芯片的五大实战经验:从SPI配置到电源与PCB布局

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 6:42:01

Nginx 403错误排查全攻略:从权限到SELinux的根因分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 6:41:49

专业焊接套筒源头厂家哪家好?衡水万泉生产厂家口碑公司汇总

衡水万泉建筑机械有限公司&#xff0c;是一家专注研发生产建筑机械及配套产品的重点民营企业&#xff0c;依托深厚的行业积淀打造建筑钢筋机械连接领域的可靠供应商&#xff0c;主营各类可焊套筒、焊接套筒、焊式套筒、可焊型直螺纹套筒及配套钢筋连接设备、配件&#xff0c;以…

作者头像 李华
网站建设 2026/9/25 6:41:08

R与RStudio版本更新全攻略:跨平台操作与包迁移技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华