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。
这些钱,本来一半都不用花。
先搞清楚:钱花在哪了
要省钱,得先知道钱花在哪。我把账单按维度拆了一下:
| 模型选型太贵 | ||
| 重复调用 | ||
| Prompt太长 | ||
| 上下文膨胀 | ||
| 死循环/无效调用 |
一目了然:模型选型和重复调用加起来占了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瘦身原则:
- 删废话
:"你需要尽可能详细和全面地回答" → 删掉,模型默认就会详细回答 - 合并同类项
:多个"请用中文""请注意语气"合并成"简洁中文" - 用结构化格式
:用列表代替段落,信息密度更高 - 控制输出长度
:加 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 "达到最大步数,强制结束" 两个关键设计:
- max_steps
:限制最多循环15次。之前有个任务Agent跑了200多步,全是无效循环。 - token预算
:单次任务最多用50000token。超了直接返回当前结果,不再烧钱。 - 上下文压缩
:当上下文超过8000字符时,把早期对话摘要成一句话,防止越跑越贵。
四招齐上,效果如何?
我把四个策略按优先级排了个序:
| 模型路由 | |||
| 语义缓存 | |||
| Prompt瘦身 | |||
| Token预算 |
四个策略叠加后,第二个月的账单:
743块 → 218块。 砍了71%。
而且第三个策略(Token预算控制)不只是省钱——它还防止了"Agent跑飞了"这种灾难性事件。一个不受控的Agent一夜之间烧掉几千块,比日常浪费可怕多了。
我踩过的3个坑
坑1:模型路由切得太狠,复杂任务翻车
一开始我把分类规则写得太简单,结果有个需要多步推理的任务也被路由到了deepseek-chat,答案完全不对。
解法:路由规则要留容错。简单模型回答后做个置信度检查,置信度低就自动升级到强模型重试。别一锤子定音。
坑2:缓存过期时间设太长,信息更新了还返回旧答案
公司政策更新了,但缓存里还是旧答案。同事按旧答案操作,差点出事。
解法:知识库类内容缓存设24小时过期,实时信息(天气、股价)不缓存或只缓存5分钟。缓存一定要带TTL。
坑3:上下文压缩太激进,丢了关键信息
为了省token,我把压缩阈值设得太低,结果Agent把任务的关键约束给压没了,输出的方案完全跑偏。
解法:压缩时保留最近3轮对话原文,只压缩更早的内容。关键约束(数字、条件、限制)用正则提取出来单独保留。
成本控制的核心心法
回顾一下,AI Agent的成本控制核心就三句话:
- 用对的模型
——简单任务用便宜模型,复杂任务才上贵的 - 别重复付费
——相同或相似的问题,缓存一次就够了 - 设好上限
——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工具实战 · 大模型
更多内容请在公众号对话框回复关键词获取资料
夜雨聆风