1. 从单兵作战到团队协同:AI Agent 架构演进的核心逻辑
1.1 为什么 ReAct 是绕不开的起点
但凡这两年动手做过 AI Agent 的人,大概率都绕不开一个词——ReAct。它最早来自一篇关于推理与行动交替进行的论文思路,核心就一句话:让模型在“想”和“做”之间来回切换。想一步,调一个工具,看一眼结果,再想下一步,直到任务完成或者判断无法继续。
这个模式之所以成为事实上的起点,是因为它解决了一个非常朴素的问题:大模型本身只会“生成文本”,它不知道今天的天气、查不到数据库里的订单、也读不了你本地的那份 Excel。ReAct 用“思考-行动-观察”的循环,把模型的推理能力和外部工具的执行能力缝在了一起。
我最早落地的一个场景是帮运营团队做数据查询助手。用户问“上周华东区的退货率是多少”,ReAct 循环大概是这样跑的:
- Thought:需要先确认“上周”的时间范围,再查华东区退货数据。
- Action:调用时间解析工具,得到起止日期。
- Observation:返回 2024-06-03 至 2024-06-09。
- Thought:现在需要查询该时间段华东区退货率。
- Action:调用数据查询接口,传入区域和日期参数。
- Observation:返回退货率 3.7%。
- Thought:信息足够,可以组织回答。
这套循环跑通之后,很多人才意识到:原来 Agent 不是“更聪明的模型”,而是“模型 + 工具 + 循环控制”的组合体。ReAct 的价值在于它给出了一个最小可用的工程范式,让开发者能快速验证想法。
但问题也很快暴露出来。当任务链路变长、工具数量变多、需要多步判断和分支处理时,单个 ReAct 循环会变得非常脆弱。我踩过最典型的一个坑是:让一个 ReAct Agent 同时处理“查数据 + 生成图表 + 写分析报告”三件事,结果它在中间步骤反复横跳,一会儿想画图,一会儿又回去查数,最后 token 烧了一大把,输出却是一团乱麻。
1.2 单 Agent 架构的天花板在哪里
单 Agent 的问题不是“不够聪明”,而是“职责过载”。一个 ReAct 循环里,模型既要理解用户意图,又要规划步骤,还要选择工具、解析参数、判断结果、决定是否继续。这就像让一个人同时当项目经理、执行工程师和质量审核,短任务还能应付,长任务必然出错。
具体来说,单 Agent 架构在工程上会遇到几个硬瓶颈:
上下文窗口的挤压。每一步的 Thought、Action、Observation 都要塞进上下文,任务越长,历史信息越多,模型越容易“忘记”最初的目标。我实测过一个 15 步以上的任务,到第 10 步左右,模型开始重复调用已经执行过的工具,因为它已经“看不见”前面的观察结果了。
工具选择的歧义。当工具数量超过 10 个,且功能有重叠时,单 Agent 选错工具的概率显著上升。比如同时有“查询订单状态”和“查询物流信息”两个工具,模型经常混用,因为它没有明确的职责边界。
错误传播与恢复困难。单 Agent 一旦在某一步做出错误判断,后续步骤会沿着错误路径继续执行,而且很难自我纠正。因为它没有“第二双眼睛”来审核当前进展是否合理。
并行能力缺失。很多任务其实可以拆成并行子任务,比如“同时查三个区域的数据”,但单 Agent 的循环本质上是串行的,只能一步一步来。
这些瓶颈指向同一个结论:当任务复杂度超过某个阈值,就需要把“一个全能 Agent”拆成“多个专业 Agent 协作”。这就是多 Agent 架构演进的现实驱动力。
1.3 多 Agent 协作到底解决了什么问题
多 Agent 协作的核心思想并不复杂:把一个大任务拆成若干子任务,每个子任务交给一个专门的 Agent 负责,Agent 之间通过消息传递、共享状态或编排层来协同。
它解决的不是“模型能力”问题,而是“工程结构”问题。打个比方,单 Agent 像是一个什么都干的自由职业者,多 Agent 像是一个分工明确的小团队。团队不一定每个人都是全才,但通过分工和协作,整体能处理更复杂的项目。
具体来说,多 Agent 架构带来了几个关键收益:
- 职责隔离:每个 Agent 只关心自己的领域,提示词更聚焦,工具集更精简,出错概率自然下降。
- 上下文分片:每个 Agent 只维护自己需要的上下文,避免单一上下文窗口被撑爆。
- 可并行执行:独立子任务可以同时跑,整体耗时大幅缩短。
- 可审核与可回滚:关键节点可以插入审核 Agent,发现错误及时中断或重试。
- 可观测性提升:每个 Agent 的输入输出独立记录,排查问题时能快速定位是哪一环出了岔子。
但多 Agent 也不是银弹。它引入了新的复杂度:通信开销、状态一致性、编排逻辑、死锁风险。我见过不少团队一上来就搞五六个 Agent 互相调用,结果调试成本比单 Agent 还高。所以架构演进的关键不是“越多越好”,而是“在合适的复杂度下选择合适的分工粒度”。
2. 多 Agent 协作的几种主流架构模式
2.1 编排式架构:一个大脑指挥多个手脚
编排式(Orchestrator)是我最推荐新手入门的多 Agent 模式。它的结构非常清晰:有一个中心编排 Agent 负责理解任务、拆解步骤、分配子任务,其他 Agent 只负责执行具体动作并返回结果。
这种模式的好处是控制流集中,调试起来相对容易。编排 Agent 的提示词里明确写着“你负责规划和调度,不直接执行工具”,执行 Agent 的提示词里写着“你只负责完成分配给你的子任务,不要自行扩展”。职责边界清晰之后,每个 Agent 的行为都变得可预测。
我做过一个“竞品分析报告生成”的项目,用的就是编排式架构。编排 Agent 拿到需求后,拆成四步:收集竞品基础信息、抓取功能对比、整理定价策略、生成分析报告。然后依次调用四个执行 Agent,每个执行 Agent 只做一件事,做完把结果回传给编排 Agent,由编排 Agent 决定下一步。
这里有个实操细节:编排 Agent 和执行 Agent 之间的消息格式一定要严格定义。我一开始用自然语言传递任务,结果执行 Agent 经常“过度理解”,把不属于自己的任务也干了。后来改成结构化 JSON,包含task_id、task_type、parameters、expected_output四个字段,问题就消失了。
注意:编排式架构的瓶颈在编排 Agent 本身。如果任务拆解逻辑太复杂,编排 Agent 的提示词会变得非常长,维护成本高。建议把拆解规则写成外部配置或规则引擎,而不是全部塞进提示词。
2.2 对等协作架构:Agent 之间平级沟通
对等协作(Peer-to-Peer)模式下,没有中心编排者,多个 Agent 平级存在,通过消息总线或共享黑板来沟通。每个 Agent 监听自己关心的消息,处理完再广播结果。
这种模式适合那种“任务边界模糊、需要反复讨论”的场景。比如内容创作场景:一个 Agent 负责写初稿,一个负责事实核查,一个负责风格润色,一个负责合规审查。它们之间不是严格的上下游关系,而是互相反馈、迭代修改。
我试过一个“技术博客协作”的实验,四个 Agent 通过共享黑板协作。写作 Agent 先发一版草稿,核查 Agent 发现两个数据引用有问题,标注出来;润色 Agent 调整了段落结构;合规 Agent 提示某处表述可能引起歧义。写作 Agent 收到所有反馈后统一修改,再进入下一轮。
对等协作的优点是灵活,缺点是容易出现“讨论死循环”。两个 Agent 互相认为对方应该先行动,或者反复修改同一处内容。我的经验是必须设置最大轮次限制和收敛条件,比如“最多迭代 3 轮”或“连续两轮无实质性修改则终止”。
2.3 层级式架构:多级编排与分治
当任务规模进一步扩大,单层编排也不够用了,就会出现层级式架构。顶层编排 Agent 负责大阶段划分,每个大阶段下面再有一个子编排 Agent 负责细粒度调度,最底层才是执行 Agent。
这种模式在复杂工作流里很常见。比如一个“市场调研”任务,顶层拆成“数据采集阶段”“分析阶段”“报告阶段”;数据采集阶段的子编排再拆成“公开信息采集”“问卷数据整理”“访谈记录归纳”;每个子任务再交给具体执行 Agent。
层级式架构的工程挑战在于状态传递。上层需要知道下层的执行进度,下层需要拿到上层分配的上下文。我通常用一个共享的TaskContext对象来承载全局状态,每个 Agent 读写自己负责的字段,避免消息在层级间反复拷贝。
2.4 三种架构的选型对照
| 架构模式 | 适用场景 | 优势 | 风险点 | 调试难度 |
|---|---|---|---|---|
| 编排式 | 任务可清晰拆解、步骤相对固定 | 控制流集中、易调试 | 编排 Agent 容易成为瓶颈 | 低 |
| 对等协作 | 任务边界模糊、需要反复迭代 | 灵活、容错性好 | 容易死循环、收敛慢 | 中 |
| 层级式 | 大规模复杂工作流 | 可扩展性强、分工细 | 状态管理复杂、通信开销大 | 高 |
选型的时候不要追求“最先进”,而是看任务特征。任务步骤明确就用编排式,需要多轮讨论就用对等协作,规模大到单层管不过来再上层级式。我见过太多项目一上来就搞层级式,结果光调试 Agent 之间的通信就花了两周,实际业务逻辑还没跑通。
3. 工程化落地的关键细节与实操要点
3.1 Agent 之间的通信协议怎么设计
多 Agent 协作最容易翻车的地方就是通信。自然语言传递任务看起来方便,实际上歧义太多。我的做法是定义一套轻量级的消息协议,所有 Agent 之间的交互都走这个格式。
一个典型的任务消息包含以下字段:
{ "message_id": "msg_20240610_001", "sender": "orchestrator", "receiver": "data_agent", "task_type": "query_database", "priority": "normal", "parameters": { "table": "sales_records", "filters": {"region": "east", "date_range": "last_week"}, "fields": ["return_rate", "order_count"] }, "expected_output": { "format": "json", "schema": {"return_rate": "float", "order_count": "int"} }, "timeout_seconds": 30, "retry_policy": {"max_retries": 2, "backoff": "exponential"} }这套协议的好处是每个 Agent 收到消息后,不需要“猜”对方想要什么,直接按字段解析就行。expected_output字段尤其重要,它让执行 Agent 知道返回结果应该长什么样,避免返回一堆自然语言描述导致编排 Agent 还要再做一次解析。
实操心得:消息协议不要设计得太复杂。我一开始加了十几个字段,结果维护起来非常痛苦。后来精简到八个核心字段,覆盖了 90% 的场景。剩下的特殊需求通过
parameters里的扩展字段解决。
3.2 状态管理与上下文传递的坑
多 Agent 系统里,状态管理是最容易被低估的环节。单 Agent 的时候,上下文就是一个不断增长的列表,简单直接。多 Agent 之后,每个 Agent 有自己的上下文,还要共享全局状态,复杂度直线上升。
我踩过的一个典型坑是:编排 Agent 把任务分配给执行 Agent 后,执行 Agent 修改了某个共享状态,但编排 Agent 的上下文里还是旧值,导致后续决策基于过期信息。这个问题在并行执行时尤其明显。
解决方案是引入一个中央状态存储,所有 Agent 读写状态都通过它,而不是各自维护副本。可以用简单的键值存储实现,也可以用消息队列广播状态变更。关键原则是:任何 Agent 都不应该缓存全局状态,每次决策前从中央存储读取最新值。
另一个坑是上下文膨胀。每个 Agent 的上下文里如果塞了太多历史消息,不仅浪费 token,还会干扰模型判断。我的做法是给每个 Agent 的上下文设置滑动窗口,只保留最近 N 轮的相关消息,更早的历史压缩成摘要。
3.3 错误处理与重试机制的设计
多 Agent 系统里,错误是常态而不是异常。网络超时、工具返回格式不对、模型输出不符合预期、Agent 之间消息丢失,这些都会发生。关键是怎么让系统在出错后能恢复,而不是整个流程崩掉。
我通常设计三层错误处理:
第一层是 Agent 内部重试。执行 Agent 调用工具失败后,先自己重试 1-2 次,用指数退避避免雪崩。如果还是失败,把错误信息结构化后返回给编排 Agent。
第二层是编排 Agent 的降级策略。编排 Agent 收到执行失败的消息后,判断是否可以换一个工具或换一种方式完成子任务。比如查询数据库失败,可以尝试从缓存读取;缓存也没有,就标记该子任务失败,继续执行其他子任务。
第三层是全局熔断。如果某个 Agent 连续失败超过阈值,或者整个流程超时,系统应该主动中断并输出已完成部分的结果,而不是无限重试。
# 简化的重试装饰器示例 import time from functools import wraps def retry_with_backoff(max_retries=3, base_delay=1.0): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_retries): try: return func(*args, **kwargs) except Exception as e: if attempt == max_retries - 1: raise delay = base_delay * (2 ** attempt) time.sleep(delay) return None return wrapper return decorator注意:重试不是万能的。如果错误是“参数错误”或“权限不足”这类确定性错误,重试只会浪费时间。只有“超时”“限流”“临时不可用”这类瞬时错误才值得重试。我在代码里会区分
RetryableError和FatalError,只对前者重试。
3.4 可观测性:怎么知道 Agent 在干什么
多 Agent 系统如果不做可观测性,调试起来就是噩梦。你看到的是一个最终输出,但中间每个 Agent 做了什么决策、调了什么工具、花了多少时间,完全是个黑盒。
我的做法是给每个 Agent 的每次决策都打结构化日志,包含以下字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| trace_id | 全局追踪 ID | trace_20240610_abc |
| agent_name | Agent 名称 | data_agent |
| step | 当前步骤序号 | 3 |
| thought | 模型推理内容 | 需要查询华东区数据 |
| action | 调用的工具 | query_database |
| action_input | 工具入参 | {"region": "east"} |
| observation | 工具返回结果 | {"return_rate": 0.037} |
| latency_ms | 本步耗时 | 450 |
| token_usage | token 消耗 | 1200 |
这些日志汇总到一个追踪系统里,出问题的时候可以按trace_id把整个链路串起来看。我实测下来,有了这套日志之后,排查一个多 Agent 流程的问题从原来的半小时缩短到五分钟以内。
另外,可视化也很重要。把 Agent 之间的调用关系画成时序图,一眼就能看出哪个环节是瓶颈、哪个 Agent 被调用次数异常。不过这里不展开画图工具,核心是日志数据结构要统一。
4. 从 ReAct 到多 Agent 的迁移实战与常见问题
4.1 什么信号说明该从单 Agent 迁移了
不是所有项目都需要多 Agent。我见过一些团队,单 Agent 还没跑稳就急着上多 Agent,结果复杂度翻倍,效果反而更差。以下几个信号出现时,才说明迁移的时机到了:
- 单 Agent 的提示词超过 2000 字,而且还在不断加规则。这说明职责已经过载,提示词在试图用“写更多规则”来弥补架构缺陷。
- 工具数量超过 15 个,且模型选错工具的频率明显上升。工具越多,单 Agent 的选择空间越大,出错概率越高。
- 任务平均步数超过 12 步,且经常在中途丢失目标。这是上下文窗口被撑爆的典型表现。
- 需要并行执行才能满足时效要求。单 Agent 串行执行导致整体耗时不可接受。
- 需要独立审核环节。业务上要求关键步骤必须有人或另一个 Agent 复核,单 Agent 无法自己审核自己。
如果只是任务稍微复杂一点,但步数在 8 步以内、工具在 10 个以内,我建议先优化单 Agent 的提示词和工具设计,而不是急着拆多 Agent。
4.2 迁移过程中的典型问题与排查表
迁移过程中遇到的问题,八成集中在通信、状态和超时这三类。我整理了一份速查表,都是实际踩过的坑:
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 执行 Agent 收到任务后不动作 | 消息格式不符合预期 | 检查消息 JSON 是否缺少必填字段 | 统一消息协议,加 schema 校验 |
| 编排 Agent 反复分配同一任务 | 执行结果未被正确回传 | 查看状态存储中任务状态是否更新 | 执行完成后强制更新任务状态 |
| 多个 Agent 同时修改同一状态 | 缺少并发控制 | 检查是否有锁机制或版本号 | 引入乐观锁或串行化关键操作 |
| 整体流程卡死无输出 | 某个 Agent 等待永远不会到达的消息 | 查看各 Agent 最后一条日志 | 设置全局超时和心跳检测 |
| 输出结果与预期偏差大 | 上下文传递时信息丢失 | 对比各 Agent 收到的上下文 | 关键信息显式传递,不依赖隐式继承 |
| token 消耗异常高 | 上下文窗口未做裁剪 | 统计各 Agent 的 token 使用量 | 引入滑动窗口和摘要压缩 |
实操心得:迁移的时候不要一次性把所有功能都拆成多 Agent。我的做法是先拆一个最痛的环节,比如把“数据查询”从主流程里独立出来做成一个专门 Agent,跑稳之后再拆下一个。渐进式迁移比大爆炸式重写成功率高得多。
4.3 性能优化的几个实用技巧
多 Agent 系统跑通之后,下一步就是优化性能。我总结下来,效果最明显的几个优化点:
并行化独立子任务。编排 Agent 在拆解任务时,识别出没有依赖关系的子任务,同时分发给多个执行 Agent。比如“查三个区域的数据”就可以并行,整体耗时从 3 倍降到 1 倍多一点。
缓存高频工具调用结果。有些工具调用是重复的,比如“获取当前时间”“查询配置项”,这些结果可以在一定时间内缓存,避免每个 Agent 都去调一次。
精简 Agent 提示词。每个 Agent 的提示词只保留与其职责相关的内容,不要把所有全局规则都塞进去。我实测过,把执行 Agent 的提示词从 800 字精简到 300 字,响应速度提升约 20%,而且输出更稳定。
设置合理的超时和重试上限。不要用默认的无限等待。每个工具调用设置 10-30 秒超时,每个 Agent 设置最大执行时间,整个流程设置全局超时。超时后走降级逻辑,而不是一直挂着。
用更小的模型做简单任务。不是所有 Agent 都需要用最大的模型。分类、格式化、简单查询这类任务,用轻量模型就够了,成本和延迟都能降下来。只有需要复杂推理的编排 Agent 才用大模型。
4.4 一个完整的迁移案例拆解
最后分享一个我实际做过的迁移案例,把原来单 Agent 的“周报生成助手”改成了多 Agent 架构。
原来的单 Agent 流程是:用户说“生成本周周报”,Agent 依次查项目进度、查代码提交、查会议记录、汇总生成。问题很明显:查代码提交经常超时,一超时整个流程就卡住;而且生成的周报格式不稳定,有时候漏掉某个模块。
迁移后的架构是:
- 编排 Agent:负责理解用户需求,拆解成“项目进度”“代码活动”“会议纪要”“汇总生成”四个子任务,按顺序调度。
- 项目进度 Agent:只负责查询项目管理工具,返回结构化进度数据。
- 代码活动 Agent:只负责查询代码仓库,返回提交统计。
- 会议纪要 Agent:只负责读取会议记录,提取关键决策。
- 汇总 Agent:拿到前三个 Agent 的结构化输出后,按固定模板生成周报。
迁移之后的效果:单个子任务失败不影响其他子任务,汇总 Agent 拿到的都是结构化数据,格式稳定性大幅提升。整体耗时从原来的平均 45 秒降到 28 秒,因为代码查询和会议读取可以并行执行。
踩过的坑也有:一开始汇总 Agent 的提示词写得太复杂,试图让它“智能判断哪些内容重要”,结果输出反而不可控。后来改成固定模板填充,只在“本周重点”部分留一点自由发挥空间,稳定性就上来了。
这个案例让我更确信一个观点:多 Agent 架构的价值不在于“更智能”,而在于“更可控”。每个 Agent 做一件明确的事,用结构化数据交接,出错时能快速定位和恢复。这套思路在大多数工程场景里都比“一个全能 Agent”更靠谱。