1. 从一条消息到一次工具调用:pi-embedded-runner 到底在做什么
如果你刚接触 OpenClaw,看到src/agents/pi-embedded-runner/这个目录名大概率会愣一下:pi 是什么,embedded 又嵌在哪。简单说,它是 OpenClaw Agents 执行引擎里负责“把一条用户消息变成一次完整 AI 行动”的嵌入式运行时。所谓嵌入式,是指它直接跑在 Gateway 主进程里,不走 RPC、不走 IPC,消息进来后在同一进程内完成上下文组装、模型调用、工具策略校验、工具执行、结果回传。这个设计带来的直接好处是延迟低,我实测下来同一条消息的处理链路比外部服务调用少了一次序列化和一次网络往返,体感上快不少。
它能做什么?你可以把它理解成 AI 的“思考与行动”调度中心。用户发一句“帮我看下 workspace 里那个 config 文件,把端口改成 8080”,pi-embedded-runner 会依次完成:拿到会话锁、拼 Prompt、调模型、识别出模型想调read和write工具、过五层工具策略管道、在 Host 或 Docker 沙箱里执行、把结果回灌给模型、流式吐回客户端。整条链路里,模型只负责“想”,runner 负责“做”和“管住别乱做”。
适合谁看?如果你正在读 OpenClaw 源码、想给 Agents 换一个统一的模型接入通道、或者单纯想把auth.json和 endpoint 从默认地址改到自己的统一 Key 通道,这篇都能直接跟做。我会把重点放在两件事上:一是 pi-embedded-runner 的执行链路拆解,二是怎么把它的模型调用出口改到 TaoToken 的统一 Key/API 通道,并给出一段可复制的 runner 配置和一次本地验证动作。
先明确一个容易混淆的点:pi-embedded-runner 不是模型本身,也不是工具本身,它是编排层。模型通过model-catalog.ts里的统一接口调用,工具通过tool-policy-pipeline.ts校验后执行。你要换模型通道,改的是模型调用这一层的 endpoint 和鉴权;你要调工具安全策略,改的是策略管道那一层。两者不要混在一起改,否则排障时会很痛苦。
我在调试时最常用的手段是在runEmbeddedPiAgent()函数开头打断点,然后顺着调用栈往下看。这个函数位于agents/pi-embedded-runner/run.ts,是所有消息处理的统一入口。只要在这里把 session、prompt、model 三个变量打出来,基本就能判断问题出在上下文、模型通道还是工具策略上。下面按执行顺序拆。
2. 把 endpoint 与 auth.json 改到 TaoToken 统一 Key 通道
OpenClaw 默认的模型调用出口指向它内置的模型目录配置,很多人在本地跑通后想换成自己的统一 Key 通道,原因通常有两个:一是想用一个 Key 管多个模型,二是想把调用日志和额度集中看。TaoToken 在这里扮演的就是统一 Key/API 通道的角色,官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。
改之前先理清 OpenClaw 里跟模型调用相关的三个东西:Base URL、Key、Model ID。这三个必须成套改,只改其中一个大概率会报 401 或者 model not found。Base URL 决定请求发到哪,Key 决定鉴权能不能过,Model ID 决定服务端路由到哪个模型。很多人排障时只盯着 Key 看,其实 Model ID 写错一样会失败,而且报错信息有时候会误导你以为是鉴权问题。
auth.json是 OpenClaw 存放模型鉴权信息的地方,通常位于配置目录下。你要做的是把里面的 provider 地址和 key 换成 TaoToken 的。注意不要直接把 key 硬编码进源码,放在auth.json里由运行时读取,这样换 key 不用改代码。如果你用的是环境变量注入的方式,也要保证 runner 启动时能读到,否则会出现“本地 shell 有变量、Gateway 进程读不到”的经典问题。
这里有个我踩过的坑:改完auth.json后没有重启 Gateway,结果 runner 还在用旧的内存缓存配置,请求一直打到旧地址。OpenClaw 的配置加载有缓存,改完鉴权文件后要么重启进程,要么触发一次配置重载。判断是否生效的最快方式是看请求日志里的目标 host,如果还是旧域名,说明没加载到新配置。
另外提醒一句,TaoToken 是统一 Key 通道,不是让你绕过任何正常鉴权。你仍然需要在自己的账号下生成 Key,并确保额度可用。把 endpoint 指过来只是让请求走这条通道,鉴权逻辑不变。如果你在auth.json里填了一个无效 Key,runner 会在模型调用那一步直接失败,错误会冒泡到流式输出层,客户端看到的就是一句模型调用失败。
3. 可复制的 runner 配置片段与 settings 写法
这一节给可直接复制的配置。先给auth.json的结构示例,路径按你本地 OpenClaw 配置目录来,字段名保持和运行时读取的一致:
{ "providers": { "taotoken": { "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的TaoTokenKey", "models": { "default": "claude-sonnet-4-20250514", "fast": "claude-haiku-4-20250514" } } }, "activeProvider": "taotoken" }如果你更习惯用 TOML 管理 runner 侧配置,可以这样写,注意base_url和model_id要和上面成套:
[agent.model] provider = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" model_id = "claude-sonnet-4-20250514" timeout_ms = 30000 max_retries = 3 [agent.toolPolicy] forceSandbox = false allowlist = ["bash", "read", "write", "http"]如果你用的是带 settings 的客户端形态(比如某些 IDE 侧接入),把 Base URL、Key、Model ID 三件套填进对应字段即可,Base URL 填https://taotoken.net/api,Key 填你生成的,Model ID 填你要用的模型名。三件套缺一不可,这是排障时第一个要核对的地方。
配置里几个参数值得说明。timeout_ms控制单次模型调用超时,默认 30 秒对大多数场景够用,但如果你跑长上下文任务可以适当调大。max_retries是失败重试次数,配合备用模型切换用。forceSandbox决定工具执行是否强制走 Docker 沙箱,调试阶段可以设 false 走 Host 直接执行,生产环境建议 true。allowlist是工具白名单,别把browser、canvas这类高危工具随手加进去。
改完配置后,建议先用一个最小请求验证通道是否通,不要一上来就跑复杂 Agent 任务。最小验证能快速区分是“通道没通”还是“工具策略拦了”。下一节给具体验证动作。
4. 一次本地验证:确认请求经统一 Key 通道发出并回传
验证分两步。第一步先确认模型通道本身通,第二步再确认 runner 整条链路通。
第一步,用 curl 直接打 TaoToken 的 API,确认 Key 和 Base URL 没问题:
curl -sS https://taotoken.net/api/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 64, "messages": [{"role": "user", "content": "只回复两个字:通了"}] }'如果返回里能看到模型输出内容,说明 Base URL、Key、Model ID 三件套是对的。如果返回 401,先查 Key 是否复制完整、有没有多余空格;如果返回 model not found,查 Model ID 拼写;如果连接超时,查网络和 Base URL 是否写成了带路径的完整地址。
第二步,在 OpenClaw 里发一条会触发工具调用的消息,比如让它读一个 workspace 下的文件。观察 runner 日志,重点看三处:模型调用前的目标 host 是不是taotoken.net,工具策略管道有没有放行,工具执行结果有没有回灌给模型。我实测下来,只要第一步通了,第二步失败基本都出在工具策略或会话锁上,而不是通道本身。
验证时可以在callLLM()附近临时加一行日志,打印实际请求的 baseUrl 和 model,这样能确认 runner 真的用了新配置,而不是还在读旧缓存。确认无误后把这行日志去掉,避免生产环境刷屏。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
这一节按真实报错对照排查。先说 401,这是最常见的。出现 401 时不要急着换 Key,先确认三件事:Key 有没有复制完整、auth.json有没有被正确加载、请求头字段名对不对。有些客户端用Authorization: Bearer,有些用x-api-key,字段名错了服务端一样返回 401。如果你在auth.json里改了 Key 但没重启,也会 401,因为进程还在用旧值。
local proxy failed这个报错通常出现在你本地配了转发层但转发层没起来,或者转发层指向的上游地址不可达。排查顺序是:先确认本地转发进程在跑,再确认它指向的 Base URL 是https://taotoken.net/api,最后确认转发层没有把鉴权头吃掉。有些转发配置会重写 header,把x-api-key丢了,结果就是上游 401,本地报 proxy failed。
reading choices这类报错一般出现在解析模型返回结构时。OpenClaw 的 runner 期望的返回格式和实际返回格式对不上,就会在读取choices字段时报错。常见原因是 Model ID 指向了一个返回结构不同的模型,或者中间层改写了响应体。解决办法是先用 curl 看原始返回结构,确认字段名,再核对 runner 里解析逻辑期望的字段。如果结构确实不同,需要在模型目录配置里做适配,而不是硬改解析代码。
OAuth 相关报错通常出现在你用 OAuth 方式鉴权但 token 过期或 scope 不足时。如果你走的是 Key 通道,一般不会碰到 OAuth 报错;如果碰到了,说明配置里还残留着 OAuth provider 的设置,runner 优先走了 OAuth 分支。检查auth.json里有没有多余的 provider 配置,把activeProvider明确指向你的 Key provider。
还有一个不报错但很坑的情况:请求发出去了,模型也回了,但工具调用没执行。这通常是工具策略管道拦了,日志里会有 fs-guard 或 allowlist 的拦截记录。去tool-policy-pipeline.ts对应环节打印一下被拦的路径或工具名,基本就能定位。别一看到工具没执行就怀疑模型通道,这两层是分开的。
6. 把通道固定下来:长期编码与 Agent 场景的接入建议
如果你只是临时验证,改完auth.json跑通就行。但如果你打算长期用 OpenClaw 跑编码类或 Agent 类任务,建议把通道配置固定成一套可复用的方案,避免每次换环境都重配。我的做法是把 Base URL、Key、Model ID 三件套抽到环境变量或独立的 secrets 文件里,auth.json只引用变量名,这样换机器时只改一处。
长期跑 Agent 任务时,模型调用的稳定性比单次速度更重要。建议把max_retries打开,并配一个备用 Model ID,主模型超时或报错时自动切换。工具策略那边,生产环境把forceSandbox设为 true,白名单只留真正需要的工具。会话压缩的maxHistoryAge别设太大,否则上下文膨胀会拖慢每次调用。
如果你要接入的是编码类客户端,Base URL 填https://taotoken.net/api,Key 和 Model ID 按你的账号填,三件套对齐后基本就能跑。需要看模型列表或做对话验证,可以走模型对话入口;需要管理 Key 和额度,走 API Keys 入口;需要看接入细节,走接入文档。这几个入口按你的实际动作选,不要只停在首页。
最后说一个实操细节:改完配置后,先用一条不触发工具的消息验证通道,再用一条触发工具的消息验证策略管道,最后再跑完整 Agent 任务。分三步走,出问题时能立刻定位是哪一层,比一上来就跑复杂任务高效得多。这套流程我在本地和测试环境都跑过,稳定后可复制到其他机器。