ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI Agent开发四大核心支柱:从API调用到生产级智能体的系统工程

AI Agent开发四大核心支柱:从API调用到生产级智能体的系统工程

1. 为什么“会调 API”离“AI Agent”还差着十万八千里

我见过太多团队在立项会上拍着胸脯说“我们要做个 Agent”,结果三个月后交付的东西,本质上就是一个套了循环的 API 调用脚本。用户问一句,它调一次模型,返回一段文本,结束。这种玩意儿你叫它聊天机器人没问题,叫它 Agent 就有点自欺欺人了。

问题的根源在于,大多数人把AI Agent理解成了“LLM + 一个 while 循环”。这个理解不能说全错,但它漏掉了真正决定一个 Agent 能不能用的那几个关键结构。你调 API,调的是模型的推理能力;而 Agent 要解决的是“在不确定环境里持续做出靠谱决策并执行”的问题。这两件事之间的差距,不是靠多写几行while True就能填上的。

我拿一个真实场景来说明。假设你要做一个能自动处理客服工单的 Agent。用户发来一段模糊的描述:“我上周买的东西到现在还没到,你们到底发没发货?”一个纯 API 调用方案会怎么做?把这句话拼进 prompt,让模型生成一段回复,然后返回。看起来没问题对吧?但实际跑起来你会发现:模型不知道这个用户是谁、不知道订单号是多少、不知道物流状态是什么、更不知道仓库那边有没有异常。它只能根据一句话生成一段“看起来很合理”的废话。

而一个真正的 Agent 需要做的事情是:先判断这句话的意图类别(查物流、催发货、投诉、还是其他),然后决定需要调用哪些工具去获取信息(订单查询接口、物流接口、库存接口),拿到结果后判断是否需要进一步追问用户,最后才生成回复。如果物流接口返回“已签收”但用户说没收到,它还得触发异常处理流程。这一整套东西,才是 Agent 的门槛所在。

所以这篇文章我想把这件事彻底讲清楚。我会把 AI Agent 拆成四个核心支柱,每个支柱讲明白它解决什么问题、为什么绕不过去、以及实际落地时最容易踩的坑。最后我会给一张对比表,让你一眼看出“调 API 脚本”和“真 Agent”之间的本质差异。不管你是刚接触 Agent 开发的新手,还是已经写过几个 demo 但总觉得哪里不对的同行,这篇内容应该都能帮你把认知对齐。

2. 支柱一:目标分解与任务规划——Agent 的“大脑前额叶”

2.1 为什么单次推理撑不起一个 Agent

人类在面对复杂任务时,不会试图一次性把所有事情想清楚。你会先拆解:这件事分几步?每步的输入输出是什么?哪些步骤有依赖关系?哪些可以并行?这套能力在认知科学里叫“执行功能”,对应到 Agent 身上,就是任务规划。

LLM 本身具备一定的分解能力,但如果你只是把用户请求丢进去让它“直接给答案”,它大概率会给你一个笼统的、不可执行的方案。我试过让模型直接处理“帮我分析上个月销售数据并生成报告”这种请求,它返回的内容基本是“首先收集数据,然后分析趋势,最后生成报告”这种正确的废话。它没有告诉你数据从哪来、用什么口径分析、报告给谁看、格式是什么。

真正可用的规划能力,需要你把任务分解这件事从“模型自由发挥”变成“有结构的推理过程”。目前业界比较成熟的思路有这么几种:

  • ReAct 模式:让模型在“思考”和“行动”之间交替。先输出一段推理(Thought),再决定调用什么工具(Action),拿到结果后继续推理(Observation),循环直到任务完成。这个模式的好处是把规划过程显式化了,你能看到模型每一步在想什么。
  • Plan-and-Execute 模式:先让模型生成一个完整的执行计划,然后按计划逐步执行。适合步骤明确、依赖关系清晰的任务。缺点是计划一旦生成就固定了,遇到意外情况需要重新规划。
  • Tree-of-Thought 模式:在每一步生成多个候选方案,评估后选择最优路径继续。计算成本高,但适合需要探索的任务。

我个人的经验是,ReAct 适合大多数交互式 Agent 场景,因为它的容错性更好。你不需要一开始就想清楚所有步骤,可以在执行过程中根据反馈动态调整。而 Plan-and-Execute 更适合批处理类的任务,比如“把这 500 条工单分类并生成摘要”这种。

2.2 规划粒度怎么定:太粗会跑偏,太细会爆炸

