靠请求数控制不住 LLM 的成本
一句话总结
普通 API 的单次请求成本大致相同,但一个 LLM 请求可能是 100 个 token,也可能是 100,000 个 token。因此,gateway 应统计 token,而不是请求。
为什么需要了解这一点
假设 rate limit 设置为每分钟 60 个请求。某个 tenant 在每次请求中放入 200,000 个 token 的 context,请求数仍在限制内,成本和 GPU 占用却是其他 tenant 的数百倍。而且,这些请求会独占 KV cache,抢占其他请求的资源。
请求数限制完全无法阻止这种情况。真正需要的是以 token 为单位的核算。
工作原理
gateway 需要完成五件事。
第一,认证与 tenant 识别。把 API key 映射到 tenant。
第二,token 核算。分别统计并记录请求的 input token 与响应的 output token。采用 streaming 时,要到 stream 结束才能确定 output token,因此中途断开也必须计算截至当时已经使用的 token。
第三,两个层级的限制。同时设置 RPM(每分钟请求数)和 TPM(每分钟 token 数)。RPM 防止滥用,TPM 控制成本与资源。token bucket 很适合 TPM——既允许平时安静的 tenant 突发请求,又能维持长期平均值。
第四,429 与 Retry-After。拒绝时要告知何时可以重试。缺少它,各 client 会自行重试,形成请求浪潮。
第五,计算成本。不同 model 的 input 与 output 单价不同,要把 token 数乘以单价并累计。通常 output token 比 input 更贵,所以必须分开统计。
除此之外还要设置预算。为每个 tenant 设置每日或每月上限,超过后阻止请求,或降级到更便宜的 model。后一种称为 fallback,很多时候比让 service 完全停止更好。
按什么进行限制
仅设置 RPM(每分钟请求数)对 LLM service 几乎没有作用。因为一个请求可能只有 100 个 token,也可能有 10 万个。因此要同时使用两个维度。
| 维度 | 防止什么 | 单位 |
|---|---|---|
| RPM | 请求洪峰、bot | 每分钟请求数 |
| TPM | GPU 时间消耗 | 每分钟 token 数(输入+输出) |
| 并发数 | queue 爆炸、内存耗尽 | 进行中的请求数 |
三者中,并发数最准确地反映 GPU。vLLM 采用 batch 处理,因此 并发请求数直接对应 KV cache 用量,超过后就会 OOM 或发生抢占(preemption)。 RPM 与 TPM 用于成本核算,并发数用于稳定性。
使用 token bucket 实现,可以在允许 burst 的同时维持平均值。
용량 = 60,000 토큰, 채우는 속도 = 1,000 토큰/초 (= 60k TPM)
요청이 오면 max_tokens 만큼 버킷에서 뺀다(예약)
완료되면 실제 사용량과의 차이를 돌려준다(정산)
버킷이 비면 429 + Retry-After: <채워질 때까지의 초>
必须发送 Retry-After。否则 client 会立即重试,让情况进一步恶化。
正确返回 429 也是设计的一部分
拒绝时提供哪些信息,会决定 client code 的质量。
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit-Tokens: 60000
X-RateLimit-Remaining-Tokens: 0
X-RateLimit-Reset-Tokens: 12s
还要在正文中写明触发了哪一种限制。RPM、TPM 与预算对应的 client 行为不同—— 前两种等待即可,超过预算则无论等多久也不会恢复。
Cache 能带来最大的节省
cache 比限制更有效,共有三层。
- 完全匹配 cache——prompt 与 parameter 相同时,返回已保存的答案。 在 FAQ 类型流量中,命中率有时会超过 30%。temperature 不为 0 时, 相同输入是否应给出相同答案,需要由产品决定。
- 语义 cache——通过 embedding 查找并复用相似问题。命中率高,但存在 错误复用的风险,所以 threshold 要设得保守。
- prefix cache——复用 system prompt 等前部相同请求的 KV cache。
vLLM 的
enable_prefix_caching就是这种方式,它不影响准确性,却能大幅降低 TTFT。 应最先启用。
实际工作中的表现
无法预先知道 token 数,是这种设计的难点。input token 可以在收到请求时统计,output token 却要等生成结束后才能知道。因此,实际系统会以 max_tokens 作为上限先行预留,结束后再按实际用量结算。如果不预留,同时到达的请求会各自判断仍在预算内并全部通过,随后一起超出预算。
成本暴涨事故通常发生在 retry loop 中。client 每次 timeout 都重新请求,而 server 仍在继续生成时,一名用户可能让同一个答案生成五次。在 gateway 接收 idempotency key,合并进行中的相同请求,就能消除这种浪费。
下个练习将做什么
在前面创建的 mock server 前部署 gateway,加入 API key 认证、RPM 限制、token 核算、TPM token bucket、Retry-After、按 model 计算成本,以及 tenant 超出预算时的阻断。