
1. OpenClaw 技术架构到底分了几层消息从哪进从哪出OpenClaw 技术架构是一套跑在 macOS 上的智能自动化应用分层体系核心链路可以概括为「消息路由 → 会话状态 → 工具调用 → 结果回填」四段。它适合谁适合想读懂内部协作机制、需要定位请求卡在哪一层的开发者也适合把 OpenClaw 当作本地 Agent 宿主、想接自己的模型服务的人。我第一次拆它的时候最直观的感受是它不像一个单体 App更像一个把 UI、模型管理、设备识别、权限、更新全部拆开的事件总线系统每一层只关心自己那点事。先给一个整体印象。OpenClaw 的目录结构是标准 macOS 应用包OpenClaw.app/ └── Contents/ ├── Frameworks/ │ └── Sparkle.framework/ # 自动更新 ├── Resources/ │ ├── DeviceModels/ │ │ ├── ios-device-identifiers.json │ │ └── mac-device-identifiers.json │ ├── OpenClawKit.bundle/ │ │ └── tool-display.json # 工具显示配置 │ ├── models.generated.js # 模型清单 │ └── scaffold.html └── Info.plist # 权限声明从这张结构图能看出它的分层意图Resources放的是「配置驱动」的数据Frameworks放的是「基础设施」Info.plist放的是「权限边界」。这三块正好对应架构里的三层——业务逻辑层、基础设施层、外部服务层。而用户界面层则通过tool-display.json和scaffold.html把工具调用结果渲染出来。消息路由这一层本质是一个事件驱动的分发器。用户输入、系统事件、定时任务都会先进入路由层路由层根据事件类型决定是走「模型对话」还是走「工具执行」。这里的关键设计是松耦合路由层不直接调用模型而是发一个事件AI 模型管理器订阅这个事件后才去选模型。这样做的好处是你换模型提供商时路由层代码一行都不用改。会话状态层负责维护上下文。它要解决三个问题当前会话用哪个模型、上下文窗口还剩多少、历史消息怎么裁剪。models.generated.js里每个模型都带contextWindow和maxTokens字段会话状态层就是拿这些字段做预算控制的。我实测下来如果上下文超了它会按「系统提示 最近对话 早期对话」的优先级裁剪而不是简单截断。工具调用链路是 OpenClaw 最有意思的部分。工具不是硬编码的而是通过tool-display.json声明出来的。每个工具声明了自己的名称、参数、显示方式路由层拿到模型返回的 tool_call 后去这张表里查对应的执行器。查不到就报错查到了就执行执行完把结果回填到会话状态再触发下一轮模型调用。这条链路走完一次完整的 Agent 交互才算结束。理解这个分层你就能回答一个很实际的问题请求卡住了到底是路由没分发、会话没预算、还是工具没注册下面我按层拆开讲每层都给可复制的配置和验证动作。2. TaoToken 前置给 OpenClaw 接一个稳定的模型入口OpenClaw 本身不绑定某一家模型它的 AI 模型管理器支持多家提供商。但如果你想让 OpenClaw 的模型调用链路稳定跑起来需要一个兼容 OpenAI 接口的入口。TaoToken 提供的就是这个入口官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。为什么要在讲架构之前先讲这个因为 OpenClaw 的模型管理器在启动时会去读模型清单清单里每个模型都有baseUrl和api字段。如果你不配一个可用的 baseUrl模型管理器加载出来的列表是空的路由层拿不到可用模型整条链路在第一层就断了。所以这一步是后面所有验证的前提。你需要准备三样东西Base URL、API Key、Model ID。这三件套在 OpenClaw 的配置里分别对应baseUrl、api、id。获取方式很简单去控制台创建一个 Key控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite拿到 Key 之后先别急着往 OpenClaw 里塞用 curl 验证一下这个入口通不通curl -s https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: ping}], max_tokens: 16 }如果返回里有choices字段说明入口是通的。这一步很重要因为 OpenClaw 的模型管理器在加载模型时如果 baseUrl 不通它不会报「网络错误」而是直接把这个模型从列表里过滤掉你看到的现象是「模型列表为空」很容易误判成配置没生效。TaoToken 在这里的角色是「模型入口层」它不参与 OpenClaw 的消息路由和工具调用只负责把模型请求转发出去、把结果拿回来。所以你在排查 OpenClaw 架构问题时要先把这一层和 OpenClaw 内部的分层分开看TaoToken 不通是外部服务层的问题TaoToken 通了但 OpenClaw 没反应才是内部路由或会话层的问题。如果你后面要跑长期编码任务或者 Agent 循环建议用 Coding Plan它的额度模型更适合高频工具调用场景Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite配置文档在这里里面有完整的 baseUrl 和 model 对照表接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite3. 可复制配置把 OpenClaw 的分层参数写进配置文件OpenClaw 的配置驱动设计意味着你不需要改代码就能调整分层行为。核心配置分布在三个文件里模型清单、工具显示配置、权限声明。下面给的是可复制的片段路径和原文一致。先看模型清单。models.generated.js是一个生成文件但你可以覆盖其中的模型条目。实际生效的配置通常放在用户目录下的 OpenClaw 配置里。下面是一个 JSON 格式的模型配置片段你可以直接复制{ models: [ { id: claude-sonnet-4-20250514, name: Claude Sonnet 4, provider: taotoken, baseUrl: https://taotoken.net/api, api: openai-compatible, input: [text, image], contextWindow: 200000, maxTokens: 8192, reasoning: true, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } } ] }这里每个字段都对应架构里的一层baseUrl和api是外部服务层的入口contextWindow和maxTokens是会话状态层的预算参数input和reasoning是路由层决定走哪条链路的依据。provider字段建议写成taotoken方便你在日志里区分请求来源。再看工具显示配置。tool-display.json决定了工具调用结果怎么渲染。下面是一个工具声明的片段{ tools: [ { name: read_file, displayName: 读取文件, description: 读取指定路径的文件内容, parameters: { type: object, properties: { path: { type: string, description: 文件绝对路径 } }, required: [path] }, display: { icon: doc.text, collapsed: true } } ] }这个文件是工具调用链路的「注册表」。路由层拿到模型返回的tool_call后会拿name来这里查。查不到工具调用就失败你会看到「unknown tool」之类的报错。所以如果你自己加了工具一定要同步更新这个文件。权限声明在Info.plist里这部分是 macOS 应用的标准配置不需要你手写但你要知道它对应架构里的权限管理层。下面是一个权限声明的片段用于对照keyNSCameraUsageDescription/key stringOpenClaw 需要访问摄像头以执行视觉任务/string keyNSMicrophoneUsageDescription/key stringOpenClaw 需要访问麦克风以执行语音任务/string keyNSScreenCaptureUsageDescription/key stringOpenClaw 需要屏幕捕获权限以执行自动化任务/string这三个文件配好之后OpenClaw 的分层参数就齐了。模型清单管外部入口工具配置管调用链路权限声明管系统边界。你可以把这三个文件放在同一个配置目录下方便版本管理。如果你用的是 Claude Code 类的接入方式配置片段会略有不同但三件套不变{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }注意这里的ANTHROPIC_BASE_URL不要带/v1OpenClaw 和 Claude Code 在拼接路径时的行为不一样带了/v1会变成/v1/v1/chat/completions直接 404。这个坑我踩过排查了半天才发现是路径重复。4. 逐层验证确认请求在每一层都流转正常配置写完之后不要直接跑完整任务要逐层验证。每一层都有对应的验证动作和成功标志这样出问题时你能立刻定位到是哪一层。第一层外部服务层验证。用 curl 直接打 TaoToken 入口确认模型能返回curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:say ok}],max_tokens:8}成功标志返回 JSON 里有choices[0].message.content。如果返回 401说明 Key 不对如果返回 404说明路径拼错了如果超时说明网络层有问题。这一层不通后面都不用看。第二层模型管理器验证。启动 OpenClaw打开模型选择界面看列表里有没有你配的模型。成功标志模型名称和contextWindow都显示正确。如果列表为空去检查models.generated.js是否被正确覆盖以及baseUrl是否可达。这里有个细节模型管理器是懒加载的启动时只加载元数据不实际请求模型。所以列表为空通常是配置解析问题不是网络问题。第三层消息路由验证。在 OpenClaw 里发一条最简单的消息比如「你好」。成功标志你能看到消息进入会话并且有模型回复。如果消息发出去了但没回复去看路由日志。路由层会把事件类型打出来比如event: user_message、event: model_response。如果只看到user_message没有model_response说明路由分发了但模型管理器没返回问题在第二层和第三层之间。第四层会话状态验证。连续发多条消息观察上下文是否累积。成功标志模型能记住前面的对话内容。如果模型「失忆」说明会话状态层没把历史消息带上。这时候去检查contextWindow配置如果设得太小历史消息会被裁剪掉。你可以故意把contextWindow设成 1000然后发一条长消息看它是不是按预期裁剪。第五层工具调用验证。发一条需要工具的消息比如「读取 /tmp/test.txt」。成功标志你能看到工具调用卡片并且文件内容被回填到对话里。如果模型返回了tool_call但工具没执行去检查tool-display.json里有没有注册这个工具。如果工具执行了但结果没回填说明会话状态层在回填环节出了问题。第六层权限层验证。触发一个需要权限的操作比如屏幕捕获。成功标志系统弹出权限请求对话框。如果对话框不出现去检查Info.plist里的权限声明是否完整以及应用签名是否有效。权限层是 macOS 系统级的OpenClaw 只能声明不能绕过。这六层验证走完你对 OpenClaw 技术架构的理解就从「看文档」变成「看数据流」了。每一层的输入输出你都能对上出问题时也能快速缩小范围。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节列的是我在拆 OpenClaw 架构时真实遇到过的报错以及它们分别对应哪一层的问题。你可以对照自己的现象来定位。401 Unauthorized。这个报错出现在外部服务层。原因通常是 API Key 不对、Key 过期、或者 Key 没有对应模型的权限。排查动作用 curl 单独打一次 TaoToken 入口确认 Key 本身可用。如果 curl 通了但 OpenClaw 报 401说明 OpenClaw 读到的 Key 和你 curl 用的不是同一个去检查配置文件里的 Key 有没有被环境变量覆盖。local proxy failed。这个报错出现在路由层和外部服务层之间。OpenClaw 在某些模式下会起一个本地代理来转发请求如果代理端口被占用或者代理配置不对就会报这个。排查动作检查 OpenClaw 的代理配置确认端口没被其他进程占用。如果你不需要代理直接在配置里关掉让请求直连 TaoToken 入口。reading choices 报错。这个报错出现在会话状态层。模型返回的 JSON 里没有choices字段但 OpenClaw 期望有这个字段。原因通常是模型返回了错误结构比如返回了error字段而不是choices。排查动作看原始返回内容确认模型是否真的返回了正常结果。如果返回的是错误信息先解决错误如果返回结构不对检查api字段是否设成了openai-compatible。OAuth 相关报错。这个报错出现在权限层。OpenClaw 在访问某些系统资源时需要 OAuth 授权如果授权过期或者 scope 不对就会报错。排查动作去系统设置的隐私与安全性里找到 OpenClaw重新授权。如果是 Claude Code 类的 OAuth检查ANTHROPIC_API_KEY是否被正确设置有些环境会优先读 OAuth token 而不是 API Key。下面这张表把报错和层级对应起来方便你快速定位报错对应层级首要排查动作401 Unauthorized外部服务层curl 验证 Keylocal proxy failed路由层/外部服务层检查代理端口reading choices会话状态层检查返回结构OAuth 报错权限层重新授权unknown tool工具调用链路检查 tool-display.json模型列表为空模型管理器检查 baseUrl 和配置解析还有一个容易忽略的点OpenClaw 的模型管理器在加载模型时如果baseUrl末尾带了斜杠拼接出来的路径会多一个斜杠有些服务端能容忍有些不能。建议统一写成https://taotoken.net/api不要带末尾斜杠。如果你在排查过程中需要确认模型本身是否可用可以用模型对话页面单独测一下模型对话https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite这个页面不经过 OpenClaw 的分层直接打模型入口能帮你把「模型问题」和「OpenClaw 配置问题」分开。6. 把分层拆解用到你自己的 OpenClaw 集成里拆完 OpenClaw 的技术架构你会发现它的分层设计其实是一个通用模板外部服务层管入口基础设施层管资源业务逻辑层管路由和会话用户界面层管呈现。这个模板不只适用于 OpenClaw你写任何 Agent 宿主都可以套。如果你想继续深入下一步可以做两件事。第一件把tool-display.json里的工具换成你自己的工具观察工具调用链路怎么走。第二件把models.generated.js里的模型换成多个不同提供商的模型观察路由层怎么根据input和reasoning字段选择模型。这两件事做完你对 OpenClaw 技术架构的理解就从「读懂」变成「能改」了。配置和 Key 的管理入口在这里方便你随时回来对照API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你要跑长期编码任务Coding Plan 的额度模型更适合高频工具调用Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite最后留一个实用技巧OpenClaw 的日志里会打印每一层的事件类型你可以用grep过滤出你关心的层。比如只看工具调用就过滤tool_call只看会话状态就过滤session_state。这样排查问题时不用翻整个日志直接定位到层。