返回 AICAP-180
B1 · Day 4evals 方法论 + tokenization 起步

OpenRouter 接入

Day 3 把 token 怎么来的讲清楚了,但 token 只有换算成钱才有决策价值。今天接通真实计费侧:OpenRouter 统一路由多模型,按 input / output token 分别计价,各模型上下文窗口不同。要在跑全套 eval 前预估成本,就得先对比候选模型的单价与窗口、并发一个 ping 确认链路通。这是 B1 从「纸面 token 数」到「真实美元」的桥。最小可判定产出:.e

阶段: B1 · evals 方法论 + tokenization 起步(Day 1-10) 标签: #openrouter #model-routing #token-pricing #cost

今日导引(由浅入深)

Day 3 把 token 怎么来的讲清楚了,但 token 只有换算成钱才有决策价值。今天接通真实计费侧:OpenRouter 统一路由多模型,按 input / output token 分别计价,各模型上下文窗口不同。要在跑全套 eval 前预估成本,就得先对比候选模型的单价与窗口、并发一个 ping 确认链路通。这是 B1 从「纸面 token 数」到「真实美元」的桥。最小可判定产出.env 就位 + 两个模型各一个 ping 的 token/latency/cost 实测表——但这需要 key,无 key 时本日为「待跑」,先记当周公开单价/窗口占位。

1. 机理精读

为什么今天要从「免费 token 数」跳到「真实美元」。Day 3 我们能用自写 tokenizer 数出 prompt 的 token 数,但这只是「估算的输入侧」。真正的成本要等模型真跑、返回真实 usage(input+output token)、乘以真实单价才算得出。今天接通 OpenRouter,就是把度量从「纸面 token」推进到「计费 token + 美元」。这一步不能省:AISA 作品集的卖点之一是「我能给出单位成本数字」,而单位成本的最小可信单位就是一次真实调用的 input/output token × 单价。

OpenRouter 是什么。它是一个统一的 OpenAI-compatible 网关:用一个 API key、一个 base URL(https://openrouter.ai/api/v1)、一套 OpenAI chat-completions 协议,就能路由到几十家供应商的几百个模型。对架构意义在于把「换模型」从「换 SDK / 换鉴权 / 换协议」降级成「换一个 model 字符串」——这正是 AI gateway 模式的核心价值(B5/B8 会再深入)。它还顺带提供跨供应商的统一计量、降级路由(一家挂了切另一家)、统一限流——这些都是「买」一个网关相对「自建」省下的工程量,也是 build-vs-buy 论证里要逐项对照的能力清单。

按 input/output 分别计价。计费不是「按请求」而是「按 token」,且 input token 与 output token 单价通常不同(output 一般更贵,因为是自回归逐 token 生成、算力占用高)。所以成本公式是: cost ≈ input_tokens × input单价 + output_tokens × output单价(单价按每百万 token 计)。 这条公式与 Day 3 的 tokenizer 直接咬合:input token 数可在发请求前用 tokenizer 估出来(Day 9 产出 1760 token),但 output token 数发请求前不可知——这是成本预估天然的不确定来源(Day 10 收口会处理)。

上下文窗口差异。不同模型的 context window 不同,决定单次能塞多少 prompt + 历史。窗口太小,长 AML 证据汇集会被截断;窗口大但单价高,又烧钱。所以「选哪个模型跑 eval」是窗口 × 单价 × 能力的三方权衡。对 AML Copilot 这类要喂大量交易证据的任务,窗口是硬约束——窗口不够,再便宜也不能用。

OpenAI-compatible 协议是真正的可移植性来源。OpenRouter 之所以能「换 model 字符串即换模型」,根因是它把所有供应商都包成 OpenAI chat-completions 协议(同样的 messages/usage/choices 结构)。本仓 buildModelcreateOpenAICompatible,所以同一份 harness 代码能打 OpenAI、DeepSeek、OpenRouter、甚至本地自部署的兼容端点——provider 只是 {name, baseURL, apiKey} 三元组。这条「协议统一 → 实现可换」是 AI gateway 抽象的工程根基,也是为什么 agentEval.ts 不绑死任何单一供应商。

计费 token ≠ 字符数 ≠ 自写 tokenizer token 数。三者是三个不同的量:字符数最直观但与计费无关;自写 BPE tokenizer(Day 7-9)给的是估算;OpenRouter 实际计费用的是模型官方 tokenizer 的切分。三者会有出入——这正是 Day 10 双数对齐要量化的偏差。今天要建立的认知是:只有 r.usage 返回的 input/output token 才是计费真相,其余都是估算。makeModelGenerate 也正是从 r.usage 取数,不从字符数或自写 tokenizer 取。

为什么要对比 DeepSeek 与 Qwen3。seed 指定对比 DeepSeek-V3Qwen3 的单价与窗口以预估 eval 成本。诚实记录仓库现状:src/agent/shared/cost.ts(2026-05 快照)的价目表已升级到 DeepSeek-V4deepseek-v4-pro input $0.435 / output $0.87 每百万 token;deepseek-v4-flash $0.14 / $0.28;legacy deepseek-chat/deepseek-reasoner 现 alias 到 v4-flash,2026-07-24 退役)。seed 文本里的「DeepSeek-V3」是写 seed 当时的版本锚点——当周重验时以 cost.ts 的 V4 价目 + OpenRouter 当周在架模型为准,版本号写全,勿写裸「DeepSeek」「Qwen」。

