1. 从 Claude Code 的 401 与 token 账本说起:为什么 Bradley-Terry 评测先要管 Key 边界
在 Claude Code 里把ANTHROPIC_BASE_URL切到https://taotoken.net/api后,如果仍看到401 invalid x-api-key,先别急着怀疑模型路由,先检查 TaoToken Key 是否把模型范围、并发和预算混在了一起。做 LLM Agent 测试时算力策略的 Bradley-Terry 评测时,Key 边界不清会让 token 账本和 Elo 一起漂移。准备调用模型前,到 TaoToken 官网 拿一把评测专用 Key,并把客户端base_url指向https://taotoken.net/api。
很多人跑 Agent 评测时,会把“能调通”当成“可复现”。这两件事在 LLM Agent 场景里差别很大。你用一个通用 Key 跑 Claude Code、Codex、CC Switch 和自定义 Agent,短期看很省事,长期看会污染实验:同一个 Key 下既有交互式补全请求,又有批量采样请求;既有轻量模型,又有高成本模型;既有串行调用,又有 16 路并发。最后你拿到一组 token 数和 Elo 分数,却无法解释它们到底对应哪条测试时算力策略。
Elo-per-token 分析想解决的问题很具体:用 Bradley-Terry 模型把任务内排序聚合成跨任务 Elo,再看不同测试时算力策略的收益递减。这个分析对 token 统计非常敏感。只要 Key 边界没有隔离,token 账本就会混入非实验流量;只要base_url在不同工具里不一致,部分请求就会走到旧配置;只要模型范围没有锁定,某个策略偷偷调用了更快的模型,跨任务 Elo 就不可比。
所以,在写 Bradley-Terry 拟合脚本之前,先把 Key 当成实验仪器的一部分。下面按“创建评测专用 Key → 配置 Agent 调用 → 统计 token → 拟合 Elo → 排障 → 写实验元数据”的顺序走一遍。
2. 在 TaoToken 官网创建评测专用 Key:模型范围、预算、并发与过期时间
评测 Key 的第一原则是:一把 Key 只服务一类实验。比如你要比较 Agent 的测试时算力策略,策略变量可能是sample_1、sample_2、sample_4、sample_8、sample_16。那就为这批实验创建一把 Key,标签写成bt-eval-sample-k,不要和日常 Claude Code 补全、Codex 重构、临时脚本共用。
创建入口在 TaoToken 官网 的控制台里,进入 API Keys 页面后新建 Key。创建时建议至少设好四个边界:
- 模型范围:只允许本次评测实际需要的模型。如果 Agent 策略比较的是同一模型下的推理轮数或采样数,就不要把其他模型放进白名单。
- 日预算或 token 上限:按实验设计估算总 token,再留 20% 余量。不要等到采样 16 路时才发现预算提前耗尽。
- 并发上限:Agent 评测经常并行跑任务。并发上限要匹配你的实验设计和上游承受能力,通常从 4 或 8 开始,不要一上来就 32。
- 过期时间:评测结束就失效。过期时间能避免旧 Key 被其他脚本捡走。
拿到 Key 后,客户端侧统一用环境变量管理,不要把 Key 写进仓库。推荐:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export TAOTOKEN_BASE_URL="https://taotoken.net/api"注意TAOTOKEN_BASE_URL只是本地变量名,实际值必须是https://taotoken.net/api,不要加 UTM,也不要加多余路径。TaoToken 兼容不同客户端协议时,客户端会自动拼接/v1/chat/completions、/v1/messages等路径;你手动加/v1反而容易造成 404。
如果你使用 Claude Code,Key 的环境变量映射到ANTHROPIC_AUTH_TOKEN;如果使用 Codex,走TAOTOKEN_API_KEY或 Codex 的env_key;如果使用 OpenAI 兼容客户端,通常映射到OPENAI_API_KEY。不要在 Codex 配置里写ANTHROPIC_*,也不要把 Claude Code 的变量复制到 Codex 的config.toml,否则排障时很难判断是哪一层覆盖了配置。
3. Agent 调用配置片段:Claude Code settings.json、Codex config.toml、CC Switch 三件套
这一节给可直接复制的配置片段。所有片段里的 Key 都使用占位符YOUR_API_KEY,Base URL 统一为https://taotoken.net/api。
3.1 Claude Code 的 settings.json
Claude Code 读取settings.json里的env字段。最小可用配置如下:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "your-claude-model", "ANTHROPIC_SMALL_FAST_MODEL": "your-small-fast-model" } }如果你之前设置过ANTHROPIC_API_KEY,建议先清理旧变量,只保留一个认证入口,避免旧 Key 覆盖新 Key。验证是否生效:
env | grep -E 'ANTHROPIC|TAOTOKEN' | sed 's/\(KEY=.\{6\}\).*/\1***/'如果输出里同时出现多个认证变量,优先保留你本次实验需要的那个。
3.2 Codex 的 config.toml
Codex 使用config.toml,不要把 Claude Code 的ANTHROPIC_*写进去。示例:
model = "your-codex-model" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"然后设置环境变量:
export TAOTOKEN_API_KEY="YOUR_API_KEY"Codex 启动时会读取env_key指定的变量。如果你在 shell 里同时保留了旧OPENAI_API_KEY,要确认 Codex 没有优先读取旧变量。可以用最小请求验证:
curl -sS "https://taotoken.net/api/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "your-codex-model", "messages": [{"role": "user", "content": "只回复 ok"}], "max_tokens": 8 }' | jq '{total:.usage.total_tokens}'3.3 CC Switch 三件套
如果你用 CC Switch 管理多套配置,建议把“Claude Code 评测 profile”“Codex 评测 profile”“OpenAI 兼容评测 profile”保存成三件套。这样切换工具时不会互相污染变量。
{ "profiles": [ { "name": "taotoken-claude-bt-eval", "tool": "claude-code", "base_url": "https://taotoken.net/api", "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY" } }, { "name": "taotoken-codex-bt-eval", "tool": "codex", "base_url": "https://taotoken.net/api", "env": { "TAOTOKEN_API_KEY": "YOUR_API_KEY" } }, { "name": "taotoken-openai-compatible-bt-eval", "tool": "openai-compatible", "base_url": "https://taotoken.net/api", "env": { "OPENAI_API_KEY": "YOUR_API_KEY", "OPENAI_BASE_URL": "https://taotoken.net/api" } } ] }三件套的核心是隔离:Claude Code 用ANTHROPIC_*,Codex 用TAOTOKEN_API_KEY,OpenAI 兼容客户端用OPENAI_*。不要交叉。每次切换 profile 后,重新开一个 shell,再执行env | grep确认变量。
4. Token 统计命令与账本格式:把每次采样写进 JSONL
Bradley-Terry 聚合需要两类数据:胜负关系和 token 消耗。胜负关系来自任务内评测结果,token 消耗来自每次模型调用的 usage。你不需要一开始就设计复杂数据库,先用 JSONL 账本即可。
先准备目录:
mkdir -p ~/bt-eval/raw mkdir -p ~/bt-eval/ledgerOpenAI 兼容调用并提取 token:
export TAOTOKEN_API_KEY="YOUR_API_KEY" export BASE_URL="https://taotoken.net/api" curl -sS "$BASE_URL/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "your-agent-model", "messages": [{"role": "user", "content": "只回复 ok"}], "max_tokens": 8 }' | tee /tmp/taotoken_chat.json | jq '{ prompt_tokens: .usage.prompt_tokens, completion_tokens: .usage.completion_tokens, total_tokens: .usage.total_tokens }'Anthropic 兼容调用并提取 token:
curl -sS "$BASE_URL/v1/messages" \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "your-claude-model", "max_tokens": 8, "messages": [{"role": "user", "content": "只回复 ok"}] }' | tee /tmp/taotoken_messages.json | jq '{ input_tokens: .usage.input_tokens, output_tokens: .usage.output_tokens }'拿到 usage 后,写进 JSONL。字段建议至少包含:experiment_id、strategy、task、run、prompt_tokens、completion_tokens、total_tokens、model、timestamp。
cat >> ~/bt-eval/ledger/token_ledger.jsonl <<'EOF' {"experiment_id":"bt-eval-001","strategy":"sample_1","task":"task-a","run":1,"model":"your-agent-model","prompt_tokens":812,"completion_tokens":96,"total_tokens":908,"timestamp":"2025-01-01T00:00:00Z"} {"experiment_id":"bt-eval-001","strategy":"sample_2","task":"task-a","run":1,"model":"your-agent-model","prompt_tokens":1630,"completion_tokens":210,"total_tokens":1840,"timestamp":"2025-01-01T00:01:00Z"} EOF按策略汇总 token:
jq -s 'group_by(.strategy) | map({ strategy: .[0].strategy, runs: length, total_tokens: (map(.total_tokens) | add), avg_tokens: ((map(.total_tokens) | add) / length) })' ~/bt-eval/ledger/token_ledger.jsonl如果你在 Agent 框架里无法直接拿到 usage,就退而求其次:在调用前后记录 prompt 和 completion 文本,用 tokenizer 本地计数。但要注意,不同模型的 tokenizer 有差异,本地计数只能用于同一模型内的相对比较。跨任务聚合时,最好以服务端返回的 usage 为准。
到这一步,你已经有了可复现的 token 账本。接下来才是 Bradley-Terry 和 Elo-per-token。
5. Bradley-Terry 聚合与 Elo-per-token:收益递减对照表怎么算
论文里的 Elo-per-token 分析方法,核心是用 Bradley-Terry 模型把任务内排序聚合成跨任务 Elo 评分,再用 token 消耗衡量测试时算力策略的扩展规律。你不需要一次实现完整论文系统,先做一个最小可运行版本:
- 每个任务内,对同一批 Agent 输出做两两比较或评分解算,得到胜负记录。
- 用 Bradley-Terry 拟合每个策略的强度参数。
- 把强度参数线性映射到 Elo。
- 按策略汇总 token,计算 Elo-per-token 和边际 Elo-per-token。
- 观察随采样数或推理轮数增加,边际收益是否递减。
Bradley-Terry 的基本形式是:
P(i 胜过 j) = exp(theta_i) / (exp(theta_i) + exp(theta_j))拟合出theta_i后,可以映射到 Elo:
Elo_i = 1000 + 400 * theta_i / ln(10)下面是可运行的最小 Python 片段。它假设你已经把任务内胜负记录整理成(winner, loser)列表:
import numpy as np from scipy.optimize import minimize # 示例:任务内比较结果,实际应从评测日志读取 records = [ ("sample_1", "sample_2"), ("sample_2", "sample_4"), ("sample_4", "sample_8"), ("sample_8", "sample_16"), ("sample_2", "sample_1"), ("sample_4", "sample_2"), ] names = sorted({x for pair in records for x in pair}) index = {name: i for i, name in enumerate(names)} def neg_log_lik(params): # 固定第一个参数为 0,避免模型不可识别 theta = np.concatenate([[0.0], params]) loss = 0.0 for winner, loser in records: sw = theta[index[winner]] sl = theta[index[loser]] loss -= sw - np.logaddexp(sw, sl) return loss res = minimize(neg_log_lik, np.zeros(len(names) - 1), method="BFGS") theta = np.concatenate([[0.0], res.x]) elo = 1000.0 + 400.0 * theta / np.log(10.0) for name, score in sorted(zip(names, elo), key=lambda x: -x[1]): print(f"{name:12s} Elo={score:8.2f}")如果你有多个任务,建议先在每个任务内拟合,再对theta做任务级标准化后聚合。也可以把任务作为分层变量,但最小版本先保证同一任务内的比较可复现。
接下来计算 Elo-per-token。下面表格是演示数据,不是真实 benchmark 结论,你需要用本地实验替换:
| 测试时算力策略 | 采样/轮数 | 总 token | 任务内胜率 | 跨任务 Elo | ΔElo | 边际 Elo/1k token |
|---|---|---|---|---|---|---|
| sample_1 | 1 | 12000 | 0.50 | 1000.0 | - | - |
| sample_2 | 2 | 23000 | 0.54 | 1028.0 | 28.0 | 2.55 |
| sample_4 | 4 | 45000 | 0.57 | 1044.0 | 16.0 | 0.73 |
| sample_8 | 8 | 89000 | 0.59 | 1053.0 | 9.0 | 0.20 |
| sample_16 | 16 | 178000 | 0.60 | 1058.0 | 5.0 | 0.06 |
计算边际 Elo-per-token:
import pandas as pd df = pd.DataFrame([ {"strategy": "sample_1", "total_tokens": 12000, "elo": 1000.0}, {"strategy": "sample_2", "total_tokens": 23000, "elo": 1028.0}, {"strategy": "sample_4", "total_tokens": 45000, "elo": 1044.0}, {"strategy": "sample_8", "total_tokens": 89000, "elo": 1053.0}, {"strategy": "sample_16", "total_tokens": 178000, "elo": 1058.0}, ]) df["delta_elo"] = df["elo"].diff() df["delta_token"] = df["total_tokens"].diff() df["marginal_elo_per_1k"] = df["delta_elo"] / (df["delta_token"] / 1000) print(df)你会看到边际 Elo/1k token 随采样数增加而下降。这就是收益递减的量化表达。关键在于:这个下降必须来自同一把 Key、同一base_url、同一模型范围下的干净 token 账本。否则你看到的“递减”可能只是流量混入或模型切换造成的假象。
6. 排障清单:401、404、model not found、429 与 token 对不上
Agent 评测最怕的不是报错,而是静默走错配置。下面按常见现象排查。
401 invalid x-api-key / 401 unauthorized
先检查 Key 是否替换了YOUR_API_KEY,有没有多余空格或换行。然后确认当前 shell 读取的是哪一套变量:
env | grep -E 'TAOTOKEN|ANTHROPIC|OPENAI' | sed 's/\(KEY=.\{6\}\).*/\1***/'如果 Claude Code 里同时存在ANTHROPIC_API_KEY和ANTHROPIC_AUTH_TOKEN,只保留一个。如果 CC Switch 切换后仍然 401,重开终端再试。
404 page not found
最常见原因是base_url末尾多了斜杠,或者手动加了/v1。统一写成:
https://taotoken.net/api让客户端自己拼接路径。可以用 curl 最小验证:
curl -sS -o /dev/null -w '%{http_code}\n' \ "https://taotoken.net/api/v1/chat/completions" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"your-agent-model","messages":[{"role":"user","content":"ping"}],"max_tokens":1}'如果返回 404,先检查路径,再检查 Key 的模型范围。
model not found / model not allowed
这通常不是模型不存在,而是 Key 的模型范围没有包含该模型。回到 TaoToken 官网 的 API Keys 页面,确认本次 Key 允许的模型列表。评测专用 Key 不要开太宽,但也不能漏掉实验要用的模型。
429 too many requests
Agent 评测并行度高,16 路采样很容易触发并发限制。先看 Key 的并发上限,再把 Agent 框架的并发降到 4 或 8。不要通过多把 Key 绕过限制,那样会破坏 token 账本的归属。
token 对不上
可能是 CC Switch 切换了 profile,但旧环境变量还在;也可能是部分请求走缓存没有产生真实调用;还可能是不同工具使用了不同模型。排查顺序:
# 1. 看当前变量 env | grep -E 'TAOTOKEN|ANTHROPIC|OPENAI' # 2. 看最近账本 tail -n 20 ~/bt-eval/ledger/token_ledger.jsonl | jq -c '{strategy,total_tokens,model}' # 3. 看按模型汇总 jq -s 'group_by(.model) | map({model: .[0].model, total: (map(.total_tokens) | add)})' \ ~/bt-eval/ledger/token_ledger.jsonl如果模型汇总里出现你不认识的模型,说明有非实验流量混入,需要回到 Key 边界重新隔离。
7. 复现清单:把 Key 边界写进实验元数据,再跑跨任务 Elo
为了让 Bradley-Terry 评测可复现,建议每次实验都写一份元数据文件。Key 本身不要写进去,只写 Key 标签、边界和base_url。
experiment_id: bt-eval-001 taotoken: base_url: https://taotoken.net/api key_label: bt-eval-sample-k key_boundary: allowed_models: - your-agent-model daily_budget_tokens: 200000 max_concurrency: 4 expires_at: "2025-12-31" agent: framework: your-agent-framework strategy: sample_k k_values: [1, 2, 4, 8, 16] eval: tasks: - task-a - task-b - task-c bradley_terry: true elo_scale: 400 elo_base: 1000每次跑完实验,把三份产物归档:
token_ledger.jsonl:每次调用的 token 账本。pairwise_records.jsonl:任务内胜负记录。elo_result.csv:策略、总 token、Elo、边际 Elo/1k token。
这样即使一周后回看,也能解释某个 Elo 分数是在什么 Key 边界、什么并发、什么模型范围下得到的。如果结果异常,先查元数据,再查账本,最后查 Bradley-Terry 拟合代码。
8. 文末 CTA:先对话验证,再上 Coding Plan,最后创建 Key 并查 Claude Code 文档
如果你还没有评测专用 Key,建议按这个顺序跑一遍:先用 模型对话 发一条最小请求,确认base_url=https://taotoken.net/api和 Key 可用;再选 Coding Plan 覆盖多轮 Agent 采样;随后到 API Keys 创建只允许本次实验模型、限定日预算和并发的 Key;Claude Code 用户可直接对照 Claude Code 文档 完成 settings.json。官网入口在这里:TaoToken 官网。