返回 AICAP-180
B10 · Day 93p95/cost 测量 + 成本级联 + tau2-bench

$/query 成本测量 (M4)

B10 把 LLM 服务量化运营:Day 91 测延迟分布、Day 92 测并发吞吐,今天补上第三个轴——钱。

阶段: B10 · p95/cost 测量 + 成本级联 + tau2-bench(Day 91-100) 标签: #cost #usd-per-query #pricing #unit-economics

今日导引(由浅入深)

B10 把 LLM 服务量化运营:Day 91 测延迟分布、Day 92 测并发吞吐,今天补上第三个轴——钱。 一个 AI Solutions Architect 的核心交付不是「能跑」,而是「能跑且单位成本说得清」。 这正是计划里反复强调的 KPI:可链接资产 + eval 数字 + 单位成本。 延迟和成本要一起报,因为它们经常对立:贵模型质量高但 $/query 高。 今天把每个请求的 token usage 乘单价,得到 $/query 的均值与 p95,追加进昨天的压测表。 今天的「最小可判定产出」:跑出 DeepSeek-V4-Flash 的实测 $/query 均价(真实值待 key,已知 REAL 基线 $0.0139/run 量级)。

1. 机理精读

成本公式很简单,难点在口径。 单次请求成本 = (in_tokens × in_price + out_tokens × out_price),其中价格是「每百万 token 美元」。 本项目的 src/agent/shared/cost.ts 把它实现成 estimateCost(model, inputTokens, outputTokens)。 公式落地为 (inputTokens / 1_000_000) × inputPerM + (outputTokens / 1_000_000) × outputPerM

输入和输出要分别计价。 LLM 服务的 output token 通常比 input 贵几倍——因为 output 是逐 token 自回归生成、占用 decode 算力。 DeepSeek-V4-Flash 是 input $0.14/M、output $0.28/M(2 倍差);V4-Pro 是 input $0.435/M、output $0.87/M。 所以同样 token 数,输出多的回答单价更高——这也是为什么「让模型简洁」既省延迟又省钱。

为什么 $/query 既要均值也要 p95? 因为回答长度是右偏长尾的——大多数回答短,少数长回答(model 啰嗦、任务复杂、被诱导进入长思考链)会消耗几倍 output token。 这些长尾把单位成本拉高,只报 mean $/query 会低估「最贵的那批查询」的成本。 做容量/预算规划时会失算:按 mean 定预算,长尾查询会超支。 p95 $/query 暴露「95% 的查询比这便宜」,是定预算上限的依据。 这与 Day 91 用 p95 暴露延迟尾部是同一思想,只是把 ms 换成 $。

价格是易变量,必须当周查。 成本测量最大的陷阱是用过期价格。 cost.ts 顶部明确标注「Snapshot 2026-05」,并注释 DeepSeek 「Legacy chat/reasoner now alias v4-flash (retire 2026-07-24)」。 价格波动(OpenRouter/DeepSeek 促销、调价)会让历史成本数字失效。 所以 SOTA 检查里把「当周必查单价」列为硬约束。

与相邻概念的边界。 今天测「裸 $/query」,不做级联优化(Day 94-95 用便宜模型先答省钱)。 这里给出的是「全程用 V4-Flash 的成本基线」,是后续级联省钱百分比的分母。

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

真实代码走读——成本 wiring 已在仓库里,本日只是把它接进压测脚本:

  • src/agent/shared/cost.tsMODEL_PRICESRecord<string, PriceEntry>PriceEntry = { inputPerM, outputPerM }
  • deepseek-v4-flash = { inputPerM: 0.14, outputPerM: 0.28 }deepseek-v4-pro = { inputPerM: 0.435, outputPerM: 0.87 }(V4-Pro 约 3× Flash 单价)。
  • estimateCost(model, inputTokens, outputTokens)(cost.ts:22):查不到价的 model 返回 0(注释说明这是「未定价」而非真免费——脚本侧要区分)。
  • src/agent/eval/agentEval.ts:16 导入 estimateCost;在 agentEval.ts:161 用 costUsd = estimateCost(opts.modelName, inTok, outTok) 从真实 token usage 算单次成本。
  • agentEval.ts:148-149 注释强调「costUsd 在未定价或 usage 缺失时留 undefined,绝不静默置 0」——这是诚信设计,避免把「没测到」伪装成「免费」。
  • 因此 bench/loadtest.ts 只需对每个 req 取 usage.prompt_tokens / completion_tokens,调 estimateCost('deepseek-v4-flash', inTok, outTok),得每查询 $。

手算示例(用真实单价验证量级):

  • 设一次 AML 评测请求 in=1200 tokens、out=600 tokens。
  • estimateCost = (1200/1e6)×0.14 + (600/1e6)×0.28 = 0.000168 + 0.000168 = $0.000336/req
  • 一个 run(约 30 任务)量级就到 ~$0.01,与 seed 给的 REAL 基线 $0.0139/run 同一量级。
  • 可见 V4-Flash 跑一整套 eval 仅约 1 分多钱,这正是它被选为默认 provider 的原因(China-friendly + 便宜)。
  • 对照 V4-Pro:同一请求 = (1200/1e6)×0.435 + (600/1e6)×0.87 = 0.000522 + 0.000522 = $0.001044/req,约 3.1× Flash——这正是 Day 94-95 级联要省的那部分。

