返回 AICAP-180
B8 · Day 76真 API + 流式 + Docker

流式接 Qwen3 对比

B1→B18 这条能力曲线,前半(B1-B7)把「评测怎么算数」立住——任务集、judge、统计、CI gate;

阶段: B8 · 真 API + 流式 + Docker(Day 71-80) 标签: #model-selection #ab-testing #cost-latency #paired-bootstrap

今日导引(由浅入深)

B1→B18 这条能力曲线,前半(B1-B7)把「评测怎么算数」立住——任务集、judge、统计、CI gate; B8 这一段(Day 71-80)转去把 runner 包成「真服务」——HTTP 边界、SSE 流式、模型流式接入、typed 错误、再把 eval 套件接到 API 上。

昨天(Day 75)刚把 29 任务当成 API 的集成测试夹具跑通通路; 今天顺势从「单模型跑通」迈到「双模型横评」——同一套 29 任务、同一个 judge,跑 DeepSeek 与 Qwen3,产出成本/延迟/质量三维权衡矩阵

这是模型选型 memo(也是 build-vs-buy 论证)的实证输入:架构师不能凭感觉说「这个模型更好」,要拿同一框架下的配对数字说话。 今天的「最小可判定产出」是一张双模型对比表:TTFT、completion%、cost/run 各一列。

1. 机理精读

横评的本质是受控实验。 同任务跑双模型,唯一允许变化的自变量是「模型」本身——prompt 必须同字、任务集必须同 29、judge 口径必须同 pass。 任何一处不同,差异就无法干净归因到模型本身。 这就是 src/agent/eval/abCompare.ts 里那行 ids = [...ma.keys()].filter((id) => mb.has(id)).sort() 存在的理由: 它强制两份报告按 taskId 对齐,只在共同任务上比较,否则直接 throw 'no common taskIds'——拒绝拿不同题目的成绩硬比。

三维权衡矩阵:TTFT × completion% × cost/run。 单看准确率会买到「贵且慢」的模型;单看价格会买到「又快又笨」的模型。 架构师要的是 Pareto 前沿上的点——在某一价位/延迟下没有更强的选择。三个维度各自的含义:

  • TTFT(Time To First Token):首 token 延迟,决定「感知响应速度」。 流式场景下 TTFT 比 total latency 更重要——用户先看到字在动,体感就「快」了。 由 src/agent/eval/stats.tssummarizeLatency 出 p50/p95/p99(p95/p99 surface 尾部,mean 会把慢尾抹平)。
  • completion%(judge=pass 通过率):能力上限,回答这模型在你的领域任务上到底答对几成。
  • cost/run:单次运行成本,喂给 build-vs-buy 的 TCO 模型——同样 79.3% 的能力,花 $0.0139 还是 $0.05 一次,决定能否规模化。

为什么用配对(paired)而非独立两样本? src/agent/eval/stats.ts 顶部注释说得直白: "Pairing on the same tasks removes task-difficulty variance, so the CI on the delta is tight." 同一道题 A 答对 B 答错,这个 ±1 的配对差消掉了「这道题本来就难」的方差。 配对 bootstrap 重采样的是差值序列 diffs = a.map((x,i)=>x-b[i]),而非两组各自的均值。 结果是 CI 比非配对窄得多——在 N 小的时候这点尤为关键,因为本就没几个样本,再被任务难度方差污染就彻底测不出差异。

与相邻概念的边界。 横评 ≠ leaderboard 刷分。 leaderboard 用的是公开通用基准(MMLU 之类),横评用的是你自己的领域任务集(这里是 AML 任务)。 对架构师,后者才是选型依据——通用强不代表在你的 AML 场景强; 反过来,一个在 MMLU 上一般的模型,可能在你窄而深的合规任务上反而够用且更便宜。

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

先做一个最小手算,理解「配对差值」为什么消方差。 设 5 道题,A 的对错向量 [1,1,0,1,0]、B 的 [1,0,0,1,0]

  • 非配对看均值:A=0.6、B=0.4,差 0.2,但每组各自的方差都参与了 CI 估计。
  • 配对看差值:diffs=[0,1,0,0,0]deltaMean=0.2,只有「A 比 B 多对的那道题」贡献方差。
  • 第 1、3、4、5 道两边都一样(tie),对差值贡献 0——任务难度被成对抵消,CI 自然更紧。

