AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"能对话"到"能干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目,从最初用几十行代码拼一个 ReAct 循环,到后来处理工具调用的参数校验、上下文爆炸、循环失控这些真实问题,踩过的坑比看过的论文多得多。这篇内容想做的事情很直接:把 Agent 从概念拆到工程落地,用七个核心要素讲清楚它由什么构成,再用七个决策点讲清楚搭建时每一步该怎么选、为什么这么选。不管你是刚接触 Agent 开发的新手,还是已经写过 Demo 但卡在稳定性上的开发者,都能从中找到可以直接参考的判断依据。关键词覆盖 AI Agent、LLM、工具调用、循环机制、Agent 架构、Agent 记忆、Agent 安全这些核心概念,全文围绕工程实现展开,不空谈趋势。
1. 先把 Agent 和普通 LLM 调用区分开
很多人第一次接触 Agent 时会有一个误解:觉得给 LLM 加个系统提示词,让它"扮演一个助手",这就是 Agent 了。实际上这两者的差别,类似于"计算器"和"会自己找数据、自己算、自己检查结果的分析师"之间的差别。理解这个差别,是后面所有工程决策的前提。
1.1 单次调用与循环执行的本质差异
普通 LLM 调用的模式是:输入一段文本,模型输出一段文本,结束。整个过程是一次性的,模型没有机会根据输出结果去调整下一步动作。你问它"北京今天天气怎么样",它要么基于训练数据编一个,要么告诉你它不知道。它不会主动去调一个天气接口,拿到结果之后再决定要不要提醒你带伞。
Agent 的核心变化在于引入了循环机制。模型输出不再只是给用户看的文本,而可能是一个"动作指令"——比如调用某个工具、查询某个数据源、写入某个文件。系统执行这个动作后,把结果再喂回给模型,模型基于新信息决定下一步。这个"思考-行动-观察-再思考"的循环,才是 Agent 区别于单次调用的根本。
我用一个具体的对比来说明。假设任务是"帮我查一下公司上季度营收,和去年同期比一下,然后写一段分析"。
普通 LLM 调用会直接生成一段看起来像分析的文字,但数字大概率是编的,因为它没有真实数据。而一个配置了数据库查询工具和分析工具的 Agent,会先调用查询工具拿到两个季度的真实数字,然后调用计算工具做同比,最后基于真实结果生成分析。中间每一步的输出都会影响下一步的决策。
这个差异带来的工程复杂度是数量级的。单次调用你只需要关心提示词和输出格式;Agent 你需要关心工具定义、参数校验、循环终止条件、错误处理、上下文管理、成本控制。这也是为什么很多人写 Demo 很顺,一上生产就各种问题。
1.2 为什么"能调用工具"不等于"会做决策"
另一个常见误解是:只要模型能调用工具,它就是一个 Agent 了。工具调用确实是 Agent 的必要能力,但"会调用"和"会决策"是两回事。
我见过不少实现,把一堆工具的定义塞给模型,然后期望模型自己规划出正确的调用顺序。结果模型要么反复调用同一个工具,要么在应该停止的时候继续调用,要么选了一个完全不相关的工具。这不是模型能力不够,而是工程上没有给模型足够的决策支撑。
真正的决策能力来自几个方面:清晰的工具描述让模型知道每个工具"什么时候该用";合理的循环控制让模型知道"什么时候该停";必要的状态管理让模型知道"已经做了什么、还差什么"。这些都不是模型自带的,而是工程层面设计出来的。
举个实际例子。我在一个文档处理 Agent 里定义了"读取文档""提取段落""生成摘要""保存结果"四个工具。最初版本模型经常在生成摘要后又去调用读取文档,陷入循环。后来我在系统提示里明确写了"摘要生成后任务即完成,不需要再次读取",同时在循环控制里加了"连续两次调用相同工具则强制终止"的规则,问题才解决。这说明工具调用的正确性,很大程度上依赖工程约束,而不是模型自觉。
2. 解构 Agent 的七个核心要素
把 Agent 拆开来看,不管用什么框架、什么语言实现,本质上都包含七个要素。理解这七个要素,你就有了分析任何 Agent 系统的框架,也能在搭建时知道哪些地方不能省。
2.1 模型:不只是选个大的就行
模型是 Agent 的"大脑",负责理解任务、规划步骤、生成工具调用参数、判断是否完成。选模型时最常见的误区是"越大越好"。实际上 Agent 场景对模型的要求和普通对话不一样。
Agent 需要模型具备几个特定能力:结构化输出能力(能稳定输出符合格式的工具调用请求)、指令遵循能力(能严格遵守系统提示里的约束)、多步推理能力(能根据中间结果调整计划)。有些模型在对话上表现很好,但在结构化输出上经常跑偏,这种就不适合做 Agent 的主模型。
我的经验是,主模型优先选工具调用能力经过专门优化的,哪怕参数量小一些。因为 Agent 的循环里模型要被调用很多次,每次调用的稳定性和成本都会被放大。一个 70B 的模型如果每次都能正确输出工具调用,比一个 400B 但十次里有三次格式错误的模型更实用。
另外要考虑成本和延迟的平衡。Agent 一次任务可能触发五到十次模型调用,如果每次调用都很慢很贵,用户体验和运营成本都会出问题。实际项目里我经常采用分层策略:主循环用能力强的模型,一些简单的判断(比如"这个结果是否为空")用轻量模型处理。
2.2 工具:Agent 的手和脚
工具是 Agent 与外部世界交互的接口。没有工具,Agent 就只是一个会说话的模型;有了工具,它才能查数据、写文件、发请求、操作软件。
定义工具时,最关键的不是功能多,而是描述清晰。模型选择工具完全依赖你给的描述。描述里要包含:这个工具做什么、什么时候用、参数是什么含义、返回什么。我见过太多工具描述写得含糊,导致模型乱调用的情况。
一个反例是工具描述只写"查询数据"。模型看到这个描述完全不知道查什么数据、什么场景下该用。正面的写法是"根据用户ID查询该用户最近30天的订单记录,返回订单号、金额、状态列表。当需要了解用户消费情况时使用"。
工具的数量也要控制。我建议单个 Agent 的工具数量控制在 10 到 20 个之间。太少了能力不足,太多了模型选择困难,而且每次调用都要把全部工具定义塞进上下文,token 消耗会很大。如果确实需要很多工具,可以考虑分组,或者用路由机制先判断任务类型再加载对应工具集。
2.3 记忆:短期和长期要分开设计
Agent 的记忆分两类,工程上必须分开处理。
短期记忆是当前任务执行过程中的上下文,包括用户输入、每一轮的模型输出、工具调用结果。这部分通常直接放在对话历史里,随循环不断增长。问题是它会迅速膨胀——一次任务十轮循环,每轮工具返回几千字,很快就超出上下文窗口。
长期记忆是跨任务、跨会话需要保留的信息,比如用户偏好、历史交互摘要、领域知识。这部分需要外部存储,通常是向量数据库或结构化数据库,在需要时检索注入。
短期记忆的处理是工程难点。我的做法是:工具返回结果先做截断或摘要,只保留关键信息再放入上下文;对于很长的历史,定期做压缩,把前面的多轮交互总结成一段简短描述。这样既保留了必要信息,又控制了 token 增长。
长期记忆的关键是检索时机。不是每次都要检索,而是在任务开始时根据用户输入判断是否需要调取历史信息。检索回来的内容也要控制数量,通常 top 3 到 top 5 就够了,太多反而干扰模型判断。
2.4 规划:让模型知道先做什么后做什么
规划能力决定 Agent 能不能处理复杂任务。简单任务模型可以边做边想,复杂任务如果不先规划,很容易做到一半发现方向错了。
规划有两种实现方式。一种是显式规划:在任务开始时让模型先输出一个步骤列表,然后按列表执行。这种方式可控性强,适合流程相对固定的任务。另一种是隐式规划:不单独规划,模型在每轮循环里自己决定下一步。这种方式灵活,适合探索性任务,但容易跑偏。
实际项目里我倾向于混合:先让模型输出一个粗略的计划(三到五步),执行过程中允许根据实际情况调整。这样既有方向感,又保留了灵活性。计划本身也作为上下文的一部分,让模型在每轮都能看到"我原本打算做什么",减少偏离。
2.5 循环控制:Agent 的心跳
循环控制是 Agent 工程里最容易被低估的部分。它决定了 Agent 什么时候继续、什么时候停止、异常时怎么办。
最基本的循环是:模型输出 -> 如果有工具调用则执行 -> 结果回填 -> 再次调用模型 -> 直到模型输出最终答案。但实际实现里要处理的情况多得多:模型连续调用同一个工具怎么办?工具执行报错怎么办?循环次数超过预期怎么办?token 消耗超过预算怎么办?
我的做法是设置多重终止条件:模型明确表示完成、达到最大循环次数、连续多次无有效进展、token 预算耗尽。任何一个触发都终止循环,并给出相应的处理。最大循环次数我一般设 10 到 15,具体看任务复杂度。这个数字不是拍脑袋,而是根据实际任务的平均循环次数上浮 50% 得出的。
2.6 工具调用的参数处理:最容易被忽视的环节
模型生成的工具调用参数经常有问题:类型不对、缺字段、值超出范围、格式不符合要求。如果不做校验直接执行,轻则报错,重则产生副作用(比如删错了数据)。
参数处理要做三件事:校验(检查必填字段、类型、范围)、修正(能自动修的自动修,比如字符串数字转数字)、反馈(不能修的,把错误信息返回给模型让它重新生成)。
我强烈建议工具的参数校验用 schema 定义,而不是手写 if-else。schema 既能用于校验,也能作为工具描述的一部分给模型看,一举两得。校验失败时返回的错误信息要具体,告诉模型"哪个字段错了、期望什么格式",而不是笼统的"参数错误"。
2.7 安全边界:Agent 能做什么不能做什么
Agent 有了工具调用能力,就意味着它能对真实世界产生副作用。发邮件、改数据、执行命令,这些操作一旦出错后果可能很严重。安全边界必须在工程层面强制,不能指望模型自觉。
基本的安全措施包括:权限控制(Agent 只能调用被授权的工具)、操作确认(危险操作需要人工确认或二次验证)、输入过滤(防止提示注入导致 Agent 执行非预期操作)、输出审查(Agent 生成的内容在对外发送前要检查)。
提示注入是 Agent 特有的安全风险。攻击者可能在 Agent 读取的数据里嵌入指令,诱导 Agent 执行非预期操作。防御方法包括:把外部数据和系统指令明确分隔、对数据来源做标记、关键操作不依赖模型判断而是走固定流程。
3. 七个决策点:搭建 Agent 时真正要做的选择
理解了七个要素,接下来是实际搭建时的七个关键决策。每一个决策都会影响系统的稳定性、成本和可维护性。
3.1 决策一:用框架还是自己写循环
这是第一个要面对的选择。市面上有 LangChain、LlamaIndex、AutoGPT 这类框架,也有各种轻量方案。我的判断标准是:看你对循环控制的需求有多精细。
框架的好处是开箱即用,工具定义、循环、记忆这些都有现成实现,上手快。但框架的抽象层往往很厚,当你想精细控制循环行为、优化 token 使用、处理特殊错误时,会发现处处受限。而且框架版本更新快,升级可能带来不兼容。
自己写循环的好处是完全可控,每一行代码你都知道在做什么,优化和排错都直接。代价是要自己处理所有细节,前期投入大。
我的实际选择是:原型阶段用框架快速验证,生产阶段自己写核心循环。核心循环其实不复杂,几百行代码就能覆盖主要逻辑,但可控性带来的收益很大。工具定义、记忆存储这些可以用现成库,但循环和决策逻辑自己掌握。
3.2 决策二:工具调用的格式怎么定
工具调用的格式决定了模型输出和系统解析之间的契约。常见的有两种:基于模型原生工具调用能力(比如某些模型内置的 function calling),或者自定义 JSON 格式让模型输出。
原生工具调用的好处是模型经过专门训练,输出稳定,解析简单。缺点是受模型限制,换模型可能要改。自定义格式的好处是灵活,任何模型都能用,缺点是需要靠提示词约束,稳定性差一些。
我的经验是:如果主模型支持原生工具调用,优先用原生的,稳定性明显更好。如果需要在多个模型间切换,或者模型不支持,就用自定义 JSON,但要在提示词里给足示例,并且解析时做容错。
自定义格式时有个技巧:让模型输出的 JSON 用特定标记包裹,比如<tool_call>...</tool_call>,解析时先提取标记内容再解析 JSON。这样即使模型在 JSON 前后加了说明文字,也能正确提取。
3.3 决策三:上下文怎么管理
上下文管理直接决定 Agent 能跑多复杂的任务、成本有多高。核心矛盾是:信息越多模型判断越准,但 token 消耗越大、越容易超出窗口。
我的策略是分层管理。系统提示和工具定义是固定的,每次都带;当前任务的目标和计划放在显眼位置;工具调用结果做截断,只保留关键字段;历史轮次定期压缩。
具体操作上,工具返回结果我会做预处理:如果是列表,只保留前 N 条加总数;如果是长文本,提取关键段落;如果是结构化数据,只保留相关字段。这个预处理可以在工具实现里做,也可以在回填上下文前做。我倾向于在工具实现里做,因为工具最清楚自己返回的数据里哪些是关键的。
还有一个技巧是用引用代替内容。比如工具返回了一个大文件的内容,上下文里只放文件路径和摘要,需要详细内容时再让模型调用读取工具。这样上下文占用小,需要时又能拿到完整信息。
3.4 决策四:循环什么时候停
循环终止条件的设计,直接关系到 Agent 会不会"卡死"或者"过早放弃"。
我设置的终止条件按优先级排列:模型输出最终答案(正常终止)、达到最大循环次数(保护性终止)、连续两次调用相同工具且参数相同(死循环保护)、工具连续报错超过阈值(异常终止)、token 预算耗尽(成本保护)。
这里有个细节:模型输出最终答案时,怎么判断它是真的完成了还是只是不想继续了?我的做法是在系统提示里明确要求,完成任务时要输出特定标记,比如"任务完成:"开头。同时检查任务目标是否达成,如果模型说完成但明显没做完,可以提示它继续。
最大循环次数的设定要结合任务类型。信息查询类任务通常 3 到 5 轮就够,复杂分析类可能 10 到 15 轮。我一般先设一个保守值,观察实际运行的循环次数分布,再调整。
3.5 决策五:错误怎么处理和恢复
Agent 运行中出错是常态:工具超时、返回格式不对、模型输出解析失败、外部服务不可用。错误处理做得好不好,决定了 Agent 是"偶尔能用"还是"稳定可用"。
我的错误处理分三层。第一层是工具内部:超时重试、异常捕获、返回结构化错误信息。第二层是循环控制:工具报错时,把错误信息回填给模型,让它决定是重试、换工具还是放弃。第三层是系统级:连续错误超过阈值时终止任务,记录日志,通知人工。
关键点是错误信息要回填给模型。很多实现里工具报错就直接终止了,其实模型看到错误信息后往往能自己调整。比如查询工具返回"用户ID不存在",模型可能会去调用另一个工具先查正确的用户ID。给模型自我修正的机会,能显著提升任务成功率。
但也要防止模型在错误上死循环。如果同一个工具连续报错三次,就不要再让它重试了,直接终止并报告。
3.6 决策六:怎么评估 Agent 好不好用
Agent 的评估比普通模型评估复杂,因为它涉及多步执行和外部交互。不能只看最终答案对不对,还要看过程是否合理、成本是否可接受。
我用的评估维度包括:任务完成率(成功完成的任务占比)、平均循环次数(反映效率)、平均 token 消耗(反映成本)、工具调用准确率(调用是否正确、参数是否合理)、错误恢复率(出错后能恢复的比例)。
评估方法上,我会准备一批测试任务,覆盖典型场景和边界情况,每次改动后跑一遍看指标变化。测试任务要包含:简单单步任务、多步任务、需要错误恢复的任务、容易触发循环的任务。这样才能全面反映 Agent 的能力。
有个容易忽视的点是评估要自动化。人工评估成本高、不一致,尽量把评估标准量化,用脚本自动跑。对于需要判断质量的场景,可以用另一个模型做评估(LLM as judge),但要设计好评判标准。
3.7 决策七:怎么部署和监控
Agent 部署和普通服务部署有区别,主要是它的执行时间长、资源消耗波动大、失败模式多。
部署上我建议异步执行。Agent 任务可能跑几十秒甚至几分钟,同步接口容易超时。用任务队列,提交任务后返回任务ID,客户端轮询或通过回调获取结果。这样也方便做限流和资源控制。
监控要覆盖几个关键指标:任务成功率、平均执行时长、token 消耗分布、工具调用失败率、循环次数分布。这些指标能帮你发现系统性问题。比如循环次数突然上升,可能是某个工具返回格式变了;token 消耗上升,可能是上下文管理出了问题。
日志要记录完整的执行轨迹:每一轮的模型输入输出、工具调用和结果、终止原因。出问题时这些日志是排查的唯一依据。我一般会把轨迹存成结构化格式,方便查询和分析。
4. 工具调用与循环机制的工程细节
前面讲了要素和决策,这一节深入两个最核心的工程细节:工具调用和循环机制。这两块做不好,Agent 基本没法用。
4.1 工具定义的 schema 设计
工具定义的质量直接决定模型调用的准确性。一个好的工具定义包含:名称(简洁明确)、描述(做什么、何时用)、参数(名称、类型、是否必填、描述)、返回值说明。
参数描述要特别用心。模型经常在参数上出错,往往是因为描述不清。比如一个"日期"参数,如果只写"日期",模型可能传"今天""昨天"这种自然语言。如果写"日期,格式 YYYY-MM-DD,例如 2024-01-15",模型就会传正确格式。
枚举类型的参数要把所有可能值列出来。比如"操作类型"参数,要明确列出"create/read/update/delete",而不是让模型猜。
必填和选填要分清。必填参数缺失时校验会失败,选填参数缺失时要用默认值。我见过把选填参数标成必填导致模型每次都硬编一个值的,反而引入错误。
4.2 参数校验与自动修正
模型输出的参数不能直接信,必须校验。校验内容包括:必填字段是否存在、类型是否正确、值是否在允许范围、格式是否符合要求。
校验失败时的处理策略:能自动修正的自动修正,不能修正的返回错误让模型重试。自动修正的典型场景:字符串数字转数字、单值转数组、日期格式标准化、枚举值大小写修正。这些修正不改变语义,可以直接做。
不能自动修正的,要把具体的错误信息返回给模型。错误信息要包含:哪个参数错了、期望什么、实际是什么。比如"参数 start_date 格式错误,期望 YYYY-MM-DD,实际收到 '2024年1月15日'"。模型看到这个信息通常能自己改对。
要注意的是,校验和修正逻辑要放在工具执行之前,避免带着错误参数执行产生副作用。
4.3 循环中的状态跟踪
Agent 在多轮循环中需要知道自己做到哪了。没有状态跟踪,模型容易重复劳动或者遗漏步骤。
状态跟踪的实现方式是在上下文里维护一个任务状态区,记录:任务目标、已完成的步骤、当前步骤、待完成步骤、已收集的关键信息。每轮循环后更新这个状态区。
这个状态区可以由模型自己维护(每轮输出时更新),也可以由系统维护(根据工具调用记录自动更新)。我倾向于系统维护关键状态(比如已调用了哪些工具、得到了什么结果),模型维护计划状态(比如当前在哪个步骤)。两者结合,既准确又灵活。
状态区要精简,只放关键信息。把所有历史都塞进去就失去了意义,反而增加 token 负担。
4.4 防止循环失控的几种手段
循环失控是 Agent 最常见的故障。表现包括:反复调用同一工具、在两个工具间来回切换、不断生成相似的计划但不执行。
防御手段有几个层次。提示词层面:明确告诉模型不要重复调用相同工具、如果连续几次没有进展要重新评估策略。循环控制层面:检测重复调用,连续两次相同工具相同参数就干预。状态层面:在状态区显示"你已经调用过 X 工具了,结果是 Y",提醒模型不要重复。
干预的方式可以是终止循环,也可以是插入一条系统消息提醒模型。我一般先用提醒,如果提醒后还是重复再终止。提醒消息比如"你刚刚已经调用过查询工具并得到了结果,请基于这个结果继续,不要重复查询"。
还有一种失控是"假进展":模型每轮都输出一些内容,看起来在推进,实际上没有实质动作。检测方法是看是否有实际的工具调用和新的信息获取。如果连续几轮只有文本输出没有工具调用,且任务未完成,就要干预。
5. 记忆管理与上下文压缩的实操
记忆和上下文是 Agent 工程里最考验设计能力的部分。这一节讲具体怎么做。
5.1 短期记忆的滚动窗口策略
短期记忆就是当前任务的对话历史。最简单的做法是全量保留,但很快会超出上下文窗口。滚动窗口是保留最近 N 轮,但会丢失早期信息。
我的做法是分层保留:最近几轮保留完整内容,更早的轮次只保留摘要。具体是最近 3 轮完整保留,第 4 到第 10 轮保留每轮的关键信息(调用了什么工具、得到了什么关键结果),10 轮以前只保留一个整体摘要。
摘要的生成可以在轮次滚出完整保留区时触发,用模型生成一段简短总结。这样既控制了 token,又保留了必要信息。
要注意的是,任务目标和关键约束要始终保留在上下文里,不能被滚动掉。这些通常放在系统提示或固定的状态区,不参与滚动。
5.2 长期记忆的检索与注入
长期记忆用于跨任务的信息复用。实现上通常是:任务开始时,根据用户输入检索相关历史,把检索结果注入上下文。
检索的关键是时机和数量。不是每个任务都需要检索,简单任务直接做就行。判断标准是任务是否依赖历史信息。比如"帮我再查一下上次那个订单",明显需要检索;"今天天气怎么样"就不需要。
检索数量控制在 3 到 5 条。太多会干扰模型,而且增加 token。检索结果要标注来源和时间,让模型知道这是历史信息。
长期记忆的存储我建议用向量数据库加结构化字段。向量用于语义检索,结构化字段用于过滤(比如按用户ID、时间范围)。纯向量检索容易召回不相关的内容,加上结构化过滤能显著提升准确率。
5.3 上下文压缩的具体做法
上下文压缩是控制 token 的核心手段。压缩的对象主要是工具返回结果和历史轮次。
工具返回结果的压缩在工具实现里做最合适。原则是:只返回模型决策需要的信息。比如查询返回 100 条记录,模型通常只需要知道总数和前几条样例,不需要全部。返回时给总数加前 5 条,需要更多时模型可以再调分页参数。
历史轮次的压缩用摘要。把多轮交互压缩成一段描述,保留关键决策和结果。摘要要包含:做了什么、得到了什么、有什么结论。不要包含过程细节。
压缩的触发时机:当上下文 token 接近阈值时触发,或者定期触发。我一般设一个阈值(比如上下文窗口的 70%),超过就压缩最老的部分。
5.4 记忆污染的防范
记忆污染是指错误或恶意的信息进入记忆,影响后续任务。这在多用户或多任务共享记忆时尤其要注意。
防范措施包括:记忆写入前做校验(格式、来源、内容合理性)、记忆读取时做相关性过滤、敏感信息不写入长期记忆、不同用户/任务的记忆隔离。
还有一个细节是记忆的时效性。有些信息会过期,比如"用户当前的会员等级"。这类信息要么不存长期记忆,要么存储时带时间戳,读取时检查是否过期。
6. 从 Demo 到生产:稳定性与成本控制
Demo 能跑通和生产能稳定运行之间,隔着稳定性、成本、可观测性三座山。这一节讲怎么翻过去。
6.1 稳定性问题的常见来源
Agent 生产环境的稳定性问题,我总结下来主要来自几个方面。
模型输出的不确定性:同样的输入,模型可能给出不同的工具调用。这在 Demo 里不明显,在生产里会导致行为不一致。缓解方法是加强提示词约束、降低温度参数、对关键决策做校验。
外部依赖的不可靠:工具依赖的 API 可能超时、限流、返回异常。必须做超时控制、重试、降级。重试要带退避,避免雪崩。
上下文膨胀:长任务中上下文不断增长,最终超出窗口导致失败。必须做压缩和截断。
循环失控:前面讲过,必须有终止保护。
并发问题:多个任务同时运行时,共享资源(比如记忆存储)可能冲突。需要做好隔离和锁。
6.2 成本控制的几个抓手
Agent 的成本主要来自模型调用。一次任务多次调用,成本是普通对话的数倍甚至数十倍。控制成本要从几个方面入手。
减少不必要的调用:能用规则判断的不用模型。比如工具返回结果是否为空,用代码判断就行,不用问模型。
用轻量模型处理简单环节:不是所有环节都需要最强模型。参数校验、结果判断这些可以用小模型。
控制上下文长度:上下文越长,每次调用越贵。压缩上下文直接降低成本。
设置 token 预算:每个任务设一个 token 上限,超过就终止。防止个别任务消耗过多。
缓存:相同或相似的查询结果可以缓存,避免重复调用。
我实际项目里,通过这些手段能把成本降低一半以上。关键是每个环节都要问一句"这个模型调用真的必要吗"。
6.3 可观测性建设
Agent 出问题时,没有可观测性基本没法排查。必须记录完整的执行轨迹。
我记录的字段包括:任务ID、用户输入、每一轮的模型输入输出、工具调用及参数、工具返回、每轮的 token 消耗、终止原因、总耗时。这些存成结构化日志,方便查询和统计。
基于这些日志可以做很多分析:哪些工具调用最频繁、哪些任务最容易失败、token 消耗的分布、循环次数的分布。这些分析能指导优化方向。
告警要设置关键指标:任务失败率、平均耗时、token 消耗异常。超过阈值就告警,及时发现系统性问题。
6.4 灰度与回滚
Agent 的改动(提示词、工具、模型)都可能影响行为,不能直接全量上线。要灰度发布,先小流量验证,观察指标正常再扩大。
回滚机制要准备好。提示词、工具定义这些配置化,出问题能快速切回旧版本。模型切换也要能快速回退。
我一般会保留最近几个版本的配置,出问题时对比新旧版本的执行轨迹,快速定位是哪个改动导致的。
7. 一些踩坑后的经验总结
最后分享几个我在实际项目中踩过坑才明白的点,都是文档里不会写的。
工具描述比工具实现更重要。我花在写工具描述上的时间,比写工具实现还多。描述写好了,模型调用准确率能提升一大截。
不要相信模型的"我完成了"。模型经常在没完成时说自己完成了。要有独立的完成判断,比如检查关键结果是否存在。
错误信息要具体。笼统的错误信息模型没法利用,具体的错误信息模型能自己修正。这个投入产出比很高。
循环次数要监控。循环次数突然上升往往是问题的早期信号,比任务失败更早暴露问题。
上下文里放什么比放多少重要。精简但关键的信息,比全量但冗余的信息效果好得多。
测试用例要覆盖失败场景。只测正常流程的 Agent,上线后会被各种异常打垮。要专门构造工具报错、参数错误、循环失控的场景来测。
提示词要版本管理。提示词的改动对行为影响很大,必须像代码一样管理,能追溯、能回滚。
别追求一次做完美。Agent 系统是迭代出来的,先跑通核心流程,再逐步优化稳定性、成本、体验。一开始就追求完美,往往什么都做不出来。
这套东西我前后迭代了好几轮,从最初几十行的 Demo 到现在能稳定处理复杂任务的系统,每一步都是被实际问题推着走的。希望这些经验能让你少走一些弯路。Agent 工程化这件事,概念不难,难的是把每个细节都处理到位,而细节恰恰决定了它能不能真正用起来。