
最近这段时间一直在折腾 OpenClaw把它从“装好能跑”一路啃到“知道每一层在干嘛”。圈子里的教程大多是部署保姆级但一旦涉及到src/gateway这个目录愿意往深里讲的人很少。我自己的体会是OpenClaw 的网关层才是整个系统的“大脑”模型路由、协议转换、工具调用决策全在这个目录里完成。这篇文章我把这段时间在 Windows、WSL2、Ollama 本地模型、云端 API 模型混跑等场景下的实战经历整理出来以 OpenClaw Gateway 为核心把src/gateway的目录职责、一次请求的完整链路、高频报错和调优思路都过一遍。适合那些已经跑通部署、想搞明白底层逻辑或者打算在 OpenClaw 上做二次开发的读者。1. 为什么说 src/gateway 是 OpenClaw 的“大脑”先理清分工边界1.1 网关不是代理一条消息进入 OpenClaw 后的第一站很多人第一次看到“Gateway”这个词直觉上会把它等同于 Nginx 那种反向代理请求进来转发到上游拿回结果完事。这种理解在 OpenClaw 里会严重误导你因为src/gateway干的远不止转发。我打个比方一个公司前台不只是把电话转给某个部门她还要先判断你是谁、有什么权限、找哪个部门、用什么话术沟通。OpenClaw 的 gateway 就是这套前台逻辑——所有客户端、SDK、HTTP 请求的第一站都是它。它会做身份校验、协议识别、路由解析然后把请求翻译成目标模型后端能听懂的格式等模型返回结果后再翻译回客户端能使用的统一协议。所以当你看到网上有人在问“OpenClaw Gateway 到底是什么”的时候答案不是“一个中间层”而是“OpenClaw 所有对外能力的统一入口”。客户端根本不需要知道背后接的是 Anthropic 还是 OpenAI 还是本地 Ollama它只需要对着 gateway 说话就行。这种设计让 OpenClaw 可以随时换后端、加模型、做多模型路由而不需要动上层业务代码。1.2 路由层、适配层、执行层三类代码各管什么我翻src/gateway源码时第一件事不是去看每个文件的具体实现而是先按职责把代码分堆。分完你就会发现这个目录内部其实只有三大类东西理解它们各自的边界后面排查问题会顺手很多代码层职责代表功能路由层维护“模型名到后端”的映射关系读模型路由配置、判断请求应该发往哪家供应商协议层处理客户端与服务端之间的消息格式Anthropic Messages API 到 OpenAI Chat Completions 的转换适配层真正向具体后端发起请求、处理响应Ollama 适配器、云端 API 适配器、流式响应解析路由层解决的是“你要去哪”协议层解决的是“你们俩语言不通”适配层解决的是“具体怎么把话说完、把结果带回来”。这三层代码在 OpenClaw 里被分开管理好处是你想接入一个新模型服务商只需要写一个新的适配器路由和协议层基本不用动。这也是我后来接入 Ollama 时实际感受到的便利——OpenClaw 把“新后端接入”这件事收敛成了“写一个 adapter”而不是“从入口开始改”。1.3 Skill、Memory、Tool 与 Gateway 的真实协作关系在 OpenClaw 相关的社区讨论里“skill”是被提到最多的词之一。很多人以为 skill 是 gateway 里的一项功能或者以为 skill 的加载执行发生在 gateway 层。实际源码里不是这么分工的。我看到的真实协作方式是skill、memory 这些概念属于智能体运行时runtime层面而 gateway 只负责与模型进行消息交互和执行工具调用的底层协议。OpenClaw 里有一个工具注册机制可以把 skill 暴露成工具tool描述注入到发送给模型的消息里。但 gateway 自己并不理解 skill 的业务逻辑它只知道“模型请求调用工具 A我就把 A 的执行结果回传给模型”。这也是我强烈建议所有刚开始看源码的人先建立认知边界的原因如果你带着“gateway 就是全部”的视角去看代码你会在 skill 加载、记忆持久化这些模块里转圈越看越觉得混乱。正确的理解方式是把src/gateway看作一个独立的消息网关它处理的是模型通信而 skill 和 memory 是搭在 gateway 之上的更高层能力。划分清楚之后后面看请求链路才不会迷路。2. src/gateway 源码逐层拆解入口、路由表与适配器2.1 从 src/gateway/index 到启动流程依赖注入比你想的重要所有源码阅读最好从入口开始。OpenClaw 的 gateway 入口文件大致负责一件事把整个网关服务“拼装”起来。它要读取配置环境变量、配置文件初始化各个传输层HTTP 服务、WebSocket 等注册模型路由表再把适配器列表加载进内存。这里有个设计细节让我印象很深整个启动过程的依赖是显式注入的。也就是说gateway 在启动时并不硬编码“我接的是 Anthropic”而是根据配置动态把对应的 adapter 塞给路由层。这种依赖注入DI设计看起来好像只是工程洁癖但实际排查问题时价值极大。比如我遇到过“配了 Ollama 却一直请求云端 API”的问题最后发现不是路由逻辑出错而是启动时两个适配器都被初始化了路由表把模型名指向了错误的那一个。依赖注入让你可以在启动阶段就通过日志确认“当前加载了哪些后端”这比在运行时猜要高效得多。如果你打开源码会看到启动流程大致分四步第一收集所有配置项第二创建并初始化各个 adapter第三把 adapter 列表绑定到路由表第四启动 HTTP/WebSocket 监听。这种结构意味着网关本身是无状态的——它不存储会话历史不保存任何业务数据这也是后面能谈集群扩展的前提。2.2 路由表的本质一张“模型名→后端端点”的映射表很多人第一次配 OpenClaw 时会被“model route”这个概念绕晕尤其是当报错信息里出现 “expected a gateway model route reference” 时。其实路由表没有那么玄乎它本质上就是一张两列数据的映射表左侧是你要暴露给客户端的模型名右侧是真正的后端地址和供应商类型。我举个例子一个典型的配置片段大概长这样gateway: modelRoutes: - name: anthropic/claude-sonnet provider: anthropic baseURL: https://api.anthropic.com apiKeyEnv: ANTHROPIC_API_KEY - name: ollama/qwen2.5 provider: ollama baseURL: http://localhost:11434 model: qwen2.5:7b客户端请求时传的模型名是anthropic/claude-sonnetgateway 查表后发现这条路由指向 Anthropic 的官方 API于是用对应的 key 和 baseURL 发起请求。另一个请求传ollama/qwen2.5就走本地 Ollama 服务。这里有一个容易出问题的点也是我自己踩过的route 里的name是你的对外模型名可以自由命名但如果你在配置里不小心把它写成“claude-sonnet-20241022”这种真实模型名而你的 SDK 又用了精确匹配就很容易出现路由不到的情况。OpenClaw 一般会有一定的模糊匹配逻辑但不要把“模型名”和“路由名”混为一谈——前者是云端模型的标识后者是你在 gateway 里定义的门牌号。2.3 协议转换层为什么你的 SDK 能“假装”在调 AnthropicOpenClaw 的一个常见用法是在代码里用 Anthropic 官方 SDK把baseURL指向本地 OpenClaw Gateway。这样你会发现原本只支持 Anthropic 的代码突然也能请求 OpenAI 甚至本地模型了。能做到这一点靠的正是src/gateway里的协议转换层。我简单说下它是怎么工作的。客户端用 Anthropic Messages API 的格式发一个请求过来gateway 收到后需要根据目标后端的类型做格式翻译。如果目标后端是 OpenAI 兼容接口它就把 Anthropic 的messages结构转成 OpenAI 的messages结构把max_tokens、temperature这些参数字段也做对应映射。流式输出时还要把 OpenAI 的data: [DONE]结束标记转回 Anthropic 的message_stop事件。这就是为什么你“以为自己在调 Anthropic其实背后是千千万万个不同接口”的原因。协议转换层把所有后端差异挡在了 gateway 内部上层 SDK 只看到一种统一格式。这种设计代价是 gateway 需要维护多套协议映射逻辑但收益非常明显——你的应用代码可以完全绑定在一个生态里后端怎么换都不受影响。3. 一次消息的完整决策链路OpenClaw Gateway 到底做了什么3.1 请求进来后先过三道“安检”读源码和只看文档最大的区别是你能看见一个请求在 gateway 内部经历的真实过程。以我最常用的场景为例本地程序通过 HTTP 向 OpenClaw Gateway 发送一条消息这条消息从进入到产生回复中间至少要经过三道检查。第一道是鉴权检查。OpenClaw 的 gateway 会根据配置决定是否校验请求头里的身份信息。如果你的服务是局域网内自用很多人会图省事把鉴权关掉但如果你开了防火墙端口转发或部署到公网这个检查就是最后一道防线。我实际测试过保持默认鉴权配置在 WSL2 下通过localhost访问基本没有感知成本所以没必要关闭。第二道是消息结构校验。gateway 会检查请求体里的model字段、messages数组、参数类型是否符合协议规范。很多莫名其妙的 400 错误其实在这一步就触发了比如messages里最后一条不是user角色OpenClaw 就会拒绝请求因为它知道多数模型要求对话轮次以用户消息结尾。第三道是路由解析。gateway 拿着请求里的模型名去路由表里查找如果查不到对应路由会返回一个明确的错误信息——那正是网上很多人遇到过的 “doesn’t look like an anthropic model” 系列报错的来源。三道检查全过请求才会被送到适配器层。3.2 上下文压缩与工具参数合并发生在这里很多人没意识到gateway 还承担了一部分“对话管理”的职责。虽然它不保存长期历史但单次请求里的消息序列它是有机会做处理和优化的。比如你接的是一个上下文窗口较小的本地模型而你的程序把一整天的对话历史全塞进来了模型很可能因为超长而报错。OpenClaw 的 gateway 配置里可以设置消息截断策略超出部分会被裁剪或丢弃。还有一类处理是工具参数合并当模型在上一轮请求了某个工具OpenClaw 会把工具调用 ID、工具名、参数 JSON 这些信息保留下来统一合并到下一轮请求的tool_use块里再带着工具执行结果一起发给模型。这块代码值得仔细读因为很多“模型回复正常但工具调用总是失败”的问题根子就在参数合并的细节上比如你把工具返回结果放在了错误的角色消息里模型就会看不到关键内容。我自己调 skill 的时候数次定位到最后都是这一层出的问题——不是 skill 本身逻辑不对而是 gateway 组装给模型的上下文里工具结果没有放在对的位置。3.3 工具调用结果回填从“模型说要调工具”到“模型拿到结果”之间发生了什么OpenClaw 的 agent 能力核心其实是“模型—工具—模型”的循环。gateway 虽然没有实现完整的 agent 循环但它提供了这个循环里最关键的消息搬运能力。我的经验是理解这个搬运过程比看懂任何一行代码都重要。当 OpenAI 或 Anthropic 的模型认为需要调用工具时返回体里会包含tool_calls或tool_use结构。gateway 的协议层会把这种结构解析出来交给上层 runtime 执行对应的 skill 或 tool然后 runtime 把执行结果写回一条新的助手工具消息里带着结果再次发送给模型。这个再请求过程在 OpenClaw 里的一个关键点是每次循环都必须保留上一次的完整消息序列因为模型没有记忆——你必须把它之前说过的话原样带回来它才知道接着该干嘛。我测试过一个比较复杂的 skill 流程先查数据库再调用外部 API最后汇总成报告。一次完整任务里模型需要先后发起多次工具调用。如果网关层的消息组装有一丁点问题比如把上一次的工具结果放错了位置模型就会像失忆一样重新问一遍“你要我做什么”。所以如果你发现 agent 经常“忘了刚才在干嘛”不要急着怀疑模型能力先去 gateway 层看消息序列的完整性。4. 实战中高频报错的完整排查链路从报错反推 Gateway 设计4.1 “doesn’t look like an anthropic model”到底错在哪网上搜 OpenClaw出镜率极高的报错是doesn’t look like an anthropic model: expected a gateway model route reference。刚开始我以为是模型名写错后来才发现这个报错的内涵比字面意思深得多。这个报错通常发生在这种场景你在代码里用 Anthropic SDK把baseURL指向 OpenClaw Gateway然后 SDK 发请求时发现自己收到的响应不是标准的 Anthropic 结构。SDK 会先检查响应里的模型路由引用如果 Gateway 返回的 body 里没有“gateway model route”标识或者整个响应的 JSON 结构都不对SDK 就会抛这个错。排查链路我建议按三步走。第一步先看你的请求是否真的打到了 gateway而不是直连了 Anthropic 官方地址——这个错误信息在直连官方 API 时是最容易被触发的。第二步看 gateway 返回的原始响应体如果响应的content字段不是数组而是字符串之类说明协议层转换出了问题。第三步检查路由表里是否真的存在你请求的那个模型名如果路由没匹配上gateway 会返回自己的错误格式而这个格式在 Anthropic SDK 看来就是“不合法响应”。修复方法也清晰确认baseURL指向了正确的 gateway 端口确认请求体的模型名与配置里的 route name 完全一致再看 gateway 日志里路由解析是否成功。大多数情况下问题出在第二点——模型名没对上路由表。4.2 WSL2 环境下启动 OpenClaw 的坑两条命令定位问题如果你在 Windows 上使用 WSL2 跑 OpenClaw很可能遇到过这类提示无法安全验证 WSL2 环境请在 PowerShell 中运行wsl -- status然后wsl --shutdown再重新进入 WSL2。我最初看到这个报错一头雾水因为 OpenClaw 本身跑在 WSL2 内部为什么会反过来检查宿主机上的 WSL 状态呢后来我理解了OpenClaw 的某些辅助进程会尝试调用宿主机 Windows 侧的wsl.exe来做外部命令交互比如在 Windows 侧访问文件。当 WSL2 的发行版状态异常或者 PATH 里找不到wsl.exe时就会触发安全校验失败。排查思路很直接先在 PowerShell 里运行wsl --status看输出的是“默认版本 2”还是报错如果状态正常再运行wsl --shutdown强制重启 WSL2 内核。我遇到过一次典型的坑是拔掉外接显示器后 WSL2 的网络桥接状态变了网关内部启动的本地服务从 WSL2 访问不通导致各种间歇性连接失败。这时候wsl --shutdown重启一次往往就好了。另一个容易被忽略的是 Node.js 版本。OpenClaw 对 Node 版本有要求老版本 Node 会导致部分依赖编译失败。如果你用 Windows 侧安装的 Node 去跑还可能出现路径和权限的问题我建议统一在 WSL2 内部安装 Node.js 环境避免两边混用。至于手机上用 Termux 装 OpenClaw套路类似但坑更多集中在 Node 版本和包管理器上建议先本地电脑跑通再去折腾手机端。4.3 502 Bad Gateway 在 Gateway 场景里与反向代理场景里的不同含义很多人看到502 Bad Gateway第一反应是“网关挂了”这个直觉在 OpenClaw 场景里只对了一半。Nginx 反向代理里的 502 通常意味着上游服务不可用但 OpenClaw 的 Gateway 自己就是个网关它的 502 更多是适配层向上游模型服务发起请求时连接被中断或响应超时。我遇到过一个非常典型的报错Bad Gateway error: EOF。这个 EOF 表示上游比如 Ollama在响应过程中突然关闭了连接。最常见的触发点有两个一是上游服务推理时间太长超出了 gateway 设置的超时时间二是流式输出时上游提前断开了 SSE 流。排查链路我会从三个地方入手。第一个是直接测试上游服务是否健康比如访问 Ollama 的/v1/models接口确认它能正常响应。第二个是看 gateway 日志里向上游请求时用了多长时间——如果接近超时阈值就需要把 gateWay 侧的timeout参数调大。第三个是检查是否开了流式响应流式模式下断流概率比普通模式高不少如果你不需要实时打字机效果可以先关掉流式测试稳定性。这里我想强调一个容易忽略的细节OpenClaw 的 gateway 超时设置是分后端的。云端 API 通常响应较快超时设短一点可以让失败快速暴露而本地模型在 CPU 推理时可能慢很多如果你沿用云端那套短超时就会频繁遇到 502。我的做法是给不同 route 分别配置超时避免“一刀切”。4.4 本地模型Ollama接不进来八成是 routePrefix 与模型名没对上社区里经常有人问“OpenClaw 是不是只能用 API 算力本地模型接不进去”答案当然是否定的。你可以用 Ollama 跑本地模型关键是要把 gateway 的路由和 Ollama 的模型名对应上。我踩过的一个典型坑是这样的我在 Ollama 里拉取了qwen2.5:7b配置里写的 model 却是qwen2.5。Ollama 允许短名访问默认 tag但在 OpenClaw 的 adapter 里如果它的逻辑是精确拼接请求路径那么qwen2.5和qwen2.5:7b可能会造成请求发到 Ollama 后返回 404 或模型不存在错误。正确做法是先明确你已下载的模型全名。用ollama list查看本地现有模型的准确名称配置里写什么gateway 才会原样传给 Ollama。另一个常见问题是端口OpenClaw 默认连http://localhost:11434但如果你在 WSL2 里跑 OpenClaw、在 Windows 宿主机跑 Ollamalocalhost可能指向的不是同一个网络命名空间。这时候要用 WSL2 的宿主 IP 或者给 Ollama 设置允许来自 WSL2 的访问。如果你是把本地模型和云端模型混在一起用还有一个设计上的细节值得注意OpenClaw 允许你同时定义多个 provider 的路由也就是说“一个 gateway 同时接 OpenAI、Anthropic、Ollama”是可行的。你只需要在客户端请求时切换模型名就能让同一个程序在云端大模型和本地小模型之间切换。这一点在控制成本、保障离线可用性上非常实用也是我至今保留本地 Ollama 路由的原因。5. 调优与进阶让 Gateway 在高负载下更稳的几点实测经验5.1 调试日志怎么开才不糊屏OpenClaw 的调试日志功能很强大但第一次打开的人很容易被刷屏。我见过不少同学直接把所有日志级别拉到 debug然后被海量输出淹没反而连问题怎么发生的都找不到。我自己的做法是“按需开”先确认问题发生在请求入口还是响应返回阶段再有针对性地打开对应模块的日志。如果你在排查路由问题重点看路由解析和请求转发的日志如果你在排查工具调用问题重点看消息组装和 tool 处理部分。这种方式比全局 debug 高效得多。还有个实用技巧把 OpenClaw 的日志同时输出到文件和终端。终端保持 info 级别用来观察整体运行状态文件里开 debug跑完一轮问题操作后再去翻文件。这样既不会被日志淹没又保留了完整的现场细节定位起问题来舒服很多。5.2 混跑云端 API 与本地模型时的超时与重试策略混跑云端和本地模型是我日常用得最多的模式这个模式里最考验人的就是超时和重试策略的取舍。云端 API 偶尔网络抖动本地模型 CPU 推理慢同样一个超时设置根本没法同时适配两类后端。我的建议是按后端类型拆分配置云端 API 的超时设短一些比如 30 秒失败后快速重试两次本地模型的超时根据模型大小和机器性能设长一些比如 5 分钟以上但重试次数要少因为多数情况下不是瞬时故障重试只会增加无用负载。重试还有一个必须考虑的点某些工具调用不是幂等的比如你已经通过工具发出去一条外部消息如果网络问题导致响应没回来gateway 的重试可能会让工具被重复执行。所以在配置重试策略时要结合上层工具的执行方式谨慎设置必要时宁可标记失败人工处理也不要盲目重试造成副作用。5.3 单实例还是 Gateway 集群从业务体量反推架构最近社区里开始有人讨论 OpenClaw 的 gateway 集群方案。我的观点是集群有价值但大多数人用不到先用单实例把业务跑通再说。为什么 OpenClaw 适合做集群因为 gateway 本身是无状态的它不持有会话数据所有持久化都在外部存储层。这就意味着你可以在前面挂一个负载均衡后面起多个 gateway 实例理论上可以水平扩展。这一点在你需要支撑多个服务商 API 并发转发的场景下很有用。但我个人的实际体会是个人使用场景下单实例完全够用瓶颈通常在模型上游而不是 gateway。只有当你会话并发很高、或者需要在多个出口节点部署时集群布局才真正有意义。而且引入集群意味着要处理外部存储同步、负载均衡健康检查、实例间连接池等一堆新问题复杂度提升不是一点半点。先把单实例的配置、监控和日志都调理顺再去考虑集群是比较稳妥的路径。最后再分享一个我个人用着很顺手的小技巧给 gateway 里的每个路由加上语义清晰的别名比如local-fast、cloud-power这种实际用途的命名而不是model-a、model-b。这样你切换模型时不用去翻文档回忆每个名字对应什么能力客户端那边的可读性也会好很多。OpenClaw 的 gateway 值得慢慢折腾每搞清楚一层设计逻辑后面写起自动化流程来都会顺手一分。