ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【OpenClaw】通过 Nanobot 源码学习架构:从 401 报错到 CC Switch 配置的完整链路

【OpenClaw】通过 Nanobot 源码学习架构:从 401 报错到 CC Switch 配置的完整链路 1. 从 401 报错切入OpenClaw 与 Nanobot 的鉴权链路到底长什么样如果你正在读 OpenClaw 或 Nanobot 的源码大概率会在某个时刻撞上401 Unauthorized或者local proxy failed。这两个报错看起来像是网络问题实际上它们指向的是同一件事请求在到达模型服务之前鉴权信息没有正确挂载到 HTTP 头里或者本地代理层没有把请求转发到正确的 endpoint。OpenClaw 是一个面向 Agent 场景的开源框架Nanobot 则是它内部负责模型调用与工具编排的轻量运行时。两者组合起来做的事情简单说就是接收用户指令拆解成若干步骤每一步可能调用一次或多次大模型 API最后把结果拼装返回。这个过程中每一次模型调用都需要携带有效的 API Key 和 Base URL。如果 Key 缺失、过期、或者 Base URL 指向了一个不存在的本地端口就会分别触发 401 和 local proxy failed。我试过在阅读 Nanobot 的provider模块时发现它把鉴权逻辑抽象成了一个AuthResolver接口。这个接口有两个实现一个是直接从环境变量读取OPENAI_API_KEY和OPENAI_BASE_URL另一个是从本地配置文件auth.json中读取。当两者都没有命中时AuthResolver会返回一个空凭证对象后续的 HTTP 客户端在构造请求时就不会带上Authorization头服务端自然返回 401。而local proxy failed通常出现在你配置了一个本地代理地址比如http://127.0.0.1:8080但那个端口上并没有服务在监听。Nanobot 在启动时会尝试连接这个地址做健康检查如果连接被拒绝就会抛出这个错误。很多人在用 CC Switch 切换不同模型供应商时忘记同步更新 Base URL就会遇到这个情况。理解这条链路的意义在于你不需要把 OpenClaw 和 Nanobot 的每一行源码都读完只需要抓住「凭证从哪里来、请求往哪里发」这两个关键点就能定位绝大多数鉴权类报错。接下来的内容会围绕这两个点展开先讲清楚 TaoToken 在这个链路中扮演什么角色再给出可复制的配置片段最后用实际请求验证配置是否生效。2. TaoToken 前置统一 Key 与 API 通道在源码架构中的位置在 OpenClaw 和 Nanobot 的源码里模型调用的配置通常分散在几个地方环境变量、auth.json、以及 CC Switch 管理的供应商配置文件。这种分散设计的好处是灵活坏处是容易不一致。比如你在环境变量里设了 Key A在auth.json里写了 Key BNanobot 的AuthResolver按优先级选了一个但 CC Switch 的界面显示的是另一个排查起来就很痛苦。TaoToken 在这里的作用是提供一个统一的 API 通道。你不需要为每个模型供应商单独维护一套 Key 和 Base URL而是把 TaoToken 的 endpoint 作为所有请求的出口。TaoToken 的 API 地址是https://taotoken.net/api这个地址可以直接填入 OpenClaw 或 Nanobot 的 Base URL 配置项。Key 则从 TaoToken 的控制台生成格式通常是一串以sk-开头的字符串。从源码架构的角度看TaoToken 相当于在 Nanobot 的provider层和真实模型服务之间加了一个中间层。Nanobot 发出的请求先到达 TaoTokenTaoToken 根据你请求中的模型 ID 和 Key 做鉴权和路由再把请求转发到对应的上游服务。这样做的好处是你只需要在 Nanobot 里配置一次 Base URL 和 Key就能访问多个模型不需要为每个模型单独改代码。在 CC Switch 中配置 TaoToken 也很直接。CC Switch 是一个用于切换 Claude Code 或其他编码助手后端配置的工具它管理的核心字段就三个Base URL、API Key、Model ID。你把 Base URL 填成https://taotoken.net/apiAPI Key 填成 TaoToken 控制台生成的 KeyModel ID 填成你要用的模型名称比如claude-sonnet-4-20250514或gpt-4o。保存之后CC Switch 会把这些配置写入对应的配置文件Nanobot 在启动时读取这个文件就能拿到正确的凭证。这里有一个容易忽略的点Nanobot 的AuthResolver在读取配置时对环境变量的优先级通常高于配置文件。也就是说如果你之前为了测试在 shell 里export OPENAI_API_KEYxxx即使 CC Switch 里配置了 TaoToken 的 KeyNanobot 还是会用环境变量里的那个。排查 401 时先检查当前 shell 会话里有没有残留的环境变量可以用env | grep -i api_key看一下。另外TaoToken 的 Coding Plan 适合长期做编码和 Agent 开发的场景它提供的是包月或包年的额度不需要每次调用都单独计费。如果你只是偶尔跑一下 Nanobot 的测试用例用按量计费的 API Key 就够了。控制台里可以随时查看用量和余额避免因为额度耗尽导致 401。3. 可复制配置CC Switch 与 auth.json 的完整片段这一节给出可以直接复制粘贴的配置片段。你需要根据自己实际使用的模型和 Key 做替换但结构保持不变。首先是 CC Switch 的配置。CC Switch 通常把配置写在用户目录下的.cc-switch文件夹里具体文件名可能是config.json或providers.json。下面是一个 TaoToken 作为供应商的配置示例{ providers: [ { name: taotoken, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, models: [ { id: claude-sonnet-4-20250514, name: Claude Sonnet 4 }, { id: gpt-4o, name: GPT-4o } ], defaultModel: claude-sonnet-4-20250514 } ], activeProvider: taotoken }这个 JSON 里baseUrl必须写成https://taotoken.net/api不要加尾部斜杠也不要写成/v1之类的路径Nanobot 在构造请求时会自动拼接/v1/chat/completions。apiKey替换成你在 TaoToken 控制台生成的 Key。models数组里列出你计划使用的模型 ID这些 ID 需要和 TaoToken 支持的模型名称一致。接下来是 Nanobot 的auth.json配置。这个文件通常位于项目根目录或用户配置目录下Nanobot 在启动时会按顺序查找。文件内容如下{ openai: { apiKey: sk-你的TaoToken密钥, baseUrl: https://taotoken.net/api }, defaultProvider: openai, timeout: 30000, maxRetries: 2 }这里把openai作为 provider 名称是因为 Nanobot 内部默认使用 OpenAI 兼容的请求格式。TaoToken 的 API 也是 OpenAI 兼容的所以可以直接复用这个 provider 配置。timeout设为 30000 毫秒maxRetries设为 2这两个参数可以根据你的网络情况调整。如果经常遇到超时可以把 timeout 调大到 60000。如果你用的是 Claude Code 并且通过 CC Switch 管理配置还需要检查 Claude Code 的 settings 文件。在 macOS 上通常是~/Library/Application Support/Claude/settings.json在 Linux 上是~/.config/Claude/settings.json。文件里需要包含{ apiKey: sk-你的TaoToken密钥, baseUrl: https://taotoken.net/api, model: claude-sonnet-4-20250514 }这三个字段分别对应 Base URL、Key 和 Model ID也就是前面提到的三件套。缺任何一个都会导致鉴权失败或模型找不到。配置写完之后有一个检查步骤不能跳过确认文件编码是 UTF-8没有 BOM 头。有些编辑器在保存 JSON 时会自动加 BOM导致解析失败。可以用file auth.json命令查看如果输出里有with BOM就需要用sed或编辑器去掉 BOM。另外如果你在 Docker 容器里跑 Nanobot要注意配置文件是否被正确挂载到容器内。常见的做法是把宿主机的auth.json挂载到容器的/app/auth.json并在启动命令里指定--config /app/auth.json。如果挂载路径写错容器内读不到配置就会回退到环境变量而环境变量可能又是空的最终触发 401。4. 验证请求用 curl 和 Nanobot 内置命令确认链路通畅配置写完之后不要急着跑完整的 Agent 流程先用最小化的请求验证鉴权链路是否通畅。这一步能帮你把问题范围缩小到「配置错误」还是「代码逻辑错误」。最直接的方式是用 curl 发一个 chat completions 请求。命令如下curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的TaoToken密钥 \ -d { model: claude-sonnet-4-20250514, messages: [ {role: user, content: 回复一个字好} ], max_tokens: 10 }如果配置正确你会收到一个 JSON 响应里面包含choices数组choices[0].message.content就是模型返回的内容。如果返回 401说明 Key 无效或没有正确传递。如果返回 404说明 Base URL 或模型 ID 写错了。如果返回 429说明额度或速率受限需要去 TaoToken 控制台检查用量。curl 验证通过之后再用 Nanobot 自己的命令做一次验证。Nanobot 通常提供一个nanobot chat或nanobot run子命令可以传入一条简单指令。比如nanobot chat --message 你好 --model claude-sonnet-4-20250514如果这个命令能正常返回说明 Nanobot 的配置读取、请求构造、响应解析整条链路都是通的。如果 curl 通了但 Nanobot 不通问题就出在 Nanobot 的配置加载逻辑上。这时候可以打开 Nanobot 的 debug 日志看看它实际读取到的 Base URL 和 Key 是什么。在配置里把日志级别调到debug或者在启动命令里加--log-level debug。Nanobot 的日志里会打印出AuthResolver解析到的凭证对象。注意看baseUrl字段是否和你在auth.json里写的一致。如果日志里显示的是http://localhost:8080之类的地址说明有其他地方覆盖了配置可能是环境变量也可能是 CC Switch 写入的另一个文件。还有一个验证技巧在 Nanobot 的代码里找到构造 HTTP 请求的那一行通常在provider/openai.go或provider/client.py里加一个临时的日志输出把req.Header.Get(Authorization)和req.URL.String()打印出来。这样你能看到实际发出的请求头里有没有 Bearer Token以及请求的完整 URL 是什么。这个方法在排查local proxy failed时特别有用因为你能直接看到 Nanobot 试图连接的是哪个地址。如果验证请求返回的是流式响应注意检查choices字段的解析逻辑。有些客户端在流式模式下会逐块读取data:行如果某一块的 JSON 解析失败就会抛出reading choices相关的错误。这个错误通常不是鉴权问题而是响应格式和客户端解析逻辑不匹配。TaoToken 的流式响应格式和 OpenAI 官方一致所以如果你用的是标准的 OpenAI 客户端库不应该出现这个问题。如果出现了检查一下客户端库的版本旧版本可能不支持某些字段。5. 常见错排查401、local proxy failed、reading choices 与 OAuth 报错对照这一节把几个高频报错和对应的排查动作列出来你可以按图索骥。401 Unauthorized是最常见的。排查顺序是先确认 Key 是否以sk-开头且没有多余空格再确认请求头里Authorization字段的格式是Bearer sk-xxx注意 Bearer 和 Key 之间有一个空格然后确认 Base URL 是https://taotoken.net/api没有写成https://taotoken.net或https://taotoken.net/v1。如果这三项都对去 TaoToken 控制台检查 Key 是否被禁用或额度是否耗尽。local proxy failed通常出现在你配置了本地代理地址的情况下。Nanobot 在启动时会尝试连接这个地址如果连接被拒绝或超时就会报这个错。排查方法是用curl http://127.0.0.1:端口号确认本地代理是否在运行检查 Nanobot 配置里的baseUrl是否误写成了本地地址如果确实需要用本地代理确认代理进程的监听端口和配置一致。如果你并不需要本地代理直接把baseUrl改成https://taotoken.net/api即可绕过这个问题。reading choices这个报错通常发生在解析响应时。可能的原因有三个一是响应体不是合法的 JSON比如返回了一个 HTML 错误页面二是响应里没有choices字段比如返回的是错误对象三是流式响应的分块解析逻辑有问题。排查时先用 curl 发一个非流式请求确认返回的 JSON 结构里有choices数组。如果 curl 正常但 Nanobot 报错检查 Nanobot 的响应解析代码看看它是否对choices做了非空判断。OAuth 相关报错一般出现在使用 Claude Code 或类似工具时。这些工具可能默认走 OAuth 流程而不是 API Key 鉴权。如果你用 TaoToken 的 API Key需要在配置里明确指定使用 API Key 模式关闭 OAuth。在 Claude Code 的 settings 里把authType设为apiKey或者直接不配置 OAuth 相关的字段。CC Switch 在切换供应商时会自动处理这个字段但如果你手动改过配置文件需要确认authType没有被设成oauth。下面用一个表格把报错、可能原因和排查动作对照起来报错信息可能原因排查动作401 UnauthorizedKey 无效、格式错误、Base URL 错误检查 Key 前缀和空格确认 Base URL 为https://taotoken.net/apilocal proxy failed本地代理未运行、Base URL 指向本地端口用 curl 测试本地端口或改用 TaoToken 远程地址reading choices响应非 JSON、缺少 choices 字段、流式解析错误用 curl 验证响应结构检查客户端解析逻辑OAuth error工具默认走 OAuth 而非 API Key在配置中指定authType: apiKey关闭 OAuth排查时还有一个通用技巧把 Nanobot 的日志级别调到 debug观察它实际发出的请求 URL 和请求头。很多时候问题就藏在日志里只是默认级别不打印这些信息。另外如果你同时用了 CC Switch 和手动编辑的auth.json注意两者的优先级。CC Switch 写入的配置可能会覆盖你手动改的内容导致你以为改了但实际没生效。这种情况下要么统一用 CC Switch 管理要么在 CC Switch 里把对应供应商禁用避免冲突。6. 继续深入源码从鉴权层到 Agent 编排层的阅读路径配置调通之后如果你还想继续读 OpenClaw 和 Nanobot 的源码建议按这个顺序推进先看provider目录下的鉴权解析和 HTTP 客户端构造再看agent目录下的任务拆解和工具调用逻辑最后看memory和planner模块。鉴权层是入口理解了它后面的请求流程就顺了。在provider层重点关注AuthResolver接口的实现类以及Client结构体的Do方法。Do方法里会构造http.Request设置Authorization头和Content-Type头然后调用http.Client.Do发送请求。你可以在这里加断点或日志观察请求的实际内容。如果发现请求头里没有Authorization就往上追AuthResolver的返回值看看为什么凭证是空的。agent层是 Nanobot 的核心。它接收用户输入调用planner生成步骤列表然后逐步执行。每一步可能是一个模型调用也可能是一个工具调用。模型调用会复用provider层的客户端所以鉴权配置在这里同样生效。如果你在 Agent 运行过程中遇到 401但单独用 curl 测试又是通的那问题可能出在 Agent 在某个步骤里用了不同的 provider 配置。检查一下planner生成的步骤里有没有指定provider字段如果有确认那个 provider 的配置是否正确。memory模块负责存储对话历史和中间结果。它通常不涉及鉴权但如果你用了外部存储比如 Redis 或 PostgreSQL需要确认连接配置是否正确。这部分报错和模型鉴权无关但容易和 401 混淆因为都表现为「请求失败」。区分方法是看报错信息里有没有redis或postgres关键字。阅读源码时建议配合实际的请求日志一起看。在 Nanobot 启动时加上--log-level debug然后把日志输出重定向到一个文件。运行一次完整的 Agent 任务然后对照日志和源码看每一步实际执行了什么。这种「日志驱动」的阅读方式比单纯看代码效率高很多因为你能看到真实的数据流。如果你在阅读过程中遇到不确定的配置项可以查阅 TaoToken 的接入文档里面列出了支持的模型 ID 和请求格式。文档地址是https://taotoken.net/doc内容会随模型更新而调整。对于长期做编码和 Agent 开发的场景Coding Plan 提供了更稳定的额度方案适合需要频繁调用模型的场景。控制台里可以生成新的 API Key也可以查看每个 Key 的用量明细方便你做成本核算。最后提醒一点在源码里修改鉴权逻辑时不要把 Key 硬编码在代码里。即使只是本地测试也建议用环境变量或配置文件注入。硬编码的 Key 一旦提交到 Git 仓库即使后续删除历史记录里仍然可以找回。用auth.json加.gitignore的方式管理本地配置是更安全的做法。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进