乐于分享
好东西不私藏

AI助手跑了一个月,账单让我后背发凉

AI助手跑了一个月,账单让我后背发凉

AI助手跑了一个月,账单让我后背发凉

前言:前几篇文章我们搭了Agent、加了RAG、还部署上云让全组都能用。大家用得挺开心,直到月底我收到API账单——那一刻我后背发凉。这期聊聊AI助手的成本控制,怎么把账单砍到三分之一。

月底收到账单的那一刻

先把时间线拉回去。

Agent部署上云后,全组8个人天天用,问年假政策、查文档、算数据、写邮件,好不热闹。我还挺得意,觉得自己搞了个生产力工具。

然后月底,邮箱里躺着一封账单提醒。

我打开一看:一个API Key,一个月,花了743块。

当时我的表情大概是这样的:😱

不是吧?我们就是个内部小工具,8个人用,怎么花了这么多?

我开始翻调用日志,发现了几个触目惊心的事实:

事实1:有个同事写了个脚本批量跑分类任务,一晚上调了3000多次API,每次都用GPT-4。

事实2:同一个问题"年假怎么算",不同人问了一遍又一遍,每次都从头调API,缓存命中率为0。

事实3:我的系统提示词有2000多token,每次调用都原封不动发一遍。8个人×每天平均20次对话×30天=4800次调用,光系统提示词就烧了将近1000万token。

事实4:Agent跑复杂任务时,ReAct循环每一步都把完整上下文重新发一遍。一个5步任务,上下文从2000token膨胀到15000token。

这些钱,本来一半都不用花。


先搞清楚:钱花在哪了

要省钱,得先知道钱花在哪。我把账单按维度拆了一下:

花钱大户
占总成本
问题
模型选型太贵
~50%
什么任务都用GPT-4,杀鸡用牛刀
重复调用
~25%
相同问题不缓存,反复付费
Prompt太长
~15%
系统提示词冗余,每次都发
上下文膨胀
~7%
Agent循环累积上下文,越跑越贵
死循环/无效调用
~3%
Agent卡住不停重试

一目了然:模型选型和重复调用加起来占了75%。 这两个问题解决掉,账单直接砍一半。


省钱策略一:模型路由——别什么活都让GPT-4干

这是见效最快的一招。

之前我的Agent不管什么问题都走GPT-4。但你想啊,"公司年假有几天"这种问题,需要GPT-4的推理能力吗?DeepSeek的免费额度都够了。

我做了个简单的任务分类器,根据问题复杂度自动选模型:

import re  def route_model(question: str) -> str:     """根据问题类型选择最具性价比的模型"""      # 简单查询类:用最便宜的模型     simple_patterns = [         r'几[天天]', r'多少', r'是什么', r'怎么算',         r'有没有', r'在哪里', r'什么时候'     ]     if any(re.search(p, question) for p in simple_patterns):         return "deepseek-chat"  # 便宜10倍      # 代码/推理类:需要强模型     if any(kw in question for kw in ['代码', 'bug', '架构', '设计', '分析']):         return "gpt-4"  # 该花钱时花钱      # 默认:中等模型     return "gpt-4o-mini"  # 性价比之王 

就这十几行代码。效果怎么样?

我把一个月的调用日志拿来分析: - 简单查询占62% → 从GPT-4换到deepseek-chat,单次成本降了90% - 中等任务占28% → 换到gpt-4o-mini,降了80% - 复杂任务占10% → 继续用GPT-4,该花还得花

一个月后看账单:743块 → 312块。 砍了58%,代码改动不超过20行。

有人可能会问:便宜模型效果行不行?

我做了个A/B测试。同样的100个问题,GPT-4和deepseek-chat各答一遍,让两个人盲评。结果:GPT-4准确率94%,deepseek-chat准确率91%。差3个百分点,但成本差了10倍。

对于内部工具来说,3个百分点的差距完全可以接受。


省钱策略二:语义缓存——重复问题别重复付费

第二个大头是重复调用。

我翻了日志,发现"年假怎么算"这个问题,一个月被问了47次。每次都调API,每次都花钱。这不纯冤大头吗?

最简单的办法:加个缓存。 相同问题直接返回上次的答案,不调API。

