通过详细测量定位 token 消耗来源(单次 hello 竟消耗 3113 prompt tokens),用 prompt 压缩策略在不换模型的前提下降低 API 成本。
几周前,我在个人作品集网站上添加了一个名为 Jarvis 的小型 AI 语音助手。
整个方案有意做得很简单:Groq 负责文本生成,浏览器的 Web Speech API 负责语音转文字和文字转语音。访问者可以向 Jarvis 询问我的工作、项目、经历或服务,也可以帮助他们预约通话。
但有一个问题。
仅仅进行了大约五分钟的轻度测试,我已经耗尽了 Groq 免费额度的用量。
我的第一个想法大概显而易见:
我需要换一家提供商或购买付费 API 套餐吗?
但在做出任何改变之前,我决定先弄清楚 Token 实际上消耗在哪里。
事后证明,这个决定是正确的。
一条"你好"消耗了 3,113 个 Prompt Token
我首先想要的是一个真实的测量数据。
不仅仅依赖 Token 计数的估算,我发起了一个带有最小输出限制的真实 Groq 请求,并检查了 API 返回的 Token 使用量。
结果让我吃了一惊:
这不是一个复杂的问题。 不是涉及多个项目的请求。 那时问题已经相当明显了。
我的模型不一定贵。 我的请求架构才是问题所在。
我发现了两个主要问题。
每次请求都在发送我的整个作品集
我的系统提示词包含了几乎所有关于我的信息:
services project descriptions testimonials FAQs statistics tech stack experience behavioral instructions
而且无论访问者询问什么,所有这些内容都会在每次请求时发送过去。
有人说"嗨",实际上是在向模型发送数千个 Token 的作品集上下文。
有人询问价格,收到的是同样的上下文。 有人询问某个项目,同时收到了所有其他项目的信息。
它能工作,但极其浪费。
我的知识库有重复内容
在审计知识对象时,我发现了更简单的问题。
我的技术栈在两个不同字段中重复出现了两次。
系统的两个部分是分开构建的,两部分都包含了相同的信息,而我浑然不觉。
所以我不只是在发送过多的上下文。 我实际上是在为重复发送部分信息付 Token 费用。
更大的认知:不是每条消息都需要 AI
这最终成为了最重要的改变。
我最初把 Jarvis 当作这样处理的:
User message → Groq → response
每条消息都经过模型。
但为什么 LLM 需要为以下内容生成答案:
那些答案本来就是已知的。
所以我在 Groq 之前添加了一个轻量级的本地意图识别层。
现在的流程变成了:
User message → local intent check → Groq only when necessary
简单的正则和关键词匹配在本地处理可预见的请求。
Greetings → predefined greeting "Who are you?" → fixed identity response Pricing questions → predefined pricing-policy response Known FAQs → existing FAQ answer Requests for private information → fixed refusal Obvious prompt-injection attempts → fixed security response
如果意图匹配这些情况之一,Groq 永远不会被调用。
该交互的 API 用量:零。
这也让助手感觉更快了,因为当应用已经知道答案时,没有理由等待模型推理。
然后我精简了系统提示词
下一个目标是提示词本身。
我没有在每次请求时发送整个作品集,而是将提示词分成了两层。
第一层:一个小型基础提示词
基础提示词只包含 Jarvis 始终需要的信息:
identity behavior privacy rules pricing policy important boundaries response style
清理之后,基础上下文降到了大约 652 个 Token,而与我之前测量的 3,113 个 Token 的请求相比。
第二层:只检索相关的作品集知识
我的作品集其余部分变成了选择性检索的上下文。
我将知识分成了以下部分:
services projects testimonials faqs contact stats
在调用 Groq 之前,应用会检查用户的消息,并确定哪些部分实际相关。
"你做过哪些 AI 自动化工作?"
Jarvis 可能收到相关的服务和 AI 项目信息。
它不需要每条评价、联系方式、常见问题和无关项目。
通常只有 2–4 个相关部分会被添加到提示词中。
这不需要复杂的向量数据库或完整的 RAG 流程。
对于这种规模的个人作品集,简单的检索完全够用。
而且更重要的是,它很便宜。
我还发现了一个重试问题
另一个不易察觉的不必要请求来源是:重试。
SDK 通常会自动重试某些失败的请求,包括限流错误。
这通常是有帮助的。
但当你已经触及配额限制时,自动重试会让情况更糟。
一个请求因 429 失败。 再次发生重试。 从应用的角度看,访问者只发送了一条消息。 从 API 的角度看,可能已经发生了多次调用。
所以我收紧了重试行为,并在语音代理周围添加了请求级保护。
最终流程包括:
request deduplication cooldown protection aborting superseded requests controlled retries request tracking
一条访问者消息最多应该导致一次有意的 Groq 生成请求。
改变之后,差异显著。
测试"hello"请求:
压缩后的基础上下文:
对于仍然需要 Groq 的请求,选择性知识检索将提示词 Token 用量减少了大约:
70–72%,在我的测试中。
现在几种常见交互完全不消耗 Groq 请求:
greetings identity questions pricing-policy questions known FAQs security refusals obvious prompt-injection attempts duplicate submissions
有趣的是,我没有换模型。 我没有换 AI 提供商。 我也没有通过简单地购买更大配额来解决问题。
我改变的是应用使用模型的方式。
当一个 AI 应用开始消耗 API 配额时,很容易认为模型或提供商是问题所在。
但在更换提供商之前,我认为值得审视一下请求路径本身。
这个请求真的需要 LLM 吗? 我发送的上下文是模型不需要的吗? 我在重复发送相同的信息吗? 这部分响应能否是确定性的? 我能否只检索与这个问题相关的知识? 一次用户操作是否可能意外触发多个 API 请求?
在我的案例中,这些问题比更换模型重要得多。
最大的优化不是找到更便宜的 LLM。 而是在调用 LLM 时少调用,并且少发送不必要的消息。
我是 Asad Ibrahim,一名全栈开发者和 AI 集成工程师。我为企业构建 AI 驱动的 Web 应用、自动化系统、语音助手和生产级集成。
我也会记录我实际构建的项目中的实验。
你可以在 asadibrahim.com 查看我的更多作品、AI 项目和工程案例研究。