网关之于 AI Solutions Architect 的意义。OpenRouter 这类 AI gateway 解决的是架构层真实痛点:多供应商、多模型、价格/可用性波动、需要 A/B 不同模型、需要统一计量计费与限流。把这些收敛到一个网关,意味着「模型选型」从耦合进代码降级成配置——这正是 AISA 作品集里 build-vs-buy 论证的一环(本仓自建 provider 抽象 buildModel 对标的就是这层)。今天接 OpenRouter 是用「买」的一面打底,后续 B5/B8 会对比「自建网关」该提供哪些等价能力(路由、计量、限流、降级)。

成本预估的手算示例。用 Day 9 将产出的 input token 数(1760,针对 29 prompt)配 cost.tsdeepseek-v4-flash 单价(input $0.14 / 百万)做一个仅 input 侧的量级估算: 1760 / 1_000_000 × 0.14 = $0.000246(约 0.25 厘美元)。 这只是 input 侧、且用自写 tokenizer 的 token 数(与模型官方 tokenizer 有出入)。output token 数发请求前不可知,无法预估——这是为什么本日只能给 input 侧量级、真实总 cost 必须 Day 5 跑 harness 实测。这条「估算只能管 input、output 必须实测」的认知,是 Day 10 双数对齐偏差分析的基础。

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

代码走读对象是 scripts/run-agent-eval.tssrc/agent/eval/agentEval.ts(provider 已在 repo 接好,走 OpenRouter / 任意 OpenAI-compatible)。真实符号走读:

  1. buildModel({ name, model, apiKey, baseURL })agentEval.ts):用 createOpenAICompatible 建一个 LanguageModel——任意 OpenAI-compatible 供应商(OpenAI / DeepSeek / OpenRouter / 自定义 base URL)都走这一个工厂,不需要新 SDK。
  2. buildOpenRouterModel(model, apiKey, baseURL='https://openrouter.ai/api/v1'):OpenRouter 的便捷包装,back-compat 保留。
  3. run-agent-eval.tsENV_KEY 映射:openrouter → OPENROUTER_API_KEYdeepseek → DEEPSEEK_API_KEY 等——决定从哪个 env 变量取 key。
  4. resolveProvider(provider):从 getProvidersrc/agent/config/providers.ts)取 defaultBaseURL 与默认模型;取不到 baseURL 再回退 <PROVIDER>_BASE_URL env。
  5. 无 key 的诚实行为run-agent-eval.ts 第 83-89 行——key 未设时打印「dry run (no model called)」+「Would run N tasks on …」并 return(exit 0)。这就是本日「待跑」状态的代码级体现:链路写好了,但没 key 不发真请求、不编数字。
  6. makeModelGenerate(model, { modelName })agentEval.ts L150):发真请求时记 latencyMs = Date.now()-t0,并从 r.usageinputTokens/outputTokens仅当 modelNameMODEL_PRICES 且 usage 齐全时estimateCostcostUsd——否则 costUsdundefined绝不静默写 0)。
  7. estimateCost(model, inTok, outTok)cost.ts):(inTok/1e6)×inputPerM + (outTok/1e6)×outputPerM——就是机理段那条公式的代码实现;model 不在 MODEL_PRICES 时返回 0(调用方据此判断「未定价」而非「免费」)。
  8. MODEL_PRICEScost.ts,2026-05 快照):每条是 { inputPerM, outputPerM }(每百万 token 美元)。注释明确「Update via scripts/refresh-model-prices if material drift」——价目是快照不是实时,当周重验是纪律不是可选。
  9. provider 配置单一事实源resolveProvidergetProvidersrc/agent/config/providers.ts)取 defaultBaseURLdefaultSubAgentModel || defaultChatModel,所以「默认跑哪个模型 / 走哪个 base URL」集中在 providers 配置,不散落在脚本里——换默认模型只改一处。

