返回 AICAP-180
B3 · Day 27agent loop + 评测统计

Qwen3 对比与稳健性

Day 26 把 agent loop 接进了 runTaskEval,能在一个模型上跑出 completion + tool-call 双指标。但「一个模型的分」无法支撑选型——你不知道它是好是坏,没有对照系。

阶段: B3 · agent loop + 评测统计(Day 21-30) 标签: #model-compare #function-calling #temperature #robustness

今日导引(由浅入深)

Day 26 把 agent loop 接进了 runTaskEval,能在一个模型上跑出 completion + tool-call 双指标。但「一个模型的分」无法支撑选型——你不知道它是好是坏,没有对照系。

今天 Day 27 引入横向对比:用同一 29 任务套件、同 temperature,把模型注入从 DeepSeek-V3 换成 Qwen3 各跑一遍,并排看 Δcompletion 与 Δavg-tool-calls。

在 B1→B18 曲线上,这是「评测从单点走向比较」的一跳,也是工程纪律的试金石:对比公平不公平,全看你有没有把非模型变量钉死。明天 Day 28 才把这个 Δ 喂进 paired bootstrap 算 CI、判显著性——今天先把公平的 Δ 拿到手。今天的最小可判定产出是:对比表模板 + 跑数清单(committed);真实 Δ 必须真模型跑出(待跑)。

1. 机理精读

模型间的 function-call 行为是有差异的,不能默认可比。

不同模型在工具调用上表现各异,差异点至少有四处:

  1. 参数怎么填——字段命名/类型容错、是否漏填必填项;
  2. 何时决定停——早停(一步到位)vs 反复试探(多绕几圈);
  3. 错误恢复——工具报 503/错误码后是重试、放弃,还是幻觉一个结果;
  4. 格式遵守——是否吐裸 JSON、是否被 prompt 注入带偏。

这些差异直接影响 completion 和 tool-call 数。所以横向对比的前提是:把一切非模型变量钉死——同一任务集、同一 temperature、同一 judge、同一 system prompt——只让「模型」这一个自由度变化,Δ 才能归因到模型本身。

temperature 是稳健性的隐藏旋钮。

temperature 越高,采样越发散,agent loop 越可能跑偏:

  • 选错工具、参数填错;
  • 步数膨胀(多绕几圈才收敛);
  • 甚至触发 Day 24 设的 maxSteps=8hitCap=trueoutput=''

横向对比时若两模型 temperature 不一致,Δ 就被「采样噪声差异」污染,不再是模型能力差。所以本日固定 temperature 是公平性硬条件,不是可选项。同理,固定 temperature 还让单次跑的随机性可控——但要彻底消噪声还得多跑几次取均值(这属 D29 power 的范畴)。

为什么复用注入式 loop、只换 provider 配置。

Day 24 的 runAgentLoop 把 model 与 tools 都做成依赖注入,正是为了今天:

  • 换模型 = 换一个 LoopModel/GenerateFn 实现;
  • loop 状态机、工具、任务集、判分逻辑全部不动

这保证「除了模型,其它都一样」在代码层面可证,而非靠人记。scripts/run-agent-eval.ts 已支持 --provider/--model 切换,所以同一脚本换两次参数即可产生两份报告,天然消除「换模型时不小心改了别的」的风险。

Δ 必须当周实测,不能引用旧 benchmark。

模型迭代极快(agent 半衰期 ~6 月),公开榜单上的 Qwen3/DeepSeek 数字是别人的任务集、别人的 temperature、别人的 judge——拿来充当「本套件 29 任务的 Δ」是张冠李戴。本日产出真实 Δ 的唯一合法来源是:在本仓 29 任务、固定 temperature、同一 judge 下,两模型各真跑一遍。离线 fixture 只能验流程,验不出真实 Δ。

2. 推导 / 手算 / 代码走读

公平对比的「控制变量」在 runner 里如何落地(scripts/run-agent-eval.ts):

  • provider/model 切换(第 68–73 行):
    • arg('--provider', 'deepseek') 取 provider;
    • resolveProvider(provider)baseURL/defaultModel/envVar
    • arg('--model', defaultModel) 覆盖模型 id;
    • arg('--judge', modelId) 默认 judge=被测模型(同一 provider);
    • 换 Qwen3 = --provider openrouter --model <qwen3-id>
  • 同一任务集:runner 顶部 import { EVAL_TASKS } from '../src/agent/eval/tasks',两次跑用的是同一个 EVAL_TASKS 数组——任务不变是天然保证。
  • 同一判分管道:两次跑都走 runTaskEval(EVAL_TASKS, { generate: makeModelGenerate(model,…), judge: makeLlmJudge(judgeModel), humanLabels }),judge 行为一致。
  • 报告落盘(第 109–114 行):每跑一次写一份 agent-evals/reports/run-<stamp>.json,含 commitprovidercompletionRatepartialCreditMeanunknownRatecodePassRatetotalCostUsdperTask。Day26(DeepSeek-V3)一份、Day27(Qwen3)一份,两份并排取差即 Δ。
  • QUOTABLE 行(第 130–132 行):每跑打印 "On N=<n> tasks, <provider>:<model> completion (LLM-judge=pass) = <pct> [commit <sha>]"——这是可引用的单行结论,对比时贴两行即成对照表。
  • dry-run 守卫(第 83–89 行):无 key 时打印 "Would run … tasks on … via provider …" 并 return——所以没 key 时产不出真实 Δ,只能出模板。

