跳到主内容
算盘
避坑2026-09-14 发布 · 约 6 分钟读完

价格表不会报错,它只会静默变错

代码里写死一个模型单价,它不会抛异常,不会让测试变红,只会在某个星期二悄悄变成假话。等你发现的时候,报价单已经发出去了。

这篇讲我们怎么量化这件事,以及怎么把「这个数字多久没核过」变成一个能让构建失败的字段。我们不是从方法论倒推出来的,是先被咬过:信任页两次上线过厂商政策的「逐字引用」,而链接过去的原文里根本没有那句话。数字全部来自 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-05101monitored
字节跳动2026-07-0373monitored
阶跃星辰 / 小米2026-07-3145monitored
OpenAI / xAI2026-08-0144monitored
DeepSeek2026-08-2124auto_official
Anthropic / Google / 讯飞2026-09-0113monitored / auto_official
智谱2026-09-086auto_official
月之暗面2026-09-131auto_official
阿里 / 百川 / MiniMax2026-09-140auto_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,而不是把解析出来的垃圾写进库。抓错一次比旧一天贵得多。

为什么不把价格直接写进文章正文?

写进去就等于承诺每次动价都回来改这一句,而这件事没人做得到。正文里提价格的地方要么从数据源派生,要么指向实时表格,剩下的交给构建闸兜。

文中价格与价格表同源、每日核对。选型前去看一眼最新价。

打开价格表 →