3. 今日实战

ping 应记录的字段(每个模型一行):模型全名(含版本)/ input token / output token / 总 token / latency(ms) / cost(USD) / 上下文窗口 / 当周 input·output 单价。其中 token 与 cost 取自 r.usage + estimateCost,不靠字符数估。

执行步骤:

  1. cp .env.example .env,把 OPENROUTER_API_KEY 填进去(或用已有的 OpenAI / DeepSeek key 走对应 provider)。
  2. DeepSeek(当周在架版本,cost.ts 现为 V4-Flash/Pro)与 Qwen3 各发 1 个 ping 请求,记:latency / 实际计费 token(input+output)/ cost。
  3. 把两模型当周公开单价 + 上下文窗口先记入对照表(占位),ping 出真实 token/latency/cost 后回填。
  4. provider 已接好(agentEval.ts 走 OpenAI-compatible / OpenRouter),无需改代码,只配 .env + 跑 ping。

4. 今日实测 / 产出

  • 状态:待跑(需 OPENROUTER_API_KEY)。
  • 将产出.env 就位 + 两模型 ping 的 token/latency/cost 实测表。
  • 无 key 时:先记录两模型当周公开单价 / 上下文窗口,留实测占位,不臆造数字
  • 仓库已有的真实价目锚点(cost.ts 2026-05 快照,可引用、非本日实测):deepseek-v4-flash $0.14/$0.28、deepseek-v4-pro $0.435/$0.87 每百万 token——这些是价目表数字,不是 ping 实测;ping 的 latency/cost 仍待跑。

待回填对照表骨架(占位,等 ping 实测)

模型(写全版本)input $/Moutput $/M上下文窗口ping latencyping in/out tokenping cost
DeepSeek-V4-Flash0.140.28当周确认待跑待跑待跑
Qwen3-(当周版本)当周确认当周确认当周确认待跑待跑待跑

价目列可先用 cost.ts / 官方页填(DeepSeek 一行已有快照值);窗口与 ping 三列在拿到 key、发出 ping 后回填,回填前一律标「待跑」,不填 0、不填猜测

4b. 单位经济视角(为什么 AISA 要算这笔账)

eval 不是「能不能跑」,而是「跑一轮多少钱、扩到生产多少钱」。AML Copilot 若每天处理 N 万笔交易、每笔触发若干次模型调用,单价差异(V4-Flash $0.14 vs V4-Pro $0.435 input)会被 N 放大成数量级差。所以 hiring manager 四问里有一问就是「成本」——而成本的最小可信单位就是今天接通的「input/output token × 单价」。把这条算清楚,Day 10 双数对齐、B10 的 cost 级联、B12 模型策略 memo 才有数据底座。今天接 OpenRouter 不是接一个 API,是接通整条「单位经济」的度量线。

5. 常见误区 / 陷阱

  • 以为 input/output 同价:output 通常更贵;用单一单价估成本会系统性偏低。
  • 用 input token 估总成本:output token 数发请求前不可知,必须实测——这是 Day 10 偏差的主因之一。
  • 写裸模型名:必须写全版本(DeepSeek-V4-Flash / Qwen3-xxx),不写「DeepSeek」「Qwen」;OpenRouter 在架模型与单价当周重验
  • 无 key 时编数字:本日明确「待跑」,dry-run 只打印「Would run …」;任何 cost/latency 数字都得是真 ping 出来的。
  • 把价目表当实时价cost.ts 是 2026-05 快照;促销价 / legacy 退役会让它过时,引用前看注释日期。
  • 混淆订阅与 API key:脚本头注释明确——Claude Code / ChatGPT 的订阅不是编程 API key,要从同账号开发者控制台单独 mint。这是新手最常卡住的点。

