
1. 从一次 elo_bt.py 的 401 说起上周把测试时算力test-time compute的评估脚本接进 CI 做回归文件叫elo_bt.py干的活并不复杂对同一批任务用不同采样预算n1/4/8/16跑同一个 Agent把每个任务内的候选答案排序后喂给 Bradley-Terry 模型聚合成跨任务 Elo 分。第一轮本机跑通第二轮换到构建机直接吃了一个 401——日志里 base_url 还指着上一位同事本地起的代理端口Key 也是他个人环境变量里的换台机器就彻底失效。排查时先把两件事拆开避免互相甩锅凭据与出口层Agent 只该关心「用哪个 Key、打哪个 base_url」。这部分到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkey_setup 拿一个 Key把客户端 base_url 指向https://taotoken.net/api即可一步到位评测口径层Elo-per-token 才真正回答「这套算力策略值不值」。它不随供应商切换而改变换 Key 不该换结论。换句话说TaoToken 的 Key 在这条流水线里只承担一件事——「这次调用是谁发的」而不是「这个模型值多少分」。新手常犯的错就是把这两件事揉在一起一出 401 就去怀疑评测代码一出分数波动就去怀疑供应商。把凭据配置钉死在版本化文件里、把评测逻辑单独版本管理回归才有意义。下面把这两层彻底解耦给出可复制的 Agent 调用配置、token 统计命令以及一张 Elo 评分与收益递减的对照表。2. Elo-per-token 的评测骨架谁在排序谁在聚合2.1 为什么不能直接比平均分一套测试时算力策略比如「多采样取多数」「best-of-n 加验证器重排」「树搜索加剪枝」单独看每个任务的准确率很难横向比任务难度不同、打分尺度不同、采样次数带来的 token 开销也不同。更麻烦的是「多花 8 倍 token 换来 3 个点的准确率」这种结论在另一个任务集上可能完全反过来。Elo-per-token 的思路是把「排序」和「成本」同时纳入一个可比尺度任务内排序对同一个任务把不同策略或同一策略的不同采样预算产出的答案放在一起做成对比较得到「谁更好」的局部顺序Bradley-Terry 聚合用 BT 模型把一堆成对比较拟合成每个策略的强度参数 θ跨任务合并把每个任务拟合出的 θ 归一化后汇总得到全局 Elo 分除以 token 成本用ΔElo / Δtoken刻画边际收益观察它怎么随预算增长而衰减。2.2 Bradley-Terry 的拟合公式对两个策略 i 和 jBT 模型假设P(i 击败 j) exp(θ_i) / (exp(θ_i) exp(θ_j))拟合成对比较数据通常用最大似然加上 L2 正则防止某些策略全胜或全负导致 θ 发散import numpy as np from scipy.optimize import minimize def bt_fit(wins, n_models, l21e-3): wins: list[(i, j)] 表示 i 击败 j 的一次观测 n_models: 策略数量 返回: 长度 n_models 的 θ 数组均值为 0 def neg_log_likelihood(theta): ll 0.0 for i, j in wins: s theta[i] - theta[j] # log sigmoid(s) ll -np.logaddexp(0.0, -s) reg l2 * np.sum(theta ** 2) return -ll reg theta0 np.zeros(n_models) res minimize(neg_log_likelihood, theta0, methodL-BFGS-B) theta res.x - res.x.mean() # 中心化便于跨任务合并 return theta def theta_to_elo(theta, base1000.0, scale400.0 / np.log(10)): return base scale * theta这段代码可以在本地直接跑数据完全来自你自己的评测日志不涉及任何在线服务。2.3 跨任务合并的坑跨任务合并时最容易踩的坑是「任务难度漂移」。同一个策略在 A 任务上 Elo1150、在 B 任务上 Elo1050直接把 Elo 平均是错的因为两个任务里的对手集合不一样。正确做法是先把每个任务内的 θ 中心化均值归零再按任务权重加权合并task_weights {math: 0.4, code: 0.4, tool_use: 0.2} theta_global sum(task_weights[t] * theta_centered[t] for t in task_weights) elo_global theta_to_elo(theta_global)中心化之后跨任务 Elo 才具备「同尺度可加」的性质也才能做下面这张收益递减表。3. 把 TaoToken Key 接进 Agent 调用链三套配置评测脚本本身不关心 Key 从哪来但它调的 Agent 需要一个稳定的出口。这里按三种常见客户端分别给配置全部用YOUR_API_KEY占位换成你在控制台生成的真实 Key 即可。生成入口统一在 TaoToken 官网UTM 已带https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keys3.1 Python Agent 调用片段OpenAI 兼容import os from openai import OpenAI client OpenAI( api_keyos.environ.get(TAOTOKEN_API_KEY, YOUR_API_KEY), base_urlhttps://taotoken.net/api, timeout120.0, max_retries3, ) def run_agent_step(messages, budget_n1): budget_n: 采样次数直接对应测试时算力预算 outputs [] for _ in range(budget_n): resp client.chat.completions.create( modelyour-agent-model, messagesmessages, temperature0.7 if budget_n 1 else 0.0, ) usage resp.usage outputs.append({ text: resp.choices[0].message.content, in_tokens: usage.prompt_tokens, out_tokens: usage.completion_tokens, }) return outputs注意base_url只写https://taotoken.net/api不要带 UTM 参数——UTM 是给文档和落地页用的接口路径上加查询串会让某些客户端把参数混进请求体。3.2 Claude Codesettings.jsonClaude Code 走 Anthropic 协议配置文件放~/.claude/settings.json项目级可放.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: your-claude-model, ANTHROPIC_SMALL_FAST_MODEL: your-claude-haiku-model } }三件套ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_MODEL缺一不可。只改 base_url 不改 modelClaude Code 会用默认模型名去请求很可能撞上 404。3.3 Codexconfig.tomlCodex 用的是 TOML不要套 Anthropic 那套变量名否则它读不到model your-codex-model model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api/v1 env_key TAOTOKEN_API_KEY wire_api chat对应地在 shell 里导出export TAOTOKEN_API_KEYYOUR_API_KEY3.4 CC Switch 三件套CC Switch 这类切换工具本质就是在多个配置档之间切同一组三件套。维护三个字段就够了字段值BASE_URLhttps://taotoken.net/apiAPI KEYYOUR_API_KEYMODEL具体的模型名把这三项写成 profile评测脚本启动前先按 profile 注入环境变量就不会再出现「换台机器 base_url 还指着老端口」这类问题。4. token 统计命令从日志到每任务成本Elo-per-token 的分母必须是自己实测的 token不能靠估。最稳的做法是在 Agent 的每次调用日志里落一条 JSONL字段至少包含task_id、strategy、budget_n、prompt_tokens、completion_tokens。4.1 用 jq awk 快速聚合# 假设 agent_run.jsonl 每行形如 # {task_id:math-001,strategy:best_of_n,budget_n:4, # usage:{prompt_tokens:900,completion_tokens:380}} jq -r select(.usage ! null) | [.task_id, .strategy, (.budget_n|tostring), (.usage.prompt_tokens|tostring), (.usage.completion_tokens|tostring)] | tsv agent_run.jsonl \ | awk -F\t { key $2 in_tok[key] $4 out_tok[key] $5 n[key] 1 } END { printf %-14s %10s %10s %12s %8s\n, strategy,in_tok,out_tok,total_tok,calls for (k in in_tok) { total in_tok[k] out_tok[k] printf %-14s %10d %10d %12d %8d\n, k, in_tok[k], out_tok[k], total, n[k] } } \ | sort -k4 -n4.2 用 Python 做每任务归一化import json, collections per_task collections.defaultdict(lambda: collections.defaultdict(int)) with open(agent_run.jsonl, r, encodingutf-8) as f: for line in f: rec json.loads(line) u rec.get(usage) or {} if not u: continue k (rec[task_id], rec[strategy]) per_task[k][in] u.get(prompt_tokens, 0) per_task[k][out] u.get(completion_tokens, 0) # 每个策略的「每任务平均 token」 agg collections.defaultdict(lambda: [0, 0, 0]) for (task_id, strategy), v in per_task.items(): total v[in] v[out] agg[strategy][0] total agg[strategy][1] 1 for strategy, (total, tasks) in sorted(agg.items(), keylambda x: x[1][0]): print(f{strategy:14s} tasks{tasks:4d} favg_tokens_per_task{total/tasks:9.1f})跑完这一步把avg_tokens_per_task填进下一节的表里Elo-per-token 的分母就实锤了。5. Elo 评分与收益递减对照表下面这张表是结构示例数值仅示意跑你自己的任务集时请用第 4 节的统计结果替换。它想表达的不是具体数字而是边际 Elo 随 token 增长掉得多快这件事策略采样/搜索预算每任务 token示意跨任务 Elo示意ΔEloΔElo / 千 tokengreedyn11.2k1000——best-of-4n44.6k10787822.9majority8n89.3k1121439.2best-of-16n1618.7k1141202.1tree-search d3约 40k41.0k1152110.5读表的三条经验甜点区通常在 48 倍预算。从 n1 到 n4每千 token 还能换回 20 分以上再往上加边际掉到个位数、甚至 1 分以下ΔElo 单调递减但不为零说明算力策略确实有上限只是接近上限的速度比很多人预期得快。做评测报告时把「每千 token 的 ΔElo」单独列一列比只报最终 Elo 更有决策价值跨任务 Elo 的方差要一起看。同一策略在 math 上可能吃到 60 分在 tool_use 上只吃 10 分加权前先看每个任务中心化 θ 的分布避免被单一任务带偏。把这张表和第 2 节的 BT 拟合脚本接起来就是一条完整的、可复现的 Elo-per-token 流水线Agent 调用 → JSONL 日志 → token 聚合 → 成对比较 → BT 拟合 → 跨任务合并 → 收益递减判断。6. 常见报错与排查顺序把踩过的坑按出现频率排一下401 / invalid api key优先看客户端读的是哪个环境变量。Claude Code 读ANTHROPIC_AUTH_TOKENCodex 读env_key里指定的名字两边不通用的。Key 在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttroubleshoot 生成后先echo $TAOTOKEN_API_KEY确认注入成功404 model not foundbase_url 写错多写或少写/v1或者 model 名没换。Anthropic 系客户端 base_url 一般到/apiOpenAI 兼容系常需要/api/v1按客户端文档确认连接超时timeout设太短。跑 n16 的多采样单次请求可能叠加排队建议客户端 timeout 加到 120s 以上同时在 CI 里加 retrytoken 计数对不上prompt_tokens一般包含系统提示和工具定义以为「只统计对话内容」会低估。评测里统计分母要按返回的 usage 原样相加别自己重算Elo 不收敛某个策略全胜或全负θ 会发散。加 L2 正则或者对全胜策略先做一次平滑处理。排查时永远先分清是「凭据/出口」问题还是「评测策略」问题。前者改动配置就能解决后者要回到数据本身两者混在一起查最耗时间。7. 收尾凭据归凭据算力归算力回到开头那个 401。真正解决它的不是改评测脚本而是把 base_url 和 Key 从「某台机器上的个人环境变量」挪进了版本化配置base_url 固定指向https://taotoken.net/apiKey 用占位符 CI secret 注入客户端只负责发请求。这样做完脚本在任何机器上都跑得通Elo-per-token 的结论也不会因为换了个出口就漂移。如果你的团队也在做测试时算力策略的横向评测建议按下面的顺序走一遍全部是可跟做的动作先拿一个 Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentcreate_key想先在对话里验证模型是否连通https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chat评测任务量大、想按套餐走https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planClaude Code 环境的完整配置说明https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_code_doc配置抄完、日志落完、BT 拟合跑完你手里就有了一条能长期复用的 Elo-per-token 曲线。它不依赖某个具体供应商也不会因为换 Key 就失效——凭据归凭据算力归算力评测结论才站得住。