import hashlib import json from datetime import datetime, timedelta  # 简单的内存缓存(生产环境用Redis) _cache = {}  def cached_chat(question: str, model: str = "deepseek-chat") -> str:     """带缓存的对话接口"""      # 生成缓存key     cache_key = hashlib.md5(         f"{model}:{question}".encode()     ).hexdigest()      # 检查缓存是否命中     if cache_key in _cache:         entry = _cache[cache_key]         if datetime.now() - entry['time'] < timedelta(hours=24):             return entry['answer']  # 命中!零成本      # 缓存未命中,调API     answer = call_llm(question, model)      # 存入缓存     _cache[cache_key] = {         'answer': answer,         'time': datetime.now()     }      return answer 

但精确匹配有个问题:用户问"年假怎么算"和"年假有几天",字面不同但意思一样。精确匹配命中不了。

所以更好的方案是语义缓存——用Embedding算相似度,意思相近就算命中:

from sklearn.metrics.pairwise import cosine_similarity import numpy as np  _semantic_cache = []  # [(embedding, question, answer)]  def semantic_cached_chat(question: str, threshold: float = 0.92) -> str:     """语义缓存:相似问题复用答案"""      # 获取问题的embedding     q_embed = get_embedding(question)      # 在缓存里找最相似的     for embed, cached_q, cached_a in _semantic_cache:         similarity = cosine_similarity([q_embed], [embed])[0][0]         if similarity > threshold:             return cached_a  # 语义命中!      # 未命中,调API     answer = call_llm(question)      # 存入缓存     _semantic_cache.append((q_embed, question, answer))      return answer 

上了语义缓存后,命中率大概在30%-40%。也就是说,三分之一的问题直接从缓存返回,API调用量砍了三分之一。


省钱策略三:Prompt瘦身——每个token都是钱

第三个问题是系统提示词太长。

我之前写的系统提示词是这样的:

你是一个专业的AI助手,负责回答公司内部员工的问题。 你需要根据知识库中的内容,给出准确、详细、有帮助的回答。 如果知识库中没有相关信息,请如实告知用户你不清楚。 回答时请注意语气友好、专业。 如果用户的问题不明确,可以追问。 请尽量用简洁的语言回答,避免冗长。 回答中使用中文。 如果涉及具体政策,请引用原文。 ……(后面还有300字) 

一算,287个token。 每次调用都发一遍,4800次调用就是138万token,纯浪费。

精简后:

根据知识库回答。不确定就说不知道。简洁中文。 

42个token。 效果几乎没区别,token省了85%。

几个Prompt瘦身原则:

  1. 删废话
    :"你需要尽可能详细和全面地回答" → 删掉,模型默认就会详细回答
  2. 合并同类项
    :多个"请用中文""请注意语气"合并成"简洁中文"
  3. 用结构化格式
    :用列表代替段落,信息密度更高
  4. 控制输出长度
    :加max_tokens参数,别让模型写小作文
# 控制输出长度 response = client.chat.completions.create(     model="deepseek-chat",     messages=[{"role": "user", "content": question}],     max_tokens=500  # 大多数回答500token够了 )  # 强制结构化输出,消灭废话 response = client.chat.completions.create(     model="deepseek-chat",     messages=[{"role": "user", "content": f"提取关键信息,只返回JSON:\n{text}"}],     response_format={"type": "json_object"} ) 

加了max_tokens和结构化输出后,输出token平均减少了30%。


省钱策略四:给Agent加个"钱包"——Token预算控制

最后一个是Agent特有的问题:ReAct循环的上下文膨胀。

Agent跑5步任务,每一步都把之前的完整上下文重新发一遍。第5步时,上下文可能从2000token膨胀到15000token。更可怕的是,如果Agent卡住进入死循环,它会一直跑下去,直到你发现——或者直到账单爆炸。

国外有个真实案例:两个LangChain Agent跑了11天没人管,最后账单47000美元。

所以必须给Agent加个"钱包"——token预算控制:

class BudgetAgent:     def __init__(self, max_tokens=50000, max_steps=15):         self.token_used = 0         self.max_tokens = max_tokens  # 单次任务token上限         self.max_steps = max_steps    # 最大循环次数      def run(self, question: str) -> str:         """带预算控制的Agent执行"""         messages = [{"role": "user", "content": question}]          for step in range(self.max_steps):             # 检查预算             if self.token_used >= self.max_tokens:                 return f"预算用完了(已用{self.token_used}token),当前进度:{step}步"              # 调用LLM             response = call_llm(messages)             self.token_used += count_tokens(response)              # 判断是否结束             if "FINAL:" in response:                 return response.replace("FINAL:", "").strip()              # 执行工具,把结果加入上下文             tool_result = execute_tool(response)             messages.append({"role": "assistant", "content": response})             messages.append({"role": "user", "content": tool_result})              # 关键:上下文超长时做摘要压缩             if len(str(messages)) > 8000:                 messages = compress_context(messages)          return "达到最大步数,强制结束" 

两个关键设计:

  1. max_steps
    :限制最多循环15次。之前有个任务Agent跑了200多步,全是无效循环。
  2. token预算
    :单次任务最多用50000token。超了直接返回当前结果,不再烧钱。
  3. 上下文压缩
    :当上下文超过8000字符时,把早期对话摘要成一句话,防止越跑越贵。

四招齐上,效果如何?

我把四个策略按优先级排了个序:

策略
改动量
省钱效果
优先级
模型路由
~20行代码
砍50%+
⭐⭐⭐ 立刻做
语义缓存
~30行代码
再砍25%
⭐⭐ 尽快做
Prompt瘦身
改几行文字
再砍10%
⭐⭐ 顺手做
Token预算
~40行代码
防止爆炸
⭐⭐ 必须做

四个策略叠加后,第二个月的账单:

743块 → 218块。 砍了71%。

而且第三个策略(Token预算控制)不只是省钱——它还防止了"Agent跑飞了"这种灾难性事件。一个不受控的Agent一夜之间烧掉几千块,比日常浪费可怕多了。


我踩过的3个坑

坑1:模型路由切得太狠,复杂任务翻车

一开始我把分类规则写得太简单,结果有个需要多步推理的任务也被路由到了deepseek-chat,答案完全不对。

解法:路由规则要留容错。简单模型回答后做个置信度检查,置信度低就自动升级到强模型重试。别一锤子定音。

坑2:缓存过期时间设太长,信息更新了还返回旧答案

公司政策更新了,但缓存里还是旧答案。同事按旧答案操作,差点出事。

解法:知识库类内容缓存设24小时过期,实时信息(天气、股价)不缓存或只缓存5分钟。缓存一定要带TTL。

坑3:上下文压缩太激进,丢了关键信息

为了省token,我把压缩阈值设得太低,结果Agent把任务的关键约束给压没了,输出的方案完全跑偏。

解法:压缩时保留最近3轮对话原文,只压缩更早的内容。关键约束(数字、条件、限制)用正则提取出来单独保留。


成本控制的核心心法

回顾一下,AI Agent的成本控制核心就三句话:

  1. 用对的模型
    ——简单任务用便宜模型,复杂任务才上贵的
  2. 别重复付费
    ——相同或相似的问题,缓存一次就够了
  3. 设好上限
    ——token预算和步数限制,防止Agent跑飞烧钱

这三条做到,一个8人团队的AI助手,月费控制在200-300块完全没问题。

如果规模更大,还可以进一步优化:批量推理(非实时任务攒一批一起调,能省50%)、Prefix缓存(系统提示词复用,Anthropic能省90%输入token)、多供应商比价。但这些是进阶玩法,上面四招够用很久了。


完整代码和成本监控脚本已整理好,公众号回复「AgentCost」获取。


你的AI助手一个月花多少钱?有没有被账单吓到过?评论区聊聊 👇


下期预告:AI Agent监控与可观测性——怎么知道你的Agent到底在干什么、干得好不好、什么时候该介入

💡 关注【AI搞事星球】

每周三篇AI实战干货,从Agent到多模态,陪你一起用AI搞大事 💪

点击上方蓝字关注,不错过下期实战案例 →

🔍 相关搜索:AI Agent · MCP协议 · Function Calling · AI工具实战 · 大模型

更多内容请在公众号对话框回复关键词获取资料