接线缺口提醒makeModelGenerate(在 agentEval.ts)当前 generateText({ model, system, prompt }) 调用未显式传 temperature。要落实「固定 temperature 公平对比」,需在 generate 接线处统一传同一温度(runner 改动点),否则两模型各用各的默认温度,对比不公平。这是本日真跑前必须先补的一处。

对比表模板(待 key 填真值,结构如下):

模型ncompletion%avg tool-callsunknown%cost/run
DeepSeek-V329待跑待跑待跑待跑
Qwen329待跑待跑待跑待跑
Δ (DS−Qwen)待跑 pp待跑

为什么 unknown% 也要进对比表。 judge 的 unknown 率不是噪声而是信号:若 Qwen3 的 unknown% 显著高于 DeepSeek,可能是它的输出更含糊(judge 不敢判),而非任务本身坏。把 unknown% 并列,能避免把「judge 拿不准」误算成「completion 低」。同理 cost/run 让「便宜一点但略差」与「贵很多但略好」的取舍可量化——选型从来不是只比 completion 一栏。

横向对比要警惕的「假 Δ」来源(除模型外都应钉死):

  1. judge 模型不同 → 判分尺子变了;
  2. temperature 不同 → 采样噪声差;
  3. system prompt 不同 → 任务难度隐性变化;
  4. 任务集版本漂移(tasks.ts 中途改了题)→ 不可比;
  5. 单次跑的随机性 → 同模型重跑一次取均值才稳。

3. 今日实战

  1. 复用 Day 24 的注入式 src/agent/loop.ts 与 Day 26 已接通的 runTaskEval,仅换 provider 配置:把模型从 DeepSeek-V3 换成 Qwen3(via OpenRouter)。
  2. 先补 makeModelGenerate 的 temperature 接线(统一固定值),保证两次跑温度一致。
  3. 同一 29 任务套件、 temperature,跑 pnpm eval:agent --provider openrouter --model <qwen3-id>,得第二份 agent-evals/reports/*.json
  4. 与 Day 26 的 DeepSeek-V3 报告并排,记 Δcompletion(百分点)Δavg-tool-calls
  5. 本日落地:对比表模板 + 跑数清单(committed),列出待跑命令、固定 temperature、judge 配置,留待 key 到位后填真值。

4. 今日实测 / 产出

  • 两模型 completion 对比表(committed) —— 待跑(需 OPENROUTER_API_KEY);离线 fixture 只能验流程,真实 Δ 必须真模型跑出。
  • 本日产出:对比表模板 + 跑数清单(已落地)。
  • 真实 Δcompletion / Δavg-tool-calls 未产出数字——不臆造,不引用旧 benchmark 充当本套件结果。

5. 常见误区 / 陷阱

  • 两模型用不同 temperature 对比:Δ 被采样噪声污染,结论无效。
  • 引用公开榜单的 Qwen3/DeepSeek 分当作本 29 任务的 Δ:任务集/judge/温度都不同,是张冠李戴。
  • judge 用了不同模型:判分尺子变了,Δ 不纯。本日两次跑应固定同一 judge。
  • 只看 completion 不看 tool-calls:可能一模型 completion 略高但 tool-call 数翻倍(更贵),需双指标一起看。
  • 忘了 temperature 接线缺口makeModelGenerate 默认没传 temperature,不补就不是真正的「同温度」对比。

6. 学习资源(每条带 YYYY-MM)

  • Qwen3 工具调用文档(via OpenRouter,2025-2026,执行当周重验版本)—— 本日 function-call 行为参考。
  • DeepSeek-V3 经 OpenRouter 的 OpenAI-兼容 tool_calls 协议(2025-2026)—— Day 24 已用的接线协议。
  • 本仓 scripts/run-agent-eval.ts —— --provider/--model/--judge 切换与报告落盘走读。
  • Anthropic, Demystifying evals(2026-01)—— 判分一致性(同 judge/口径)为公平对比前提。

SOTA检查 (2026-06 更新)

  • 当前主流:固定任务集 + 固定 temperature + 固定 judge 的「单变量横向对比」是 eval 选型的标准做法,无方法学过时风险,仍是 SOTA。
  • 执行当周重验:Qwen3 与 DeepSeek-V3 经 OpenRouter 的可用性 / version 必须当周重验——模型迭代快,model id 可能变。
  • 过时黑名单:勿引用旧 benchmark 数字充当本套件结果;勿用不同 temperature/judge 对比。
  • 下次复查点:Day 28 把本日 Δ 喂进 pairedBootstrap 算 95% CI;OpenRouter 模型清单按周复查。

衔接

  • 昨天:Day 26 — runTaskEval 打通(agent loop 接评测 harness,双指标)。
  • 今天:同任务集/同 temperature 换 Qwen3 横向对比,记 Δcompletion / Δavg-tool-calls;真实 Δ 待真模型跑。
  • 明天:Day 28 — paired bootstrap + CI(把 Δ 配对重采样,算 95% CI 判显著)。