OpenClaw智能体网关部署实战:WSL2环境与401鉴权排查全记录 上周接到一个有点特殊的测试任务在公司 Windows 测试机上把 OpenClaw 智能体网关搭起来验证它对接 API 的鉴权链路和协议转发是否可靠。这个网关的作用一句话概括把智能体工具的请求收敛到统一入口再按规则分发到不同的模型算力后端。结果从第一次启动开始401 就没消停过——一会儿 incorrect api key一会儿 missing bearer远程压缩任务也报 401。等到把鉴权链理顺新的问题又出来了怎么把请求从云端 API 重定向到本地 Ollama 上的 qwen2.5-3b。这篇文章是这次部署和排障的完整记录重点是排查思路和配置细节。适合正在搭 OpenClaw 或类似智能体网关的测试、研发、运维同学参考照着走能少踩一半坑。1. 部署前的环境判断WSL2 才是第一道门槛很多人装 OpenClaw 的第一步是搜安装命令然后直接在 Windows PowerShell 里敲。结果进程还没起来先收到一句无法安全验证 WSL2 环境请在 PowerShell 中运行 wsl --status。我在网上看到不少同学卡在这一步其实这个报错被说得很玄本质就一句话安装程序要求网关以 WSL2 发行版作为运行底座但当前机器没有满足条件。1.1 为什么网关进程要挂在 WSL2 下面OpenClaw 这类智能体网关不是个简单的静态服务它要同时处理多路长连接、文件系统事件监听、子进程管理和一套接近完整 Linux 工具链的依赖。Windows 原生环境在这些方面不是不能跑但很多第三方依赖在 Windows 上的行为有差异路径分隔符、符号链接、Unix socket、信号处理都不太一样。与其每个模块单独适配 Windows官方直接要求走 WSL2 反而更稳定。这和 Docker Desktop 默认把容器运行在 WSL2 里是同一个思路。我这台测试机的实际情况是机器装了 WSL 发行版但默认版本是 1OpenClaw 的预检脚本直接判定不合格。报错信息里让你在 PowerShell 中运行 wsl --status不是一句废话——它真的就是靠这个命令来判断默认版本是不是 2。1.2 把环境拉齐的三板斧第一步在 PowerShell 里跑wsl --status重点看默认版本是不是 2。如果不是继续wsl --set-default-version 2 wsl --update第二步确认发行版本身确实是 WSL2wsl --list --verbose输出的 VERSION 列必须是 2。有时候发行版是从旧版本升级上来的VERSION 列还写着 1需要在发行版内部执行:wsl --set-version 发行版名称 2第三步装完 WSL2 后别急着跑 OpenClaw先把发行版启动一次等初始化脚本跑完再开网关。我第一次就是升级完立刻执行结果发行版还处于正在安装状态OpenClaw 的预检又挂了。等wsl -l -v显示 State 为 Running 且版本为 2 之后再执行 OpenClaw 的启动命令就顺利过去了。如果你用了 OpenClaw Windows Companion这里还有个容易忽略的点Companion 进程在 Windows 侧它管理的是 WSL 里的网关进程配置时要注意绑定的发行版名称必须和wsl -l -v输出里的名字完全一致大小写都不能差。我在测试机里试过把发行版名叫成 Ubuntu-22.04Companion 里却填 Ubuntu结果 Companion 一直显示网关离线最后才发现是名字不一致。1.3 Ubuntu 与 Termux不同底座各有各的注意点如果直接在 Ubuntu 服务器上部署前面这些 WSL 的折腾可以全部跳过直接走 Linux 安装流程。我的建议是生产或准生产验证优先用纯 Linux 环境Windows WSL2 更适合本地开发自测。Termux 手机版我也试过装上跑通但对智能体网关这个场景来说手机端的网络环境相对复杂进程容易被系统回收更适合做功能冒烟而不是稳定运行。真要在手机端玩优先用功能精简过的版本别把完整的路由表和大模型调用全压到手机进程里。这一步给后面埋下的伏笔是环境版本不一致往往会表现为莫名其妙的 401 或者连接重置。先花二十分钟把 WSL2 环境校准后面排查鉴权问题时思路会清晰很多。2. 401 鉴权陷阱三种 401 的报文差异与完整排查链路把环境跑起来只是开始。我这次碰到的第一个硬骨头是 401。但 OpenClaw 的 401 不是同一种从报错文本到排查方向完全不同。2.1 先看清三张脸报错关键片段在哪一层返回更可能的根因incorrect api key provided: sk-svcac****网关调用上游 APIkey 本身无效、过期或已吊销missing bearer or basic authentication上游 API 的鉴权中间件请求头没有携带认证信息或带成了其他格式code invalid_api_key上游 API 的业务层 JSON 返回key 能连上服务但不被该接口接受常见于权限范围不匹配网上流传最广的报错是 unexpected status 401 unauthorized: incorrect api key provided: sk-svcac****。这里有个值得留意的细节sk-svcac 这个前缀意味着你用的是服务账号service account类型的 key而不是普通的项目 key。服务账号 key 的特点是权限更收敛通常绑定到特定的项目和模型访问范围。所以如果你把它配在某个路由上调其他模型接口时完全可能只在某些路径下 401其他路径正常。这也是为什么很多人说我明明换了 key 还是 401——key 本身没死是作用域不够。2.2 先别改 key先定位是哪一层拒绝的我的习惯是把排查顺序固定成下面五步不要一上来就换 key第一步确认网关进程实际读到的是哪个 key。很多时候你改了环境变量但进程是之前启动的根本没重新加载env | grep -i key echo $OPENCLAW_API_KEY | wc -c第二步检查配置文件里是否存在覆盖环境变量的字段。有些网关配置里 provider 段下面有 api_key 字段优先级高于环境变量。你在环境变量里配得再对配置文件里一段旧值就能把你顶掉。第三步检查 key 前后有没有不可见字符。从网页复制 key 到 .env 文件时引号被当成 key 的一部分、行尾带回车符这类问题我见过太多次。可以把 key 输出出来核对printf %s $OPENCLAW_API_KEY | xxd | tail -n 3第四步用 curl 分别打上游和网关把问题隔离到单侧curl -v https://上游API地址/v1/models -H Authorization: Bearer $OPENCLAW_API_KEY如果直连上游返回 200问题一定在网关的转发链路如果直连也 401那基本就是 key 本身的问题去控制台查 key 状态。第五步看网关日志。这一步最关键。OpenClaw 日志里通常会打印转发时最终带上的 Authorization 头前缀和上游返回的响应头。401 的响应头里有个 WWW-Authenticate 字段它明明白白写着上游期望的认证方式比如 Bearer 或 Basic。如果上游期望 Bearer而你转发时用了 Basic自然 401。2.3 Bearer 与 Basic 混用最容易踩的转发坑智能体网关做的是替身转发客户端把请求交给网关网关决定用自己的凭据还是客户端的凭据去访问上游。很多网关默认支持多种上游鉴权方式但配置入口很容易混。我这次就遇到一个典型上游要求 Authorization: Bearer网关侧配置却用的是 basic auth 字段结果网关把 key 编码成 Basic base64 格式发过去上游直接报 missing bearer or basic authentication。这个报错很反直觉——字面上说缺少 Bearer 或 Basic但实际请求头里是有认证信息的只是格式不是上游认可的。处理方式不复杂要么在网关配置里把 auth scheme 改成 bearer要么在转发规则里显式声明透传客户端的 Authorization 头。做测试用例时我建议把这两种模式各写一个用例断言响应不是 200 时返回体里的错误码必须来自上游而不是网关本地抛的——这样能快速判断问题在哪一层。2.4 登录态和 API key 混用造成的幽灵 401另一个容易出现的情况是用工具自带的 ChatGPT 账号登录方式配置后网关报了 401。原因是有些智能体客户端支持 OAuth 登录登录后拿到的是 access_token不是 API key。网关如果只在配置里认 API key它可能把这个 access_token 当作 bearer token 转发但上游对这类 token 的校验上下文完全不同返回 401 是必然的。在网关这种场景里我个人的建议是统一用 API key 方式接入。OAuth 刷 token 的逻辑更适合客户端直连放到网关里要额外处理 refresh 流程搞不好就变成定时炸弹。如果确实要用登录态模式至少单独建一条路由别和 API key 路由共用配置。3. 协议重定向把流量从云端 API 切到本地 Ollama 算力鉴权问题理顺之后我接着做的是协议重定向优化。为什么要做这件事因为 OpenClaw 这类网关默认把所有请求打到云端 API但测开场景里你不可能一直用生产 key 做回归成本、配额、并发都受不了。而且有些测试数据不方便出内网。把一部分流量重定向到本地算力就成了刚需。3.1 到底有没有本地算力这条路关于网上常问的OpenClaw 只能用接入 API 的方式使用算力吗我的答案很明确不是。关键是把网关路由的 target 指到本地模型的 OpenAI 兼容接口。现在本地推理引擎基本都提供 OpenAI 兼容接口Ollama 就是最典型的例子。装上之后本地模型对外表现得和一个 OpenAI 服务一样网关只要能配置目标地址和模型映射就能把请求引过去。这一步的效果很直观同一个网关云端 key 用于线上联调本地 Ollama 用于回归测试和离线验证切换成本降到一个配置项。这也是我理解的协议重定向的核心价值——不是简单把 URL 换一下而是把协议、模型、参数都适配到目标后端能接受的形式。3.2 关联本地模型qwen2.5-3b 的完整接入过程我用 qwen2.5:3b 做过完整接入步骤可以照抄。第一步启动 Ollama 并拉模型ollama pull qwen2.5:3b ollama list第二步确保 OpenAI 兼容接口已经监听。Ollama 默认在 11434 端口提供 /v1 路径curl http://localhost:11434/v1/models能看到模型列表就说明兼容接口可用。如果网关跑在 WSL2 而 Ollama 跑在 Windows 宿主机注意把 OLLAMA_HOST 设置为 0.0.0.0并且网关里不要写 localhost要写宿主机的可访问地址否则 WSL2 里的请求会打到发行版自己的回环地址上连不上宿主机服务。这个local 到底是谁的 local的问题是环境类故障里最高发的一个。第三步在网关配置里增加一条指向本地算力的路由。以我用的版本为例配置结构类似下面这样具体字段名以你安装的版本为准routes: - id: chat-local match: - /v1/chat/completions target: http://localhost:11434/v1/chat/completions auth: mode: pass-through # 本地不校验 key但不能留空填个非空占位符避免网关本地校验失败 placeholder_api_key: ollama-local body_transform: type: model_remap mapping: gpt-4: qwen2.5:3b注意这里最重要的一行是 model_remap。客户端请求里如果写的是 gpt-4而本地模型叫 qwen2.5:3b不把 model 字段改写的话请求到了 Ollama 会直接返回 model not found。很多同学第一步就连不上不是路由没通是模型名没映射。第四步重启网关加载配置然后用最小请求验证curl -s http://localhost:网关端口/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $OPENCLAW_API_KEY \ -d {model:gpt-4,messages:[{role:user,content:hi}],max_tokens:16}关注两个证据响应是否 200以及 Ollama 侧日志是否出现新请求。两个都满足才说明重定向真的生效了。3.3 协议适配远不止换 URL模型映射以外的三个细节第一次做重定向的人会觉得换个 target 就好实际上还要处理三个适配细节。第一个是路径级路由别混。/v1/chat/completions 和 /v1/embeddings 是两类完全不同的接口如果一条路由用前缀匹配把 /v1 全吞掉嵌入请求会被转发到聊天接口返回 400 或者一串莫名其妙的错误。我的习惯是显式声明全路径匹配并且把不打算重定向的路径单独列出来。第二个是参数字段裁剪。本地 3B 模型的上下文长度和 max_tokens 上限通常小于云端大模型客户端按云端参数发过来本地模型可能直接报错。这时需要在网关做参数下限处理把超过模型上限的 max_tokens 压回去。这个逻辑不要写在业务代码里就在网关配置里做不然每次接入新模型都要改业务。第三个是响应差异归一化。Ollama 的响应结构和 OpenAI 大体一致但 usage 字段、finish_reason 的取值在某些版本里有差异。智能体客户端往往对响应结构很敏感所以网关最好在返回给客户端之前做一层归一化。做测试时重点断言 usage.total_tokens、choices[0].message 这些关键字段必须存在。3.4 验证协议重定向是否生效测试用例得这么写在测开视角下协议重定向不是配置完就算完要能持续验证。我会把它整理成三个冒烟用例。用例一路由命中测试。配置本地路由后发送最小 chat 请求断言响应体里的 model 字段是 qwen2.5:3b。如果模型名还是 gpt-4多半是 model_remap 没生效或者命中的是另一条优先级更高的路由。用例二鉴权头透传测试。用 curl -v 看实际发出的请求头里 Authorization 是否符合预期。这里特别建议把 Authorization 头的前缀也打出来别只看是否非空格式错了照样 401。用例三回滚测试。把 target 切回云端 API重启网关再发同样的请求断言返回 200。这个用例看着简单但能保证你在测试环境改路由后还能快速切回生产链路。4. compact task 与 local proxy远程任务的异常侧面鉴权和路由的坑解决完还有两类比较隐蔽的问题都是在真实跑智能体工作负载时暴露的。一类是远程压缩任务报 401另一类是切换本地转发服务时报错。这里一并把排查逻辑说清楚。4.1 error running remote compact task401 只出现在压缩任务里智能体对话一长工具就要触发上下文压缩compact把早期对话总结成摘要腾出上下文空间。这里的坑在于压缩任务往往由一条独立的路由配置驱动它使用的模型不一定和主对话模型相同。如果你把主对话模型切成 qwen2.5-3b但压缩任务的路由还指向云端、并且这条路用的 key 恰好没权限就会看到error running remote compact task: unexpected status 401 unauthorized: incorrect api key provided: sk-svcac****这个报错最大的迷惑性在于从字面上看就是key 错了但你已经验证过主链路明明能用同一个 key。实际上问题不在 key而在压缩任务请求的 URL 和 model 根本是另一套配置。排查顺序我建议这样第一步去网关日志里找 compact task 相关的完整请求行看清楚它访问的 URL 是云端还是本地。第二步看压缩任务配置里的 model 名称和主对话模型对比。第三步如果压缩任务也指向云端单独验证这个 key 对该模型有没有权限如果压缩任务要指向本地把它和主对话路由一起切成模型映射。把这步做完你基本就能解释为什么别的请求都正常只有 compact 报 401。4.2 cc switch local proxy failed切换本地转发失败时查什么另一个高频报错是 cc switch local proxy failed while handling...。这里的 local proxy 指的是网关里的本地转发服务也就是把请求转给本地模型的那一层。我在实操中也被这个报错堵过它通常是路由切换失败不是网络问题。第一优先查端口监听。本地模型服务没起来或者端口不对时网关转发出去直接连接失败但报错文案可能归到 switch failed 里。用下面的命令快速确认ss -ltnp | grep 11434 # 或 lsof -i:11434第二优先查路由优先级。如果配置里同时存在两条匹配 /v1/chat/completions 的路由网关默认按配置顺序取第一条。你以为切到了本地实际上流量还在走云端甚至空路由。解决方式是显式给路由加优先级字段把本地路由的优先级调高或者干脆删掉不用的那条别留着和正式路由互相干扰。第三优先查配置加载。很多网关修改配置文件后不会自动热更新需要手动 reload 或重启进程。如果你的配置改了但行为没变先别怀疑语法看进程启动时间和配置文件修改时间是不是对得上。我遇到过一种情况配置文件权限不对进程启动时静默回退到默认配置任何修改都不生效日志里也没有明显报错。遇到这种改了等于没改的诡异问题先用命令行手动指定配置文件路径启动试试。第四优先查跨环境地址。网关在 WSL2、本地模型在 Windows 宿主机时网关里写 localhost 是连不到宿主机的。在 WSL2 里从发行版访问 Windows 宿主机的服务这个地址问题如果不处理本地转发永远切不过去。宿主机上的服务监听 0.0.0.0 后网关里用宿主机在 WSL2 网络中的地址即可。4.3 把这几种异常转成可回归的断言排掉坑之后我习惯把它们固化到网关健康检查脚本里。每次升级网关版本或者改路由配置先跑一遍这三个检查# 检查一本地模型端点存活 curl -sf http://localhost:11434/v1/models # 检查二网关路由命中 curl -s http://localhost:网关端口/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $OPENCLAW_API_KEY \ -d {model:gpt-4,messages:[{role:user,content:ping}],max_tokens:4} | grep -o qwen2.5:3b # 检查三配置文件权限 ls -l 网关配置文件路径 | awk {print $1}第三条看起来蠢但配置文件权限导致的静默回退是我这次故障里最耗时的部分写进脚本以后至少不用再靠肉眼排查一遍。5. 卸载与回滚部署翻车也要有后路最后说说卸载和回滚。OpenClaw 涉及 Windows、WSL2、配置文件、自启动服务好几个层面不在动手前想清楚卸载方式很容易留下残骸。5.1 不同安装方式的卸载路径如果 OpenClaw 是通过 npm 全局安装的卸载命令如下npm uninstall -g openclaw如果装在 WSL2 发行版里先按对应的包管理器卸载再决定是否清理发行版本身。wsl --unregister 发行版名称会把整个发行版删掉包括里面所有数据操作前务必确认没有需要保留的业务数据。这个命令很危险我一般只在彻底重建环境时才用。Windows 侧的残留主要在配置目录常见的是用户目录下的 .openclaw 目录以及 Windows 侧的 app data 目录。卸载后手动检查删除删除前把配置文件备份一份别直接 rm万一要排查问题还能对比。如果配置过自启动还要检查 systemd 服务或 Windows 计划任务。WSL2 内部的服务一般用systemctl disable --now openclawWindows 计划任务则要到任务计划程序里找到相关条目禁用删除。这个很容易被忽略导致卸载后进程又被拉起来看起来像没卸载干净。5.2 回滚优先于卸载先保存现场卸载是最后手段。正常情况下我更推荐回滚到已知正常版本。如果最近一次改动是升级导致的故障可以在 npm 上装回之前的版本号。配置方面我的习惯是每次改动前留一个带日期的备份cp ~/.openclaw/config.yaml ~/.openclaw/config.yaml.$(date %F)有了这个习惯回滚的粒度可以细到只还原昨天那份配置。比起整个卸载重装这个路径快得多也安全得多。5.3 凭据残留这种隐蔽问题最后提醒一件测开经常忽略的事OpenClaw 的 key 很可能不只存在于配置目录。你安装时敲过的命令、source 过的 .env 文件、日志输出都可能把 API key 留在机器上。卸载之后如果这台机器还要移交别人使用建议额外检查 shell 历史文件和日志目录history | grep -i OPENCLAW_API_KEY grep -r sk- ~/.openclaw/logs/ 2/dev/null | head -n 5发现日志里有完整 key 的话直接清掉。卸载不光是把程序删掉把敏感信息一起清掉才叫真正的卸载收尾。好了写到这里基本把这次从部署到调优、从排障到收尾的过程讲完了。最后说一点我个人体验最深的东西在智能体网关这类新型中间件上任何报错都不要只盯着错误码本身。401 这个状态码背后可能是 key 失效、认证格式错误、路由权限不足、登录态混用四类完全不同的原因。而协议重定向也不是改一行目标地址就完事模型映射、参数裁剪、响应归一化缺一不可。我自己的排查顺序已经固定成了一条流水线先确认环境底座再隔离请求层级然后看认证格式最后才动配置。这个顺序帮我省了很多时间。如果你也在搭类似的网关建议别等到全链路报错堆起来才开始处理先把最简的一条 chat 请求从网关到任意一个上游打通再逐步加复杂度这样每一步的变量都是可控的排错效率会高非常多。