1

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,不至于出现完全不设防的情况。


spacewander
5.6k 声望1.5k 粉丝

make building blocks that people can understand and use easily, and people will work together to solve the very largest problems.