跑 Buzz 的代码审查 Agent,TaoToken Key 放服务端还是客户端
跑 Buzz 的代码审查 Agent最容易卡住的往往不是选哪个模型而是「Key 到底落在哪一层」。Buzz 是基于 Nostr 的人机协同框架把聊天、Git 仓库、代码审查和 AI Agent 塞进了同一个窗口——这也意味着模型凭据可能同时暴露在 relay、桌面客户端、CI runner 三个位置上。我这边长期维护 Buzz Agent 的模型入口前后踩过几次坑客户端打包产物里残留明文 Key、relay 转发时把 Authorization 头透传给了错误的事件订阅者、本地 curl 通了但 Agent 回帖时 401。这篇把字段对照、请求日志和可复现步骤一次写清。TaoToken 的 Key 统一在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_review_agent 获取Base URL 固定为 https://taotoken.net/api 。1. 先认清 Buzz 的四层进程哪一层允许持有模型 Key很多「Agent 不漏 Key」的说法之所以翻车是因为没先把 Buzz 的进程边界画出来。Buzz 把聊天、Git 仓库、代码审查、AI Agent 放在一个窗口里但底层其实是四层不同的东西它们的信任级别完全不同。第一层Nostr 客户端桌面 / Web。它持有用户的nsec私钥负责给事件签名。nsec是身份凭据一旦泄露等于账号被接管。这一层天然是「不可信终端」——打包产物可被反编译localStorage 可被读取Electron 的渲染进程甚至能在 DevTools 里直接console.log内存里的变量。第二层Relay中继。事件在这里存储和转发。Relay 只应该看到已经签名的事件不应该看到任何明文凭据。它的职责是路由不是保管秘密。第三层Agent Runner模型调用进程。这是真正需要api_key的地方。它订阅带有特定标签的代码审查请求事件拉取 diff调用模型再把结果作为新事件发回 relay。第四层Git 侧仓库 / CI。它触发审查比如 PR 创建时发一个事件。它可以用只读凭据但同样不该持有模型 Key。结论很直接模型 Key 只能出现在第三层。层能否持有模型 Key原因Nostr 客户端否会被打包、反编译、共享屏幕泄露Relay否只做事件路由持 Key 等于扩大爆炸半径Agent Runner是唯一出口可集中做日志、限流、轮换Git / CI否用最小权限的仓库凭据即可如果你把 Key 写进客户端的配置文件最坏情况不是「多花点钱」而是 Key 随着事件或调试日志扩散到别人的机器上。轮换一次 Key你得通知所有装了客户端的人重新配置——这在协作场景里几乎不可运营。2. TaoToken 模型入口字段对照表从 nsec 到 api_key 的完整映射梳理完进程边界下一步是把「哪个字段填到哪」列成一张可执行的对照表。Buzz 的 Agent 配置界面里经常混着几类字段名字都很像填错就 401。先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_field_mapping 拿到你的 Key然后按下面的表逐项落位。注意nsec和api_key是两个世界的字符串永远不要互换。字段语义变量名 / 配置项落点说明Nostr 身份私钥NSEC/BUZZ_NSEC客户端或 Runner 本地用于事件签名不进模型请求头relaya 地址BUZZ_RELAY_URL客户端 Runnerwss://开头模型供应商 Base URLBASE_URLRunner固定https://taotoken.net/api模型鉴权 KeyTAOTOKEN_API_KEY仅 Runner占位符写YOUR_API_KEY模型 IDMODEL_IDRunner从平台的模型列表里选请求超时REQUEST_TIMEOUT_MSRunner建议 120000 起重试次数MAX_RETRIESRunner建议 23配合指数退避单次最大输出MAX_OUTPUT_TOKENSRunner代码审查建议 4096 以上客户端可见的代理地址AGENT_PROXY_URL客户端只指向自己的 Runner不指向模型供应商这张表的核心判断标准只有一个这个字段会不会出现在客户端打包后的产物里。会的话它就不能是模型 Key。实际配置里我习惯再加一层间接客户端只知道AGENT_PROXY_URLRunner 才知道BASE_URL和TAOTOKEN_API_KEY。这样即使客户端的配置文件被完整 dump 出来泄露的也只是一个内网地址。3. 服务端 Runner 的可复现配置Claude Code、Codex、裸 HTTP 三条路Runner 用什么是你的自由但配置范式必须正确。下面三套都是可以直接复制改的。3.1 Claude Code 作为 Runner 的模型执行器Claude Code 走的是settings.jsonANTHROPIC_*环境变量这一套字段名不要写错。{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: YOUR_MODEL_ID, ANTHROPIC_SMALL_FAST_MODEL: YOUR_SMALL_MODEL_ID, ANTHROPIC_DEFAULT_HAIKU_MODEL: YOUR_SMALL_MODEL_ID } }几个容易踩的点ANTHROPIC_BASE_URL后面不要手动拼/v1先按https://taotoken.net/api填跑不通再看实际请求路径。ANTHROPIC_AUTH_TOKEN和ANTHROPIC_API_KEY在某些版本里只认其中一个两个都配时以更具体的为准建议只留一个。这份settings.json放在 Runner 的独立用户目录下不要跟着客户端一起分发。如果 Runner 是容器环境变量更省事export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELYOUR_MODEL_ID3.2 Codex 作为 Runner 的模型执行器Codex 用的是config.toml字段体系跟 Claude Code 完全不同。千万不要把ANTHROPIC_*套到 Codex 上那是两套协议。model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api responses [profiles.buzz-review] model_provider taotoken model YOUR_MODEL_ID approval_policy never运行前把 Key 放进环境变量export TAOTOKEN_API_KEYYOUR_API_KEY codex --profile buzz-reviewenv_key指的名字要和export的名字完全一致大小写敏感。3.3 裸 HTTP 冒烟测试在接进 Buzz 的 Agent 之前先用一条 curl 确认 Base URL 和 Key 本身是通的。这一步能挡掉后面一半的排障时间。curl -sS -w \nHTTP_STATUS%{http_code}\n \ https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer ${TAOTOKEN_API_KEY} \ -H Content-Type: application/json \ -d { model: YOUR_MODEL_ID, messages: [ {role: user, content: reply with the single word: pong} ], max_tokens: 16 }看到HTTP_STATUS200并且返回里有pong说明 Key 和 Base URL 都没问题剩下的就都是 Buzz 侧的配置问题。4. 客户端只保留切换能力CC Switch 三件套的最小暴露面如果 Runner 部署在开发者本机上个人使用场景很常见用 CC Switch 管多套供应商会方便很多但要理解它到底改了哪三样东西。三件套指的是供应商条目providers一组{name, base_url, key_ref}的声明。key_ref是引用不是明文。当前激活项同一时刻只有一套生效避免请求发到错误的供应商。注入与重启切换后要把字段写进目标工具的环境并重启对应进程才生效。CC Switch 里的供应商条目可以这样描述概念示意{ providers: [ { name: taotoken, base_url: https://taotoken.net/api, key_env: TAOTOKEN_API_KEY, models: { default: YOUR_MODEL_ID, fast: YOUR_SMALL_MODEL_ID } } ], active: taotoken }关键在key_env配置里存的是环境变量名真正的值放在 Runner 的进程环境或系统钥匙串里。这样即使配置文件被同步到别的机器也没有可用的密钥。客户端侧的红线不把 Key 写进localStorage、sessionStorage、IndexedDB。不把 Key 写进 Electron 的renderer进程可见的任何变量。不打日志打印完整的Authorization头。客户端只允许配置AGENT_PROXY_URL请求先打到自建 Runner由 Runner 去调模型。做到这四条客户端被随便翻都不太会有事故。5. 请求日志这样打401、404、429 一眼定位Buzz 的 Agent 出错时最容易误判的链路是「客户端 → relay → Runner → 模型」。所以日志里必须带一个贯穿全链路的trace_id从事件 ID 派生即可。推荐的结构化日志每行一条 JSON便于jq处理{ ts: 2025-01-01T10:00:00.123Z, trace_id: evt_8f3c..., stage: agent.review.request, provider: taotoken, base_url: https://taotoken.net/api, model: YOUR_MODEL_ID, key_fingerprint: sha256:ab12..., diff_bytes: 18234, status: 200, latency_ms: 4210, retry: 0 }注意key_fingerprint只存哈希前 8 位用来确认「用的是哪把 Key」绝不能存原文。排障对照表现象可能的日志特征检查点401stageagent.review.requeststatus401key_fingerprint为空Authorization 头没到 RunnerKey 被截断客户端签发的是nsec而不是模型 Key404status404base_url末尾多了或少了路径段Base URL 是否被误改成带/v1的形态429status429retry2后仍失败并发太高需要把审查任务串行化并加退避超时无status有latency_ms timeout单次 diff 太大按文件或按 hunk 分片空回复status200但output_tokens极小MAX_OUTPUT_TOKENS太小或提示词被截断查最近 50 条失败请求jq -c select(.status 400) | {trace_id, stage, status, latency_ms, retry} \ agent-runner.log | tail -n 50按 trace_id 还原完整链路jq -c select(.trace_id evt_8f3c...) agent-runner.log日志里能同时看到「事件 ID、请求状态、Key 指纹、Base URL」这四样大多数问题五分钟内能定位。6. 端到端复现从建 Key 到代码审查事件回帖下面这套步骤是我在本地复现过的最小链路按顺序做即可。步骤 1获取 Key 并确认 Base URL。到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_log_check 拿到 Key记住 Base URL 是https://taotoken.net/api。步骤 2把 Key 写进 Runner 的环境而不是客户端。# 只在 Runner 进程里执行 export TAOTOKEN_API_KEYYOUR_API_KEY export ANTHROPIC_BASE_URLhttps://taotoken.net/api步骤 3跑 curl 冒烟测试。用第 3.3 节的命令确认返回 200。步骤 4启动 Runner确认它订阅到了正确的事件类型。agent-runner \ --relay ${BUZZ_RELAY_URL} \ --subscribe-kind 7000 \ --provider taotoken \ --base-url https://taotoken.net/api \ --log-level info--subscribe-kind的取值以你那套 Buzz 实现的约定为准关键是订阅范围要窄别把聊天事件也拉进来。步骤 5在 Buzz 里对一个测试仓库发起审查请求。观察 Runner 日志是否出现stageagent.review.request。步骤 6核对请求日志四项。trace_id有值、base_url正确、status200、key_fingerprint稳定不变。步骤 7确认审查结果作为新事件发回。Runner 输出里应出现stageagent.review.publishevent_id与上一步的trace_id对应。步骤 8断网 / 改错 Key 做一次反向验证。把 Key 改成YOUR_API_KEY_WRONG确认日志明确给出 401 且key_fingerprint变化说明你的排障链路是通的。这八步走完你就有了一个「Key 只在服务端、链路可观测、出错可定位」的 Buzz 代码审查 Agent。之后再换模型或加并发都只是改 Runner 的参数客户端一行不用动。7. 把模型入口收敛到一个地方剩下的才是迭代回到最开始那个问题TaoToken 的 Key 放服务端还是客户端。答案不是「服务端更安全」这种口号而是客户端根本不具备保管长期凭据的能力——它会被打包、被同步、被共享屏幕、被调试工具读取。Buzz 把聊天、仓库、代码审查和 Agent 放在一个窗口里体验上是一体的但信任边界必须分层。落地上就三句话客户端只认代理地址Runner 独占模型 Keyrelay 只做事件路由。配套的是字段对照表 结构化日志 一条 curl 冒烟测试。这套组合跑顺之后加新模型、调并发、换供应商都只是 Runner 侧的一次配置改动。想先验证模型本身能不能跑通可以直接在模型对话里试一轮https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_review_agent如果你的 Runner 要长期跑代码审查这种高频任务先看下 Coding Plan 的额度形态https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_review_agent准备好正式接入就去控制台创建 Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_review_agentClaude Code 侧的字段细节和环境变量优先级参考这份文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentbuzz_review_agent