1. 为什么Agent一跑起来就成了“黑盒”
过去半年我陆续接手了几个Agent项目的性能排查工作,一个很深的感触是:Agent项目的demo阶段都很好跑,最难的部分往往不是“让它跑起来”,而是“让它稳定地、可控地、便宜地跑下去”。很多团队在原型阶段用OpenAI或开源模型加一个ReAct循环quick win,效果惊艳,但一到压力测试、多用户并发、长会话场景,就完全看不清楚系统内部到底发生了什么。
我用一个真实的例子说明问题。有一次我要排查一个RAG问答Agent的响应卡顿,这个Agent的工作流是:接收用户问题,调用意图识别模型,触发知识库检索,再经过一轮内容抽取,最后喂给大模型做带引用的生成。单看每一步,耗时都在合理范围,但整条链路跑下来,用户反馈明显变慢。如果系统对每一步都没有观测点,你根本没法判断瓶颈到底出在检索阶段的向量匹配上、模型推理的排队上,还是后处理阶段做了多余的token拼接。我的同事开玩笑说,这种排查相当于蒙着眼睛修汽车,只能靠“感觉哪里不对劲”来猜测。
这正是Agent观测与优化要解决的核心问题。Agent和传统后端服务最大的区别在于它有一个“循环思考——行动——观察”的自治过程。传统服务是确定性的:一个请求进来,经过固定的中间件栈,返回一个可预期的结果。Agent不一样,它的每一步依赖它自己的中期输出、外部工具返回的数据、甚至上一个错误信息,路径是多变的、非确定的。这种天然的不确定性放大了两方面问题:第一,出错之后难以回溯,因为你不知道它为什么会选择这一步;第二,性能与成本几乎不可控,因为token消耗和工具调用次数完全由模型自主决定。
我在做摸底的过程中接触了不少做Agent开发的工程师。总结下来,大家最迫切的需求集中在三块:一是基础的链路追踪,即每一个Agent step调用过大模型、外部工具、内部记忆库的全程耗时与入参出参;二是状态观测,即Agent在每一轮循环中记忆了什么、决定执行什么,以及这个决策是否合理;三是成本与质量的数据化,也就是每一轮对话到底花了多少钱、性能是否在用户可接受范围、能不能在开发阶段就内置一套回归对比机制。
这篇博文就想把这些实战中的经验完整梳理一遍。内容包含三层:先讲Agent观测的基本模型和埋点思路,再讲优化方向,最后分享一份可以复用到自己项目中的摸底报告模板。不管你是刚接触Agent开发,还是已经在生产环境里维护Agent服务,这篇文章都值得花几分钟过一遍——很多坑我是真金白银烧了token才踩明白的。
2. Agent观测的基本模型:从“链路”到“行为”再到“质量”
2.1 第一个层面:链路追踪,Agent版“降雨雷达”
传统分布式系统的链路追踪我们已经很熟了:一个请求穿越网关、微服务、数据库、缓存,每个环节打一个span,最后串联成一条完整的trace。Agent系统沿用这套思路完全没有问题,但需要扩展。
在Agent场景里,一个trace对应一次完整的用户请求,其中的span主要包含三大类:LLM调用(模型名称、temperature、max_tokens、prompt摘要、响应摘要、token用量、耗时)、外部工具调用(工具名称、参数、返回结果截断、状态码、耗时)、内部逻辑操作(记忆读取、意图路由、检索filter等)。我习惯给每种span打上type标签,这样在追踪系统里就能一眼筛出哪一类环节占比最大。
实操时要注意一个细节:LLM调用的prompt和响应不要全量上报。一次复杂的Agent循环里,prompt可能包含整段历史对话、检索回来的长文本切片,动辄几千token,全量存下来无论是存储成本还是检索速度都不划算。我的做法是prompt取前100字符加后100字符做摘要,响应在关键节点保留完整结果,其余只存token数和总耗时。排查细节问题的时候再根据traceId去日志系统捞完整数据。
还有一个很多团队会忽略的点:工具调用的参数和返回结果往往比LLM调用本身更能说明问题。有一次我排查一个Agent反复调用同一个搜索工具的死循环问题,就是因为没有在工具span里记录返回内容的指纹(hash值)。加上指纹后发现,它搜出来的结果一模一样,但Agent没有判断“结果重复”,于是又加了一道基于相似度的结果去重逻辑,死循环直接消失。这个教训值一大笔钱。
2.2 第二个层面:行为与状态观测,理解Agent“当时的想法”
链路追踪可以看到“做了什么”,但看不到“为什么这么做”。要想回答后者,需要状态观测。Agent框架(LangGraph、CrewAI、自研的ReAct循环等)一般都有一份内部状态对象,里面保存了当前步骤、历史消息、中间变量、记忆库的最近条目。我的建议是,在每个主要步骤切换时打一套“状态快照”,快照里包含当前步骤名、上一步输出摘要、当前记忆条目数、接下来准备调用的函数名。
这不是为了给运维看,而是为了给开发阶段调试用的。有一次模型在循环里反复确认“是否已完成用户请求”,我通过状态快照发现,上下文窗口里积累了大量系统内部提示和工具返回结果,把用户的最终需求挤到了注意力边缘。这种问题如果你只看链条,看到的是“模型反复调用同一个工具”,但看状态快照才知道是“记忆管理策略错误,上下文被污染了”。
状态观测在生产环境里要控制采样率。全量采样每个步骤的状态对象会让存储压力急剧上升,尤其是长会话场景。我个人的经验是开发环境全量采样,生产环境按用户维度的百分比采样,配合错误链路自动联动全量采样——具体实现就是,当链路最终状态标记为error或timeout时,自动把这一步前后的状态快照补齐。这样既控制了成本,也能保证问题发生时拿到完整的证据链。
2.3 第三个层面:质量观测,指标化Agent的“表现好坏”
链路追踪和行为观测解决了“发生了什么”,但还差一个“做得好不好”的维度。Agent没有传统意义上的HTTP状态码,判断一次交互成不成功,需要额外定义一套质量指标。
我常驻在指标体系里的包括:任务完成率(用户评价、硬性结果校验)、单轮平均工具调用次数、上下文压缩触发频率、记忆覆盖冲突次数、兜底策略(比如“我不知道”或转人工)触发次数、用户无反馈沉默率。其中任务完成率是最有说服力的一个指标。如果是代码生成Agent,就检查生成结果能否通过编译或单元测试;如果是客服Agent,就通过会话结束后的“用户是否继续追问”来反推。
做质量观测的前提是有一套离线评测集。我至少会准备50个典型的用户输入样本,覆盖正常请求、含歧义请求、需多步工具调用的复合请求、包含隐式指代的历史关联请求。每次对提示词、模型参数、工具逻辑做任何改动后,都拿这套样本跑一遍回归评测,记录几个核心指标的升降。没有这套机制的Agent项目,优化起来基本靠手感,改一次prompt就不知道是变好了还是变坏了。
3. 埋点方案与工具选型,上下游打通的实战记录
3.1 明确埋点数据模型,先有数据再谈分析
观测体系能不能落地,很大程度取决于埋点数据结构设计得好不好。我在多个项目里总结了一套通用的Agent事件模型,核心字段包括:run_id(一次会话的唯一ID)、step_id(当前步骤序号)、parent_id(上一步ID,用于串链)、agent_state(当前状态摘要)、event_type(可选值:llm_start、llm_end、tool_start、tool_end、memory_read、memory_write、error)、content_summary(内容摘要)、cost_usd(折算费用)、latency_ms(耗时)、raw_ref(原始日志索引)。
这套模型之所以实用,是因为它兼容了“链路追踪”和“状态观测”两种诉求。链路追踪通过parent_id就能画出完整执行树;状态观测则靠agent_state字段记录每一步的动态变化。数据落到存储层时,我习惯同时写两个地方:ClickHouse存结构化的事件数据,用于聚合统计;对象存储或日志服务存全量原始输入输出,用于按run_id检索复盘。两个存储之间用run_id关联,既保证了分析速度,又不丢细节。
采集层还有一个很容易踩的坑:同步上报会影响Agent主流程的响应时间。尤其是调用外部API的环节,如果每次工具调用后都要同步上报一次遥测数据,额外会多出几十毫秒的开销。正确做法是本地用一个有界队列缓存事件,后台线程批量异步上报,队列满时直接丢弃最早期的事件,并记录丢弃计数。有人觉得丢事件不专业,但实际中如果观测系统都挂了,优先保住核心Agent流程才是对的,事后能看到丢弃了多少本身就是一种重要告警信号。
3.2 工具选型:自建还是用开源生态
市面上的Agent观测工具,大体分三类:一是LLM应用观测平台(LangSmith、Langfuse、W&B Weave等),二是通用可观测性平台自带的GenAI观测能力(比如OpenTelemetry近两年的GenAI语义约定,以及Datadog、Grafana的AI监控模块),三是自己用日志系统加数据看板搭的一套轻量方案。
我的建议是:小团队、刚起步的项目,别一上来就引一堆平台。先用LLM应用观测平台(比如Langfuse)跑通埋点与基础看板,通常这类平台对LangChain/LlamaIndex等主流框架是原生支持的,接入成本低,很快能看到成效。等团队规模变大、需要跟现有监控告警体系打通时,再考虑走OpenTelemetry协议统一上报,把Agent事件和微服务Metrics全部汇聚到中心化平台。
如果你选用LangGraph这类框架,它的内置checkpointer和流式事件机制可以直接钩子埋点。我的经验是在Graph的每个节点外面包一层Wrapper函数:节点开始、结束、异常都触发事件回调,统一格式后上报。这样即使后面节点逻辑改了,埋点代码也不需要大动。尽量不要把埋点逻辑散写在业务代码内部,一是不好维护,二是容易在改业务时被误删。
自建方案适合已有成熟监控体系的团队。我就见过一个团队,用Grafana加Prometheus加Loki,完全在内部快速搭出了一套Agent观测看板:指标用Prometheus抓(延迟、token数、错误率),日志用Loki收(完整轨迹),看板用Grafana拼。这套方案的好处是和数据中台、告警系统天然打通,代价是高级分析功能,比如轨迹对比、评测回放、成本分摊报表,都需要自己实现,需要花不少精力。结论就是:没有最好,只有适不适合现阶段团队规模。
4. 优化方向拆解:延迟、成本、质量,三条线分开推进
4.1 延迟优化:先用数据定位,再做定向手术
Agent响应慢,常见原因无非那么几个:LLM推理耗时占比过大、工具调用串行化导致总延迟累加(比如检索、搜索、计算各要等一次)、上下文过长导致每轮生成的prefill时间上升、外部工具本身响应慢(下游接口延迟也算在Agent头上)。遇到响应慢的问题,第一件事不是调prompt,而是拿链路上的耗时分布说话。我通常直接看两个指标:整条链路里所有LLM调用的耗时占比,以及所有工具调用的耗时占比。哪个占比高就先优化哪个。
如果是LLM耗时占大头,优先考虑三个动作:一是精简上下文,把历史消息做摘要压缩,控制prompt总token数在合理区间,这会直接降低prefill时间;二是给决策型步骤换更小更快的模型,只在最终生成阶段用最强模型,很多框架支持按节点指定不同的model;三是启用流式输出,用户至少能第一眼看到字在动,心理上的感知延迟会大幅下降。工具调用完后再边生成边输出,体验提升非常明显。
工具串行化是另一个经常被忽视的延迟瓶颈。比如有一个总结类Agent需要先搜索A话题、再搜索B话题、最后合并成报告。如果三个搜索都互不依赖,完全可以并行发起。现在主流Agent框架都支持tool并行执行,只需在工具定义里标记哪些tool允许并发,再加上token或并发数的限量控制。我实测过一个场景,串行总耗时约8秒,改造为并行后降到3.2秒,整条链路直接从“难以接受”变成“丝滑”。而且一个是搜索类工具,本来就有网络延迟,并联收益显著;另一个是推理类工具,确保单次并发数不要太高,避免触发限排。
4.2 成本优化:每分钟都在烧钱,必须盯着“每任务成本”
很多人在意Agent的token消耗,但实际更值得关注的是“每任务完成成本”,也就是用户一个需求从发起到完成为止,平均消耗一个修正(error retry)、缓存命中率、不必要的格式化(例如大模型反复输出JSON又解析失败重试)、以及prompt里重复带入的长文档。建模时先用trace数据算出基准线,再逐步削减那些“低价值token”。
结合我在多个项目的经验,成本优化最有效的三张牌:一是引入语义缓存。对用户的问题向量化,命中相似度阈值就直接复用历史答案,这一步对高频重复类Agent尤其划算,我见过最高能把成本砍掉接近一半;二是合理设置max_tokens和temperature。很多Agent的生成步骤并不需要写超长内容,max_tokens给个合理上限就行,防止模型偶尔抽风写一大篇小作文;三是压缩重复工具结果。RAG场景下检索回来的文档会在多轮生成中一遍遍被塞进prompt,我通常的做法是第一轮引用过的文档在下一轮就只保留摘要,需要展开再单独检索,可以显著降低重复计费。
还有一招容易被忽略:控制工具调用“重试风暴”。有一次我排查成本翻倍的原因,发现外部接口偶发超时,Agent每超时一次就自动重试三次,每次重试都是一轮新的LLM调用,等于一个失败的步骤烧了四次生成的钱。后来在工具封装里加了重试上限,同时把重试之间的退避时间拉长,成本肉眼可见地回落。工具失败的容忍度设计,应该纳入成本优化的考量范围,而不是只当稳定性问题来看。
4.3 质量优化:评测集驱动回归,别靠感觉调参
质量优化是三个方向里最容易被玄学化的,因为“效果变好”没有一个硬性定义。解决方法是把质量衡量指标化:正确性、完整性、忠实度(没有编造)、遵守约束的程度。制定好打分维度后,找两三个业务同学和两三个技术同学,针对评测集逐条打1到5分,算平均分作为当前版本的基线。
然后每次修改都只改一个变量。比如这次只换prompt,下次只调整检索阈值的top_k,再下次只给工具增加一个输入校验。改完跑一遍完整评测集,对比分数变化。这样慢慢地就会积累出“哪些变量对效果影响最大”的经验。我见过踩坑最深的例子,是一次性改了模型、重写了system prompt、换了检索库,结果分数大幅下跌,但完全无法判断是哪个步骤导致的回退。
另外,质量优化不要把精力全花在prompt上。工具本身的能力边界非常关键。有一次客服Agent频繁答非所问,排查后才发现是商品信息的结构化数据里缺了库存字段,导致模型只能靠猜。补上字段之后,答非所问率直接下降好几个百分点。这提醒我们:Agent的效果上限由它能够获取的信息质量和工具完整性决定,提示词只是把已有的信息有效组织起来,别指望提示词凭空变出它根本拿不到的东西。
5. 摸底报告模板:先建基线,才能谈优化
5.1 报告结构与关键指标
每次Agent项目要动大手术之前,我都会先产出一份摸底报告。这份报告的作用不是给领导看“做了什么”,而是给团队一个可对照的基准:优化前后到底有没有变好、好在哪、坏在哪。报告结构我固定为六块:背景与目标、系统架构图、观测数据概览、瓶颈定位、优化方案与效果、遗留风险。
观测数据概览是整个报告的核心,我会附上这些数据——平均端到端延迟(P50、P90)、单任务平均LLM调用次数、单任务平均工具调用次数、单任务平均token消耗与费用、工具调用失败率、兜底触发率、评测集平均分。这些数字组合起来就能反映一个Agent的真实健康度。延迟和成本可以合并看,但一定要分开报告,因为两者优化手段不同;评测集平均分则是“最终效果好不好”最直接的证据。
报告发布时还要附带一个“数据置信度说明”:样本量多少、来自哪个环境(测试环境还是生产环境)、哪些指标是采样得到的、采样率是多少。没有置信度说明的数据很容易误导后续判断。我看到过团队拿着生产环境1%采样的延迟数据去反推整体性能,结果被样本噪声带偏,白折腾了好几周。
5.2 一次摸底排查实录:从模糊“卡顿”到精准定位
为了让大家更直观地理解报告怎么用,分享一个刚做完不久的排查案例。接手的是一个用于市场调研的Agent服务,用户上传几份PDF文档,Agent负责提炼要点、对比数据,最后输出一份分析报告。业务方反馈“越来越慢,而且回答经常答非所问”,但对根因完全没有头绪。
我按摸底模板走了一遍流程。首先在测试环境里准备了30个样本,从最简单的“单文档摘要”到复杂的“多文档对比”,全量跑完后发现端到端P50已经到11秒,而用户可接受的阈值是6秒左右。进一步拆链路看到,有一次多文档对比任务里,Agent做了17次LLM调用和6次工具调用,上下文窗口几乎全程拉满,token消耗是平均情况的两倍还多。问题很明显了:Agent在尝试“记住”所有文档内容,而不是检索后按需引用。
根因定位后,优化方案分两步:第一步在“文档加载”环节加入内容索引,改成先建立摘要库,按用户问题动态检索最相关的片段;第二步在“对比分析”环节把对比项拆解为结构化查询,避免模型自己逐条去原文里翻数据。改造之后,同样30个样本重新跑一遍,P50从11秒降到4.8秒,单任务平均费用降了六成,评测集平均分反而从3.9提升到4.5。回头看,如果没有基线数据和链路拆分,这个优化过程大概率会沦为空谈。
5.3 报告落地后的行动清单与周期性复验
摸底报告的最终价值落在行动清单上。我通常会把报告末尾的优化建议分优先级:P0是“不修会持续烧钱或持续报错”的问题,P1是“体验受损但短期可容忍”的问题,P2是“锦上添花”的优化项。P0项在当周就排期修复,P1项进下个迭代,P2项记录在案等有空再回来处理。
还需要强调一点:摸底报告不是一次性产物。Agent涉及到的模型版本、提示词、外部工具、评测集都会变,基线数据每过一段时间就失去了参考价值。我的实践是每个月固定跑一遍完整评测与延迟/成本基准,把数据变化趋势记录在同一张表里,做成一个时间序列。这样做的好处是,任何一次改动导致效果回退都能在几天内被数据捕捉到,而不是等到用户大面积抱怨时才发现。长线来看,Agent领域的“观测驱动开发”最终形态就是这样一个闭环:埋点、基线、优化、回归、再基线。
6. 常见Agent认知误区与排查心得
6.1 认知误区:Agent能搞定一切的预期管理
和不少刚接触Agent的同行交流下来,我发现一个很普遍的认知误区:以为只要把框架搭好、prompt写清楚,Agent就能自动处理各种边界情况。这个预期如果不被纠正,后面的观测和优化工作都没法正常展开。Agent不是万能的,它的行为上限由模型能力、工具完善度、上下文管理策略三者共同决定,任何一个掉链子都会影响最终交付。
预期管理的实际动作,是在系统设计上就给Agent留“后手”。比如设定最大循环次数,超过就停止并给用户一个明确的提示,而不是让它无限烧钱重试;再比如关键业务场景设计“人工兜底”通知,Agent自认为完成的任务,在低置信度场景下自动转人工复核。这些设计不是技术上的妥协,而是产品成熟的表现。摸底报告里也应该加入一条“系统边界能力”的说明,列出当前Agent已知做不好的场景,能省掉后面大量无意义的优化尝试。
6.2 真实项目里最常踩的坑
把我在多个Agent项目里遇到的高频问题整理成一个速查表,方便大家对照排查。有些问题看起来不起眼,实际引发的连锁反应非常大。
| 问题现象 | 根因方向 | 排查建议 |
|---|---|---|
| Agent重复调用同一工具 | 缺少结果去重/状态标记机制 | 在工具返回里加内容hash,设置“已见”标记 |
| 回答开始偏离主题 | 上下文被大量工具结果污染 | 检查状态快照里的记忆条目数,做摘要压缩 |
| 延迟突然升高 | 上下文窗口缓慢拉长 | 监控LLM调用的prompt token数是否逐轮上涨 |
| 成本翻倍且无业务量增长 | 重试风暴/长文本重复进prompt | 检查失败后重试次数,压缩工具返回内容 |
| 同一问题反复答错 | 对外部知识/数据依赖不充分 | 排查知识库字段完整性,而非只调prompt |
| 并发量上来后大量超时 | Agent调度与外部工具限流冲突 | 加并发信号量,工具调用前做令牌桶限流 |
这个表最有价值的地方在于,它把“症状”和“排查方向”串联起来了。很多新手一遇到Agent表现不佳,第一反应就是改prompt,但实际上多数问题出在状态管理和工具调用策略上。我自己最早也犯过这个毛病,后来养成了习惯:遇到问题先查观测数据再动手,能把无效改动减少一半以上。
6.3 经验沉淀:优化改动前先打对照基线
最后分享一个我坚持了很久的好习惯:任何优化改动落地之前,先跑一遍完整的指标基线。这不只是跑评测集,还包括记录上下文覆盖形态、工具调用的类型分布、分包过大的token开销等过程性数据。只有基线完整了,才能保证改动前后的对比是可信的,不会被个例噪声带偏。
我一般准备两份基线:一份是快速基线,用20到30条覆盖各业务场景的样本跑一遍,耗时不过十几分钟,适合日常微调;另一份是全面基线,用上百条样本加多种边界情况跑一遍,耗时较长,适合每次大版本发布前夕。发布后一周内,再对比生产环境的真实指标。这种“发布前全面基线加发布后生产监控”的组合,我自己用下来效果最稳定,推荐给所有正在做Agent产品化的团队。
7. 摸底工作收尾时的一些个人感悟
把Agent当黑盒用,可能在原型阶段跑得通,但越往生产走越寸步难行。观测与优化不是“上线之后再考虑”的事,而应该是Agent开发流程的一部分。每写一个节点、每调一次工具,就应该顺手带上埋点;每修改一次prompt、每换一次模型,就应该顺手跑一遍基线。把这些习惯内化之后,你会发现Agent项目的可维护性和可扩展性会有一个明显的提升。
我个人最深的体会是:Agent优化的本质不是把某一个指标压到极限,而是在延迟、成本、质量三条线之间做平衡。省了token不一定体验好,跑得快不一定答得对。只有把三条线的数据都拿到手里,才能做出理性的取舍。这个过程靠感觉走不远,靠数据才能越做越扎实。希望大家都能把各自的Agent项目,从“跑起来”推进到“跑明白”的阶段。