这是我在实际项目里踩过的最大的坑之一。一开始我把任务分解得很粗,比如“查询订单状态”就是一个步骤。结果模型在执行时经常漏掉边界情况:订单号格式不对怎么办?查询超时怎么办?返回多个订单怎么办?

后来我走向另一个极端,把每个步骤拆得极细,连“验证订单号是否为 12 位数字”都单独列一步。结果 token 消耗暴涨,而且模型经常在无关紧要的步骤上浪费推理能力,真正关键的决策反而被淹没了。

经过几轮迭代,我总结出一个比较实用的原则:按“决策点”来划分粒度,而不是按“操作”来划分。什么意思?如果一个步骤内部不需要做任何判断,那它就不应该成为一个独立的规划单元。比如“调用订单查询接口”这个操作,如果参数已经确定、不需要分支判断,那它就应该被封装成一个工具,而不是规划里的一个步骤。规划应该只关注那些需要模型做决策的地方:要不要追问用户?要不要换一个工具?要不要终止流程?

2.3 规划失败的典型信号与修复思路

你在跑 Agent 的时候,如果发现以下现象,基本可以判断是规划环节出了问题:

  • 模型反复调用同一个工具,陷入死循环
  • 任务执行到一半突然“忘记”了原始目标
  • 生成的步骤顺序明显不合理(比如先查物流再查订单)
  • 面对稍微复杂一点的请求就直接放弃,返回“我无法完成这个任务”

修复思路通常是两个方向。一是在 prompt 里加入 few-shot 示例,让模型模仿正确的规划结构。这个办法见效快,但泛化能力有限。二是引入外部规划器,比如用一个专门的模型或规则引擎来做任务分解,LLM 只负责执行。这个方案工程量大,但稳定性好很多。

实操心得:在规划阶段加入“反思”步骤。让模型在生成计划后自己检查一遍:这个计划能完成用户的目标吗?有没有遗漏关键步骤?依赖关系对吗?这一步额外的推理成本很低,但能显著降低后续执行阶段的错误率。

3. 支柱二:工具调用与函数编排——Agent 的“手脚”

3.1 Function Calling 不是万能钥匙

现在各大模型厂商都在推 Function Calling 能力,看起来好像只要把工具定义好,模型就能自动调用。但实际用下来你会发现,Function Calling 解决的是“格式”问题,不是“决策”问题。

模型能按照你定义的 JSON Schema 输出一个函数调用请求,这没错。但它什么时候该调用哪个函数、参数怎么填、调用失败后怎么办,这些它并不擅长。我见过模型在需要查天气的时候去调用汇率接口,也见过它把用户说的“明天”理解成“2024-01-01”这种固定日期。

更麻烦的是,当工具数量超过 10 个以后,模型的调用准确率会明显下降。它会在相似的函数之间反复犹豫,或者干脆编造一个不存在的函数名。这个问题在工具命名相似、功能有重叠的时候尤其严重。

3.2 工具设计的三个核心原则

经过多个项目的打磨,我总结出工具设计的三条原则,每一条都是用血泪换来的:

原则一:一个工具只做一件事,但要把这件事做完整。不要设计“查询订单”这种模糊的工具,而要设计“根据订单号查询订单详情”和“根据用户ID查询最近订单列表”两个独立工具。每个工具的参数要明确、返回值要结构化。我习惯在返回值里加上status字段,明确标识成功、失败、部分成功等状态,方便模型判断下一步。

原则二:工具描述要写给模型看,不是写给人看。很多人写工具描述的时候习惯用简短的标签,比如“查询订单”。但模型需要的是完整的上下文:这个工具在什么场景下使用?输入参数有什么约束?返回值的含义是什么?我通常会把描述写成一段完整的说明,包括使用场景、参数说明、返回值示例。虽然会多消耗一些 token,但调用准确率的提升非常明显。

原则三:为每个工具设计“失败路径”。工具调用失败是常态,不是异常。网络超时、参数错误、权限不足、数据不存在,这些情况都要有明确的返回值和处理逻辑。我习惯在工具返回值里包含error_code和error_message,并在 prompt 里告诉模型:如果看到error_code非零,应该怎么处理。比如参数错误就重新提取参数,权限不足就提示用户登录,数据不存在就换一个查询条件。

3.3 工具编排:串行、并行与条件分支

单个工具调用相对简单,真正复杂的是多个工具之间的编排。我把它分成三种模式:

编排模式适用场景实现要点常见坑
串行步骤有严格依赖,前一步输出是后一步输入确保中间结果正确传递,加入校验点中间步骤失败导致整个链路中断
并行多个工具之间无依赖,可以同时调用注意结果聚合顺序,处理部分失败并发数过高导致限流或超时
条件分支根据中间结果决定下一步调用哪个工具分支条件要明确,避免模糊判断模型在分支点犹豫不决,反复横跳

我实际项目里最常用的是串行 + 条件分支的组合。比如处理退款请求:先查订单(串行),然后根据订单状态决定是走“直接退款”还是“转人工审核”(条件分支)。并行编排用得比较少,因为大多数业务场景的步骤之间都有依赖关系。

注意:工具编排的逻辑最好写在代码里,而不是完全交给模型决定。模型适合做“选择”,不适合做“调度”。你可以让模型决定“要不要查物流”,但“先查订单再查物流”这个顺序应该由代码控制。

4. 支柱三:记忆与状态管理——Agent 的“海马体”

4.1 没有记忆的 Agent 每次都在从零开始

这是最容易被忽视、但影响最大的一个支柱。很多 Agent demo 跑单轮对话看起来很美,一旦进入多轮就原形毕露。用户在第一轮说了“我要查订单”,第二轮说“就是上周那个”,Agent 完全不知道“那个”指的是什么。

LLM 本身是无状态的,每次调用都是独立的。你传给它的上下文窗口就是它的全部记忆。但上下文窗口是有限的,而且随着对话轮次增加,早期信息会被稀释甚至截断。我遇到过最极端的情况是:一个客服 Agent 在处理到第 8 轮的时候,已经完全忘记了用户最初描述的问题是什么。

4.2 三层记忆架构的落地实践

我在项目里用的是三层记忆架构,分别对应不同的时间尺度和用途:

第一层:工作记忆(Working Memory)。就是当前对话的上下文窗口。我通常会把最近 5-10 轮对话保留完整,更早的对话做摘要压缩。摘要的 prompt 很关键,不能简单地说“总结以上对话”,而要明确告诉模型:保留用户身份信息、已确认的事实、未解决的问题、已执行的操作。这样压缩后的摘要才能支撑后续推理。

第二层:会话记忆(Session Memory)。跨轮次但限于单次会话的信息。比如用户的订单号、已查询到的物流状态、用户表达过的偏好。这些信息我会用结构化的方式存储,比如一个 JSON 对象,在每次调用模型时作为系统提示的一部分注入。这样不占用对话历史的空间,而且模型能直接读取结构化字段,比从自然语言里提取要准确得多。

第三层:长期记忆(Long-term Memory)。跨会话的持久化信息。比如用户的历史订单、常见问题、偏好设置。这一层通常需要配合向量数据库做检索。我的做法是把用户的历史交互做 embedding 后存入向量库,每次新会话开始时根据当前问题检索最相关的几条记录注入上下文。

4.3 状态管理的工程细节

记忆和状态管理在工程上有很多细节需要注意,我挑几个最容易出问题的点说一下:

状态更新时机。不要等到对话结束才更新状态,而要在每次工具调用返回后立即更新。比如查到了订单状态,马上写入会话记忆。这样即使后续对话中断,重新恢复时也能拿到最新状态。

状态冲突处理。当新信息与旧信息冲突时怎么办?比如用户先说“订单号是 A”,后来说“不对,是 B”。我的做法是保留最新值,但在状态里记录一个previous_value字段,方便追溯。同时要在 prompt 里告诉模型:如果用户更正了信息,以最新为准。

状态过期策略。不是所有状态都值得永久保留。物流状态可能几小时就变了,用户偏好可能几个月都不变。我会给每个状态字段设置一个 TTL(Time To Live),过期后自动清除或重新查询。这个策略要根据具体业务场景来定,没有通用答案。

实操心得:在系统提示里明确告诉模型“你可以访问以下状态字段”,并列出当前所有可用字段及其值。这比让模型自己去对话历史里翻找要可靠得多。我通常会把状态以 JSON 格式注入,模型对结构化数据的理解能力明显强于自然语言。

5. 支柱四:评估与迭代闭环——Agent 的“免疫系统”

5.1 没有评估的 Agent 就是在裸奔

我见过太多团队把 Agent 部署上线后就不管了,直到用户投诉才去查日志。这种做法在传统软件里可能还能凑合,但在 Agent 场景里是致命的。因为 LLM 的输出是不确定的,同样的输入可能产生不同的输出,你今天修好的问题明天可能换个形式又出现。

