从一张超支的账单说起
「回声」的私信回复功能上线三个月,使用量涨了两倍,账单涨了七倍。
团队第一反应是“用量涨了,正常”。但把账拆开之后,问题不在用量:单次请求的平均输入 token 从 900 涨到 2400,输出长度从 120 涨到 310,重试率从 2% 涨到 9%,缓存命中率从 31% 掉到 6%。四个数字各自看起来都能解释,合在一起才是七倍。
这不是模型变贵了,是架构把成本放大了。
卷一《模型接入与能力边界》讨论过预算、缓存与降级。它回答的是“边界在哪”:一次请求最多花多少、失败时怎么办、什么东西可以复用。但边界之内还有巨大的空间:同样的预算,可以做得很省,也可以做得很浪费。
卷二讨论的就是这个空间。
为什么性能与成本必须一起看
单独优化成本很容易伤害性能,单独优化性能很容易推高成本。
一个典型例子是批处理。把多个请求合成一批,吞吐上去了,单位成本下来了,但排队时间变长,首字延迟上升。对于聊天场景,首字慢半秒用户就能感知;对于离线摘要,晚十秒没人注意。同一项优化,在两个能力上的结论相反。
另一个例子是缓存。命中率提高会降低平均成本,但缓存未命中的请求仍然要完整生成,峰值时的容量并不会因为平均成本下降而自动变小。只看平均成本做容量规划,会在流量尖峰时排队雪崩。
所以卷二不按“性能篇”“成本篇”分开写,而是每一章都同时问三个问题:快了多少、省了多少、质量与体验付出了什么。
两卷的分工
| 问题 | 卷一 | 卷二 |
|---|---|---|
| 这项能力承诺什么 | 六面契约 | 契约里的延迟与成本如何达标 |
| 请求怎么到模型 | 网关、路由 | 队列、并发、公平调度 |
| 上下文怎么组织 | 提示版本与预算 | 前缀复用、压缩与裁剪 |
| 输出怎么返回 | 流式协议与校验 | 长度控制与提前终止 |
| 失败怎么办 | 降级等级 | 降级链的成本约束 |
| 花了多少钱 | 计量字段 | 归因、对账与优化实验 |
| 怎么验收 | 契约评审 | 质量-成本-延迟联合门禁 |
两卷共用同一个案例与同一个实验工程 ai-lab。卷一建立的能力契约、错误模型、网关流水线与观测字段,是卷二所有实验的输入。卷二不重新定义这些部件,只在它们上面做测量与优化。
一次真实的优化顺序
「回声」把那七倍账单压回三倍,用了四步,顺序值得记录:
- 先修计量。把成本按能力、模型、提示版本拆开,发现 62% 的增量来自两个提示版本,而不是全站。
- 再修缓存键。缓存键漏了提示版本,改版后旧结果继续命中,命中率虚高但质量下降;修好键之后命中率从 6% 回到 24%。
- 然后压上下文。检索片段从固定 10 条改成按相关性 3–5 条,输入 token 降了 41%,人工抽检通过率没有下降。
- 最后才换模型。前两步做完,经济档模型在摘要能力上已经够用,成本再降一档。
如果顺序反过来,先换小模型,质量会先掉,团队会把问题归因于“小模型不行”,而真正的浪费还在。
这个顺序也是卷二的组织逻辑:先度量,再归因,再优化,最后才是替换。
实验工程怎么延续
卷一的 ai-lab 是一个零第三方依赖的 TypeScript 工程,包含契约、网关、路由、流式、提示、结构化输出、降级、安全与观测模块。
卷二在同一个工程里新增 src/perf/:
src/perf/
metrics.ts 延迟、吞吐与 goodput 的度量
scheduling.ts 队列、优先级与公平调度
context.ts 上下文成本与前缀复用
output.ts 输出长度与提前终止
cache-econ.ts 结果缓存的经济性
compress.ts 量化、蒸馏与级联的成本模型
batch.ts 离线批处理与分片
capacity.ts 容量与余量
cost.ts 成本归因与对账
experiment.ts 对照实验与结果解读
quality.ts 质量-成本-延迟联合门禁
stability.ts 限流、分流与成本事故
baseline.ts 卷二收口基线
每个模块都配确定性测试。卷二里的所有数字,都来自这些测试或脚本的真实运行,并标注为“示例样本上的运行结果”。没有生产环境的数据,也不假装有。
卷二不承诺什么
- 不承诺具体的模型价格。价格变动频繁,书中只使用统一的“微元”口径与相对倍数。
- 不承诺某个框架或推理引擎的调优参数。这些参数与硬件、版本强相关,写死会很快过期。
- 不承诺生产级压测结论。实验工程是单进程、确定性负载,用来建立判断口径,不替代真实容量测试。
- 不承诺“最优解”。每一章给出的是判据与取舍,不是一套放之四海皆准的配置。
- 不承诺跨供应商的可比性。不同供应商的计费单位与批处理策略不同,书中只做同口径下的相对比较。
- 不承诺一次优化长期有效。模型、价格与流量都会变,基线需要按季度重跑。
这些“不承诺”不是免责声明,而是方法的一部分。知道边界在哪,才知道什么时候该重新测量。
写给谁
- 已经按卷一接入了模型,开始被账单或延迟困扰的团队。
- 正在做容量规划,需要把业务量翻译成 token 与成本的架构师。
- 负责平台与网关,需要在多租户之间分配额度与优先级的工程师。
- 准备做模型替换或压缩,需要一套验收方法的技术负责人。
如果还没有建立能力契约与观测字段,建议先读卷一。卷二的所有度量都依赖那些字段;字段缺失时,优化会退化成猜测。
怎么读
三条路径:
- 排障路径:1.1 → 1.2 → 9.1 → 9.2 → 12.3,先学会测量与归因,再看事故。
- 优化路径:3.1 → 3.2 → 4.3 → 5.1 → 6.3 → 10.2,按上下文、输出、缓存、替代的顺序做实验。
- 容量路径:2.2 → 8.1 → 8.2 → 8.3 → 11.3,从并发与背压走到配额与灰度。
三条路径都建议先读第 1 章。度量口径不统一,后面的对照实验没有意义。
成本失控的五个放大器
把「回声」那次超支拆开,能找到五个反复出现的放大器。它们单独看都不致命,叠在一起就会让账单脱离业务增长。
放大器一:重复的上下文。 每次请求都把完整历史、全部检索片段、工具结果原样带上。用户只多问了一句,输入却翻了一倍。上下文没有预算,就没有上限。
放大器二:失效的缓存。 缓存键漏了提示版本,改版后旧结果继续命中。表面命中率很高,实际返回的是过期内容,用户重试,成本反而上升。
放大器三:无界的重试。 超时重试没有和总预算绑定,一次请求最多打四次模型。上游越慢,重试越多,越重试越慢。
放大器四:无上限的输出。 没有设置输出上限,模型偶尔生成长文。长度分布的长尾贡献了不成比例的成本,平均值却看不出来。
放大器五:按平均值做容量。 平均 QPS 不高,但峰值是平均值的四倍。按平均值配置并发,尖峰时排队,排队导致超时,超时触发重试,回到放大器三。
五个放大器有共同的根源:系统在不知道的地方重复花钱。卷二的大部分章节,本质上是在给这五个地方装上刻度。
一个能力的三份预算
「回声」的私信回复能力,最终被写成三份预算:
| 预算 | 数值 | 超了会怎样 |
|---|---|---|
| 延迟 | 首字 800ms,完整 4s | 用户感知卡顿,转人工 |
| token | 输入 1800,输出 400 | 截断或拒绝,不静默超发 |
| 金额 | 单次 1200 微元,租户月度上限另计 | 触发告警,降级到经济档 |
三份预算互相约束。输入预算变大,金额预算就会被挤压;输出预算变大,延迟预算就会被挤压。只调一份,另外两份必然失衡。
卷二的每一章,最终都会落到这三份预算的某一项上。调度影响延迟,上下文影响 token 与金额,输出同时影响三者,缓存与替代主要影响金额。
一个反例:把所有请求都换成小模型
有一类优化非常诱人:既然小模型便宜十倍,为什么不全部换掉?
「回声」试过一次,结论是:成本降了 58%,人工抽检通过率从 94% 掉到 81%,客服转人工率上升 12%。省下的模型费用,被人力成本抵消还有余。
失败的原因不是小模型不好,而是替换前没有做分能力验收:
- 摘要与重排任务,小模型足够,换掉没有损失。
- 私信回复涉及语气与关系,小模型明显变差。
- 审核分类需要稳定边界,小模型的误杀率更高。
正确的做法是第 6 章与第 11 章的组合:先按能力建立验收集,再逐个能力替换,最后用联合门禁卡住回退。
这个反例也解释了卷二为什么把“质量”放在第 11 章而不是最后附赠:优化如果不同时验收质量,省下的钱会以另一种形式花出去。
度量之前先问的三个问题
在打开任何监控面板之前,先回答三个问题:
- 这次请求的“完成”怎么定义? 首字出现、最后一段生成、还是用户点击发送?定义不同,延迟数字完全不同。
- 统计窗口是什么? 一分钟、一小时还是一天?突发流量在长窗口里会被平均掉。
- 分母是什么? 全部请求、成功请求,还是满足延迟目标的请求?用成功请求做分母,会把超时请求从报表里抹掉。
三个问题没有统一答案,但必须有明确答案。第 1 章会给出可执行的口径,这里先建立习惯:任何性能数字,都要能说清定义、窗口与分母。
本书的数字从哪里来
卷二里的每一组数字,只有三个来源:
| 来源 | 标注方式 | 用途 |
|---|---|---|
| 确定性模拟 | “示例样本上的运行结果” | 建立口径、对照方法 |
| 公式推导 | 直接给算式与代入值 | 容量与成本估算 |
| 公开价格口径 | 统一换算成微元与倍数 | 说明相对关系 |
没有第四种来源。没有生产环境的数据,也没有“业内通常”的模糊数字。
这样做会牺牲一点真实感,换来两件事:读者可以在自己的机器上复现每一个结论;书中的数字不会因为某家供应商调价而整段失效。
如果你在自己的系统里跑出不同的数值,那很正常。方法不变,数值本来就该不同;真正需要一致的是判据:什么算快、什么算省、什么算质量回退。
一张图看懂十三章的关系
度量 1 延迟、吞吐、goodput
│
杠杆 2 调度 ── 3 上下文 ── 4 输出
│ │
│ └── 5 缓存 ── 6 压缩替代 ── 7 离线批处理
│
规划 8 容量
│
归因 9 成本归因
│
验证 10 对照实验 ── 11 质量联合门禁
│
治理 12 规模化稳定性
│
收口 13 成本基线
读图的方式:
- 第 1 章是入口。没有度量,后面的箭头都不成立。
- 第 2–7 章是可动的杠杆。每一章都能独立产生一次成本或延迟变化。
- 第 8–9 章把杠杆的结果翻译成容量与账单。
- 第 10–11 章负责验证:改动是否有效,质量是否回退。
- 第 12–13 章把验证过的结论固化成稳定性方案与基线。
箭头不是严格的前置依赖。第 5 章可以在第 3 章之前读,第 12 章可以单独看。但跨越第 9 章直接做优化,通常会缺少归因依据。
三个常见误解
误解一:成本优化就是换便宜模型。 换模型是最后一招,不是第一招。先修上下文、缓存与输出,往往能省下更多,而且质量损失更小。
误解二:性能优化就是加机器。 加机器能解决容量,解决不了排队策略、重试放大与长尾输出。资源翻倍,账单也会翻倍。
误解三:平均值达标就行。 用户感知的是自己那一次请求。平均值达标、p95 超标的产品,会被少数极慢的请求持续投诉。
三个误解的共同点是:把单点手段当成系统方法。卷二要建立的,正是系统方法。
一个可以立刻做的检查
读完这篇序,不必等读完正文,可以先做一件事:打开自己系统的成本报表,试着回答三个问题。
第一,过去一周最贵的三个能力是什么?如果答不上来,说明归因维度不够,第 9 章会给出字段设计。
第二,这三个能力的单次成本是多少?如果只有月度总额,说明计量粒度太粗,第 1 章与第 9 章都需要补。
第三,单次成本里有多少来自重试与缓存未命中?如果从来没算过,第 9.1 节会给出一个可落地的拆法。
三个问题都答不上来,正说明卷二有存在的必要。它们也是后面所有优化的起点:不知道钱花在哪,任何“优化”都只是换一种花法。
最后的提醒
成本问题通常不是“模型太贵”,而是“系统在不知道的地方重复花钱”:重复的上下文、失效的缓存、无界的重试、没有上限的输出、按平均值做的容量规划。
卷二的目标不是把账单压到最低,而是让每一分钱都能被解释:花在哪个能力、哪个租户、哪段上下文、哪次重试上。能解释,才能优化;能优化,才敢规模化。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。