
1. 从 Bradley-Terry 排序脚本跑偏说起先把 Key 和 Base URL 钉死如果你正在用 Bradley-Terry 模型聚合 LLM Agent 的跨任务 Elo最先遇到的往往不是数学问题而是日志里 token 字段对不上同一套 CoT self-consistency 策略在 A 任务上 Elo 涨得猛在 B 任务上却几乎不动回头查调用记录才发现 input/output token 混在一起重试次数也没落盘。要复现 Elo-per-token 分析第一步不是调参而是把模型调用入口统一到 TaoToken到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentkey_setup 拿 Key把客户端 base_url 指向 https://taotoken.net/api后续所有 Agent rollout 才有一致的 usage 口径。这篇内容面向 LLM Agent 评测与推理成本工程师。我们不讨论抽象排行榜而是把一条可复现链路拆开TaoToken Key 怎么拿、Claude Code / Codex / CC Switch 怎么配、Agent 调用片段怎么写、token 统计命令怎么跑、Bradley-Terry 怎么把任务内 pairwise 排序聚合成跨任务 Elo、最后怎么看 Elo 与 token 的收益递减对照表。你可以在本地把每一步跑通不需要把评测数据传到生产库也不依赖任何灰色链路。目标产出有三个一份 Agent 调用配置片段保证每次请求都返回可归因的 usage一组 token 统计命令把 JSONL 日志压成 task_id strategy model tokens 的聚合表一张 Elo 评分与收益递减对照表用来判断“继续加采样、加工具轮次、加辩论轮数”到底还值不值。2. 拿 Key 与客户端 Base URLTaoToken 侧的最小准备先到 TaoToken 官网完成注册与 Key 创建https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentenv_setup 。在控制台里创建 API Key 后你会得到一串以 sk- 或类似前缀开头的密钥。本文所有配置里的占位符统一写成 YOUR_API_KEY你替换成自己的 Key 即可。注意不要把它提交到 Git建议放在本地 .env 或系统环境变量里。TaoToken 的 Base URL 是https://taotoken.net/api这个地址在工具配置里不加 UTM 参数保持干净。不同客户端对路径拼接方式不同有的会在 base_url 后自动补 /v1有的需要你手动写到 /v1。原则是先按 https://taotoken.net/api 配置如果客户端报 404再检查它实际请求的完整路径而不是反复换 Key。推荐的本地环境变量写法export TAOTOKEN_API_KEYYOUR_API_KEY export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你要用 Python 做批量评测建议再建一个目录mkdir -p agent_eval/logs agent_eval/scripts agent_eval/output touch agent_eval/logs/agent_runs.jsonl后续每次 Agent 调用都把原始响应里的 usage 追加到 agent_runs.jsonl。这个文件是后面 token 统计和 Bradley-Terry 聚合的唯一数据源。不要边跑边手工改表否则跨任务 Elo 的误差会被放大。3. Agent 调用配置片段Claude Code、Codex、CC Switch 三件套这一节给出可复制的配置。再次强调Claude Code 使用 ANTHROPIC_* 环境变量Codex 使用 config.toml不要混用。Claude Code 的 settings.json 可以这样写{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: YOUR_API_KEY, ANTHROPIC_MODEL: YOUR_CLAUDE_MODEL_ID } }其中 YOUR_CLAUDE_MODEL_ID 以 TaoToken 模型对话页展示的可用模型名为准。配置完成后重启 Claude Code 会话让环境变量生效。如果你在终端里临时验证也可以直接导出export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENYOUR_API_KEY export ANTHROPIC_MODELYOUR_CLAUDE_MODEL_IDCodex 的 config.toml 示例model YOUR_CODEX_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat然后在 shell 里设置export TAOTOKEN_API_KEYYOUR_API_KEYCodex 不会读 ANTHROPIC_*所以不要把 Claude Code 的环境变量复制过来。wire_api 按你的 Codex 版本支持情况选择 chat 或 responses如果启动时报协议不匹配优先查阅你本地 Codex 版本的配置说明再回到 TaoToken 的 Claude Code 文档确认路径。CC Switch 三件套可以理解成三个字段provider、base_url、api_key。一个最小 YAML 配置如下provider: taotoken base_url: https://taotoken.net/api api_key: YOUR_API_KEY在 CC Switch 里切换供应商后记得完全退出并重启对应的 CLI 会话。很多“配置没生效”其实是旧进程还持有旧环境变量。你可以用下面的命令确认当前 shell 里的值env | grep -E ANTHROPIC_|TAOTOKEN_|OPENAI_如果你需要在一个脚本里同时跑 Claude Code 和 Codex 的评测建议用两个独立的 shell 或两个独立的配置文件不要让它们共享同一个 Key 环境变量名。这样日志里的 model 字段和 provider 字段才能对应上。4. token 统计命令从 JSONL 到 Elo-per-token 输入表Elo-per-token 分析最怕的是 token 统计口径不一致。我们统一只统计两件事prompt_tokens 和 completion_tokens。如果响应里还有 total_tokens也记录但聚合时以 prompt completion 为准避免重复计数。假设 agent_runs.jsonl 每一行是一条调用记录结构类似{task_id:task_001,strategy:cot_single,model:model_a,usage:{prompt_tokens:820,completion_tokens:410},win:1} {task_id:task_001,strategy:cot_self_consistency_3,model:model_a,usage:{prompt_tokens:2400,completion_tokens:1500},win:0}先用 jq 把它压成 TSVjq -r [.task_id, .strategy, .model, .usage.prompt_tokens, .usage.completion_tokens] | tsv \ agent_eval/logs/agent_runs.jsonl agent_eval/output/runs.tsv再用 awk 做第一层聚合awk -F\t {key$1\t$2\t$3; in_tok[key]$4; out_tok[key]$5; n[key]} END {for (k in in_tok) print k\tin_tok[k]\tout_tok[k]\tn[k]} \ agent_eval/output/runs.tsv | sort agent_eval/output/token_by_task_strategy.tsv如果你更习惯 Python用下面这段import json from collections import defaultdict tot defaultdict(lambda: {prompt: 0, completion: 0, n: 0}) with open(agent_eval/logs/agent_runs.jsonl, r, encodingutf-8) as f: for line in f: r json.loads(line) usage r.get(usage) or {} key (r[task_id], r[strategy], r[model]) tot[key][prompt] usage.get(prompt_tokens, 0) tot[key][completion] usage.get(completion_tokens, 0) tot[key][n] 1 with open(agent_eval/output/token_by_task_strategy.tsv, w, encodingutf-8) as out: for (task_id, strategy, model), v in sorted(tot.items()): total v[prompt] v[completion] out.write(f{task_id}\t{strategy}\t{model}\t{v[prompt]}\t{v[completion]}\t{total}\t{v[n]}\n)到这里你得到的是“每个任务、每个策略、每个模型”的 token 总量。下一步要把它变成跨任务可比较的 Elo 输入。注意不要直接拿总 token 做归一化因为不同任务本身难度不同长任务天然消耗更多 token。正确做法是先得到任务内排序再用 Bradley-Terry 聚合成跨任务 Elo最后才计算 Elo-per-token。5. Bradley-Terry 聚合脚本任务内排序变跨任务 EloBradley-Terry 模型的核心假设很简单对于策略 i 和策略 ji 战胜 j 的概率是P(i j) 1 / (1 exp(-(r_i - r_j)))其中 r_i 是策略 i 的 Elo 分数。我们可以在每个任务内做 pairwise 比较再把所有任务的比较对丢进同一个优化器得到跨任务 Elo。下面是一个可运行的 Python 脚本输入是包含 task_id、strategy、win 的 JSONL。win1 表示该策略在该任务内胜出win0 表示落败。import json import math from collections import defaultdict import numpy as np from scipy.optimize import minimize # 读入 pairwise 记录每行包含 task_id, strategy, win # 这里假设同一 task_id 内你已经通过任务指标生成了胜负标签 records [] with open(agent_eval/logs/pairwise.jsonl, r, encodingutf-8) as f: for line in f: records.append(json.loads(line)) strategies sorted({r[strategy] for r in records}) idx {s: i for i, s in enumerate(strategies)} # 统计每对策略的胜负次数 pairs defaultdict(lambda: [0.0, 0.0]) # (i,j) - [i胜, j胜] for r in records: s r[strategy] opponent r.get(opponent) if not opponent: continue i, j idx[s], idx[opponent] if r[win] 1: pairs[(i, j)][0] 1 else: pairs[(i, j)][1] 1 def neg_log_likelihood(theta): n len(theta) loss 0.0 for (i, j), (wi, wj) in pairs.items(): diff theta[i] - theta[j] # 防止溢出 if diff 30: p 1.0 elif diff -30: p 0.0 else: p 1.0 / (1.0 math.exp(-diff)) if wi 0: loss - wi * math.log(max(p, 1e-12)) if wj 0: loss - wj * math.log(max(1.0 - p, 1e-12)) # 简单 L2 正则避免分数漂移 loss 0.01 * np.sum(theta ** 2) return loss x0 np.zeros(len(strategies)) res minimize(neg_log_likelihood, x0, methodBFGS) # 将 Elo 缩放到常见量级均值 1000缩放 400/ln(10) raw res.x raw raw - raw.mean() elo 1000 400.0 / math.log(10) * raw for s, e in sorted(zip(strategies, elo), keylambda x: -x[1]): print(f{s}\t{e:.2f})这个脚本刻意保持了最小依赖numpy scipy。你可以在本地 agent_eval/scripts/bt_elo.py 里运行。注意两点 第一pairwise.jsonl 里的 opponent 字段必须真实存在。如果你只记录了胜者没有记录败者需要先补全比较对。 第二任务内排序可以来自准确率、通过率、人工偏好或规则打分但同一个任务内必须使用同一套标准。跨任务 Elo 只负责聚合不负责修正任务内偏差。把 Elo 输出和前面的 token 聚合表按 strategy 做 join就得到 Elo-per-token 的原始表python agent_eval/scripts/bt_elo.py agent_eval/output/elo_by_strategy.tsv然后用 Python 做 joinimport csv from collections import defaultdict token defaultdict(int) with open(agent_eval/output/token_by_task_strategy.tsv, r, encodingutf-8) as f: for row in csv.reader(f, delimiter\t): task_id, strategy, model, prompt, completion, total, n row token[strategy] int(total) elo {} with open(agent_eval/output/elo_by_strategy.tsv, r, encodingutf-8) as f: for line in f: s, e line.strip().split(\t) elo[s] float(e) with open(agent_eval/output/elo_per_token.tsv, w, encodingutf-8) as out: out.write(strategy\telo\ttotal_tokens\telo_per_1k\n) for s in sorted(elo): e elo[s] t token.get(s, 0) per_1k (e - 1000) / (t / 1000.0) if t 0 else 0.0 out.write(f{s}\t{e:.2f}\t{t}\t{per_1k:.3f}\n)这样你就有了判断收益递减的基础数据。6. Elo 评分与收益递减对照表怎么判断策略该停下面这张表是格式示例数值请用你自己的 elo_per_token.tsv 替换。它的用途不是给出结论而是让你一眼看出继续加 token 到底换来了多少 Elo。算力策略平均总 token/任务跨任务 EloElo/1k token边际 Elo/1k tokenCoT 单次1.2k10108.4基准CoT 3 次自洽采样3.8k106015.8约 19.2CoT 8 次自洽采样9.1k10859.3约 4.7工具调用 2 轮14.0k10956.8约 1.5多智能体辩论 3 轮28.0k11003.6约 0.4怎么读这张表“边际 Elo/1k token”比“Elo/1k token”更重要。它回答的是“再加 1000 token还能涨多少 Elo”。当边际值降到 1 以下通常意味着这个策略已经进入明显收益递减区。跨任务 Elo 是聚合值不是每个任务都涨。你要回到 task_id 维度看哪些任务贡献了增益哪些任务在拖后腿。如果某个任务上 token 翻倍但 Elo 不涨考虑单独调整该任务的策略。不同模型的 baseline 不同。用 TaoToken 的 Base URL 切换模型时务必在日志里记录 model 字段。否则你看到的 Elo 变化可能来自模型替换而不是算力策略。收益递减不是“停止投入”的同义词。如果边际 Elo/1k token 低但任务本身是高风险决策可能仍然值得。关键是让成本可见而不是拍脑袋。如果你想把这张表做成自动刷新可以写一个本地 SQLite 查询。所有数据都由读者本地执行不涉及任何生产库直连-- 在本地 SQLite 中执行 CREATE TABLE runs ( task_id TEXT, strategy TEXT, model TEXT, prompt_tokens INTEGER, completion_tokens INTEGER, win INTEGER ); -- 导入 TSV 后统计 SELECT strategy, SUM(prompt_tokens completion_tokens) AS total_tokens, COUNT(*) AS n FROM runs GROUP BY strategy ORDER BY total_tokens DESC;把 SQL 结果和 Elo 表人工或脚本 join就能持续观察收益递减。7. 排障401/404/429 与重试日志落盘接入 TaoToken 时最常见的错误不是模型能力问题而是配置路径问题。401 / 403通常是 Key 没传对。Claude Code 用 ANTHROPIC_AUTH_TOKENCodex 用 config.toml 里的 env_keyCC Switch 用 api_key 字段。三者的变量名不同不要交叉复制。先在终端验证curl -sS https://taotoken.net/api/models \ -H Authorization: Bearer YOUR_API_KEY | head如果这里返回 401回到官网检查 Key 是否有效https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contenttroubleshooting 。404多半是 base_url 路径拼接问题。客户端可能自动追加 /v1/chat/completions也可能不会。先用最小 curl 确认完整路径。如果你在 Codex 里看到 404检查 wire_api 是否和客户端版本匹配。不要通过反复更换 Key 来“解决”404。429触发限流。评测脚本必须做指数退避并且把重试次数写进日志。示例import time import random def call_with_retry(fn, max_retry5): for attempt in range(max_retry): try: return fn() except Exception as e: if 429 not in str(e) and rate not in str(e).lower(): raise sleep min(60, (2 ** attempt) random.random()) time.sleep(sleep) raise RuntimeError(max retry exceeded)重试会消耗额外 token。如果你不记录 attempt 字段token 统计会偏低Elo-per-token 会被高估。建议在 JSONL 里加两个字段{task_id:task_001,strategy:cot_self_consistency_3,model:model_a,attempt:2,usage:{prompt_tokens:2400,completion_tokens:1500},win:0}然后统计时按 attempt 聚合或者只保留最后一次成功调用的 usage。两种口径都可以但必须全流程一致。CC Switch 三件套切换后如果仍报错先确认进程是否重启。很多 CLI 会把环境变量缓存到会话里。你也可以在项目目录下放一个 .env并在启动脚本里显式 sourceset -a source .env set a最后不要用 MCP 或 Agent 直连 Oracle/生产库。评测日志写本地 JSONL 或本地 SQLite所有 SQL 和命令由读者本地执行。这既是安全边界也是保证 token 统计可复现的前提。8. 可复现产出打包与文末 CTA到这里你应该已经拿到三样东西Agent 调用配置片段Claude Code 的 settings.json、Codex 的 config.toml、CC Switch 的 provider / base_url / api_key 三件套token 统计命令jq awk 或 Python 把 JSONL 压成 task_id strategy model tokens 的聚合表Elo 评分与收益递减对照表Bradley-Terry 聚合出跨任务 Elo再除以 token 总量观察边际增益。如果你还没开始建议按这个顺序走一遍到 TaoToken 官网拿 Keyhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentfinal_key 把客户端 base_url 指向 https://taotoken.net/api 先跑 10 条最小 Agent 调用确认 JSONL 里有 usage再扩大任务集跑 pairwise最后跑 Bradley-Terry 脚本。文末 CTA 按高转化路径整理如下想先试模型对话直接访问https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chat想了解 Coding Plan访问https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_plan想创建 API Key访问https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keys想看 Claude Code 接入细节访问https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_code_doc把 Bradley-Terry 排序和 token 统计跑通之后你会发现 Elo-per-token 真正有价值的地方不是给出一个绝对分数而是让你在“继续加采样、加工具轮次、加辩论轮数”之前先看到边际收益已经降到什么程度。TaoToken 在这里的角色很明确统一模型调用入口让每次 Agent 调用的 usage 可记录、可聚合、可复现。剩下的就交给你的本地脚本和那张对照表。