评估体系要解决三个问题:Agent 做对了吗?做对的比例是多少?错的时候错在哪里?这三个问题分别对应评估指标、评估频率和错误归因。

5.2 构建可复现的评估集

评估的第一步是有一组固定的测试用例。我通常从三个来源收集:

  • 真实用户日志:从线上环境采样,覆盖高频场景和已知的边界情况
  • 人工构造的对抗样本:故意设计一些模糊、矛盾、多意图的输入,测试 Agent 的鲁棒性
  • 回归测试集:每次修复一个 bug,就把对应的 case 加入回归集,防止后续改动导致问题复现

每个测试用例要包含:输入、期望的工具调用序列、期望的输出关键信息、以及一个可自动判断的通过标准。注意,不要用精确匹配来判断输出是否正确,因为 LLM 的输出措辞每次都不一样。我通常用“关键信息是否包含”“工具调用序列是否匹配”“是否触发了禁止的操作”这几个维度来综合判断。

5.3 错误归因:把问题定位到具体支柱

当评估发现错误时,最关键的是快速定位问题出在哪个支柱。我总结了一个简单的归因流程:

错误现象可能的问题支柱排查方向
任务没完成,但工具调用看起来正常规划检查任务分解是否合理,是否遗漏步骤
调用了错误的工具工具调用检查工具描述是否清晰,是否有功能重叠
多轮对话后丢失上下文记忆检查状态更新时机和摘要压缩策略
同样输入有时对有时错评估检查是否有随机性来源,温度参数是否过高
遇到边界情况就崩溃综合检查是否有兜底逻辑和异常处理

这个表看起来简单,但在实际排查时非常有用。它能帮你快速缩小范围,而不是盲目地改 prompt。

5.4 迭代闭环:从评估到上线的完整流程

评估不是目的,迭代才是。我通常的迭代流程是这样的:

  1. 跑评估集,记录所有失败 case
  2. 对失败 case 做归因,定位到具体支柱
  3. 针对该支柱做修改(改 prompt、改工具描述、改状态管理逻辑)
  4. 重新跑评估集,确认修复生效且没有引入新问题
  5. 灰度上线,观察真实用户反馈
  6. 把新的失败 case 加入评估集,回到第 1 步

这个循环看起来简单,但坚持做下来不容易。我的经验是,每次迭代只改一个变量。如果你同时改了 prompt 和工具描述,出了问题你根本不知道是哪个改动导致的。慢一点没关系,关键是每一步都可追溯。

6. 一张表看清“调 API 脚本”和“真 Agent”的本质差距

说了这么多,最后用一张对比表来收束。这张表是我在内部培训时经常用的,能帮新人快速建立认知框架。

维度调 API 脚本真 Agent
任务处理单次请求-响应,无分解多步骤规划,动态调整
工具使用硬编码调用,无决策根据上下文选择工具,处理失败
状态管理无状态,每次从零开始三层记忆,跨轮次保持上下文
错误处理直接抛异常或返回错误归因、重试、降级、兜底
评估方式人工抽查自动化评估集 + 错误归因
迭代能力改一次测一次闭环迭代,持续优化
适用场景简单问答、单轮任务复杂业务流程、多轮交互

这张表的核心信息是:Agent 的门槛不在“调 API”这个动作上,而在 API 调用之外的那套系统工程上。规划、工具编排、记忆管理、评估迭代,这四个支柱缺一个,你的 Agent 就只能在 demo 环境里跑跑,上不了生产。

我个人的体会是,从“会调 API”到“能做出可用的 Agent”,中间最大的障碍不是技术难度,而是认知框架。很多人一开始就把注意力放在“怎么让模型输出更好的文本”上,但真正决定 Agent 好不好用的,是文本之外的那些结构。你把规划做清楚了,工具设计合理了,状态管理到位了,评估闭环跑起来了,模型本身的输出质量反而变成了一个相对次要的变量。

最后分享一个我在实际项目里反复验证过的小技巧:在 Agent 的每个决策点都加入“自检”步骤。比如模型决定调用某个工具之前,让它先输出一句“我为什么要调用这个工具”,然后再执行。这个额外的推理步骤成本很低,但能拦截掉大量低级错误。踩过几次坑之后,我现在已经把这个自检步骤作为所有 Agent 项目的标配了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进