
1. 项目缘起一笔让人肉疼的账单去年年底复盘公司 AI 产品的月度支出时财务甩过来一张表大模型 API 那一栏赫然写着47000 元。说实话当时我盯着这个数字看了很久——产品日活才刚过两千客单价也不高光模型调用成本就吃掉了将近三成的毛利。更让人难受的是翻了一遍调用日志发现大量请求其实是无效消耗用户点一下没输入几个字就退出的、系统内部做格式校验时反复调用的、同一段上下文被不同模块重复塞进去的全都在烧 Token。这个项目就是在这种背景下启动的。目标很直接在不牺牲核心体验的前提下把大模型 API 的月度成本压下来。最终结果是从 47000 元降到了 12000 元出头降幅约 74%。整个过程没有什么黑科技靠的是一套硅碳相变式的组合拳——硅基的工程手段缓存、路由、截断、批处理加上碳基的产品判断砍需求、改交互、定策略两者互相咬合才把成本真正摁住。这篇文章写给两类人一类是正在被大模型账单困扰的技术负责人或独立开发者另一类是准备把大模型能力接进产品、但还没想清楚成本模型的团队。我会把每一步的思考逻辑、具体参数、踩过的坑都摊开讲你能直接抄作业也能根据自己业务做裁剪。全文涉及的关键词包括大模型、API、Token、成本优化、max_tokens这些词会贯穿始终但我尽量不堆术语说人话。2. 成本结构拆解钱到底花在哪了2.1 先搞清楚计费逻辑别急着优化很多人一上来就想着换个便宜的模型这是最容易走偏的一步。大模型 API 的计费本质是按 Token 计费而 Token 又分输入prompt和输出completion两部分两者单价通常不同输出往往更贵。以主流厂商的定价为例输入可能是每百万 Token 几块钱输出则是十几块甚至几十块。这意味着输出 Token 的优化优先级远高于输入。我做的第一件事是把过去 30 天的调用日志全部拉出来按维度拆解。拆解维度包括调用来源模块、模型名称、输入 Token 数、输出 Token 数、请求时间、是否命中缓存、用户会话 ID。这一步没什么技术含量但极其关键因为没有度量就没有优化。当时我用 Python 写了个脚本把日志从对象存储里拉下来用 pandas 做聚合半小时就出了第一版报表。import pandas as pd df pd.read_parquet(llm_calls_30d.parquet) df[total_tokens] df[prompt_tokens] df[completion_tokens] # 按模块聚合 by_module df.groupby(module).agg( calls(id, count), prompt(prompt_tokens, sum), completion(completion_tokens, sum), cost(cost_yuan, sum), ).sort_values(cost, ascendingFalse) print(by_module.head(20))跑出来的结果让我有点意外排名第一的消耗大户不是面向用户的主对话功能而是内部的内容审核模块。这个模块每次用户发消息都会调用一次大模型做合规判断输入是完整对话历史输出是一段 JSON。它占了总成本的 31%但用户完全感知不到它的存在。排名第二的是标题自动生成占 18%第三才是主对话占 15%。2.2 三类浪费重复、冗余、过度把数据摊开之后浪费可以归成三类我给它起了名字方便团队沟通重复浪费同一个问题、同一段上下文被反复调用。典型场景是用户连续追问每次都把完整历史重新塞进去而历史里大部分内容对当前回答毫无帮助。冗余浪费输入里塞了大量模型不需要的信息。比如系统提示词写了八百字其中六百字是你是一个专业的助手这类废话又比如把整个知识库文档原文塞进去其实只需要相关段落。过度浪费输出远超实际需要。最典型的是max_tokens设得过大模型话痨式输出用户只看前两句。还有一种是模型选型过度简单分类任务用了最贵的旗舰模型。这三类浪费的比例大概是 4:3:3。也就是说光靠换便宜模型最多解决三成问题剩下七成得靠工程和产品手段。这就是我标题里说的硅碳相变——硅基工程解决重复和冗余碳基产品判断解决过度。2.3 定一个可量化的目标优化不能凭感觉。我定的目标是单位有效会话成本下降 60% 以上同时用户满意度点赞率不下降超过 2 个百分点。为什么是 60% 而不是 74%因为 74% 是最终结果中间还叠加了一些产品改动带来的自然下降纯技术手段的目标定在 60% 更务实也更容易向团队交代。这里有个经验成本优化一定要设护栏指标。只盯成本很容易把体验做崩。护栏指标可以是点赞率、追问率、会话时长、任务完成率选一个和你业务最相关的。我们选的是点赞率因为它最直接反映用户对回答质量的感知。3. 硅基手段工程侧的五把刀3.1 第一刀语义缓存砍掉重复调用重复浪费最直接的解法是缓存。但大模型场景下的缓存和传统缓存不一样——同样的输入未必有同样的输出温度参数而不同的输入可能语义相同。所以要做语义缓存把用户 query 做向量化在缓存库里找相似度超过阈值的已有回答直接返回。我用的是精确缓存 语义缓存两层结构。精确缓存用 Rediskey 是 query 的哈希命中率大概 12%成本几乎为零。语义缓存用向量数据库阈值设在 0.92命中率额外贡献 18%。两者加起来30% 的请求根本不用调模型。import hashlib import redis from sentence_transformers import SentenceTransformer r redis.Redis() encoder SentenceTransformer(BAAI/bge-small-zh) def get_cached(query: str, threshold: float 0.92): # 第一层精确缓存 key exact: hashlib.md5(query.encode()).hexdigest() if r.exists(key): return r.get(key).decode() # 第二层语义缓存 vec encoder.encode(query) hits vector_db.search(vec, top_k1) if hits and hits[0].score threshold: return hits[0].payload[answer] return None注意语义缓存的阈值不能拍脑袋定。0.92 是我在业务数据上试出来的太低会返回不相关答案太高命中率上不去。建议用一批标注数据画一条 precision-recall 曲线选业务能接受的点。这里有个坑缓存要设过期时间而且不同模块的过期策略不同。事实类问答比如公司地址在哪可以缓存 7 天时效类问答比如今天天气只能缓存几分钟甚至不缓存。我们一开始统一设了 24 小时结果用户投诉回答过时后来按模块分级才解决。3.2 第二刀上下文压缩给历史瘦身多轮对话是 Token 消耗的重灾区。用户聊到第十轮如果把前九轮原文全塞进去输入 Token 轻松破万。但模型真正需要的往往只是最近几轮加上一个摘要。我的做法是滑动窗口 摘要压缩保留最近 3 轮原文更早的对话用一个小模型便宜的那种压缩成一段 100 字以内的摘要。这样输入 Token 从平均 6000 降到 1800 左右降幅 70%。具体参数上滑动窗口大小设为 3 轮是试出来的。2 轮太少模型经常失忆4 轮以上收益递减Token 却线性增长。摘要触发阈值设在历史超过 5 轮时因为 5 轮以内原文塞进去也不贵没必要增加摘要调用的开销。def build_context(history, max_recent3): if len(history) max_recent 2: return history # 短对话直接用原文 recent history[-max_recent:] older history[:-max_recent] summary summarize_with_small_model(older) # 用便宜模型压缩 return [{role: system, content: f历史摘要{summary}}] recent提示摘要本身也要花 Token所以只在省下的 Token 大于摘要成本时才做。粗略估算如果历史超过 5 轮压缩收益就为正。这个阈值随模型单价变化建议自己算一遍。3.3 第三刀max_tokens 精细化别让模型话痨max_tokens是最容易被忽视的参数。很多人图省事直接设成 4096 甚至更大结果模型经常输出一大段废话。实际上不同任务需要的输出长度差异巨大分类任务可能 10 个 Token 就够摘要任务 200 个创意写作才需要上千。我把所有调用按任务类型重新设了max_tokens任务类型原 max_tokens优化后说明意图分类51216只需输出类别标签内容审核102464输出 JSON 判定结果标题生成51232一句话标题摘要2048256控制在一段内主对话40961024保留完整回答能力光这一项输出 Token 总量就降了 55%。因为输出单价通常是输入的 3-5 倍这一刀的性价比极高。但要注意max_tokens设太小会导致回答被截断用户体验反而变差。我的经验是设成典型输出的 1.5 倍留一点余量。比如标题生成典型输出 20 个 Token设 32 就够设 16 就可能截断。3.4 第四刀模型路由让便宜的干便宜的活不是所有任务都需要旗舰模型。意图分类、格式校验、简单问答用轻量模型完全够用价格可能只有旗舰的十分之一。我搭了一个两级路由先用规则判断任务类型规则覆盖不到的用一个小分类模型兜底。路由规则大概是这样的如果任务是分类/抽取/校验→ 轻量模型如果输入长度小于 200 Token 且是事实问答 → 轻量模型如果涉及多步推理、代码生成、创意写作 → 旗舰模型其他 → 中等模型上线后旗舰模型的调用占比从 100% 降到 22%整体成本直接砍半。这里的关键是路由要可观测每次路由决策都打日志定期抽样人工检查防止该用旗舰的用了轻量导致质量下降。3.5 第五刀批处理与异步薅并发折扣有些调用不要求实时返回比如夜间的内容审核、批量摘要、数据标注。这些任务可以攒起来批量提交很多厂商对批量调用有折扣通常是五折。我把所有非实时任务挪到夜间批处理又省了一笔。批处理的实现要点是控制单批大小。太大容易超时太小折扣吃不满。我试下来单批 50-100 条比较稳具体看厂商限制。另外要处理好失败重试批处理里一条失败不能拖垮整批。4. 碳基手段产品侧的三个判断4.1 砍掉伪需求从源头减少调用工程手段再强也不如不调用。复盘时我发现有几个功能是典型的伪需求比如每次用户输入时实时预测下一句调用量巨大但用户几乎不用还有自动生成会话标签生成了也没人看。这些功能直接下线成本立降 8%。判断伪需求的标准很简单看功能的使用率和它对核心指标的贡献。使用率低于 5% 且对留存、转化没有可观测影响的功能都可以砍。这个判断必须由产品负责人拍板工程师别自己决定因为涉及用户体验的取舍。4.2 改交互把每次调用变成按需调用有些成本是交互设计带来的。比如原来用户每输入一个字就触发一次联想改成停顿 800 毫秒后才触发调用量降了 60%。又比如原来每次打开页面就预加载 AI 回答改成用户点击后才加载无效调用大幅减少。这类改动的核心思路是把主动推送改成被动触发。用户没明确表达需求时不要替他调用模型。这需要和产品、设计一起讨论因为会影响交互流畅度但收益通常很可观。4.3 定策略什么场景用什么模型写进规范最后一步是把所有决策固化成规范写进团队的开发文档。规范内容包括任务类型与模型的映射表、max_tokens的默认值、缓存策略、上下文压缩规则、路由规则。新功能开发时必须对照规范特殊情况要申请。规范的价值在于防止成本反弹。优化做完后如果不固化新功能一上来又会把成本拉回去。我们上线规范后连续三个月成本稳定在 12000 元左右没有明显反弹。5. 实操全流程从 47000 到 12000 的每一步5.1 第一周度量与基线第一周只做一件事把数据搞清楚。拉取 30 天全量调用日志按模块、模型、任务类型、Token 数拆解算出每个模块的成本占比和单位会话成本。同时建立监控看板实时展示每日成本、调用量、缓存命中率、路由分布。这一周的产出是一份《成本基线报告》明确告诉团队钱花在哪、浪费在哪、优化空间有多大。没有这份报告后面的优化就是无头苍蝇。5.2 第二到三周工程手段上线按投入产出比排序先上性价比最高的手段。我的顺序是max_tokens精细化 → 精确缓存 → 模型路由 → 语义缓存 → 上下文压缩 → 批处理。每上线一项观察三天数据确认成本下降且护栏指标稳定再上下一项。这个顺序的逻辑是先做改动小、风险低、见效快的。max_tokens改个参数就行风险极低语义缓存涉及向量库工程量大放后面。分批上线还有个好处出问题时容易定位是哪一项导致的。5.3 第四周产品侧调整工程手段做完后成本大概降到 22000 元。剩下的空间要靠产品侧。这一周和产品团队开了三次会砍掉两个伪需求功能改了联想输入的触发逻辑下线了预加载。成本再降到 15000 元。产品侧调整的难点不在技术在沟通。工程师容易觉得这功能挺好的为什么要砍产品容易觉得砍了体验会差。解决办法是拿数据说话把功能使用率、对核心指标的影响摆出来让决策基于事实而非感觉。5.4 第五周固化规范与监控最后一周把前面所有决策写成规范同时完善监控告警。设置成本日环比告警涨幅超过 20% 就触发排查设置缓存命中率告警低于阈值说明缓存失效设置路由分布告警旗舰模型占比异常升高要检查。规范落地后成本稳定在 12000 元出头。从 47000 到 12000降幅 74.5%超额完成目标。6. 常见问题与排查实录6.1 缓存命中率上不去怎么办这是最常见的问题。排查顺序是先看精确缓存 key 是否包含时间戳等易变字段有的话命中率必然低再看语义缓存阈值是否过高最后看 query 分布是否过于分散长尾 query 多的话缓存天然难命中。我遇到过一次精确缓存命中率只有 3%查了半天发现 key 里混进了用户 ID导致每个用户的缓存都是独立的。去掉用户 ID 后命中率升到 12%。这种低级错误很常见排查时先怀疑自己的代码。6.2 上下文压缩后模型失忆摘要压缩会丢信息模型可能忘记关键细节。解决办法是在摘要里保留实体和关键决策比如用户提到的名字、时间、金额、已确认的选项。摘要 prompt 要明确要求保留所有专有名词和数字。另一个技巧是关键信息不压缩。比如用户第一轮说的需求可以单独拎出来始终保留在上下文里不参与摘要。这样既省 Token 又不丢关键信息。6.3 模型路由导致质量下降路由的典型问题是该用旗舰的用了轻量。排查方法是抽样对比随机抽 100 条路由到轻量模型的请求人工评估回答质量如果差评率超过阈值说明路由规则太激进。我的经验是路由规则宁保守勿激进。一开始只把最明确的任务分类、抽取路由到轻量模型观察一段时间再逐步扩大范围。质量下降的代价远大于省下的成本。6.4 成本优化后突然反弹反弹通常有三个原因新功能没走规范、缓存大面积失效、路由规则被改。排查时先看监控看板的异常指标定位到具体模块再深入。预防办法是把规范写进代码 review checklist新功能上线前必须检查成本影响。6.5 常见问题速查表问题现象可能原因排查方向解决手段缓存命中率低key 含易变字段 / 阈值过高检查 key 构造、看 query 分布去掉易变字段、调低阈值回答被截断max_tokens 过小对比典型输出长度调到典型值的 1.5 倍模型失忆上下文压缩过度检查摘要是否丢实体保留关键信息不压缩质量下降路由过于激进抽样人工评估收窄路由范围成本反弹新功能未走规范看模块成本分布补规范、加 review7. 几个反直觉的经验第一个反直觉的点最贵的模型不一定最费钱。旗舰模型单价高但如果它一次就能答对而便宜模型要试三次总成本可能反而更低。所以模型选型不能只看单价要看单次任务完成成本。我们有个任务一开始用轻量模型失败率高重试三次的成本超过了直接用旗舰后来改回旗舰反而更省。第二个反直觉的点缓存不是越多越好。缓存太多会导致回答陈旧用户感知到这 AI 怎么答非所问信任度下降。我们一度把缓存过期时间设成 7 天结果用户投诉回答的是上周的信息。后来按模块分级事实类 7 天、时效类 1 小时才平衡好。第三个反直觉的点省 Token 不等于省成本。有些优化省了输入 Token但增加了调用次数比如为了压缩上下文多调了一次摘要模型总成本反而上升。每次优化都要算总账别只盯一个指标。8. 后续还能怎么压12000 元不是终点。我还在试几个方向一是本地小模型兜底把最简单的分类、抽取任务放到本地跑边际成本接近零二是蒸馏用旗舰模型的输出训练一个小模型替代部分场景三是更细粒度的路由按 query 复杂度动态选模型而不是按任务类型粗分。但说实话继续压的空间在收窄。从 47000 到 12000 是把明显浪费挤掉再往下就要动体验了。我的判断是成本优化做到单位会话成本进入合理区间就该收手把精力放回产品本身。毕竟省钱的目的是让产品活得更好不是为省而省。最后分享一个我踩过的坑优化做完后一定要持续监控至少三个月。我们第二个月成本悄悄涨了 15%查出来是一个新上的功能没走规范把完整知识库塞进了 prompt。如果没有监控这个漏洞可能几个月都发现不了。成本优化不是一次性项目是持续运营。