攻
AI 网关通常有这样的功能:基于 token 消耗量来做限额操作。有些地方叫做 ai-rate-limiting,有些地方叫做 ai-quota。无论名字为何,原理同出一辙,都是基于推理请求结束时返回的 token usage 信息。
那么绕过限制的方式就显而易见了,只需找找有没有能让网关看不到推理请求结束时的 usage 信息即可。有些时候,用户在无意中就能绕过这些限制。比如在 OpenAI chat 接口里,默认 streaming 的时候就不会返回 usage,除非用户请求时指定了 include_usage:https://platform.openai.com/docs/api-reference/chat/create#ch...。
假设模型供应商总是会提供 token usage,抑或网关在处理用户请求时做了点 hack 额外加上 include_usage,保证了 token usage 总是在推理请求结束时存在,那该怎么办了?方法还是有的,让推理请求提前中断即可。我们可以插入一段 prompt,指定在返回结果结束时输出一个 stop word,然后再执行一个耗时的任务。当客户端收到这个 stop word 后,就可以安心地把连接中断掉。只要请求是提前中断的,网关就不会继续保持和上游的请求,自然收不到上游最后发过来的 usage 了。当然有些配置项可以修改这种行为,比如 Nginx 的 proxy_ignore_client_abort。但如果这么做的话,万一是正常的客户端想要提前终止推理,结果因为网关还是继续和上游通信而导致被多算钱就麻烦了。这种小伎俩可以骗过中间件,不过推理引擎侧还是能知道 prefill 时收到多少 input token,decode 时发出了多少 output token。所以最后给到来的账单还是正常的。
守
上述各种攻击手段,本质上揭示了当前 AI 网关在流式传输场景下的架构痛点:计费的异步性。在传统的 Request-Response 模型中,网关可以轻松拦截并统计流量;但在 LLM 的流式交互中,Token 的消耗是一个随着时间推移动态累加的过程,而精准的 token usage 报告往往滞后于请求的结束。只要网关依赖于“事后”的上报数据,客户端就有机会利用断连等手段制造“计费黑洞”。
那么有什么可靠的方式,能够不依赖推理请求中的 token usage 信息,自己在通信过程中算出实际的 token 用量?
最简单粗暴的方法,就是将字节数乘上一个 magic number 系数,作为找不到 token usage 时的 fallback。如果能在准确性上睁一只眼闭一只眼,这倒是开销最小的方案。
官方的做法,是调用模型提供商自己的 count token 接口。对于开源的推理引擎像是 vllm 或 TensorRT-LLM,也有对应的 tokenize 接口。只是要让网关在每次请求时额外发起多次 HTTP 调用,代价有点高,尤其在流式处理响应的时候。
一些编码库提供了本地 tokenize 的能力,如:
- huggingface/tokenizers
- openai/tiktoken 和它的 Go 移植:pkoukk/tiktoken-go
但是这些 tokenizer 在工作时需要知道模型的 tokenizer 配置,而模型提供商大概率不会公布这些数据。不过市面上也有这些私有模型的开源版本,比如 Gemma 之于 Gemini。不知道这些开源版本的 tokenizer 配置和私有的差别有多少,基于它们的 tokenizer 配置和官方的 count token 接口返回结果是否近似。
如果是自己部署的模型,那么理论上有了 tokenizer 配置就能自己本地做 tokenize,无需依赖一个远程的 tokenizer 服务。
假设 token usage 不是由本地提供,而是依赖远程的返回结果,出于谨慎起见,最好在基于 token 限额的同时加上基于请求数(或字节数,有的话更好)的限额,这样一旦远端无法返回 token usage,不至于出现完全不设防的情况。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。