把Agent从Demo跑到生产环境,是另一场战斗。我见过太多团队,在本地跑通一条Agent流程,Demo演示时效果惊艳,结果一上生产就原形毕露:响应超时、Token账单失控、用户一个非常规输入就把Agent送进死循环。这个系列来到第九课,我想好好聊聊生产级调优这件事——性能、成本、稳定性三个维度,每个维度背后都有大量“看似玄学、实则必然”的工程问题。这篇文章没有面面俱到的理论堆砌,全是踩过坑之后的沉淀,适合正在做Agent工程化、或者准备把项目从实验阶段推向线上服务的团队参考。
1. 先建立调优坐标系:生产级Agent和Demo的分水岭在哪里
1.1 三层结构拆解:规划层、执行层、接口层的不同优化目标
很多人一说调优,上来就改Prompt、换模型,这是典型的“头痛医头”。我习惯把Agent系统拆成三层来看:
规划层是Agent的“大脑”,包含大模型推理主循环、任务规划、步骤拆解、工具选择决策。这一层的核心指标是推理质量和决策时延。
执行层是Agent的“手脚”,包含工具调用、API请求、数据库查询、检索增强(RAG)等具体动作。这一层的核心指标是调用成功率、响应时延和结果质量。
接口层是Agent对外的“门面”,包含用户请求接入、鉴权、限流、并发控制、流式输出、错误响应包装。这一层的核心指标是并发承载能力、可用性、错误兜底能力。
三层之间互相影响,但优化手段完全不同。比如规划层一个模型调用要3秒钟,你在执行层做再多优化,总时延也降不下来;反过来,如果执行层的工具接口有5秒的超时毛刺,规划层再快,用户体感也会被拖垮。所以第一步永远是先分层建立指标,再谈优化。
1.2 可量化指标基线:没有基线就没有调优
调优的第一件事不是动手改,而是先量出来。我们内部做任何Agent项目,上线前必须建立一套基线指标,这套指标也是后续所有调优动作的“标尺”。核心指标就这么几项:
| 指标 | 计算方式 | 说明 |
|---|---|---|
| P50响应时延 | 所有请求时延的中位数 | 代表大多数用户的真实体验 |
| P95响应时延 | 95%请求低于该时延 | 代表最差体验,生产环境比P50重要得多 |
| 端到端成功率 | 成功完成任务请求数 / 总请求数 | 注意失败的定义要先明确 |
| 单会话平均成本 | 一个完整任务产生的Token费用总和 | 新指标,传统后端没有,必须单独建 |
| 工具调用失败率 | 工具调用失败次数 / 工具调用总次数 | 定位执行层健康度 |
这里有一个特别容易犯的错误:Demo阶段看不出来、生产环境才暴露的问题,基本都是P95问题。Demo演示时只有几个请求,每次都是理想状态;生产环境有成千上万请求,模型偶尔抽风、外部API偶发超时、上下文偶尔超长,这些“偶发”汇聚起来就是无法忽视的P95恶化。
我遇到过最典型的情况:一个Agent在处理普通请求时P50只有1.2秒,看起来很不错,但P95到了15秒。深挖后发现,凡是上下文超过1万Token的请求,模型推理时间会暴涨,而这类“长上下文请求”约占10%。用户体感就是“这个Agent有时候秒回,有时候卡死”,这种体验比始终慢还糟糕。所以基线不仅要记录平均值,更要把分布记录下来。调优的第一原则:先量后改,没有基线数据支撑的优化都是自我感动。
2. 性能攻坚:延时从哪里来,又该如何降下去
2.1 延时的构成拆解:LLM推理时间才是大头
先说结论:Agent请求的端到端时延里,LLM推理时间通常占70%以上,工具调用网络耗时一般只有几百毫秒到一两秒。所以性能攻坚的主战场,永远是“怎么让模型少算一点、算快一点”。
一次典型的Agent请求,时间大致这样分配:用户输入解析和路由(50-200ms)、LLM首次token生成时间TTFT(0.5-2秒)、完整的模型推理时间(2-8秒)、工具调用往返(1-3秒)、最后的结果生成(1-3秒)。如果Agent需要多轮工具调用,时间还会成倍叠加。
这里要分清两个概念:TTFT(首Token延迟)和总生成时长。TTFT主要受Prompt长度和模型排队情况影响,Prompt越长TTFT越慢;总生成时长则受输出Token数量影响,输出越长越慢。人眼对“开始响应”的容忍度比对“结束响应”高得多,所以优化TTFT往往能带来最明显的体感提升。
我实测过一组数据:同一个任务,系统提示词从3000Token压缩到1500Token,TTFT从1.2秒降到0.8秒;输出从800Token缩短到500Token,总时延从4.2秒降到3.5秒。效果直接体现在用户反馈上。
具体怎么拆解时延?强烈建议接入链路追踪平台,用量化数据说话。如果没有现成平台,用最朴素的日志打点也能做:每个环节记录一个timestamp,后续分析的时候把各环节耗时列出来,一眼就能看到瓶颈在哪。
2.2 规划链路瘦身:减少无效LLM调用是性价比最高的优化
性能优化最大的杠杆,不是让模型跑得更快,而是让模型少跑几次。Agent的规划设计如果过于复杂,每一步都调用LLM来做决策,整个链路就会非常臃肿。
我见过一个典型的“过度Agent化”案例:一个工单分类任务,设计成了“意图识别 → 优先级判断 → 路由决策 → 分类结果生成”四步LLM调用,每一步之间还要校验输出格式,平均一次请求要花10秒钟,Token成本是直接Prompt方案的3倍。但实际上,前三步完全可以用规则引擎或者一个小模型一步搞定,只有最终的复杂分类才需要大模型介入。
优化思路有几个方向,按性价比排序:
- 合并连续LLM调用:相邻的几步能否合成一步?比如“提取关键信息 + 判断类别”完全可以在一次调用中完成;
- 用规则替代LLM决策:字段拼接、格式校验、简单映射这类确定性的操作,用代码写死,不要交给模型;
- 为Agent循环设置退出条件:很多框架默认让Agent“装到不能装为止”,但我们实际使用中发现,达到“已完成全部任务目标”这个状态后,再继续循环就是在浪费Token和时间。通过定义任务完成判据,可以提前截断循环;
- 减少冗余输出:明确告诉模型“只需要输出最终结论,不要输出大段的中间推理”,能显著缩短输出Token数。
以工单Agent为例,改造前一次任务平均要经历5轮LLM调用;改造后缩减到2-3轮,端到端时延从8.5秒降到4.2秒,成本也几乎减半。这比换任何更快但更贵的模型都划算。
2.3 缓存策略:语义缓存与短会话缓存的组合拳
缓存是性能优化里最被人低估的手段。Agent场景下的缓存,和传统Web后端非常不一样,因为用户的自然语言输入几乎没有完全相同的重复请求。
但实际业务里,语义相似的请求远比想象中多。比如“帮我查一下昨天的订单物流”“看看我昨天买的商品到哪里了”,这两句话的字面完全不同,但语义几乎一致。对这种请求做语义缓存:把用户的Query做向量化,和缓存池里的历史向量算相似度,超过阈值直接返回之前的结果,不再调用大模型。
Semantic Cache的落地要注意两点。第一是相似度阈值要反复调:阈值设得高,命中率低,缓存形同虚设;阈值设得低,容易误命中,把不相关的结果返回给用户。第二是缓存要有业务条件过滤,比如带时间敏感性的请求(“现在的库存”“今天的天气”)绝对不能命中旧缓存。
另一类缓存是相同会话内的中间结果缓存。Agent在同一个会话里反复调用同一个工具、查询同一份数据的情况非常多,尤其RAG场景下,同一份文档可能被反复检索。我们做过一个项目,加上会话级中间结果缓存之后,工具调用次数直接下降了40%。这一类缓存实现成本极低,收益却很明显。
2.4 并行化改造:让独立步骤同时执行
Agent链路里的很多步骤,其实天然是并行的,但一开始的代码实现往往是串行的。比如一个“分析客户邮件并生成回复”的Agent,需要同时查询订单系统和客户历史记录,这两步完全互不依赖,完全可以同时发起。
串行实现时,两步查询的耗时会相加;并行实现后,耗时等于最慢的那一步。几个独立请求同时并发,总时延能压缩到原来的三分之一左右。
我就犯过一个很蠢的错误:一个Agent需要调用三个内部API,三个API彼此独立,结果我按顺序一个一个调,每个API耗时800毫秒,三步加起来2.4秒。后来改成并发请求,总耗时直接降到900毫秒,只改了一处代码,效果立竿见影。
但并行化也要注意一个坑:同时发起太多请求,可能触发下游服务的限流。建议并发请求数量控制在3-5个左右,超过这个数量就用信号量做控制。另外并行请求的异常处理要仔细,不能一个失败就整个任务挂掉,每个子请求都要有独立的超时和错误处理。
3. 成本精算:Token消耗的构成与压减手段
3.1 算一笔账:一次Agent请求的Token到底花在了哪里
成本优化之前,先要知道钱花在哪。Agent请求的Token消耗,远比一次普通的模型调用复杂。我用一个实际案例来算账。
假设一个客服工单分类Agent,典型的请求流程是:用户输入一句话 → Agent判断需要调用API获取用户订单信息 → 依据订单信息生成分类结果。
- 系统提示词:2000 Token,每次必付;
- 用户Query:200 Token;
- 第一次模型调用:输入2200 Token,输出200 Token(生成“需要调用工具”的决策和调用参数);
- 工具返回结果:800 Token,作为上下文加入下一轮调用;
- 第二次模型调用:输入约3200 Token,输出300 Token(生成最终分类结果和解释)。
看起来单次任务Token并不夸张,但注意:工具返回结果会保留在上下文里,随着对话轮次增长,历史消息会持续累积。如果一个Agent执行复杂任务需要5-8轮工具调用,最后一轮的输入Token可能高达1.5万到3万。这才是成本失控的主要来源。
还有一个容易被忽略的成本黑洞:重试。一次LLM调用超时或输出格式错误,重试等于双倍甚至三倍的成本。如果Agent大范围失败再大范围重试,账单就非常难看了。
3.2 上下文压缩与召回质量:省钱不能损失效果
上下文压缩是成本优化的核心手段,但很多人做过头了。我曾经看到一个方案,为了省钱把历史对话全部压缩成一句摘要,结果Agent丢失了关键的用户偏好信息,最终回答质量大幅下降。压缩不是“一味砍”,而是在保留关键信息的前提下,减少冗余Token。
实践中我的几板斧:
- 系统提示词精简:很多人把系统提示词写得像论文,方法论、案例、格式说明一大堆。每次调用都要带着这堆文字,成本是持续的。把系统提示词精简到“完成任务的最小必要信息”,通常能压缩20%-40%的Token;
- 历史消息滑动窗口:不是所有历史消息都对当前决策有用,超过一定轮数或者超出Token阈值,直接把最老的消息截断。注意截断前要把关键信息(比如用户已确认的偏好、已经完成的操作)提取出来,放进一个预摘要里;
- 工具返回结果截断:工具返回的原始数据往往很大,但模型真正需要的可能只是其中几个字段。在工具调用层就做好截断和格式化,只把关键信息返回给模型,能省下大量输入Token。
以我们的工单Agent为例,经过这三板斧的优化,单会话平均Token消耗从2.8万降到了1.4万,成本直接减半,而最终的分类准确率几乎没有变化。
3.3 模型路由分层:让小模型干小事,大模型干大事
成本优化的另一个重要手段是模型分层使用。不是所有请求都需要旗舰模型的推理能力,“杀鸡用牛刀”是Agent成本失控的主要原因之一。
实践中可以把任务按难度分层:
- 简单任务直接走小模型:意图明确、上下文清晰、不需要复杂推理的任务(比如“查一下订单状态”“给我看下库存”),只需调用能力较弱但成本只有几十分之一的小模型;
- 中等任务走中档模型:需要一定理解能力但不需要深度推理的任务,用中档模型完全够用;
- 复杂任务走旗舰模型:涉及多步推理、矛盾信息消解、复杂代码生成等高难度任务,才动用旗舰模型。
关键问题是:怎么判断当前请求属于哪个难度层级?我用过的方案有两种。一种是前置路由:用户请求进来后,先用一个小模型对任务复杂度做一次三分类打分,然后按分类路由到不同模型。另一种是动态升级:先让小模型尝试处理,如果模型自评置信度低、或者输出的结果被校验规则判定为不合格,就升级到更大模型重新处理。
两种方案各有优劣。前置路由多一次小模型调用,但总成本仍然可控;动态升级效果更精准但逻辑更复杂,还要防止“小模型永远在做无用功”的浪费。我建议业务规则比较明确的项目先用前置路由,跑通之后再考虑动态升级。
3.4 成本监控:按用户、按场景、按模型维度归因
成本优化如果只做“省”不做“看”,很快就会出现新的浪费点。生产环境的成本监控,至少要能做到三个维度的归因:
- 按用户维度:哪个用户或哪类用户消耗的Token最多?是否存在恶意刷请求或者异常高频调用?
- 按场景维度:哪个业务场景的成本最高?是RAG检索类的因为上下文太长,还是任务规划类的因为调用轮次太多?
- 按模型维度:哪个模型产生的费用占比最大?有没有小模型就能完成的任务,却被路由到了旗舰模型?
我们没有用复杂的商业BI工具,就是最简朴的方案:在链路追踪平台里给每个请求打上user_id、scene_id、model_name、prompt_tokens、completion_tokens几个标签,每天定时跑聚合分析,输出一份成本报表。有了报表之后,成本优化才有的放矢。
一个很值得关注的指标是单次有效响应的成本——不管中间调了多少次模型、多少个工具,最终只要产出一次让用户满意的回答,这件事的成本是多少。用这个指标来衡量,能过滤掉很多无效调用的噪音,让成本优化的目标更聚焦。
4. 稳定性工程:超时、重试、降级与可观测
4.1 生产环境的失败模式:远不止模型抽风
Agent生产环境的失败模式,比传统后端多得多,而且更容易被忽视。我总结下来大概有这几类:
- 模型调用超时:大模型服务在高负载时,响应时间可能从秒级飙升到几十秒;
- 模型输出格式异常:明明Prompt里写了“必须输出JSON”,模型偶尔会输出带Markdown标记的JSON,直接解析失败;
- 工具API不稳定:下游服务5xx、网络抖动、超时,都会让Agent卡住;
- 上下文超长熔断:Agent循环轮次太多,Token总量超过模型的上下文上限,直接报错;
- 并发限流:业务量突增时,模型服务或下游工具接口都可能触发限流。
这些失败模式的共同特点:呈现长尾分布,单一问题不可预测,但整体概率不低。所以稳定性建设的核心,不是祈祷不出错,而是假设一定会出错,然后把出错后的影响降到最低。
4.2 超时与重试的正确姿势:参数不能拍脑袋
超时设置是稳定性工程里最基础也最容易被轻视的一环。很多团队的超时时间都是拍脑袋设的,导致一遇到慢请求就大量超时失败,或者反过来超时设得太长导致用户体感极差。
我的设置原则:每一层都要设置独立的超时,并且总超时必须有上限。
- 单次LLM调用超时:建议30-60秒。太长容易拖垮整体链路,太短又会影响大模型在复杂任务上的发挥;
- 单次工具调用超时:建议3-5秒。工具接口大部分是内部服务,正常情况下几百毫秒就返回了,3-5秒已经是很大的冗余;
- 整个Agent请求的总超时:建议60-120秒。如果一轮任务连一分钟都跑不完,用户基本已经流失了,这时候不如直接返回一个降级结果。
重试策略也有讲究,严格遵循“指数退避 + 抖动”的原则。第一次失败后等200毫秒重试,第二次失败等500毫秒,第三次等1秒,最多重试2-3次,避免同时失败的大量请求同时重试,形成雪崩效应。加抖动(随机延迟)是为了防止多个请求在同一时间点集体重试。
另外要注意:重试必须幂等。Agent调用工具重试时要考虑,如果第一次调用其实已经成功、只是响应超时,第二次重试会不会造成重复操作?比如“创建订单”“发送邮件”这类非幂等操作,必须在协议层面做幂等控制,或者至少在重试前查询确认前一次调用的结果。
4.3 优雅降级:Agent系统必须有B计划
生产环境里,全线成功是一种不切实际的幻想。稳定性工程的精髓,在于失败时的表现是否还能接受。我给所有Agent项目都定了一个降级阶梯:
- L1黄金路径:所有服务正常,Agent完整执行,返回高质量结果;
- L2部分降级:某个工具API挂了,但Agent可以跳过这一步,用已有的上下文给出一个不够完美但可用的答案;
- L3模板兜底:LLM大规模不可用或者严重超时,不再调用模型,直接从预设模板或最近缓存中选择一个答案返回;
- L4人工接管:完全失败时,把任务信息推送到人工处理队列,由人工兜底。
举个例子:工单Agent在调用客户订单系统失败时,如果没有降级逻辑,整个任务就报废了;如果有降级逻辑,Agent会基于用户对话中已有的信息继续回答,并明确告知“订单系统的信息暂时获取不到,请问您是否可以提供订单号以便后续跟进?”这一句话,就把一次失败变成了一个还说得过去的交互。
降级逻辑必须在设计阶段就想好,而不是出故障时候再去补。生产事故发生时,临时写降级代码是绝对写不出优雅方案的。
4.4 可观测性:不引入观测能力的Agent就是黑盒
最后说可观测性,这是Agent生产化最容易忽略的一环。传统后端只需要知道“请求成功还是失败”,Agent不一样,一个请求可能包含多次模型调用、多次工具调用、多次决策分支,你不仅要知道“失败了”,还要知道“在哪一步失败的、模型当时看到了什么上下文、为什么做出这个决策”。没有这个能力,出了问题就只能靠猜,排查效率极低。
最低可行方案是在链路追踪平台里记录以下关键事件:
- 每一次LLM调用的输入和输出(至少记录Token数和模型参数);
- 每一次工具调用的请求、响应、耗时和状态码;
- Agent每一步决策的类型和时间;
- 整个请求的trace_id,把用户反馈和后台日志串起来。
我强烈建议把Prompt快照也记录下来。生产环境投入运行后,你会经常收到“某条回答效果不好”的反馈,没有Prompt快照就无法复现模型当时看到了什么上下文,优化就无从谈起。
可观测性不只是被动查问题,也可以主动做质量分析。我们每周会拉取一次全量请求日志,统计各场景的成功率、Token消耗、工具调用失败率,任何异常趋势都能提前发现,而不是等用户投诉了再去排查。
5. 调优效果的验证闭环:压测与效果回归
5.1 场景集构建:把回归测试的根基建好
性能调优最大的陷阱是改了之后不知道效果是变好了还是变坏了。尤其是Agent这种生成式的系统,结果质量受波动影响大,一次两次的测试结果根本说明不了问题。所以我在调优之前,一定会先建一个场景测试集。
场景集怎么建?从历史真实会话里抽样,覆盖高频场景、边界场景、异常场景,至少100条以上。每条测试样本包含三部分:输入(用户请求)、期望行为(比如应该调用哪个工具)、可接受结果(比如回答里必须包含的关键信息点)。
很多团队会漏掉异常场景。比如用户输入乱码、用户连续追问5轮却不给必要信息、用户请求超出Agent能力边界,这些场景才是最能暴露不稳定性的。
场景集建好之后,每次调优跑一遍全量测试,把结果和你记录的基线对比,就知道改动到底是正向还是负向的了。
5.2 压测实践:并发上升后的雪崩才是真问题
单请求时延优化得再好,并发一上来,雪崩还是会发生,这类问题只有在压测阶段才能暴露。
压测时重点关注这几个现象:
- 外部模型服务的限流:Model API对账号有每分钟请求数限制,Agent一次任务会调用多次模型,并发用户一旦上来非常容易触发限流。压测之前要先算清楚:如果每秒10个用户,每个用户平均调用3次模型,模型服务的QPS就是30,这个数字要在账号配额允许范围内;
- 线程和连接池耗尽:Agent链路涉及大量IO操作,线程池配置不当,并发一高就全部卡在等待IO上。建议用异步链路的框架,另外给连接池设置合理的最大连接数,防止连接耗尽和后续排队连锁反应;
- 下游服务被打爆:Agent会把用户请求放大成对下游服务的调用,尤其频繁调工具的场景,一个用户可引发5-10次下游请求,压测时要评估下游服务能不能扛住放大后的流量。
压测发现雪崩之后,解决手段基本就是三板斧:限流(请求接入层限制最大并发)、熔断(下游大量失败时快速失败,不再雪上加霜)、扩容(下游服务水平扩容)。
5.3 成本、质量、时延的三角博弈:调优没有银弹
做了这么多调优之后,我越来越觉得一个道理:Agent生产级调优,本质上是在成本、质量、时延三个维度之间找平衡点,而这三点互相制约。
- 想提升质量,调用更大更强的模型,Token成本和时延都会上涨;
- 想降低成本,用更多规则和缓存替代LLM,比较复杂的长尾问题质量就会下降;
- 想降低时延,控制输出Token数量,但过短的输出可能让解释和推理过程不完整,质量受损。
所以每次调优决策,都要先把目标定清楚:这次优化,优先保哪个指标?可以牺牲什么?我之前负责的一个Agent项目,明确的调优目标是“P95从12秒降到8秒以内,单会话成本上浮不超过30%”,这样所有优化动作都有清晰的取舍标准,不会被冲动带偏。
建议每次改动都在同一个测试集上记录三角指标(平均成本、质量通过率、P50/P95时延),形成一张趋势表,时间长了就能看出不同改动方向的规律,对后续调优有很强的指导价值。
6. 还有一些容易被忽略的角落,用经验聊几句
6.1 关于Token统计,不要完全相信返回的数字
模型接口返回的token统计和实际计费数量经常存在差异,尤其流式输出、特殊字符编码、多模态输入等场景下偏差更明显。建议在成本监测里预留5%-10%的误差余量,否则月底账单会对不上。
6.2 流式输出:时延没变,但体感变了
同样的总时延,流式输出让用户先看到一段文字在滚动,心理体感比干等一个完整响应快很多。对面向用户的Agent应用,强烈建议做流式输出。注意流式输出的首字时间(TTFT)优化比总时延优化更能提升体验,系统提示词精简、提前发起预请求都是有效手段。
6.3 灰度发布:别一次性导全量
Agent调优后结果效果到底是好是坏,除了跑回归测试,更稳妥的方式是做小流量灰度。新配置先让5%的流量试运行,观察指标没问题再逐步放量。被吐槽一次“效果变差了”,比慢慢放量付出的代价大得多。
这些实操中的细节,很多不在官方文档里,也不在评估报告里,都是在一次次真实故障和用户反馈中磨出来的经验。Agent生产化这条路,没有任何捷径,把坐标系立起来、把指标量出来、把失败想清楚,才有可能在性能、成本和稳定性之间找到最合适的平衡点。