ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI Agent工程化实战:七个必须做对的决策点

AI Agent工程化实战:七个必须做对的决策点 AI Agent 这个词在过去一年里被反复提及但真正动手搭过一套能跑起来的系统的人都知道从知道它是什么到让它稳定干活之间隔着一道巨大的鸿沟。我见过太多团队兴冲冲地拉起一个 Demo结果卡在工具调用返回格式不对、循环停不下来、上下文越滚越长这些具体问题上。这篇内容不打算再重复Agent 等于 LLM 加记忆加工具这类概念科普而是把 Agent 拆成七个必须做决策的工程要素逐个讲清楚每个要素在实现层面到底要做什么选择、为什么这么选、以及选错了会怎样。如果你正在准备搭自己的 Agent 项目或者已经搭了一个但总觉得哪里不对劲这篇应该能帮你把思路理顺。1. 先搞清楚 Agent 和普通 LLM 调用的分界线在哪很多人第一次接触 Agent 时最容易犯的错误是把带函数调用的 LLM 请求直接当成 Agent。这两者之间的差别不是功能多少而是控制权在谁手里。1.1 单次调用与循环决策的本质区别普通的 LLM 调用是一个线性过程你给一个 prompt模型返回一段文本或一个工具调用请求流程结束。哪怕你加了 function calling本质上还是一问一答模型没有机会根据工具返回的结果再决定下一步做什么。Agent 的核心在于把控制权交给模型。模型不只是回答一个问题而是进入一个循环观察当前状态、决定下一步动作、执行动作、获取结果、再观察、再决策直到它自己判断任务完成或者触发终止条件。这个循环机制才是 Agent 的骨架。我通常用一个简单的判断标准来区分如果你的系统里下一步做什么是由代码里的 if-else 决定的那它只是一个工作流如果下一步做什么是由模型根据当前上下文推理出来的那它才算 Agent。这个区分非常重要因为它直接决定了你后面所有的工程决策——工作流你可以穷举所有分支Agent 你没法穷举只能设计好约束和边界。1.2 为什么这个分界线决定了后续所有架构选择一旦你承认控制权在模型手里你就必须面对一系列新问题模型可能选错工具、可能陷入死循环、可能把上下文撑爆、可能调用一个根本不存在的函数。这些问题在工作流范式里几乎不存在因为每一步都是你写死的。所以 Agent 的工程实现本质上不是怎么让模型更聪明而是怎么在一个模型可以自主决策的系统里保证它不跑偏。这个认知转变很关键。很多人搭 Agent 时把精力全花在 prompt 调优上结果发现模型再聪明也架不住工具描述写得含糊、循环没有终止条件、错误没有兜底。工程上的严谨程度比模型能力更能决定一个 Agent 能不能上线。2. 七个要素里哪几个是真正卡脖子的把 Agent 拆成七个要素——模型、提示词、工具、记忆、循环、编排、可观测性——这个框架本身没问题但七个要素的权重完全不一样。根据我自己的踩坑经验真正容易让项目翻车的是工具、循环和记忆这三个其余四个更多是做好了加分、做差了能用。2.1 工具定义描述写不好模型再强也白搭工具是 Agent 的手脚。一个工具对模型来说就是一段描述加一个参数 schema。模型完全靠这段描述来判断什么时候该用这个工具、怎么填参数。我见过最典型的翻车场景是两个工具的功能有重叠描述里又没写清楚边界模型就在两者之间反复横跳一会儿调 A 一会儿调 B任务永远完不成。写工具描述有几个实操要点。第一描述里要写清楚什么时候用而不只是这是什么。比如一个查询订单的工具不要只写查询订单信息而要写当用户询问订单状态、物流进度、预计送达时间时使用此工具需要提供订单号。第二参数描述要给出格式示例尤其是日期、枚举值这类容易填错的字段。第三工具数量要克制我个人的经验是单次暴露给模型的工具不要超过 15 个超过之后选择准确率会明显下降这时候应该考虑做工具分组或者分层路由。2.2 循环终止没有刹车再好的车也不敢开循环机制是 Agent 的心脏但也是最容易被忽视的地方。一个没有终止条件或者终止条件写得含糊的循环轻则浪费 token重则陷入死循环把账单跑爆。终止条件至少要设计三层。第一层是模型自主判断完成也就是模型在输出里明确表示任务结束不再请求工具调用。第二层是硬性步数上限比如最多循环 20 次到了就强制停止并返回当前结果。第三层是异常熔断比如连续三次工具调用失败、或者检测到重复调用同一个工具同样的参数就立即中断。这里有个细节值得展开硬性步数上限设多少合适我的经验是看任务复杂度。简单的信息查询类任务5 到 8 步足够涉及多步推理和多次工具调用的复杂任务15 到 25 步比较合理。设太小会导致任务没做完就被截断设太大又失去了保护意义。更好的做法是动态调整——根据任务类型预设不同的上限而不是全局一个值。2.3 记忆管理上下文不是越多越好记忆这块最大的误区是把所有历史都塞进上下文。上下文窗口确实在变大但塞得越多模型注意力越容易被稀释而且成本是线性增长的。我的做法是把记忆分成三层来处理。短期记忆是当前任务的对话历史保留完整但要及时压缩工作记忆是任务执行过程中的中间结果比如工具返回的关键数据这部分要结构化存储而不是原样堆在对话里长期记忆是跨会话的知识需要时通过检索召回而不是常驻上下文。压缩策略上我常用的是滑动窗口加摘要保留最近 N 轮完整对话更早的内容用模型生成一段摘要替代。摘要的 prompt 要明确要求保留已确认的事实、未完成的待办、关键决策丢掉寒暄和冗余的中间过程。实测下来这样能把上下文长度控制在原来的三分之一左右而任务完成率几乎不受影响。3. 七个决策点每个都对应一个具体的工程取舍理解了要素之后真正落地时你会遇到七个必须做的决策。这些决策没有标准答案但有明显的优劣之分选错了会在后期付出很大代价。3.1 决策一模型选大还是选小还是混用这是第一个要拍板的事。大模型推理能力强、工具调用准确率高但贵且慢小模型便宜快但复杂任务容易掉链子。我的建议是分层混用。把任务拆成规划和执行两类规划阶段用大模型因为它需要理解复杂意图、拆解步骤、选择工具执行阶段用中小模型因为这时候任务已经被拆得很具体了模型只需要按部就班地填参数、调工具。这样能在保证效果的前提下把成本压下来一大截。具体怎么分一个简单的判断标准如果这一步需要理解用户到底想要什么用大模型如果这一步只是把已知的参数填进已知的工具用小模型。实测下来这种混用方案相比全程用大模型成本能降 40% 到 60%而任务成功率只下降几个百分点。3.2 决策二工具调用用原生还是用提示词模拟现在主流模型都支持原生的 function calling但有些场景下你可能会考虑用提示词让模型输出特定格式的 JSON 来模拟工具调用。这两者怎么选原生 function calling 的优势是格式稳定、解析可靠模型经过专门训练输出结构基本不会跑偏。缺点是受模型支持情况限制而且有些模型的原生调用对复杂嵌套参数支持不好。提示词模拟的优势是灵活你可以定义任意复杂的输出结构不依赖模型的原生能力。缺点是格式稳定性差模型偶尔会漏字段、加注释、或者输出非法 JSON你需要写健壮的解析和重试逻辑。我的选择是能用原生就用原生原生搞不定的复杂结构再用提示词模拟并且一定要加 JSON 修复和重试机制。所谓 JSON 修复就是在解析失败时尝试用正则提取、补全括号、去掉多余逗号等方式抢救实在不行再把错误信息喂回模型让它重新生成。3.3 决策三循环用固定步数还是动态判断前面提到循环要有步数上限但这里有个更细的决策是简单地设一个固定上限还是根据任务进展动态判断该不该继续。固定步数的实现最简单但不够智能。有些任务三步就完成了却要等到上限才停有些任务明明已经卡住了却还在傻傻地循环。动态判断的做法是引入一个进展评估环节每轮循环后让模型或者一个轻量规则判断距离目标是否更近了。如果连续两轮没有实质进展就主动终止。这个判断可以很简单比如检查是否产生了新的工具调用结果、是否更新了任务状态。复杂一点可以用一个小模型来做进展打分。我自己的项目里用的是混合方案设一个宽松的硬上限作为兜底同时加一个连续无进展则终止的动态条件。这样既不会因为上限太紧而误杀也不会因为模型卡住而空转。3.4 决策四错误处理是重试还是降级还是上报Agent 执行过程中出错是常态——工具超时、返回格式不对、模型输出不合规这些都会发生。关键是怎么处理。我的原则是分级处理。可恢复的错误比如网络抖动导致的超时自动重试重试次数控制在 2 到 3 次并且用指数退避避免雪崩。不可恢复的错误比如工具返回了明确的业务错误码不重试直接把错误信息作为观察结果喂回模型让它决定下一步怎么办。连续失败达到阈值比如同一个工具连续失败 3 次就触发降级要么换一个替代工具要么把问题上报给用户。这里有个容易忽略的点错误信息要写给模型看而不是写给人看。很多人习惯把错误日志原样返回但模型看不懂堆栈信息。正确的做法是把错误转换成模型能理解的描述比如订单查询失败原因是订单号格式不正确请确认后重试这样模型才知道该怎么调整。3.5 决策五状态存内存还是存外部Agent 执行过程中会产生大量状态对话历史、工具调用记录、中间结果、任务进度。这些状态存哪里直接影响到系统的可靠性和可扩展性。存内存最简单但进程一重启就全丢了而且没法水平扩展。存外部数据库、Redis、对象存储更可靠但增加了复杂度和延迟。我的建议是按状态的生命周期分开处理。会话级的短期状态可以放内存加定期持久化任务级的执行状态必须落库因为任务可能跨请求、跨进程。尤其是涉及人工介入的场景比如 Agent 需要用户确认才能继续状态必须持久化否则用户确认回来时上下文已经丢了。3.6 决策六可观测性做到什么颗粒度Agent 是个黑盒出了问题如果只看到任务失败了根本没法排查。可观测性必须做到能还原每一步决策。至少要记录这几类信息每一轮循环的输入上下文摘要、模型的原始输出、工具调用的参数和返回、耗时、token 消耗。这些数据要能按任务 ID 串起来形成一个完整的执行链路。颗粒度上我的经验是宁可细一点。因为 Agent 的问题往往出在意想不到的地方你事先不知道哪个字段会成为排查的关键。日志存储成本相比排查问题的时间成本完全不值一提。但要注意脱敏用户隐私数据、密钥这类信息不能进日志。3.7 决策七编排用代码还是用框架最后一个决策是技术选型自己写编排逻辑还是用现成的 Agent 框架。自己写的优势是完全可控没有黑盒出问题好排查性能也更好。劣势是要处理很多底层细节开发速度慢。用框架的优势是开箱即用很多常见模式已经封装好了。劣势是框架本身可能有 bug抽象层可能挡住你需要的能力而且框架升级可能带来不兼容。我的建议是如果只是验证想法用框架快速搭原型如果要上生产核心编排逻辑自己写只在非核心环节用框架的组件。因为生产环境对稳定性和可排查性的要求往往超出框架的设计预期。我见过不少团队一开始图快用了框架后来为了改一个细节不得不 fork 整个框架维护成本反而更高。4. 把七个决策点串成一条可落地的实现路径前面把要素和决策点拆开讲了但实际搭建时它们是交织在一起的。这一节我把它们串成一条完整的路径从零到一说明每一步该做什么。4.1 第一步定义任务边界和成功标准动手写代码之前先想清楚这个 Agent 要解决什么任务、什么算成功、什么算失败。这一步看起来虚但决定了后面所有设计。具体要回答几个问题任务是开放式的还是封闭式的开放式任务比如帮我调研某个话题需要更宽松的循环和更强的规划能力封闭式任务比如查询并汇总订单可以设计得更确定。成功标准是什么是任务完成就行还是有质量要求失败时是重试、降级还是直接报错我习惯把这些问题写成一个简短的文档哪怕只有半页纸。因为 Agent 开发过程中很容易迷失在细节里有个明确的目标文档能帮你随时校准方向。4.2 第二步设计工具集和调用契约工具设计是 Agent 的地基。我的做法是先列出任务需要的所有原子能力然后合并同类项最后为每个工具写清楚描述和参数 schema。原子能力的粒度要适中。太细会导致工具数量爆炸模型选择困难太粗会导致单个工具逻辑复杂参数难以描述。一个经验法则是一个工具只做一件事但这件事要有完整的业务含义。比如查询订单是一个合适的粒度查询订单号和查询订单状态分开就太细了。调用契约要明确几件事参数的类型和格式、必填还是选填、返回值的结构、可能的错误码。这些都要写进工具的 schema 里让模型能准确理解。4.3 第三步搭建循环骨架和终止条件循环骨架是整个 Agent 的主循环。一个典型的实现是这样的初始化上下文进入循环每轮先检查终止条件然后调用模型解析模型输出如果是工具调用就执行工具并把结果加入上下文如果是最终答案就退出循环。终止条件要在这里全部落实模型自主完成、步数上限、无进展检测、异常熔断。这几个条件之间是或的关系任何一个满足就退出。这里有个实现细节每轮循环开始前都要重新评估终止条件而不是只在模型输出后评估。因为有些异常比如工具连续失败是在执行过程中产生的需要在下一轮开始前就拦截。4.4 第四步接入记忆和上下文管理记忆管理要嵌入到循环里。每轮循环开始时从记忆系统组装当前需要的上下文每轮结束后把新的信息写回记忆系统。组装上下文时要注意优先级当前任务的目标和约束最重要其次是最近几轮的对话再次是检索召回的相关知识最后才是更早的历史摘要。这个顺序决定了模型注意力的分配。写回记忆时要做压缩和结构化。原始的工具返回往往很冗长直接存进去会迅速撑爆上下文。我的做法是提取关键字段用结构化的形式存储需要时再展开。4.5 第五步加上可观测性和错误处理可观测性和错误处理是横切关注点要贯穿整个循环。我的做法是在循环的关键节点埋点每轮开始、模型调用前后、工具调用前后、循环结束都记录一条结构化日志。错误处理要分层工具层面的错误在工具内部捕获并转换成模型能理解的描述循环层面的错误比如模型输出解析失败在循环里处理决定是重试还是终止系统层面的错误比如依赖服务不可用向上抛出由外层决定降级策略。5. 实测中那些文档不会告诉你的坑前面讲的都是应该怎么做这一节讲实际做的时候会碰到什么。这些坑我在不同项目里反复踩过写出来希望能帮你省点时间。5.1 模型会假装调用了工具这是最隐蔽的坑之一。模型有时候会在文本输出里写我已经调用了查询工具结果是……但实际上它根本没有发起工具调用结果全是它编的。这个问题的根源在于模型被训练成尽量给出完整回答当它觉得调用工具麻烦或者不确定怎么调时就会走捷径编一个结果。解决办法是在 prompt 里明确要求必须通过工具调用获取信息不得自行编造同时在解析层严格检查——如果模型输出了工具调用的描述但没有实际的调用请求就判定为无效输出要求重新生成。5.2 工具返回太长直接把上下文撑爆有些工具比如网页抓取、文档检索返回的内容非常长一次返回几万字很常见。如果原样塞进上下文下一轮模型调用可能直接超限。解决办法是在工具层做截断和摘要。截断要智能不能简单砍掉后面而是保留开头和结尾中间用省略标记。更好的做法是用一个小模型对返回内容做摘要只保留和当前任务相关的部分。这个摘要步骤会增加一次模型调用但相比上下文超限导致的任务失败这个成本完全值得。5.3 循环里的假进展所谓假进展是指 Agent 看起来在做事实际上在原地打转。比如反复调用同一个工具查询同样的信息或者在不同工具之间来回切换但没有任何实质推进。检测假进展的方法是比较相邻两轮的状态。如果工具调用序列高度重复、或者关键状态字段没有变化就判定为无进展。触发后不要立即终止可以先给模型一个提示比如你已经查询过这个信息了请基于已有信息继续看它能不能跳出来。如果连续两轮还是无进展再终止。5.4 并发场景下的状态污染单机单任务的 Agent 很好做一旦要支持并发状态管理就成了大问题。最常见的错误是把会话状态存在全局变量或者单例里结果多个请求互相覆盖。正确的做法是每个任务一个独立的状态容器用任务 ID 隔离。如果状态存在外部key 的设计要包含任务 ID 和会话 ID避免冲突。另外要注意工具调用的幂等性并发场景下同一个工具可能被多个任务同时调用如果工具有副作用比如写数据库必须做好并发控制。5.5 成本失控往往发生在你没注意的地方Agent 的 token 消耗比普通对话高一个数量级因为每轮循环都要把完整上下文重新发一遍。如果不加控制一个复杂任务跑下来消耗几十万 token 很正常。控制成本有几个抓手一是前面说的分层用模型规划用大模型执行用小模型二是上下文压缩别把没用的历史一直带着三是设置单任务的 token 预算上限超了就终止四是缓存相同的工具调用结果可以复用。我自己的项目里加上这些控制之后平均单任务成本降了差不多一半。6. 从能跑到好用还差哪些工程化的工作一个 Agent 能跑通 Demo 和能上生产中间隔着大量工程化的工作。这一节讲几个关键的提升方向。6.1 评测体系的建立没有评测就没法优化。Agent 的评测比普通模型评测复杂因为它涉及多步交互和工具调用。我的做法是建一个任务集每个任务包含输入、期望的关键步骤、期望的最终结果。评测时不仅看最终结果对不对还要看步骤是否合理——有没有绕远路、有没有不必要的工具调用、有没有触发本可避免的错误。这个任务集要持续积累每次线上发现 bad case 就加进去形成回归测试。6.2 提示词的版本管理Agent 的 prompt 往往很长很复杂改一个词可能影响整体行为。必须做版本管理每次改动都要记录改了什么、为什么改、评测结果如何。我习惯把 prompt 拆成几个模块角色定义、任务说明、工具使用规范、输出格式要求、约束和禁忌。这样改的时候能定位到具体模块也方便做 A/B 测试。不同模块的改动要分开评测避免一次改太多导致无法归因。6.3 灰度发布和回滚机制Agent 的行为有不确定性新版本上线前一定要灰度。先放一小部分流量观察关键指标任务成功率、平均步数、成本、错误率确认没问题再逐步放量。回滚机制要提前准备好。因为 Agent 的问题有时候不是立即暴露的可能是某个特定输入触发的。保留旧版本的配置出问题时能快速切回去。6.4 人工介入通道再好的 Agent 也会遇到搞不定的情况。设计一个人工介入通道让 Agent 在不确定时能请求人工确认比让它硬着头皮瞎猜要好得多。介入的触发条件可以是连续失败达到阈值、涉及高风险操作比如支付、删除、模型自己表示不确定。介入时要把当前上下文完整呈现给人工让人能快速理解情况并做决策。人工的决策结果要反馈回 Agent让它继续执行。7. 关于 Agent 工程实现的一点个人体会搭 Agent 这件事技术选型和框架选择其实都不是最难的最难的是建立一种和不确定性共处的工程思维。传统软件开发里你写什么代码就执行什么逻辑确定性是默认的。但 Agent 里模型是个概率系统同样的输入可能给出不同的输出你必须假设它随时可能出错然后设计好每一层的兜底。我自己的经验是把 80% 的精力花在约束和兜底上20% 花在能力上。工具描述写清楚、循环终止条件设好、错误处理做扎实、可观测性做到位这些看起来不酷的工作才是决定 Agent 能不能真正干活的关键。模型能力会随着版本迭代不断提升但工程上的严谨程度是你自己能控制的变量。另外一点体会是不要追求一步到位。先搭一个最小可用的版本跑通一个最简单的任务然后逐步加工具、加记忆、加错误处理。每加一个能力就评测一次确认没有引入回归。Agent 系统很容易越改越复杂保持克制、持续做减法比不断堆功能更重要。
返回列表