输入 vs 输出成本占比的手算(指导优化方向):

  • 短输入长输出场景:in=200、out=2000。Flash 成本 = (200/1e6)×0.14 + (2000/1e6)×0.28 = 0.000028 + 0.00056 = $0.000588,输出占 95%。
  • 长输入短输出场景(RAG/长上下文):in=8000、out=200。Flash 成本 = (8000/1e6)×0.14 + (200/1e6)×0.28 = 0.00112 + 0.000056 = $0.001176,输入占 95%。
  • 结论:成本结构随 in/out 比例剧烈变化——优化前先看自己的工作负载是「输入重」还是「输出重」,再决定压 prompt 还是压回答长度。
  • 这也解释了为何 prompt caching(复用前缀 KV、前缀按折扣计价)对「输入重」工作负载省钱最多。

MODEL_PRICES 表里的现行 id 速查(cost.ts,快照 2026-05):

  • deepseek-v4-flash:input 0.14 / output 0.28(默认 provider)。
  • deepseek-v4-pro:input 0.435 / output 0.87(级联升级目标)。
  • deepseek-chat / deepseek-reasoner:legacy 别名,当前仍指向 v4-flash 价位,但 2026-07-24 退役——禁止在新脚本里用。
  • 未列出的 model 走 estimateCost 返回 0 分支,必须靠 agentEvalundefined 语义区分「未定价」与「免费」。

3. 今日实战

  1. bench/loadtest.ts(仍属待建脚手架)的每个 req 记录 usage 的 in/out tokens(从 API 返回的 usage 字段取)。
  2. estimateCost('deepseek-v4-flash', inTok, outTok)(从 src/agent/shared/cost.ts 导入),得每查询 $。
  3. 用当周 deepseek-v4-flash 单价——执行前先核对 OpenRouter/DeepSeek 定价页(价格易变,注释 Snapshot 2026-05 需当周确认)。
  4. 对 $/query 数组算均值与 p95(复用 percentile/mean,stats.ts),追加进 Day 92 的压测表,新增「$/query 均值」「$/query p95」两列。
  5. 与已知 REAL 基线 $0.0139/run 对照量级,验证脚本计价正确。

4. 今日实测 / 产出

  • 已有 REAL 成本基线:V4-Flash $0.0139/run(来自 B10 实测,逐字保留)。
  • loadtest 的 $/query 均值/p95 列 = 待跑(需 key 跑)
  • 跑后产出 DeepSeek-V4-Flash 实测均价数字(预期接近 $0.0139/run 量级)
  • 状态如实:$/query 列待 key,已知基线 $0.0139/run 不改动、不外推为「已测完压测表」。

从 $/query 到 $/run、$/解决任务 的口径升级(成本要挂在业务单位上):

  • $/query 只是原子成本;一个 agent 任务可能多轮调用,真实单位是 $/解决一个任务。
  • 多轮场景:$/task = Σ(每轮 $/query),被工具调用次数和重试放大——必须按完整 transcript 累加。
  • REAL 基线 $0.0139/run 就是「跑完约 30 任务一整套 eval」的口径,不是单 query——引用时要写清单位。
  • 给 stakeholder 报成本时用业务单位($/SAR 草稿、$/调查案)比 $/query 更有说服力,但底层都从 token usage 累加而来。

5. 常见误区 / 陷阱

  • 用过期单价:价格波动大,引用 Snapshot 2026-05 而不当周复查,会算出错误成本。执行当周必查 OpenRouter/DeepSeek 定价页。
  • 引用 legacy 价格:不要按 deepseek-chat 的旧 id 报价;V4-Flash/V4-Pro 为现行 id,2026-07-24 后 legacy 退役。
  • 只报 mean $/query:长尾长回答会拉高单位成本,必须同时报 p95,否则预算上限定低了。
  • 把未定价的 0 当免费estimateCost 对查不到价的 model 返回 0,agentEval 已用 undefined 区分「未测」与「真 0」——自写脚本时勿把 0 直接当真实成本。

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

  • OpenRouter 定价页(执行当周查,价格易变须重验)——seed 指定来源,in/out 单价权威源之一。
  • DeepSeek 官方定价/模型页(执行当周查)——V4-Flash/V4-Pro 现行 id 与单价。
  • 项目源码 src/agent/shared/cost.ts(本仓,estimateCost at line 22,价格快照 2026-05)。
  • 项目源码 src/agent/eval/agentEval.ts(本仓,line 161 调 estimateCost;148-149 注释「未定价留 undefined 不静默置 0」)。

SOTA检查 (2026-06 更新)

  • 当前主流:分 in/out token 计价、$/query 报 mean+p95 是 2026 单位经济核算标准;本项目的 estimateCost + per-call usage wiring 仍是正确口径。
  • 过时黑名单:避免引用 legacy deepseek-chat 价格;V4-Flash/V4-Pro 为现行 id,2026-07-24 后 legacy 退役。不要用过期价格快照而不当周复查。
  • 下次复查点:单价当周必查(OpenRouter/DeepSeek 价格波动);2026-07-24 legacy 退役节点重核 cost.ts 里的 id 与单价。

衔接

  • 昨天:Day 92 — 并发压测方法(N=50 @ concurrency 1/4/8 的 p95 + TTFT)
  • 今天:把 token usage 乘单价转成 $/query,报均值与 p95,追加进压测表;对照 REAL 基线 $0.0139/run
  • 明天:Day 94 — LiteLLM 路由/级联(M6):cheap-first 编排,便宜模型先答、低置信度才升级贵模型,省成本换可接受质量