价格表不会报错,它只会静默变错
代码里写死一个模型单价,它不会抛异常,不会让测试变红,只会在某个星期二悄悄变成假话。等你发现的时候,报价单已经发出去了。
这篇讲我们怎么量化这件事,以及怎么把「这个数字多久没核过」变成一个能让构建失败的字段。我们不是从方法论倒推出来的,是先被咬过:信任页两次上线过厂商政策的「逐字引用」,而链接过去的原文里根本没有那句话。数字全部来自 llmabacus 自己的 src/data/models.json 和 price-changes.json。
半年里动过价的模型,改的不是零头
我们的 price-changes.json 记了 28 条变动,时间跨度是 2026 年 2 月到 9 月。剔掉新模型上线和下线,2026 年内真正改了单价的有 14 条,落在 13 个模型上。库里一共 57 个模型,不到半年,近四分之一的条目至少作废过一次。
更要紧的是幅度。这 14 条里能算出输入价变动比例的,中位数是 60%,最大两条是 200%。星火 Ultra 从 CNY 2/M 降到 0.8,降幅六成。这种量级下,「价格表有点旧」和「价格表是错的」没有区别:你按旧数据算出来的月成本,可能是实际的两倍半,也可能是四成。
把价格当常量写进代码,风险不在精度,在方向。你既不知道它偏高还是偏低,也不知道它已经错了多久。这两件事,比错多少更难解释给客户。
自动抓的都新,靠人盯的都烂
models.json 里每家供应商有两个字段:lastVerified(最近一次核到官方页的日期)和 verifyMethod(怎么核的)。把 16 家按日期排开,一条相关性直接跳出来。
7 家标 auto_official 的,最旧的一家是 24 天前;9 家标 monitored(靠人或半自动盯)的,最新的一家已经 13 天,最旧的两家停在 2026-06-05,101 天没动过。换句话说,越靠人的记性,字段就越旧,而且是一直旧下去,不是偶尔忘。
| 供应商 | 最近核验日 | 距今天数 | 核验方式 |
|---|---|---|---|
| 百度 / 腾讯 | 2026-06-05 | 101 | monitored |
| 字节跳动 | 2026-07-03 | 73 | monitored |
| 阶跃星辰 / 小米 | 2026-07-31 | 45 | monitored |
| OpenAI / xAI | 2026-08-01 | 44 | monitored |
| DeepSeek | 2026-08-21 | 24 | auto_official |
| Anthropic / Google / 讯飞 | 2026-09-01 | 13 | monitored / auto_official |
| 智谱 | 2026-09-08 | 6 | auto_official |
| 月之暗面 | 2026-09-13 | 1 | auto_official |
| 阿里 / 百川 / MiniMax | 2026-09-14 | 0 | auto_official |
全库新鲜度,别拿最新那家冒充
站上要展示「最近核验日」,最省事的写法是取 16 个日期的最大值。今天取最大值会得到 2026-09-14,看着全库都是当天的数据,实际上有两家停在三个多月前。这种指标是自己骗自己。
src/lib/models.ts 里的口径改成了下限:lastChecked 取最旧那家的日期,另外单独给出覆盖率。窗口定 14 天(freshnessWindowDays = 14),今天算下来 8 家在窗口内,覆盖率 50%。这个 50% 难看,但它是真的,而且它会推着你去把那两家改成自动抓。
指标设计上这一条可以直接搬走:任何一个「我们的数据有多新」的数字,分母都不该由最好的那个样本决定。
写死的数字骗不过构建闸
光有时间戳还不够,因为正文里的数字不会跟着 JSON 变。国产价格改成每日自动核对之后,models.json 会自己动,而 compares.ts、厂商页里那些「模型名(¥入/¥出)」的叙述句不会动,于是静默变成过期错价。这类问题 tsc 查不出来,评审时肉眼读也读不出来。
package.json 的 prebuild 里挂了两道脚本,构建前必跑:check-prose-prices.mjs 把正文里的「模型名 ¥X/¥Y」逐一对照 models.json 的展示价,漂移就报;check-content-integrity.mjs 更狠,英文定价页里只要出现 ¥1.00 这样的价格字面量就直接非零退出,逼你从 models 派生。
第二道闸还管另一件事。上面说的那次「逐字引用」翻车之后,这个词本身成了被禁的正则之一,信任页只许放链接,不许替厂商概括政策。闸门拦不住所有错,但它把「需要人记得去核」的地方缩到了很小一块。
抄到自己项目里的四步
如果你的项目里也躺着一张第三方价目表或者费率表,下面四步基本就够了,不需要引入任何依赖。
- 每条数据带 lastVerified 和 verifyMethod。没有来源和日期的数字不许录,这比录进来再补要省事得多。
- 聚合指标取下限。展示「最近更新」用最旧那条的日期,另外给一个窗口内覆盖率,别用最大值。
- 把正文里的数字改成从数据源派生。做不到的地方,写一个正则脚本扫价格字面量,挂进 prebuild 或 pre-push,有漂移就让它失败。
- 缓存价、阶梯价这类附属字段单独核。我们 57 个模型里有 40 个带独立的缓存输入价,它和主价是两次独立的过期风险。
还有一个不在数据里的变量
按美元计费的厂商,人民币成本每天都在动,哪怕官方美元价一个字没改。models.json 存的是美元原价,加载时乘 usdToCny 换算,今天这个值是 6.7156,核价 cron 09-14 实采。
所以「我上个月核过价」对美元厂商来说并不等于「这个月的人民币数字还对」。汇率字段也该有自己的时间戳,和价格字段分开看。
常见问题
多久核一次价才够?
看你的容错。我们把窗口定在 14 天,是因为观测到的动价幅度中位数是 60%,两周内漏掉一次调价的后果还能接受。如果你的报价直接对客户,就得更短,而且只能靠自动抓官方页,人工排期到不了这个频率。
自动抓官方页会不会抓错?
会,所以要给抓取失败留退路。我们的抓取脚本在表头缺失、列数不对或列序对不上时放弃本次观测,保留旧价并记一次 stale_fallback,而不是把解析出来的垃圾写进库。抓错一次比旧一天贵得多。
为什么不把价格直接写进文章正文?
写进去就等于承诺每次动价都回来改这一句,而这件事没人做得到。正文里提价格的地方要么从数据源派生,要么指向实时表格,剩下的交给构建闸兜。
文中价格与价格表同源、每日核对。选型前去看一眼最新价。
打开价格表 →