开门见山说一句:如果你正在做智能体类应用,大概率已经经历过来自账单的惊吓。一个看起来并不复杂的问答任务,最后却消耗了几十万token;一个本该三步结束的工具调用,因为模型绕来绕去的思考而变成十几步。这些问题如果不在设计阶段就处理,等Agent跑起来之后再去优化,成本会呈指数级上升。这篇主要讲三件事:资源感知优化、推理技术、评估与监控。这三者放在一起,本质上是在解决同一个问题:让智能体不仅“能做”,而且“做得起、做得快、做得好”。很多人把资源感知当成省钱工具,其实不止。一次长时间运行的Agent任务,如果上下文越来越长,响应速度会越来越慢,最终可能直接超时。资源问题会传导到产品体验、稳定性和可观测性,是Agent能否从Demo走向生产的核心约束。所以这期的内容不聊概念,全部按可落地的思路来讲。1. 资源感知优化:让智能体像老会计一样精打细算1.1 资源感知不是提前压预算,而是让Agent知道自己还能花多少首先要纠正一个误区:资源感知优化不是把所有Prompt压缩到极致,也不是一律选用小模型。这些东西如果做得太激进,Agent会变得“听不懂人话”,最终靠重试和反复运行来补救,总体成本反而更高。我理解的资源感知,是Agent在每一步行动时,都清楚自己当前还有多少Token预算、多少步骤预算、多少成本限额,并能根据这些约束主动调整策略。比如:当上下文还剩15000个Token时,模型应当决定是否调用一个可能返回大量内容的工具;当任务已经消耗了90%预算但还没找到答案时,Agent不应该继续埋头执行,而应该明确告诉上层“我建议换一种方式”。本质上,这就是给Agent加了一个“大脑里的记账本”。每一次模型调用、每一轮工具返回、每一步思考消耗,都要被记录并且反馈到下一步决策中。很多框架已经支持usage回传,但真正把usage数据当决策依据的项目很少。大多数情况是日志里记了,代码里没用。1.2 先把常见模式的资源消耗摸透,再谈优化先看一张我平时做设计时经常用到的对照表,把常见的Agent模式、主要消耗和优化方向放在一起看:模式主要资源消耗点典型风险推荐优化方向基础循环(LLM调用+工具执行)Prompt中的上下文重复、模型输出token上下文膨胀,响应变慢缓存历史、裁剪对话、压缩总结单轮工具调用工具返回结果直接塞进上下文工具输出过大导致超限截断、结构化提取、按需获取字段检索增强生成每轮检索重复Embedding、文档片段过多检索片段占满上下文缓存向量、只取关键片段、重排序多Agent协作多个模型并行调用,协商轮次多token按倍数消耗,延迟高限制对话轮数、引入仲裁模型自我反思/自我修正重复生成反思结果与原答案反思过度导致成本翻倍设定反思次数上限,只在小范围重试长文档处理长文本分段、多轮摘要、压缩单次处理成本过高先粗筛再精读、增量式摘要这张表其实揭示了一个规律:Agent消耗资源的真正大头,往往不是回答本身,而是“反复”。上下文反复携带、工具反复调用、思考反复生成、错误反复修正。要控制资源,最重要的是控制重复。1.3 三种立刻能用的资源控制手段:预算、缓存、降级先说预算。很多开源Agent框架都有max_iterations这类参数,但它只限制步数,不限制Token和成本。更合理的做法是三元预算:步数预算、Token预算、成本预算。任意一个被击中,就触发终止或者降级。举个例子,可以这样定义一个运行上下文:class AgentBudget: def __init__(self, max_steps, max_cost, max_tokens): self.max_steps = max_steps self.max_cost = max_cost self.max_tokens = max_tokens self.current_steps = 0 self.total_cost = 0 self.total_tokens = 0 def can_continue(self): return ( self.current_steps < self.max_steps and self.total_cost < self.max_cost and self.total_tokens < self.max_tokens ) def register_usage(self, usage_data, cost_data): self.current_steps += 1 self.total_tokens += usage_data.get("total_tokens", 0) self.total_cost += cost_data每次模型返回后,把usage和cost注册进去,循环判断can_continue。这套逻辑大概半小时就能接完,但对稳定性的提升非常明显。再说缓存。缓存分三层:Prompt缓存:同一段系统提示词在大模型侧本身就可能有缓存,这部分能省就省,不要在每次请求里动态拼接重复内容。工具结果缓存:同一个查询条件、同一个用户、同一个工具,在5分钟内返回过相同结果,可以直接复用。向量检索缓存:相同Query命中的文档片段结果可以缓存,避免重复Embedding。第三是降级。不要全局只用一个大模型。判断任务难度后,可以用一条简单的路由规则:需要复杂推理时走大模型,简单分类、提取、改写直接走小模型。这个路由不一定要靠模型来判,用关键词、参数数量、工具类型等规则就能覆盖80%的场景。预算越紧张,路由越要前置。2. 推理技术:既要答得对,又要算得快2.1 控制推理深度,比控制模型参数更有效控制推理技术最容易犯的错,是把所有任务都设计成“先让模型想一遍再回答”。对于复杂任务,这没问题;但对于明确的任务,比如“把这个地址解析成结构化字段”“将日期转换成时间戳”,让模型做深度推理就是纯浪费。如果你检查一下真实项目里的账单,会发现大量Token都花在了模型重新发明轮子上。模型在推理时把JSON schema重新描述了一遍,把工具说明重新理解了一遍,甚至把用户问题转述了一遍。这些都属于可压缩成本。一个比较有效的办法,是在Prompt中显式设置推理预算。不是只设置max_tokens,而是把“推理应该多长”写进系统提示词,例如:对于简单指令:“直接返回结果,不要解释过程。”对于中等任务:“最多给出三条推理过程,然后直接输出。”对于复杂任务:“允许分步思考,但每步不超过50个Token。”这样模型会倾向于在给定范围内输出。滥用长思考链,在正确率上不一定有回报,但在账单上一定有回报。2.2 用“守门员模式”拦截明显可以零成本解决的问题推理优化不是每次都要请大模型出场。很多请求在到达大模型之前,就可以用规则、函数或者小模型解决,这就是守门员模式。我常写的一个基础拦截逻辑大致是这样:def preflight_check(user_input, available_tools): # 1. 如果输入已经是合法JSON,直接解析,不调用LLM if looks_like_json(user_input): return parse_and_return(user_input) # 2. 如果用户明确要求调用某个工具,且参数齐全 if extract_intent(user_input) in available_tools: return call_tool_directly(user_input) # 3. 如果这是一个数学计算或时间日期查询 if is_calculator_query(user_input): return run_calculator(user_input) # 4. 以上都不满足,才进入LLM return route_to_llm(user_input)这个模式看起来简单,但大多数人不敢做,因为担心规则太脆。实际经验是:只需要拦截掉最明显的几个高频场景,就能省下10%到30%的推理成本,而且这些场景的规则几乎不会误杀。2.3 用结构化输出压缩往返次数推理优化另一个大头是重试。模型输出不符合预期,然后解析失败、再调用一次模型、再失败,这种循环比模型本身贵得多。解决思路是:把输出格式从“自由文本”变成“强约束结构”。在调用模型时,直接声明输出JSON Schema,并要求模型按Schema返回。如果是工具调用,优先使用函数调用模式,让模型输出参数而不是自由结果。这里有个容易被忽略的细节点:很多模型在输出结构化对象时,会把多余的说明文字也带出来。要解决,可以在解析时只提取第一个合法JSON块,其余内容全部丢弃,不要因为旁边多了几个字就判定失败并重试。当然,也不要因为校验失败就直接重试整个推理过程。更好的做法是:如果格式差一点点,就做修正;如果语义都错了,才需要重新走模型。重试本身也应该消耗预算,否则模型会利用“反正可以无限重来”的空间反复尝试。2.4 并行与串行的选择:该并行时不要等待推理技术的另一个关键点是任务拆分。当一个Agent任务需要执行多个独立工具调用时,串行执行不仅慢,而且每一步都会把前一步结果重新塞进上下文,造成Token重复增长。如果工具之间没有依赖关系,一定要设计成并行调用。比如同时查询天气、日历、航班信息,这三个调用互相独立,完全可以在同一个循环里发出,然后统一接收结果。并行不仅能降低延迟,还能减少上下文里的中间轮次。核心原则:能独立就并行,能合并就合并,能缓存就缓存。不过并行也要有上限,不能一次开几十个线程去调外部接口。一般我会给Agent设置一个“最大并发工具数”,默认3到5个,避免外部服务限流或者下游系统被打爆。运行时还要有超时控制,单个工具超过10秒就标记失败并继续其他任务。3. 评估与监控:没有观测感的智能体就是一只深不见底的黑箱3.1 先定义“好”,再把“好”变成可测指标很多项目把Agent写好以后,靠人工点几个页面觉得“效果不错”就上线了。这在技术评审上还凑合,一旦用户规模上来,问题根本没法定位。评估和监控的前提,是先有一套统一指标。我自己常用的Agent效果指标体系如下:指标类别具体指标用途任务成功率目标是否完成、最终回答是否满足要求判断Agent有没有能力效率指标完成步数、耗时、总Token数判断Agent有没有绕路稳定指标超时率、失败率、重试次数判断Agent可不可靠成本指标单任务成本、模型调用次数判断Agent值不值得跑质量指标工具调用正确率、上下文命中率判断中间决策是否准确这套指标不是给监控面板看的,它必须能拆到每一次运行里。一个任务为什么花了20步?拆开看,可能是某一步反复调用了同一个工具;一个任务为什么Token消耗巨大?拆开看,可能是某一个中间结果太大了。不能只看最终结果,过程指标才是定位问题的关键。3.2 离线评估:用“考场”把Agent的底细摸清楚在把Agent投入线上之前,最好先建一个离线评估集。不要追求规模大,要追求覆盖到真实场景。至少包含三类样本:简单指令:明确、单步能完成,用于检验基础执行能力。复杂任务:需要多步推理、工具联动,用于检验规划能力。边界场景:缺参数、条件冲突、需要拒绝执行,用于检验安全能力。评估脚本里需要同时校验最终结果和中间过程。最终结果可以用字符串匹配、语义相似度或LLM裁判来打分;中间过程要检查“是否调用了预期工具”“参数是否正确”“是否出现了多余步骤”。这一步比最终结果评估更重要,因为很多错误会在中间环节被补救掉,但补救本身也意味着资源浪费。离线评估不是做一次就结束。每次修改Prompt、模型或工具逻辑,都应该重跑同一套基线集。我个人习惯把评估结果写入每次提交的备注里,确保“效果变好”是有数据支撑的,而不是“感觉变好”。3.3 线上监控:每次Agent动作都要能回放监控的第一原则:所有关键信息都要落日志。不要只记录最终答案,要记录每一个步骤的输入、输出、模型名称、Token用量、耗时、工具名、参数、异常信息。举个例子,一次运行的日志可以这样组织:{ "trace_id": "a1b2c3", "session_id": "user-123-session-456", "step": 2, "model": "large-model-v3", "input_tokens": 4521, "output_tokens": 126, "cost_usd": 0.012, "latency_ms": 834, "tool_name": "search_documents", "tool_arguments": {"q": "site architecture"}, "error": null }每行日志都必须带上trace_id和session_id。这样一旦线上出现高成本任务或异常回答,可以直接把整条链路拉出来复盘,而不是对着一个错误信息猜。监控指标上,至少要有四块面板:成本、延迟、错误率、Token消耗分布。成本面板要能按用户、按模型、按工具下钻;延迟面板要能按模型和工具维度分析;错误率面板要重点盯“同一工具连续失败”和“Agent进入死循环”;Token分布面板要看上下文重复率和高消耗单步。3.4 用“死循环检测”保护运行时智能体最常见的线上事故,就是陷入某个回路反复执行。比如Agent发现查询结果不够,调用搜索,搜索返回更多结果,又把结果全部塞进上下文,继续搜索……这种循环不消耗步骤预算吗?不一定,因为很多死循环里的每一步都算步骤预算,但步骤预算往往设置得很大。我建议至少做三件事:设置最大步骤限制,默认不超过10到15步。设置“同一工具重复调用检测”,同一工具连续调用超过3次且参数相似,立即终止。设置“状态无变化检测”,如果前后两轮的工具结果和模型输出没有实质差异,就说明在空转。这些检测优先级很高,比模型本身是否聪明更重要。因为模型偶尔会犯糊涂,但系统不能跟着一起糊涂。4. 从资源感知到运营的闭环:一个可以直接抄作业的最小样板4.1 给Agent循环加装“仪表盘+限速器”把资源感知、推理优化、评估监控串起来的方式,不是去改每一个Prompt,而是在Agent运行循环外加一层统一运行时。这层运行时负责:预检、预算判断、模型路由、结构化校验、日志埋点、循环检测。一个最小实现可以这样写:def run_agent_with_guard(agent, user_input, config): budget = AgentBudget( max_steps=config["max_steps"], max_cost=config["max_cost"], max_tokens=config["max_tokens"] ) session_log = [] context = initialize_context(user_input) while budget.can_continue(): # 预检:先用规则/小模型判断可以直接回复还是需要工具 preflight_result = preflight_check(context) if preflight_result.should_stop: return preflight_result.answer, session_log # 模型路由:根据任务复杂度选择不同模型 selected_model = route_to_model(context, budget) # 推理预算:动态设置max_tokens max_tokens = calculate_dynamic_max_tokens(context, selected_model) # 调用模型 response, usage = selected_model.invoke( context, max_tokens=max_tokens, output_schema=preflight_result.expected_schema ) # 记录用量,更新预算 budget.register_usage(usage, estimate_cost(usage, selected_model)) # 校验输出 if not validate_step(response): handle_fallback(response, context) continue # 死循环检测 if is_repeat_without_change(session_log, response): return "检测到重复执行,已自动终止。", session_log # 记录日志 session_log.append({ "step": budget.current_steps, "model": selected_model.name, "token_usage": usage, "cost": budget.total_cost, "response": response, }) # 更新上下文(可能裁剪、压缩) context = update_context_under_budget(context, response, budget) return handle_budget_exceeded(context), session_log这里每一步都有对应函数,每个函数都不复杂,组合起来就能覆盖绝大多数场景。不需要一开始就上一套复杂框架,先把这条循环跑通,再逐步扩展。4.2 上线前一定要避开的五个坑第一坑:为了省Token,把系统提示词删得只剩下关键词。结果模型输出质量肉眼可见下降,靠重试找回效果,成本反而更高。提示词压缩可以,但不能把约束和示例全部删光。第二坑:只设max_steps,不检查步骤内容。步骤上限能挡死循环,但挡不住重复劳动。就算只跑6步,可能6步都在调同一个工具,所以必须有重复调用检测。第三坑:评估只看最终回答,不看中间规划。这样Agent会变成“结果对了但路全走错”的选手。一旦环境变化或工具参数变化,正确率会立刻崩塌。第四坑:日志里什么都有,但没有关联ID。离线排查时根本没有办法把一次用户会话的过程串起来,只能靠时间戳碰运气。建议从第一天起就强制设计trace_id和session_id。第五坑:刚开始做模型路由就追求“所有任务都用小模型”,最后小模型反复无法处理任务,大模型又被降级,整体体验和成本双输。路由要先跑日志,分析清楚哪些任务高频、哪些任务廉价,再定策略。4.3 迭代节奏:先跑通,再省钱,再上监控如果你现在手头已经有一个Agent,建议按这个顺序改造:先把运行日志补齐,能回放每一步;再跑一周线上数据,找出成本最高的20%任务;针对这些任务做预算限制和模型路由;然后离线评估集跟上;最后把监控面板和预警接好。不要一上来就同时做所有优化。我见过不少团队,第一天就把Prompt压缩、动态模型路由、日志埋点全部铺开,结果反而无法判断是哪个改动带来的效果变化。一步一步来,每一步都有对照组,比一次性改造可维护得多。回到开头那句话:智能体不是“在思考”,而是在“消费”。每一次推理消耗Token,每一次工具调用消耗时间和成本,每一次错误重试消耗预算。做资源感知优化、推理控制、评估监控,不是为了显得工程严谨,而是让Agent作为产品能长期立得住。这套东西越早落地,后期返工就越少。
开门见山说一句:如果你正在做智能体类应用,大概率已经经历过来自账单的惊吓。一个看起来并不复杂的问答任务,最后却消耗了几十万token;一个本该三步结束的工具调用,因为模型绕来绕去的思考而变成十几步。这些问题如果不在设计阶段就处理,等Agent跑起来之后再去优化,成本会呈指数级上升。这篇主要讲三件事:资源感知优化、推理技术、评估与监控。这三者放在一起,本质上是在解决同一个问题:让智能体不仅“能做”,而且“做得起、做得快、做得好”。很多人把资源感知当成省钱工具,其实不止。一次长时间运行的Agent任务,如果上下文越来越长,响应速度会越来越慢,最终可能直接超时。资源问题会传导到产品体验、稳定性和可观测性,是Agent能否从Demo走向生产的核心约束。所以这期的内容不聊概念,全部按可落地的思路来讲。1. 资源感知优化:让智能体像老会计一样精打细算1.1 资源感知不是提前压预算,而是让Agent知道自己还能花多少首先要纠正一个误区:资源感知优化不是把所有Prompt压缩到极致,也不是一律选用小模型。这些东西如果做得太激进,Agent会变得“听不懂人话”,最终靠重试和反复运行来补救,总体成本反而更高。我理解的资源感知,是Agent在每一步行动时,都清楚自己当前还有多少Token预算、多少步骤预算、多少成本限额,并能根据这些约束主动调整策略。比如:当上下文还剩15000个Token时,模型应当决定是否调用一个可能返回大量内容的工具;当任务已经消耗了90%预算但还没找到答案时,Agent不应该继续埋头执行,而应该明确告诉上层“我建议换一种方式”。本质上,这就是给Agent加了一个“大脑里的记账本”。每一次模型调用、每一轮工具返回、每一步思考消耗,都要被记录并且反馈到下一步决策中。很多框架已经支持usage回传,但真正把usage数据当决策依据的项目很少。大多数情况是日志里记了,代码里没用。1.2 先把常见模式的资源消耗摸透,再谈优化先看一张我平时做设计时经常用到的对照表,把常见的Agent模式、主要消耗和优化方向放在一起看:模式主要资源消耗点典型风险推荐优化方向基础循环(LLM调用+工具执行)Prompt中的上下文重复、模型输出token上下文膨胀,响应变慢缓存历史、裁剪对话、压缩总结单轮工具调用工具返回结果直接塞进上下文工具输出过大导致超限截断、结构化提取、按需获取字段检索增强生成每轮检索重复Embedding、文档片段过多检索片段占满上下文缓存向量、只取关键片段、重排序多Agent协作多个模型并行调用,协商轮次多token按倍数消耗,延迟高限制对话轮数、引入仲裁模型自我反思/自我修正重复生成反思结果与原答案反思过度导致成本翻倍设定反思次数上限,只在小范围重试长文档处理长文本分段、多轮摘要、压缩单次处理成本过高先粗筛再精读、增量式摘要这张表其实揭示了一个规律:Agent消耗资源的真正大头,往往不是回答本身,而是“反复”。上下文反复携带、工具反复调用、思考反复生成、错误反复修正。要控制资源,最重要的是控制重复。1.3 三种立刻能用的资源控制手段:预算、缓存、降级先说预算。很多开源Agent框架都有max_iterations这类参数,但它只限制步数,不限制Token和成本。更合理的做法是三元预算:步数预算、Token预算、成本预算。任意一个被击中,就触发终止或者降级。举个例子,可以这样定义一个运行上下文:class AgentBudget: def __init__(self, max_steps, max_cost, max_tokens): self.max_steps = max_steps self.max_cost = max_cost self.max_tokens = max_tokens self.current_steps = 0 self.total_cost = 0 self.total_tokens = 0 def can_continue(self): return ( self.current_steps < self.max_steps and self.total_cost < self.max_cost and self.total_tokens < self.max_tokens ) def register_usage(self, usage_data, cost_data): self.current_steps += 1 self.total_tokens += usage_data.get("total_tokens", 0) self.total_cost += cost_data每次模型返回后,把usage和cost注册进去,循环判断can_continue。这套逻辑大概半小时就能接完,但对稳定性的提升非常明显。再说缓存。缓存分三层:Prompt缓存:同一段系统提示词在大模型侧本身就可能有缓存,这部分能省就省,不要在每次请求里动态拼接重复内容。工具结果缓存:同一个查询条件、同一个用户、同一个工具,在5分钟内返回过相同结果,可以直接复用。向量检索缓存:相同Query命中的文档片段结果可以缓存,避免重复Embedding。第三是降级。不要全局只用一个大模型。判断任务难度后,可以用一条简单的路由规则:需要复杂推理时走大模型,简单分类、提取、改写直接走小模型。这个路由不一定要靠模型来判,用关键词、参数数量、工具类型等规则就能覆盖80%的场景。预算越紧张,路由越要前置。2. 推理技术:既要答得对,又要算得快2.1 控制推理深度,比控制模型参数更有效控制推理技术最容易犯的错,是把所有任务都设计成“先让模型想一遍再回答”。对于复杂任务,这没问题;但对于明确的任务,比如“把这个地址解析成结构化字段”“将日期转换成时间戳”,让模型做深度推理就是纯浪费。如果你检查一下真实项目里的账单,会发现大量Token都花在了模型重新发明轮子上。模型在推理时把JSON schema重新描述了一遍,把工具说明重新理解了一遍,甚至把用户问题转述了一遍。这些都属于可压缩成本。一个比较有效的办法,是在Prompt中显式设置推理预算。不是只设置max_tokens,而是把“推理应该多长”写进系统提示词,例如:对于简单指令:“直接返回结果,不要解释过程。”对于中等任务:“最多给出三条推理过程,然后直接输出。”对于复杂任务:“允许分步思考,但每步不超过50个Token。”这样模型会倾向于在给定范围内输出。滥用长思考链,在正确率上不一定有回报,但在账单上一定有回报。2.2 用“守门员模式”拦截明显可以零成本解决的问题推理优化不是每次都要请大模型出场。很多请求在到达大模型之前,就可以用规则、函数或者小模型解决,这就是守门员模式。我常写的一个基础拦截逻辑大致是这样:def preflight_check(user_input, available_tools): # 1. 如果输入已经是合法JSON,直接解析,不调用LLM if looks_like_json(user_input): return parse_and_return(user_input) # 2. 如果用户明确要求调用某个工具,且参数齐全 if extract_intent(user_input) in available_tools: return call_tool_directly(user_input) # 3. 如果这是一个数学计算或时间日期查询 if is_calculator_query(user_input): return run_calculator(user_input) # 4. 以上都不满足,才进入LLM return route_to_llm(user_input)这个模式看起来简单,但大多数人不敢做,因为担心规则太脆。实际经验是:只需要拦截掉最明显的几个高频场景,就能省下10%到30%的推理成本,而且这些场景的规则几乎不会误杀。2.3 用结构化输出压缩往返次数推理优化另一个大头是重试。模型输出不符合预期,然后解析失败、再调用一次模型、再失败,这种循环比模型本身贵得多。解决思路是:把输出格式从“自由文本”变成“强约束结构”。在调用模型时,直接声明输出JSON Schema,并要求模型按Schema返回。如果是工具调用,优先使用函数调用模式,让模型输出参数而不是自由结果。这里有个容易被忽略的细节点:很多模型在输出结构化对象时,会把多余的说明文字也带出来。要解决,可以在解析时只提取第一个合法JSON块,其余内容全部丢弃,不要因为旁边多了几个字就判定失败并重试。当然,也不要因为校验失败就直接重试整个推理过程。更好的做法是:如果格式差一点点,就做修正;如果语义都错了,才需要重新走模型。重试本身也应该消耗预算,否则模型会利用“反正可以无限重来”的空间反复尝试。2.4 并行与串行的选择:该并行时不要等待推理技术的另一个关键点是任务拆分。当一个Agent任务需要执行多个独立工具调用时,串行执行不仅慢,而且每一步都会把前一步结果重新塞进上下文,造成Token重复增长。如果工具之间没有依赖关系,一定要设计成并行调用。比如同时查询天气、日历、航班信息,这三个调用互相独立,完全可以在同一个循环里发出,然后统一接收结果。并行不仅能降低延迟,还能减少上下文里的中间轮次。核心原则:能独立就并行,能合并就合并,能缓存就缓存。不过并行也要有上限,不能一次开几十个线程去调外部接口。一般我会给Agent设置一个“最大并发工具数”,默认3到5个,避免外部服务限流或者下游系统被打爆。运行时还要有超时控制,单个工具超过10秒就标记失败并继续其他任务。3. 评估与监控:没有观测感的智能体就是一只深不见底的黑箱3.1 先定义“好”,再把“好”变成可测指标很多项目把Agent写好以后,靠人工点几个页面觉得“效果不错”就上线了。这在技术评审上还凑合,一旦用户规模上来,问题根本没法定位。评估和监控的前提,是先有一套统一指标。我自己常用的Agent效果指标体系如下:指标类别具体指标用途任务成功率目标是否完成、最终回答是否满足要求判断Agent有没有能力效率指标完成步数、耗时、总Token数判断Agent有没有绕路稳定指标超时率、失败率、重试次数判断Agent可不可靠成本指标单任务成本、模型调用次数判断Agent值不值得跑质量指标工具调用正确率、上下文命中率判断中间决策是否准确这套指标不是给监控面板看的,它必须能拆到每一次运行里。一个任务为什么花了20步?拆开看,可能是某一步反复调用了同一个工具;一个任务为什么Token消耗巨大?拆开看,可能是某一个中间结果太大了。不能只看最终结果,过程指标才是定位问题的关键。3.2 离线评估:用“考场”把Agent的底细摸清楚在把Agent投入线上之前,最好先建一个离线评估集。不要追求规模大,要追求覆盖到真实场景。至少包含三类样本:简单指令:明确、单步能完成,用于检验基础执行能力。复杂任务:需要多步推理、工具联动,用于检验规划能力。边界场景:缺参数、条件冲突、需要拒绝执行,用于检验安全能力。评估脚本里需要同时校验最终结果和中间过程。最终结果可以用字符串匹配、语义相似度或LLM裁判来打分;中间过程要检查“是否调用了预期工具”“参数是否正确”“是否出现了多余步骤”。这一步比最终结果评估更重要,因为很多错误会在中间环节被补救掉,但补救本身也意味着资源浪费。离线评估不是做一次就结束。每次修改Prompt、模型或工具逻辑,都应该重跑同一套基线集。我个人习惯把评估结果写入每次提交的备注里,确保“效果变好”是有数据支撑的,而不是“感觉变好”。3.3 线上监控:每次Agent动作都要能回放监控的第一原则:所有关键信息都要落日志。不要只记录最终答案,要记录每一个步骤的输入、输出、模型名称、Token用量、耗时、工具名、参数、异常信息。举个例子,一次运行的日志可以这样组织:{ "trace_id": "a1b2c3", "session_id": "user-123-session-456", "step": 2, "model": "large-model-v3", "input_tokens": 4521, "output_tokens": 126, "cost_usd": 0.012, "latency_ms": 834, "tool_name": "search_documents", "tool_arguments": {"q": "site architecture"}, "error": null }每行日志都必须带上trace_id和session_id。这样一旦线上出现高成本任务或异常回答,可以直接把整条链路拉出来复盘,而不是对着一个错误信息猜。监控指标上,至少要有四块面板:成本、延迟、错误率、Token消耗分布。成本面板要能按用户、按模型、按工具下钻;延迟面板要能按模型和工具维度分析;错误率面板要重点盯“同一工具连续失败”和“Agent进入死循环”;Token分布面板要看上下文重复率和高消耗单步。3.4 用“死循环检测”保护运行时智能体最常见的线上事故,就是陷入某个回路反复执行。比如Agent发现查询结果不够,调用搜索,搜索返回更多结果,又把结果全部塞进上下文,继续搜索……这种循环不消耗步骤预算吗?不一定,因为很多死循环里的每一步都算步骤预算,但步骤预算往往设置得很大。我建议至少做三件事:设置最大步骤限制,默认不超过10到15步。设置“同一工具重复调用检测”,同一工具连续调用超过3次且参数相似,立即终止。设置“状态无变化检测”,如果前后两轮的工具结果和模型输出没有实质差异,就说明在空转。这些检测优先级很高,比模型本身是否聪明更重要。因为模型偶尔会犯糊涂,但系统不能跟着一起糊涂。4. 从资源感知到运营的闭环:一个可以直接抄作业的最小样板4.1 给Agent循环加装“仪表盘+限速器”把资源感知、推理优化、评估监控串起来的方式,不是去改每一个Prompt,而是在Agent运行循环外加一层统一运行时。这层运行时负责:预检、预算判断、模型路由、结构化校验、日志埋点、循环检测。一个最小实现可以这样写:def run_agent_with_guard(agent, user_input, config): budget = AgentBudget( max_steps=config["max_steps"], max_cost=config["max_cost"], max_tokens=config["max_tokens"] ) session_log = [] context = initialize_context(user_input) while budget.can_continue(): # 预检:先用规则/小模型判断可以直接回复还是需要工具 preflight_result = preflight_check(context) if preflight_result.should_stop: return preflight_result.answer, session_log # 模型路由:根据任务复杂度选择不同模型 selected_model = route_to_model(context, budget) # 推理预算:动态设置max_tokens max_tokens = calculate_dynamic_max_tokens(context, selected_model) # 调用模型 response, usage = selected_model.invoke( context, max_tokens=max_tokens, output_schema=preflight_result.expected_schema ) # 记录用量,更新预算 budget.register_usage(usage, estimate_cost(usage, selected_model)) # 校验输出 if not validate_step(response): handle_fallback(response, context) continue # 死循环检测 if is_repeat_without_change(session_log, response): return "检测到重复执行,已自动终止。", session_log # 记录日志 session_log.append({ "step": budget.current_steps, "model": selected_model.name, "token_usage": usage, "cost": budget.total_cost, "response": response, }) # 更新上下文(可能裁剪、压缩) context = update_context_under_budget(context, response, budget) return handle_budget_exceeded(context), session_log这里每一步都有对应函数,每个函数都不复杂,组合起来就能覆盖绝大多数场景。不需要一开始就上一套复杂框架,先把这条循环跑通,再逐步扩展。4.2 上线前一定要避开的五个坑第一坑:为了省Token,把系统提示词删得只剩下关键词。结果模型输出质量肉眼可见下降,靠重试找回效果,成本反而更高。提示词压缩可以,但不能把约束和示例全部删光。第二坑:只设max_steps,不检查步骤内容。步骤上限能挡死循环,但挡不住重复劳动。就算只跑6步,可能6步都在调同一个工具,所以必须有重复调用检测。第三坑:评估只看最终回答,不看中间规划。这样Agent会变成“结果对了但路全走错”的选手。一旦环境变化或工具参数变化,正确率会立刻崩塌。第四坑:日志里什么都有,但没有关联ID。离线排查时根本没有办法把一次用户会话的过程串起来,只能靠时间戳碰运气。建议从第一天起就强制设计trace_id和session_id。第五坑:刚开始做模型路由就追求“所有任务都用小模型”,最后小模型反复无法处理任务,大模型又被降级,整体体验和成本双输。路由要先跑日志,分析清楚哪些任务高频、哪些任务廉价,再定策略。4.3 迭代节奏:先跑通,再省钱,再上监控如果你现在手头已经有一个Agent,建议按这个顺序改造:先把运行日志补齐,能回放每一步;再跑一周线上数据,找出成本最高的20%任务;针对这些任务做预算限制和模型路由;然后离线评估集跟上;最后把监控面板和预警接好。不要一上来就同时做所有优化。我见过不少团队,第一天就把Prompt压缩、动态模型路由、日志埋点全部铺开,结果反而无法判断是哪个改动带来的效果变化。一步一步来,每一步都有对照组,比一次性改造可维护得多。回到开头那句话:智能体不是“在思考”,而是在“消费”。每一次推理消耗Token,每一次工具调用消耗时间和成本,每一次错误重试消耗预算。做资源感知优化、推理控制、评估监控,不是为了显得工程严谨,而是让Agent作为产品能长期立得住。这套东西越早落地,后期返工就越少。