八成做过 Agent 的兄弟都经历过这个阶段:本地调试的时候美滋滋,感觉模型调用一次就几分钱,根本不算事。等真把工作流跑起来,接上真实业务流量,月底一看 API 账单,直接傻眼——费用比预期翻了四五倍,甚至十倍。
我自己的简历筛选 Agent 就是活例子。最初设计时拍脑袋觉得"反正调用一次没几厘钱",结果上线后每天几千份简历涌进来,每个候选人都要过信息抽取、初筛评分、深度评估、报告生成四道工序,每一道都得调大模型。第一个完整月的账单出来,我盯着那个数字反思了整整一个下午,最后得出一条核心结论:Agent 最大的成本黑洞不是模型单价,而是工作流里那些"反正每次都多带一点"的隐形 Token 消耗。
这篇文章不聊虚的,直接讲我在 Coze、Dify 和自建 Agent 框架里反复试出来的 6 个控成本技巧。每个技巧都会说清楚底层逻辑、具体操作步骤,以及我在实践中踩过的坑。适合正在做 AI Agent、工作流编排,或者被 API 账单困扰的开发者参考。
1. 先看清钱烧在哪:Agent 成本暴涨的四类根因
很多人在优化成本时容易陷入一个误区:只看模型单价,觉得换个便宜模型就万事大吉。但实际排查过账单后你会发现,Agent 场景下费用暴涨的原因往往藏在更隐蔽的地方。
1.1 四类隐形消耗源
我把自己的 Agent 账单按调用链路拆了一遍,结合周围朋友的项目反馈,发现成本飙升几乎都逃不出这四类问题:
| 成本源 | 典型表现 | 特征 |
|---|---|---|
| 上下文膨胀 | 系统提示词越写越长、历史消息越积越多 | 每次调用输入 Token 持续增长 |
| 任务颗粒度过大 | 一个 Prompt 里让模型同时做提取、判断、生成 | 哪怕只用到一部分能力,所有 Token 照样全额计费 |
| 重复计算 | 同一份文档在多轮对话中反复注入 | 同一段内容被计费 N 次 |
| 异常放大 | 上游超时触发重试、并发失控 | 失败请求也可能产生费用,叠加放量 |
1.2 定位主因:先拉一条费用日志
在做任何优化之前,我强烈建议先在工程侧落一个"费用埋点"。不要等云厂商的账单出来才看,那个太滞后了。具体做法是在每次调用 API 后同步读取返回结果里的 usage 字段,把prompt_tokens、completion_tokens、model、耗时、场景标识五元组落库或落日志。
我在自己的 Agent 里加了这么一段轻量埋点逻辑:
# 以 OpenAI 兼容接口为例 resp = client.chat.completions.create( model="your-model", messages=conversation_history, ) usage = resp.usage log_entry = { "scene": "resume_deep_eval", "model": resp.model, "prompt_tokens": usage.prompt_tokens, "completion_tokens": usage.completion_tokens, "total_tokens": usage.total_tokens, "latency_ms": resp.response_ms if hasattr(resp, "response_ms") else None, } # 写入本地日志或 ClickHouse / 云日志服务有了这份数据以后,你再去看"哪个场景调用最频繁""哪类 Prompt 的 Token 消耗最大""哪段时间出现费用尖峰",全是一目了然的事,后续所有优化动作也有了衡量基准。
提示:很多开源框架(比如 Dify、Coze 控制台)本身就带调用日志和费用统计,如果你用的是自建链路,上面这段逻辑一定要尽早补上,否则后面优化都是抓瞎。
2. 技巧一:按"窄工作流"拆分,别让一个 Agent 包揽所有事
第一个对我触动最大的认知转变,是把"一个 Agent 干完所有事"改成"多个窄 Agent 各干一件事"。这个调整带来的成本下降立竿见影。
2.1 为什么窄工作流更省 Token
大模型计费按输入+输出 Token 计算,而输入 Token 取决于你塞给模型的上下文长度。一个包揽全部的 Agent 意味着它的系统提示词里要描述所有任务规则,还要在对话里保留所有中间结果,上下文必然越滚越长。
打个比方:这就像你雇了一个全能管家,每次吩咐他都得把所有家务注意事项背一遍,哪怕这次只是让他倒个垃圾,他也得把"如何擦玻璃、如何拖地、如何整理衣柜"的完整手册在脑子里过一遍。费用自然就上去了。
拆分成窄工作流以后,每个 Agent 的系统提示词只描述自己负责的那一小块规则,上下文短、聚焦、无干扰,单次调用 Token 数量通常会下降 50% 以上。
2.2 在编排平台里的落地姿势
以最常见的简历筛选场景为例。最初我把"读简历、提取信息、算匹配分、写反馈"全部塞进同一个 Prompt,一次调用输出很长,还经常出现前后字段互相污染的问题。
后来我把流程拆成了四段:
- 简历结构化 Agent:只负责把 PDF/Word 文本转成 JSON 字段(姓名、工作年限、技能列表)
- 初筛评分 Agent:输入结构化 JSON,输出硬性条件匹配得分
- 深度评估 Agent:需要调用外部岗位 JD 做语义匹配,输出能力维度的评估
- 报告生成 Agent:输入前几步的结构化结果,合成自然语言评语
每个 Agent 的上下文都很"窄",系统提示词我可以控制在一两百字。实测下来,同一条简历的处理成本比原来"一锅端"降低了接近 60%,而且输出稳定性明显更好。
如果你用的是 Coze 或 Dify,操作上就是新建多个子工作流,在总流程里用"节点调用"把它们串起来。每个子工作流可以独立测试、独立更换模型、独立加缓存,后续优化非常灵活。
2.3 拆分的边界在哪里
这里有个容易走极端的坑:拆得太碎同样会带来成本问题。
工作流每增加一个节点,就多一次模型调用、多一轮传输开销。如果你把一个简单任务拆成十个小 Agent,总费用可能反而比一个中型 Agent 更贵,而且节点间数据传递出错的可能性也变大。
我的经验是,拆分到"每个 Agent 的输入输出都是结构化数据"为止。如果一个子任务的输出是自由文本、且需要下一步 Agent 重新理解,那说明拆得太碎了,应该合并回去。
3. 技巧二:上下文瘦身,比堆全文省得多
如果说拆分是改变流程结构,那上下文瘦身就是在单次调用层面压榨费用。这一步做得好不好,直接决定账单的数量级。
3.1 上下文膨胀的两个元凶
第一个元凶是系统提示词越写越长。很多人的 Agent 系统提示词动辄两三千字,包含各种规则、示例、输出格式要求。问题是,大模型每处理一个 Token 都要付钱,这些提示词在每次调用时都会被完整计费。一次两次无所谓,乘以每天上千次调用,就是一笔不小的开销。
第二个元凶是 RAG 的"暴力注入"。常见错误是把检索出来的文档整篇塞进上下文,哪怕和用户问题相关的只有其中一小段。我见过一个 Dify 工作流报 "maximum context length" 错误,排查后发现是知识库命中了几个长文档,每个都完整塞进去了,上下文一下子顶到十几万 Token。
3.2 检索增强压缩法:只注入相关片段
正确的做法是把知识库做切片,检索时只把命中片段注入上下文,而不是整篇文档。
关键词是"切片粒度"。我测试过不同粒度,经验值是:按 300~500 字切片,检索时取 Top 3~5 个切片。这个量级既能覆盖答案所需的信息,又不至于把上下文撑爆。
切片的具体操作在 Dify 知识库里可以直接配置,Coze 里就是用"知识库"节点加"分段设置",自建的话用 LangChain 的 RecursiveCharacterTextSplitter 就能实现。
另外还有一招很实用:在把检索结果注入 Prompt 之前,先用一个轻量模型做一次"无关内容压缩"。比如检索出 5 个切片共 2000 字,让它先提炼成 300 字的摘要,再把摘要主模型。这样主模型的输入 Token 进一步减少,而摘要丢失的信息有限,对输出质量影响很小。
3.3 控制对话历史:该扔就扔
Agent 跑多轮之后,对话历史会成为最大的 Token 消耗源。很多工作流把每一轮问答都丢进 messages 数组,几十轮下来上下文直奔几万 Token。
解决办法一是滑动窗口,只保留最近 N 轮对话;办法二是定期摘要,每隔几轮把前面的关键信息总结成一两句话,再作为后续对话的 context。
提示:如果你用 Dify 这类平台,它自带"对话历史"控制面板,可以设置保留轮数。自建的话一定要自己实现一个上下文裁剪逻辑,别偷懒。
4. 技巧三:提示词缓存,把重复 Token 的费用直接清零
第三个技巧是零成本省钱法:利用各家大模型平台提供的提示词缓存能力。这个特性很多人不知道,但它对 Agent 场景特别友好。
4.1 缓存的工作原理
大模型平台的缓存机制其实很容易理解:如果你两次请求的前缀 Prompt 完全一致,第二次就不用重新计算前面那部分内容,只计算新增加的部分。费用上,缓存命中的输入 Token 价格通常只有未命中的十分之一左右(不同平台比例略有差异,有的甚至更低)。
Agent 工作流恰好符合这个特征:系统提示词固定不变,工具定义固定不变,对话历史从前往后看大部分也是重复的。只有每次新追加的那个问题在变。这意味着缓存命中率天然就很高。
4.2 如何提升缓存命中率
缓存命中有几个硬性条件,很多人栽在这里:
第一,前缀必须完全一致,逐字节相同才算命中。所以不要把时间戳、随机数、用户 ID 等动态内容放到 Prompt 开头,否则每次开头都变,整个前缀就全部失效了。
第二,静态内容要放前面,动态内容要放后面。系统提示词、工具描述、固定示例放最前面;当前问题、临时变量放最后面。这样前面的部分保持不变,才能命中缓存。
第三,上下文裁剪不要频繁变动前缀。有些人在处理对话历史时喜欢每次都做不同粒度的裁剪,导致前缀长度和内容不断变化。我的做法是固定裁剪策略:保留最近 10 轮,每轮格式化方式完全一致,这样前缀变化最小。
4.3 缓存失效的典型场景
缓存不是永远有效的,各家平台都有 TTL(缓存失效时间)。比如有的平台是 5 分钟不活跃就失效,有的是 1 小时,具体看服务商文档。
这里有个实践中的坑:工作流如果被拆成多轮编排,相邻两轮节点调用之间的间隔可能会超过 TTL,导致缓存频繁失效。解决办法是尽量让同一工作流的连续调用在时间上紧凑,或者调整平台侧的缓存 TTL 设置(如果支持)。
提示:DeepSeek、智谱、OpenAI、Anthropic 等主流平台都支持提示词缓存,但开通方式、计费规则、前缀长度要求各不相同。以 DeepSeek 为例,它的自动提示词缓存只要命中就自动优惠,不需要额外配置,这对我这种懒人非常友好。用之前务必去各平台的文档确认一下"缓存说明"章节。
5. 技巧四:按任务难度分级路由,让贵模型只干技术活
第四个技巧是模型层面的成本优化。很多 Agent 开发者习惯一套班子打天下——所有任务都调用同一个强模型。资源是够用了,但费用往往比实际所需高出好几倍。
5.1 任务分级的标准
我的做法是把工作流里的所有模型调用分成三个等级:
| 等级 | 适用场景 | 模型选择 |
|---|---|---|
| L1 简单 | 格式转换、实体抽取、分类打标 | 便宜快速的轻量模型,如 deepseek 系的 chat 档位 |
| L2 中等 | 结构化生成、意图识别、规则判断 | 主力均衡模型 |
| L3 复杂 | 深度推理、多步骤分析、最终决策 | 需要强推理能力的最大模型 |
分级规则不是拍脑袋定的。我的经验是看两个维度:输出是否结构化(结构化容易,自由文本难),以及是否需要多步推理(只查一步容易,需要回溯多步难)。
5.2 在流程里插一个分发节点
在 Coze 或 Dify 里,这个分级路由可以用"条件分支"节点实现。比如简历筛选场景中:
- 先让 L1 模型把简历转成结构化 JSON;
- 然后写一个判断节点:如果 JSON 缺失关键字段(比如工作经历为空),走 L3 强模型做深度补全;如果字段完整,走 L2 模型做匹配评分;
- 最终报告生成再用 L2/L3 结合。
我在自建链路里常用一段简单路由代码:
def route_task(task): # 结构化 + 低复杂度 → L1 if task.type in ("extract", "format"): return "cheap-fast-model" # 有明确规则但需要一定理解 → L2 elif task.type == "score": return "mainstream-model" # 需要多步推理或长文本生成 → L3 elif task.type == "deep_eval": return "strong-reasoning-model" else: return "mainstream-model"5.3 降级后的质量保障
有人担心换来便宜模型后输出质量会崩。我的实测结论是:在 L1/L2 场景下,轻量模型的质量下降很小,而速度往往更快。真正需要强模型的场景其实只占总调用量的 20% 左右。
为了保险起见,我加了两个质量阀门:一是对结构化输出做 JSON schema 校验,不合法就自动降级到强模型重试一次;二是对 L2 的输出做关键词抽查。实测下来,整体效果没有明显下降,费用却降了约三成。
6. 技巧五:重试与并发控制,堵住异常导致的费用尖峰
这个技巧针对的是线上最容易出血的地方——异常流量。Agent 跑起来之后,业务一波动,重试、并发、超时等问题会一股脑涌出来,账单瞬间飙高。
6.1 重试风暴是怎么烧钱的
假设你的 Agent 依赖上游模型 API,某个时段模型负载高、响应变慢,你的代码判断为超时,自动重试。这本来没什么,但如果重试间隔设置不合理,比如失败后 0.1 秒就重试,很可能连续触发 5~6 次,每次失败请求同样会产生费用(尤其是超时发生在模型已经开始生成之后)。
更可怕的是并发场景。一个用户请求超时后重试 3 次,100 个用户就是 300 个额外请求,直接打满并发配额,然后新一轮重试再触发。费用尖峰就是这么来的。
我在实际排查中甚至遇到过这样的案例:由于 API Key 配置错误(类似社区里常见的 "no api key for provider route" 报错),整个工作流一直在空转重试,虽然不是成功调用,但日志刷屏、服务资源被耗尽。这种问题如果不从根上治理重试策略,再多预算都不够烧。
6.2 重试策略的正确配置
我的重试参数长期稳定在下面这组值,分享出来供参考:
MAX_RETRIES = 2 # 最多重试 2 次,不贪多 BASE_RETRY_DELAY = 1.0 # 第一次重试等待 1 秒 BACKOFF_FACTOR = 2.0 # 指数退避,第二次等待 2 秒 RETRYABLE_ERRORS = (429, 500, 502, 503, 504) # 仅对限流和服务器错误重试- 重试次数上限设为 2。3 次以上的重试在大多数场景下只是增加费用和延迟,成功率的边际提升非常小。
- 指数退避是必须的。1 秒、2 秒、4 秒的间隔比固定间隔更有效,能避免重试请求在短时间内挤爆服务。
- 只重试可重试的错误码。400(参数错误)、401(认证失败)、403(权限不足)这类错误,重试多少次都不会成功,必须直接返回错误而不是盲目重试。
6.3 并发上限与快速失败
除了重试,并发控制同样是在给费用上保险。我的方案是在 Agent 外层套一个简单的信号量或令牌桶:
import asyncio semaphore = asyncio.Semaphore(10) # 限制全局并发数 async def guarded_call(*args, **kwargs): async with semaphore: return await call_model(*args, **kwargs)并发上限的值要结合你的业务场景定:实时交互场景可以给高一点,批量处理任务可以压低。压低并发最大的好处是:当上游变慢时,排队机制会自然削峰,而不是无限重试放大压力。
超时时间也要设得果断。我的经验是首字节等待时间设 15 秒,整体调用 60 秒上限。比这更长还不出结果,继续等下去大概率是浪费钱。
7. 技巧六:费用观测与告警,不等账单日再被迫复盘
最后一个技巧,同时也是很多人最不重视的:把费用观测做成日常能力,而不是月度惊吓。
7.1 按请求链路打标签
在第 1 节里我已经提过一次费用的埋点了。这里补充一点:埋点要带上业务标签,这样才能在出了问题后快速定位到具体链路。
推荐几个必打的标签:工作流名称、场景 ID、用户 ID(如果可以)、模型名、是否命中缓存。有了这些标签,你就可以随时回答"哪个业务方烧钱最多""哪条工作流单均费用最高"。
7.2 设定多级告警
告警阈值建议按照这三个维度各设一个:
| 维度 | 建议阈值 | 告警方式 |
|---|---|---|
| 日费用 | 超过前一天 1.5 倍 | 即时通知 |
| 单次调用费用 | 超过该场景日常均值的 3 倍 | 即时通知 |
| 请求成功率 | 低于 95% 持续 5 分钟 | 即时通知 |
第一条防的是整体失控,第二条防的是某个异常请求突然吞掉大量 Token,第三条防的是上游故障导致的重试风暴。
告警工具用现成的就行:Dify 自带告警配置,Coze 可以在节点里接 webhook 到企业微信/钉钉/飞书,自建的话选 Prometheus + Alertmanager 或云监控都可以。
7.3 每周做一次费用复盘
埋点数据积累多了以后,我养成了一个习惯:每周花十分钟拉一张表,看每一路工作流的总费用、有效请求数、单请求均费三个指标。节奏是固定的,不搞临时突击。
这种复盘帮我发现过好几个隐蔽问题:某个子工作流因为系统提示词写太长,单请求均费悄悄涨了 20%;某个"定时任务"因为配置错误,每小时多调了 200 次模型,每天浪费不少钱;某个知识库切片因为粒度太细导致召回片段过多,单次调用 Token 数翻倍。
没有数据支持,这些问题几乎不可能在账单里被快速定位到,只会变成"不知道钱去哪了"的月度困惑。
8. 写在账单之外:三个能救命的小习惯
做完上面 6 个技巧,如果你的账单已经回到合理区间,那恭喜你。不过最后我还想分享三个"软性"但实际非常有用的习惯,它们不一定直接省钱,但能在关键时刻帮你避免大出血。
第一个习惯:每次上线前,先跑一遍"费用体检"。我指的体检是:拿你预计最高单量的一半,先在测试环境用生产数据跑半小时,统计单均费用,再乘上你预期的峰值调用次数,看总费用是否可承受。看起来很笨,但实测非常有效,能提前发现很多"只跑通但没算过账"的工作流。
第二个习惯:关注模型供应商的计费变更。提示词缓存的价格调整、新的便宜模型发布、上下文窗口扩容,这些信息直接影响你的成本结构。我见过一个同事因为没关注到新模型发布,一直用贵模型跑了三个月,白白多花了不少钱。
第三个习惯:把你优化成本的过程记录下来。每次改了什么配置、降了多少费用、遇到什么坑,都记一笔。一方面以后新项目可以直接复用,另一方面面试或者团队分享时,这些都是特别硬核的经验材料。
按这 6 个技巧逐个落地后,我的简历筛选 Agent 的单均费用降到了原来的五分之一左右,而且输出质量完全没受影响。核心就一句话:别把钱花在模型有多聪明上,要花在"模型这次到底做了什么有用的事"上。多想想每次调用是不是都在干眼前的活,你的 API 费用自然就稳了。