把AI Agent拆成“六个模块”来聊,是我在做了多个Agent项目之后觉得最合适的一种切入方式。很多人一上来就盯着提示词写得多花哨,或者纠结该调哪家大模型,结果做出来的东西一跑就崩、一换场景就废。真正的Agent系统,核心不在于模型多强,而在于外围这六个模块能不能扛得住真实业务里的脏活累活。这篇文章我按自己的实战理解,把感知、记忆、规划、工具、执行、反思这六块逐一拆开讲清楚,适合正在从Demo走向落地、想把Agent做得更稳的开发者参考。
1. 为什么说Agent的核心是模块而不是模型
1.1 一个能跑通的需求背后到底站着谁
先说我最近做的一个内部项目,需求很简单:让Agent每周自动整理项目群里的讨论记录,提炼出待办事项,再按照负责人把任务派发到对应群。听起来不复杂,但真做起来发现,如果只靠着一次大模型调用,这件事根本闭环不了。你得先能从各种聊天记录里定位到关键信息,这是感知;你得记住上周已经处理过哪些任务,这是记忆;你得判断讨论内容怎么拆成可执行的任务,这是规划;你还得调用群机器人的接口把消息发出去,这是工具;发之前要确认状态流转对不对,这是执行;下个星期回过头看哪些待办漏了、哪些结论错了,这就是反思。
你会发现,任何一个真实Agent场景,都是这六类能力在同时工作。它们不是相互独立的技术切片,而是围绕“目标完成度”串起来的一条流水线。这也是为什么我特别建议初学者不要只盯着“提示词工程”或者“某个Agent框架的API”,而是先建立模块意识。模块意识一旦建立,你遇到任何新需求,第一反应会是“哪个模块需要加强”,而不是“换个更大的模型试试”。
1.2 六大模块的边界到底是什么
六个模块我分别定义为:感知与输入解析、记忆管理、规划与任务分解、工具调用、执行与状态流转、反思与自校正。它们之间有一个隐形的数据流转顺序:感知模块把原始输入统一成结构化语义,记忆模块提供相关背景,规划模块产出步骤,工具模块负责和环境交互,执行模块维护整个过程的中间状态,反思模块在关键节点做质量校验并形成闭环。
这里想多说一句,很多人把工具调用理解成Agent的全部,这其实是个误区。工具调用只是Agent的“手脚”,但真正决定Agent是否聪明的,是规划模块能分解任务到什么粒度,以及反思模块能不能发现自己在走弯路。手脚再灵活,方向错了也白搭。模块边界的价值在于,你在做故障排查时可以快速定位问题在两个模块的交界处——比如“记忆取回的内容污染了规划的结果”,而不是笼统地觉得“整个Agent坏了”或“模型能力不行”。
2. 感知与输入解析:Agent理解世界的第一道闸门
2.1 多模态输入怎么统一成一种语义
Agent的输入远比大家想象得复杂。文本、图片、语音、结构化表格、甚至用户在交互过程中的操作行为,都会涌进输入层。我见过不少项目的第一版只处理纯文本,等到上线才发现用户会丢截图过来,而截图里恰恰藏着最关键的信息。做感知模块的时候,第一步不是急着写解析逻辑,而是定义清楚“Agent到底需要从输入里提取哪些信号”。
我现在的做法是构造一个中间语义层。不管是文本、OCR结果还是语音转写文本,最终都统一成一套带时间线、发言人、原始内容、关键实体标注的结构化事件流。事件流的好处是,后续无论是做关键词召回还是交给规划模块做上下文聚合,都有统一格式可供操作。举个反例,如果每种输入类型走一条单独的解析分支,那么当你在规划阶段需要融合同一时刻的文本加图片信息时,就只能临时拼字符串,语义很容易错乱。
2.2 上下文框架才是感知的灵魂
解析输入的下一层是维护“当前交互的上下文框架”。这个框架不完全等同于模型上下文窗口里的原始内容,而是感知模块抽取出来的结构化场景信息。比如“当前用户在面包店场景里追问提拉米苏的成分”,感知模块要抽取出来的不是一整段对话原文,而是“场景=甜品咨询,实体=提拉米苏,意图=成分查询,约束=可能有过敏提示需求”。
我在项目里会用一个轻量的分类模型去做意图初筛,再用主模型做实体填充。这里有一个实战细节,分类模型做初筛不是因为它比大模型更准,而是为了省成本——大量简单请求根本不需要走长上下文的大模型,分类模型划分好路径后,只有复杂case才走完整链路。这也从侧面回答了一个很多人关心的问题:Agent不是每一层都要堆最贵的模型,分层处理本身就能优化成本。
2.3 感知模块最常见的三个坑
第一个坑是信息丢失。我早期做一个会议纪要Agent,直接把语音转写文本截断到上下文窗口上限,结果会议后半段的决议全部丢失。后来改成段落级摘要压缩,才保住了关键信息。第二个坑是上下文膨胀。有些开发者图省事,把每次输入的全量历史都塞给感知模块,结果Agent的响应越来越慢、越来越跑偏。感知模块应该有“摘要优先、全文召回靠检索”的机制。第三个坑是格式污染。结构化输入和自由文本混在一起会让大模型分不清优先级,我现在要求所有解析输出必须走统一的JSON结构,哪怕某个字段为空也要显式给个空值,这样模型才不会被隐形内容误导。
3. 记忆管理:短期工作台与长期知识库怎么配合
3.1 短期记忆不等于大模型的上下文窗口
很多初学者把短期记忆等同于“把聊天历史都拼接到Prompt里”,这是误解。大模型的上下文窗口只是一块临时工作区,它无法区分哪些历史信息对当前任务更重要,也无法处理“一段信息使用完就该被丢弃”的场景。真正的短期记忆应该是一个有容量上限、有淘汰策略的工作存储区。
我自己设计短期记忆时参考了操作系统的分页管理思路:把会话切成若干个有标题的记忆块,每块绑定时间戳和相关实体;当工作区快满时,优先淘汰“和当前用户目标关联度最低”的块,而不是机械地删除最老的内容。听起来有点抽象,但直白讲就是——如果一个小时前用户曾提到某个特殊偏好,而这个偏好现在正相关,那即使它更老也应该留下来,反而是5分钟前的闲聊内容可以被压成一句话摘要。
3.2 长期记忆的本质是分层索引不是原样存储
长期记忆如果只靠向量检索库把所有历史对话都塞进去,那多半会在召回阶段翻车。因为用户输入的问题表述和原有信息之间往往隔着好几层同义转换,纯向量相似度并不总是抓得住。我习惯把长期记忆分成三层:事实层、摘要层、行为偏好层。事实层存的是明确的实体关系,比如“该用户常用邮箱是什么”;摘要层按话题维度压缩定期沉淀;行为偏好层则记录用户在交互中表现出的习惯,比如“更偏好简洁回复,对技术细节要求不高”。
取回策略上,我并不是每次请求都把三层全量召回,而是先由规划模块给出“需要哪些方向的知识”的信号,再按信号去各层里检索。这里有一个我自己的经验,偏好类记忆权的权重一定要高于事实类,因为Agent如果违背了用户明确的偏好,哪怕事实回答得再对,体验上也是失败的。
3.3 记忆污染和选择性遗忘
记忆模块有个很容易忽视的副作用:脏数据写入。如果一次请求中模型判断错了一个实体的含义,这个错误写进长期记忆后,后续几周都会被反复取回、反复放大。我现在会在写入长期记忆前加一道校验门槛——不是所有抽取出来的信息都允许落库,只有同时满足“高置信度”和“对后续交互有潜在价值”两个条件才写入。遗忘机制也不是锦上添花,而是长期记忆里的数据必须设置有效期,超过一定时间没有再次被验证过的信息会被降级或清除,否则记忆库会逐渐变成错误堆积场。
4. 规划模块:目标拆解的粒度直接决定Agent的上限
4.1 显式规划与隐式规划的取舍
规划模块承担的是“把模糊目标变成可执行步骤”的职责。目前的落地实现大致分两类:一类是显式规划,先让模型输出一份完整的步骤计划,再按步骤执行,典型形态是Plan-then-Execute;另一类是隐式规划,Agent一边执行一边根据当前结果决定下一步,典型形态是ReAct的思考-行动循环。
我在项目里的经验是,不要迷信某一种方式,而是按任务复杂度来选。简单任务用隐式规划就够了,它灵活、token消耗少;越是复杂、多依赖、对时序敏感的任务,越值得用显式规划先把大框架定下来。显式规划的额外好处是,你可以把计划本身展示给用户,让用户在看到执行结果前就有机会纠偏。比如让Agent做季度数据分析,规划模块先给出“拉取数据、清洗指标、生成图表、撰写结论”四步,用户看到计划就能提前说“图表这步不需要”,省去了执行一半再中断的尴尬。
4.2 规划输出必须约束在“可校验”的边界里
规划模块最大的坑是生成一份看起来完整但无法执行的任务清单。我处理的办法是给规划模块单独的提示约束和Schema校验,要求每一步任务必须包含:操作对象、所需工具、期望产出、判断完成的条件。这一步做完,规划结果就可以做程序化校验,凡是缺失关键字段的步骤直接返回规划模型重新生成。
还遇到过规划步骤数量失控的情况。一个简单的“总结本周进展”需求,模型可以拆出12个步骤,其中一半是重复动作。后来我加了预算意识,给规划模块一个隐性约束:每一步都必须是前一步无法直接跳过的必要操作。同时限制未经过用户确认的自动步骤上限,超过上限就强制进入“请求用户决策”的状态。规划不是步骤越多越好,而是步骤之间要有因果必要性。
4.3 规划的容错和重规划策略
真实环境里,计划赶不上变化是常态。某个外部工具临时挂了、某个API返回了意料之外的空值,都会导致原计划无法推进。所以规划模块必须自带重规划能力。我现在设计时会为每一步设定最大重试次数,并预先给出备选路径定义。如果主路径连续失败,重规划不是从零开始,而是保留已经成功的前置结果,只对失败节点做局部替换。这一点极其重要,很多Agent系统一遇到局部失败就整体重来,既浪费token又极容易永久卡死在同一块石头上。
5. 工具调用:Agent与真实世界握手时最容易出戏的地方
5.1 Function Calling的协议设计比数量更重要
工具调用模块的核心不是接了多少个API,而是协议设计是否清晰。我见过一个Agent接了四十多个工具,但模型选工具的正确率只有五成,因为工具描述写得太含糊,大量功能重叠。真正好用的工具协议应该满足三个要求:工具边界明确、参数Schema精确、失败返回值可控。
我在项目里定义工具协议时会刻意做“单向意图”划分。比如“查天气”和“查空气质量”如果拆成两个工具,模型很容易搞混;合并成一个“查询天气与环境数据”工具,再用参数里的type字段做细分,效果反而更好。另外参数Schema要写严格,用枚举值的地方一定要给枚举,模型在参数上的幻觉大部分是枚举约束不严导致的。
5.2 工具描述怎么写模型才听得懂
写工具描述有技巧,核心原则是“描述返回结果能支撑什么下一步动作”,而不是只描述工具本身的功能。举个例子,与其写“该接口返回用户订单列表”,不如写“通过该接口获取用户完整订单记录,可用于查询最近的订单状态、统计年度消费金额、校验商品是否支持退货”。当模型知道这个工具的数据用于什么决策时,它选择工具的准确率会明显提高。
我有时会故意在工具描述里附上失败场景的返回样例。比如“当订单号不存在时返回error_code=404,此时应提示用户核对订单号,而不是重试”。这样做等于提前给模型打了失败处理的预防针,省去了后续大量的异常处理提示。
5.3 工具异常不能被吞进黑洞
工具调用的错误处理,我一直在强调“可见性”。每次工具调用返回后,不管成功失败,都要把状态摘要写进上下文,让规划模块知道刚才发生了什么。很多Agent应用跑着跑着就“失忆”了,就是因为工具返回里的错误信息没有被透传,规划模块不知道结果堪忧,还在按成功状态往下推。更具体一点,我会把工具结果做结构化封装:成功标志、业务数据、错误码、消耗时长,四项齐全,再交给后续模块处理。这套封装让整个链路具备可观测性,调试时能一眼定位到哪个环节出了问题。
6. 执行与状态流转:让多步任务在混乱世界里安全抵达终点
6.1 状态机的核心是“每一步都知道自己在哪”
执行模块本质上是Agent系统的“进程管理器”。它负责维护任务从启动到完成全过程的状态变化,包括当前步骤、已完成步骤、待确认事项、失败计数、外部依赖条件等。没有清晰状态管理的Agent就像没有存档的游戏,一旦会话中断就只能从头再来。
我在项目里使用有限状态机来管理Agent的生命周期,状态包括等待输入、规划中、工具调用中、等待用户确认、执行完成、异常终止等。状态机的好处是让每一步转移都有明确触发条件,用户确认、工具返回成功、重试耗尽这些事件都可以驱动状态变化。你不需要把状态逻辑写进提示词里,状态机天然让行为变得可预期。
6.2 超时、并发与中断恢复怎么处理
真实场景下,工具调用不可能永远如你所愿地三秒返回。超时如果处理不好,Agent会傻傻地一直等,或者直接把超时当成失败重试,雪上加霜。我在执行模块里做了一套分级超时策略:识别工具的预期耗时范围,预期3秒的工具10秒不返回就抛弃;预期30秒的长任务,则先返回“处理中”的中间状态,让Agent可以先去干别的,等外部回调再恢复。
并发控制也是执行模块的职责。规划模块可能会同时产出多个独立步骤,这时候不是一股脑全部并行执行,而是要考虑到外部API的限流。我在并发层加了令牌桶,每个工具组有自己的并发上限,超过上限的任务自动排队。中断恢复方面,我会给每个Agent会话维护一个持久化的状态快照,用户哪怕第二天回来,系统也能从当前状态节点继续,而不是把整段历史重新推理一遍。
6.3 人类介入不是失败,而是系统成熟的表现
很多人总觉得“Agent问了人”就算不够智能,这个观念该改改了。让Agent在关键节点主动向用户确认,恰恰是降低风险、提升接受度的重要手段。我在执行模块里设计了确认点位——涉及会产生外部影响的步骤,比如发送消息、扣费、删除数据,在动手前必须强制请求用户确认。这种设计表面上多了一步交互,实际上大幅减少了错误执行带来的返工和信任损耗,长期来看是真正的效率提升。
7. 反思与自校正:模型跑错了怎么自己发现
7.1 自我反思不是让模型“再想想”
反思模块是最容易被误解的一个模块。有人把反思做成失败后的“重新生成一遍答案”,比如给模型加一句“请再检查一下”,这基本没用,因为模型大概率会在同样的地方摔倒。真正有效的反思必须注入“外部反馈信号”——也就是说,反思不能只靠模型内部再思考,而是要靠额外的结构化校验来发现错误。
我在Agent链路里嵌了几种外部校验器:格式规范校验器检查输出是否符合预定Schema;业务规则校验器检查结果是否违背硬性约束,比如金额字段不能为负;一致性校验器比较前后步骤的实体引用是否相互矛盾。只有当这些外部校验器给出了明确错误信号,反思模块才启动,让模型基于这个具体错误进行修正。用这种方式,反思行为是被证据驱动的,而不是被“拍脑袋”驱动的。
7.2 用回归评测集守住模块迭代的底线
反思模块再强,也没法保证每个case都自愈。所以我在项目里同时维护了一套回归评测集。每次调整任何一个模块的提示词或逻辑参数,都会先跑一遍评测集,观测总体成功率的变化。曾经有一版规划模块的prompt明显提升了复杂任务的拆解质量,但与此同时,简单任务的步骤数也变多了。如果不是评测集指出“简单任务成功率微降”,我可能就带着这个缺陷上线了。模块化系统的每一次变更,都必须放在一个全局评测棋盘上看,不能只看局部亮眼。
7.3 什么情况下不要过度反思
反思不是无限循环。有些Agent系统在失败后反复重试,每次尝试都消耗token和时间,用户早就不耐烦了。我设定的默认规则:一次任务最多允许两轮反思修正,两轮之后如果仍然失败,就进入人工介入流程,把当前状态、已尝试方案、可疑原因打包交给用户。这样做既给了Agent自愈的机会,又不至于让系统钻牛角尖。尤其是涉及实时交互的场景,响应时间本身也是一种用户体验指标,过度反思换来的“完美但迟到”的答案,价值会大打折扣。
8. 模块整合的实战心法:编排顺序、可观测性与成本
8.1 模块之间不是插拔芯片那么简单
六个模块设计完之后,最考验工程能力的其实是把它们编排成一条可运行的流水线。模块之间会有隐性的数据依赖。举一个简单例子,记忆模块取回的内容可能影响规划模块是否感知到“用户正在重复提问”;而执行模块保存的中间状态,又反过来影响记忆模块写入什么样的摘要。所以编排的第一步不是画架构图,而是梳理清楚每个模块产生的数据会流向哪里、被谁消费、需要什么格式。
我一般先在逻辑上定义出一份“数据契约”,也就是模块间传递消息的统一规范。感知模块的输出消息里有scene和entites字段,规划模块就承诺只消费这两个字段,不自己再翻原始输入。这个契约机制让我后续想替换或升级内部模块时,把影响面控制在一个比较小的范围。虽然看起来只是规范上的约束,但对一个会持续演进的Agent系统来说,价值不亚于任何一个核心模块本身。
8.2 可观测性:Agent系统最贵的隐性需求
和传统后端系统不同,Agent系统的中间过程不是稳定可预期的,同样的输入可能因为模型微小的概率波动跑出完全不同的路径。这种不确定性,让我在项目中把可观测性放到了和功能一样的优先级。我会给每一次Agent运行生成全程日志,记录每个决策节点用到的输入片段、模型输出、置信度和耗时。在客户反馈“这个Agent这一次回答怪怪的”的时候,如果没有这些分步日志,排查会像大海捞针。
实践中,我把日志压缩成三层:性能层记录各模块延迟和Token开销;决策层记录规划路径和工具选择结果;校验层记录外部校验器是否通过、反思是否触发。三层日志组合起来,基本可以还原一次Agent决策的全部链条。这套机制在项目出Bug的时候帮我节省了大量时间,我不需要去猜测哪里出了问题,直接看日志链路就能指到具体环节。
8.3 成本意识:模块化的另一层收益是能省钱
聊到成本,很多人会默认“Agent越强大越贵”,但我恰恰觉得模块化架构是控制成本最有效的武器。感知模块里的轻量分类模型可以挡掉一大半简单请求;记忆模块的摘要机制避免了每次请求都拉全量历史;规划模块的任务重写和复用能让后续步骤的Prompt更短。这些都让Token消耗更理智,也让整体系统能在可控成本的前提下支撑复杂业务。
我在刚搭建这套架构时也走过“什么任务都走大模型全家桶”的弯路,结果一个月的API账单高到吓人。后来我明确了一条原则:大模型只做“需要理解和生成”的部分,其他能靠规则和逻辑代码完成的部分绝不上模型。比如状态流转、超时重试、字段校验这些纯确定性逻辑,我全部用代码实现,只有真正需要语义理解的地方才调用模型。优化之后,单次任务的平均成本降到了原先的五分之一左右,同时延迟也降了不少。这套优化思路,恰恰是因为模块化让我能清楚地看到钱花在了哪个环节,才能精准开刀。