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 行为是有差异的,不能默认可比。
不同模型在工具调用上表现各异,差异点至少有四处:
- 参数怎么填——字段命名/类型容错、是否漏填必填项;
- 何时决定停——早停(一步到位)vs 反复试探(多绕几圈);
- 错误恢复——工具报 503/错误码后是重试、放弃,还是幻觉一个结果;
- 格式遵守——是否吐裸 JSON、是否被 prompt 注入带偏。
这些差异直接影响 completion 和 tool-call 数。所以横向对比的前提是:把一切非模型变量钉死——同一任务集、同一 temperature、同一 judge、同一 system prompt——只让「模型」这一个自由度变化,Δ 才能归因到模型本身。
temperature 是稳健性的隐藏旋钮。
temperature 越高,采样越发散,agent loop 越可能跑偏:
- 选错工具、参数填错;
- 步数膨胀(多绕几圈才收敛);
- 甚至触发 Day 24 设的
maxSteps=8而hitCap=true、output=''。
横向对比时若两模型 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,含commit、provider、completionRate、partialCreditMean、unknownRate、codePassRate、totalCostUsd、perTask。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 填真值,结构如下):
| 模型 | n | completion% | avg tool-calls | unknown% | cost/run |
|---|---|---|---|---|---|
| DeepSeek-V3 | 29 | 待跑 | 待跑 | 待跑 | 待跑 |
| Qwen3 | 29 | 待跑 | 待跑 | 待跑 | 待跑 |
| Δ (DS−Qwen) | — | 待跑 pp | 待跑 | — | — |
为什么 unknown% 也要进对比表。 judge 的 unknown 率不是噪声而是信号:若 Qwen3 的 unknown% 显著高于 DeepSeek,可能是它的输出更含糊(judge 不敢判),而非任务本身坏。把 unknown% 并列,能避免把「judge 拿不准」误算成「completion 低」。同理 cost/run 让「便宜一点但略差」与「贵很多但略好」的取舍可量化——选型从来不是只比 completion 一栏。
横向对比要警惕的「假 Δ」来源(除模型外都应钉死):
- judge 模型不同 → 判分尺子变了;
- temperature 不同 → 采样噪声差;
- system prompt 不同 → 任务难度隐性变化;
- 任务集版本漂移(
tasks.ts中途改了题)→ 不可比; - 单次跑的随机性 → 同模型重跑一次取均值才稳。
3. 今日实战
- 复用 Day 24 的注入式
src/agent/loop.ts与 Day 26 已接通的runTaskEval,仅换 provider 配置:把模型从 DeepSeek-V3 换成 Qwen3(via OpenRouter)。 - 先补
makeModelGenerate的 temperature 接线(统一固定值),保证两次跑温度一致。 - 用同一 29 任务套件、同 temperature,跑
pnpm eval:agent --provider openrouter --model <qwen3-id>,得第二份agent-evals/reports/*.json。 - 与 Day 26 的 DeepSeek-V3 报告并排,记 Δcompletion(百分点) 与 Δavg-tool-calls。
- 本日落地:对比表模板 + 跑数清单(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 判显著)。