5b. dry-run 是诚信设计,不是占位符

run-agent-eval.ts 在无 key 时打印「dry run (no model called)」+「Would run N tasks on …」并 exit 0——这不是「功能没做完」,而是刻意的诚信设计:没有 key 就绝不发请求、绝不编造 latency/cost。脚本头注释甚至明确写「No key set? It prints what it WOULD do and exits 0. The eval MATH … is unit-tested and needs no key.」这与这套笔记的诚信底线完全一致:待跑就是待跑,不假装已完成。本日产出表里所有 ping 列标「待跑」,正是这条设计在笔记侧的镜像。

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

  • OpenRouter 官方文档 Models & Pricing / Routing(执行当周重验,单价与可用性常变)。
  • DeepSeek 官方定价页(V4-Pro / V4-Flash,2026-04 起促销价;2026-07-24 legacy 退役提示)。
  • Qwen3 官方模型卡 / 定价(当周确认版本号与窗口)。
  • Vercel AI SDK 文档 Provider: OpenAI-compatiblecreateOpenAICompatible 用法,2026 持续维护)—— buildModel 的底层。
  • Anthropic Claude API / pricing(2026,按 input/output/cache token 计价模型)—— 对照另一家计价结构,理解 output 更贵与 prompt caching 的成本含义。
  • 仓库代码:scripts/run-agent-eval.ts(provider 解析 + dry-run,L83-89 无 key 行为)、src/agent/eval/agentEval.tsbuildModel/makeModelGenerate L150)、src/agent/shared/cost.tsMODEL_PRICES 2026-05 快照 + estimateCost)、src/agent/config/providers.ts(provider 单一事实源)。

SOTA检查 (2026-06 更新)

  • 当前主流:OpenRouter 统一网关 + 按 token 分别计价仍是标准做法;AI gateway 模式(一个 key 路由多模型、统一计量/降级/限流)是 2026 主流架构。
  • 是否仍 SOTA:网关模式仍 current。但模型单价 / 在架状态 / 版本号必须执行当周重验——这是高频变动项,半衰期以周计。
  • 过时黑名单
    • 避免写裸「DeepSeek」「Qwen」——必须写全版本(DeepSeek-V4-Flash / V4-Pro / Qwen3-xxx)。
    • 避免引用退役模型:cost.ts 标注 legacy deepseek-chat/deepseek-reasoner 2026-07-24 退役、alias 到 v4-flash;seed 的「DeepSeek-V3」需当周确认是否仍在架,否则切 V4。
    • 避免把价目表快照当实时价——MODEL_PRICES 是 2026-05 快照,注释要求 material drift 时刷新。
  • 下次复查:执行当周重验 OpenRouter 的 DeepSeek-V4、Qwen3 单价/窗口;2026-07-24 后确认 legacy DeepSeek alias 是否已下线、价目表是否需刷新。

5c. 与 Day 5 的接力点

今天接通的 makeModelGenerate 是 Day 5 harness 的 GenerateFn 实现:harness 对每个 task 调它一次,拿回 {output, latencyMs, costUsd}。所以今天的 ping 本质是「单 task 的 generate」,Day 5 是「全套 task 的 generate + judge + 聚合」。今天确认链路通(ping 出真 token/latency/cost),Day 5 才敢跑全套。两天的「待跑」共享同一把 key——一旦配好,Day 4 的 ping 表和 Day 5 的三数报告可以同一轮产出。这就是为什么本批次把「接入」与「跑通」拆成相邻两天:先验证单点链路,再放大到全套,降低首跑的调试面。

衔接

  • 昨天:Day 3 — BPE/tokenization 原理(token 怎么来的)。
  • 今天:OpenRouter 统一路由 + input/output 分别计价 + 窗口差异;接通真实计费侧(待 key)。
  • 明天:Day 5 — 跑通 eval harness(completion% + cost + commit 三数首跑;harness 数学离线可测,真 cost 待 key)。