代码走读 scripts/ab-compare.tspnpm eval:ab无 key,只读已存报告):

  1. 入口零参数时自动选最近两份报告: 扫 agent-evals/reports/,过滤 run-*.jsonsort(),取 files[len-1] 为 A(newest)、files[len-2] 为 B(previous)。 目录不存在或 <2 份就打印提示「跑两次不同 --modelpnpm eval:agent」后 exit(0)——优雅降级,不崩。
  2. 核心计算委托给 abCompare(A, B)src/agent/eval/abCompare.ts): 先 passMap 把每份报告映射成 taskId -> 0/1judge.label === 'pass' ? 1 : 0),再对齐取交集 taskId 并 sort()
  3. 配对 bootstrapabCompare 把对齐后的两个 0/1 向量喂给 pairedBootstrap(av, bv),默认 iters=2000 次重采样; 每次重采样累加 diffs[Math.floor(rng()*n)],返回 deltaMeanci95 = [percentile(2.5), percentile(97.5)]
  4. 逐任务胜负: 遍历对齐任务,d = av[i]-bv[i]d>0 计 A win、d<0 计 B win、d===0 计 tie。 这正是 seed 里「3 wins / 0 losses / 26 ties」的来源结构——26 道两边都一样、3 道 A 赢、0 道 B 赢。
  5. 显著性裁决scripts/ab-compare.ts 第 41 行—— r.ci95[0] > 0 ? 'A significantly better' : r.ci95[1] < 0 ? 'B significantly better' : 'not significant (CI crosses 0)'CI 跨 0 即不显著,这是冷冰冰的代码判定,不是人嘴上松紧。
  6. 产出 QUOTABLE 行: 脚本末尾打印一行可直接贴进 memo 的话术—— "<modelA> vs <modelB> on N=..: Δ completion = X pp [95% CI .., ..]",并把完整结果写进 agent-evals/ab.json

3. 今日实战

  1. 用 Qwen3 跑同 29 任务:pnpm eval:agent --model qwen3 ...(夹具来自 src/agent/eval/tasks.ts,29 任务),生成第二份 run-*.json
  2. DeepSeek 侧同样跑出一份报告(model=deepseek-v4-flashdeepseek-v4-pro),落进 agent-evals/reports/
  3. pnpm eval:ab 自动取最近两份报告,产出对齐后的 completion 对比、配对 Δ + 95% CI、wins/losses/ties 与裁决。
  4. TTFT 维度:在流式接入路径用 summarizeLatency 收集每模型 p50/p95/p99,与通过率、cost/run 拼成三列对比表,落入 worklog。
  5. 把脚本打印的 QUOTABLE 行原样贴进选型 memo——措辞不走样、CI 不丢。

4. 今日实测 / 产出

已落地的真实 A/B(同框架,无 key 只读报告)

  • V4-Pro 89.7% vs V4-Flash 79.3%
  • 配对 Δ +10.3pp,95% CI [0.0, 20.7]
  • 3 wins / 0 losses / 26 tiesN=29
  • 方向更优,但 N=29 不显著(CI 下界触 0;达 power 需 ~70 任务)

Qwen3 对比表为 待跑(需 key 跑):产出 TTFT + 通过率双模型对比表。

诚实标注——Qwen3 这一列尚未实测,只有 DeepSeek V4-Pro / V4-Flash 之间的 A/B 是真数字; TTFT 维度同样待 key 跑出,不在本笔记里写死任何未测的延迟数。

5. 常见误区 / 陷阱

  1. 小 N 看见方向就宣称「显著」: Δ+10.3pp 看着很大,但 CI 下界压在 0.0 上,代码判定 not significant。报告必须带 CI,不能只报点估计。
  2. 横评时偷偷改 prompt/任务: 换了模型又顺手调 prompt,差异就污染了,归因失效。受控实验的命门是只动一个自变量。
  3. 只看 mean latency,忽略尾部: mean 会被快请求拉低,p95/p99 才暴露慢尾。summarizeLatency 同时出三个分位就是为这个。
  4. 用非配对统计比配对任务: 丢掉了任务难度方差的消除,CI 被无谓放宽,本就紧张的 N=29 更难达显著。

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

  • Qwen3 Technical Report / 模型卡(Alibaba, 2025-04)——当前开源对照系基线。
  • DeepSeek-V3/V4 系列模型卡与定价页(DeepSeek, 2025-12 / 2026-05)——v4-pro/v4-flash 新 id 与单价。
  • Bootstrap Methods and Their Application(Davison & Hinkley, 1997)——配对 bootstrap 的经典打底。
  • Anthropic「Demystifying evals」(Anthropic, 2026-01)——领域任务集而非 leaderboard 作选型依据。
  • 本仓代码:src/agent/eval/abCompare.tssrc/agent/eval/stats.tsscripts/ab-compare.ts(2026-06,AICAP-180 B12/B17)。

SOTA检查 (2026-06 更新)

  • Qwen3 仍为当前开源对照系,作 DeepSeek 闭源横评的开源锚点有效。
  • DeepSeek 用 v4-pro / v4-flash 新 id;legacy deepseek-chat / deepseek-reasoner 将于 2026-07-24 退役,新代码勿硬编码。
  • 统计教训(强制项):小 N 的 Δ 即便方向正确也勿宣称显著,必须报 CI;N=29 不足以达 power,扩集至 ~70 任务待办。
  • 过时黑名单:用通用 leaderboard 分数替代领域横评作选型;非配对统计比配对任务;只报点估计不报 CI。
  • 下次复查点:2026-07-24(DeepSeek legacy id 退役日,确认新 id 路径无回退);任务集扩到 ~70 后重跑 A/B 复核显著性。

衔接

  • 昨天:Day 75 — API 接 eval 套件(29 任务当 API 集成测试夹具,单模型跑通通路)
  • 今天:同任务、同 judge 跑双模型,产出成本/延迟/质量三维权衡矩阵——选型 memo 的实证输入;真 A/B(V4-Pro 89.7% vs V4-Flash 79.3%)已落地,Qwen3 列待跑
  • 明天:Day 77 — 多阶段 Docker(把跑通的服务装进瘦镜像,开始部署侧工程)