☰
多人多AI协同系统架构拆解:从消息总线到状态治理
2026/10/14 10:03:04 网站建设 项目流程

最近在整理多智能体系统的设计方案时,有个感受越来越强烈:当AI代理从“单机陪聊”走向“多人团队协作”的那一刻,技术核心就瞬间从模型层转移到了系统层。单个模型的问题我们已经有相对成熟的手段去缓解,但“多个AI代理代替不同成员去和其他AI代理交互”这件事,牵扯的是信息流、状态所有权、权限边界、失败恢复和可观测性,这些恰恰是传统分布式系统研究了几十年的老问题,换了一层“AI”皮之后,变得既熟悉又陌生。

这篇文章想聊的,就是基于AI代理代为交互的多人多AI协同系统架构研究:一个项目组里几十个用户、若干专业AI代理共存,每个用户可能有自己的AI代理助手,这些代理之间要分派任务、共享上下文、给出结论,还要接受审计。我不会只给“未来趋势”,而是希望把架构拆到能落地的颗粒度:代理角色怎么分、消息怎么传、状态怎么存、上下文怎么压缩、基础设施怎么搭,以及坑在哪里。

适合正在设计或准备重构多代理平台的架构师、后端工程师,以及被业务方要求“让AI协同起来”却不知道从哪里下手的团队负责人。如果你只是想调一个OpenAI接口,这篇对你偏重;如果你想构建真正支撑多用户多代理的系统,这篇应该能给你一张可以照着画的地图。

1. 单一AI代理的墙:多人多AI协同到底解决了什么

1.1 三个绕不过去的矛盾

第一类矛盾是能力的广度与深度不可兼得。你要一个代理既擅长写代码,又擅长数据分析,又擅长流程审批,还要懂行业术语,这在单模型时代只能靠不断叠加Prompt,叠加到最后就是上下文爆掉、指令冲突、输出质量明显下降。实际工程里更常见的方案是让多个专业代理各干一摊,每个代理只维护一份聚焦的系统提示词和一套精简工具,再通过上层协调机制拼起来。

第二类矛盾是权限与隔离。当系统里只有一个AI代理服务几个用户时,所有对话都堆在一个上下文里,还能勉强运行;可一旦出现“多人”参与,每个成员能看什么数据、能调用什么工具、能触发什么业务操作,必须分得清清楚楚。这个“可审计的权限边界”,单靠一个共享模型是表达不了的,它必须由系统架构在代理实例和路由层显式托住。

第三类矛盾是失败控制的粒度。单体大代理一旦进入错误状态,整个任务链全崩。拆成多个AI代理之后,每个代理有清晰的状态机:运行中、等待中、失败、超时,上层可以只重跑失败的环节,而不是对整个流程叫停。粒度小了,容错手段才多。

1.2 “多人”这个词才是架构分水岭

单人多代理和多人多代理,看起来都是“多代理”,但架构复杂度完全不在一个量级。单人多代理的核心是编排:把一个任务拆成子任务,按顺序交给不同代理。多人多代理的核心则要在编排之上再加三件事:

  • 会话归属与任务所有权:消息必须能追溯到某个用户、某个项目、某个授权等级,否则审计无从谈起。
  • 共享状态的并发控制:多个代理可能同时读写同一个项目文档、同一个业务单据,没有锁和版本控制,很快就是数据互相覆盖。
  • 协同策略的版本管理:今天允许代理A直接通知代理B,明天发现风险要改成必须经过协调者审批,这个策略不应该写死在某个代理的提示词里,而应该成为独立可下发的配置。

这些都不是模型层的能力,而是架构层的必需品。

1.3 架构设计需要守住的目标

我给这类系统定的目标,可以参考:

  1. 可替换性:任何一个AI代理(模型供应商、提示词、工具集)都能被升级或替换,而不影响其他代理的接口。
  2. 可扩展性:增加一个参与者,不是改代码,而是注册一条配置。
  3. 可观测性:任意一条任务链路,能回放“谁收到了什么、谁做了什么决定、调用了什么工具”。
  4. 可控制性:在任何一环出问题时,管理员能暂停、降级、重放、回滚,而不是只能看着模型自由发挥。

这四个目标会在后面反复出现,因为它们直接决定选型。比如为了可替换性,代理之间就不能互相调用SDK,必须走统一消息总线;为了可观测性,每个代理都要输出结构化日志,把跟踪ID贯穿到底。这个判断看着朴素,但很多项目最后架构混乱,恰恰就是在一开始把代理之间的点对点调用做得太“方便”了。

2. 代理拓扑与角色划分:调度者、执行者、观察者怎么落位

2.1 拓扑选型:纯星型、纯总线,还是混合

多代理之间怎么连接,先要定拓扑。常见的三种:

  • 星型拓扑:所有代理都只和中心协调者通信,优点是决策收敛、权限集中,缺点是中心节点容易成为瓶颈。
  • 总线拓扑:所有代理通过消息总线发布和订阅主题,优点是扩展性好、解耦充分,缺点是缺少一个强力大脑时容易出现“各说各话”。
  • 网状拓扑:代理之间点对点直连,灵活但极难治理,在多人参与的场景下基本不建议。

我的建议是混合:决策走星型,事件走总线。需要权限判断、任务拆分、审批确认的操作,统一由协调代理接管;而不需要严格顺序处理的系统事件、通知、状态变更广播,则放到总线上让订阅方自行消费。这样既保留决策的收敛性,又保留事件的开放性,后续往系统里加一个新的专业代理时,不需要改中心调度代码,只要让代理注册自己的能力和订阅主题即可。

2.2 五个基础角色

从多人协同的实际需求倒推,系统里面至少要有这几类角色:

角色职责典型输入典型输出
用户网关代理身份认证、意图理解、结果展示、反馈收口用户自然语言请求规范化的任务请求
调度协调代理任务拆解、分配、DAG管理、冲突仲裁规范化任务请求子任务分派消息
专业执行代理写代码、做分析、检索、生成文档子任务消息结构化执行结果
审计观察代理抽样检查、越权检测、敏感信息拦截总线上的消息副本审计告警记录
知识状态服务维护共享状态、向量索引、版本控制状态读写请求一致性视图

用户网关代理的意义经常被低估。如果用户直接面对某个执行代理,会造成两个问题:一是身份分散,用户到底在跟谁说话,边界模糊;二是提示词注入很难拦截,用户的消息很容易渗透到执行代理的指令里。加一层网关做统一的输入规范化和权限校验,后面所有代理都只认结构化请求,安全系数会高很多。

调度协调代理是多AI协同里的“大脑皮层”,它不直接产生业务答案,而是负责判断哪个执行代理最适合回答、任务之间有没有依赖、是不是需要多轮协商,以及冲突出现时怎么仲裁。为了让仲裁有依据,调度者必须能看到当前各个代理的状态和负载,而不能只靠“大致感觉”。

审计观察代理是一个容易被省略但很重要的角色。它通过订阅总线上的副本消息,做抽样验证:某个代理是不是试图读取了自己权限之外的数据、是否在输出中携带了不应该出现的敏感字段、是否有出乎意料的自主行动。它不参与实时任务流,却能在问题扩散前拉响警报。

2.3 代理状态机:给“自主行动”装上边界

AI代理本质上是一个会主动发起行为的实体,但如果每个代理想动就动,协同就变成了布朗运动。我建议每个代理都在内部维护一个显式的状态机:

IDLE -> RUNNING -> TOOL_CALLING -> WAITING -> COMPLETED | | v v FAILED TIMEOUT
  • IDLE:当前无任务,等待消息。
  • RUNNING:正在处理请求。
  • TOOL_CALLING:正在调用外部工具,比如检索、写库、执行代码。
  • WAITING:等待其他代理或用户提供补充信息。
  • COMPLETED / FAILED / TIMEOUT:终态,触发上层回调。

每个状态都有超时时间,超过阈值就把任务踢回调度者重新规划,而不是让代理一直挂在“思考中”。这个状态机不只是给前端展示进度用的,它本身就是控制接口:上层可以据此把某个代理设置为“暂停接受新任务”,也可以在协调者检测到异常状态时强制降级。

3. 代理间通信机制:消息信封、路由规则与同步异步取舍

3.1 为什么不能直接让代理互相调用API

很多开发者的第一反应是“代理A需要数据时,直接调用代理B的接口不就行了”。在小规模原型里,这确实最快;可一旦体系扩张,直接调用的坏处就全来了:调用链强耦合,上游换一个实现,下游所有调用方都要改;链路无法回放,出了问题时连“到底哪条消息触发了这个动作”都查不到;权限更无从拦截,因为消息不经过任何一个统一的边界。

在多人多AI协同架构里,我坚持让所有代理间通信都走统一消息通道,代理彼此之间不感知对方的内部实现。这个通道相当于系统的“神经中枢”,既负责转发,也负责记录和拦截。

3.2 统一消息信封:先把字段定死

既然要统一通道,就要先定消息结构。建议所有代理间的消息都封装在一个标准信封里,字段大致如下:

class AgentMessage(BaseModel): message_id: str # 全局唯一消息ID trace_id: str # 链路追踪ID,一次协同任务贯穿到底 parent_message_id: str # 父消息ID,用于回放和循环检测 session_id: str # 会话ID,标识属于哪个用户/项目 sender_agent: str # 发送方代理标识 targets: list[str] # 接收方代理标识或主题 task_type: str # 任务类型,如 plan / execute / review / notify task_goal: str # 一句话描述任务目标 policy_version: str # 当前生效的策略版本号 schema_version: str # 消息结构版本号 payload: dict # 实际业务数据 timestamp: int # 毫秒时间戳 hop_count: int # 消息流转跳数,用于防循环

schema_version特别重要。AI代理由不同团队迭代,消息结构不可避免要演进;带上版本号之后,新旧代理可以先按照兼容版本通信,而不必被迫同时升级。

3.3 路由规则:先匹配能力,再匹配策略

有了信封之后,消息怎么到达正确的代理,一般有两种路由思路:

  • 基于主题的发布订阅:某个代理发布了“数据查询完成”这个事件,订阅者自行决定是否响应。
  • 基于能力注册的定向路由:调度者维护一张“能力-代理”映射表,例如“SQL分析→data_agent”,“代码审查→code_review_agent”。

多人协同场景里,两种要结合。宏观任务用能力注册表做定向路由,这样调度者有全局视角;微观的状态变化、工具执行结果这类事件,用发布订阅广播给关注者,减少中心节点的消息负担。

路由还有一个容易被忽略的规则:优先级。来自用户网关代理的实时请求,要优先于后台异步计算的结果;带审批性质的消息要优先于普通通知。不给消息分优先级,系统在高负载下会表现得“所有事都很慢”,影响体验。

3.4 同步与异步的取舍

代理之间的交互,不一定要“一问一答”等到底。我的判断标准是:

场景选择原因
用户在前端等待问答结果同步链路延迟敏感,需要快速返回
多代理协同做复杂分析异步事件子任务耗时长,不应阻塞整条链路
外部工具调用异步+回调工具可能执行几十秒甚至更久
审计、通知、状态广播异步实时性要求低,可靠性优先
用户需要确认后才能继续异步+人工介入中间状态要持久化,等用户回来继续

如果系统里有人工参与确认的流程,强烈建议把请求做成异步:先把“等待确认”的状态写进共享存储,给前端发一个通知,等用户点完按钮再通过回调消息驱动后续链路。让一个代理同步等用户回复,既不现实也容易导致超时失控。

3.5 消息持久化是底线,不是可选项

所有消息都应该持久化,不删。这一步不是单纯为了出问题时“留个案底”,而是要把消息流当作整个系统的“事实日志”:代理当前状态可以由消息历史推导出来,审计回放只是查询日志,甚至冲突仲裁也可以回溯到具体某条消息。用事件溯源的思想去设计这层,整个系统会清晰很多。

有人会说“消息量太大怎么办”,那是存储规划问题;有没有按统一格式保留完整消息,则是架构原则问题。原则立住了,后面所有治理手段才有数据基础。

4. 上下文、状态与记忆:多代理协同中的治理策略

4.1 多代理最浪费不起的就是上下文

在单模型场景里,把历史对话一股脑拼进Prompt是常见做法;但在多代理协同里,这么做几个回合就会把模型的上下文窗口塞爆。更严重的是,各个代理看到的上下文不一致:代理A知道某个决策已经被推翻,代理B还拿着旧结论往外写,用户看到的最终结果就是内部打架。

真正可靠的做法是区分两类数据:对话历史和任务工作记忆。对话历史属于某个用户与会话,是审计回放用的;任务工作记忆则是当前多个代理正在协同处理的中间结果。只有任务工作记忆才需要被相关代理共享,完整对话历史不应该一揽子打包进每个代理的上下文。

4.2 记忆分级:短期、中期、长期

我把多代理系统中的记忆分成三级:

  • L1 短期工作记忆:当前这一个任务的中间状态,比如“正在分析的输入数据”“已经生成的三条候选方案”。粒度小,生命周期短,只在小范围相关代理之间共享。
  • L2 项目共享状态:项目文档、接口约定、决策记录、术语表。生命周期长,所有参与该项目的代理都能按权限读取。
  • L3 长期知识库:跨项目的通用经验和历史模式,需要靠向量检索把相关片段抽出来注入提示词。

三级的决定性差异在于访问频率和更新方式。L1更新频繁,适合放Redis这类高速存储;L2适合放带版本管理的文档仓库或数据库;L3适合做向量索引,只读为主,定期重建。别把三个东西塞进同一个存储,否则要么性能不行,要么版本管理一团糟。

4.3 上下文压缩不仅要减量,还要加结构

当需要把共享状态喂给模型时,不要直接把原始记录全量塞进去。我按以下顺序处理:先把结构化字段直接抽取出来(比如任务状态、关键决策、待办事项),再对非结构化文本做摘要压缩,最后才把向量检索召回的相关片段拼接到提示词里。原则是:先结构化,再压缩,最后才落进模型调用。

举一个具体做法:每次任务节点完成后,调度者让负责的代理生成一段“决策摘要”,把背景、结论、下一步这三个要素写清楚,存进L2。后续代理需要时,只用摘要做输入,没有特殊情况不需要读完整过程。这既控制token,也避免把模型自己不稳定的中间推理带进后续环节。

4.4 共享状态的并发一致性

多个代理对同一份共享状态做修改,是一件危险但无法避免的事。最典型的例子是多个执行代理同时编辑同一份项目文档,每个代理基于自己拿到的版本做了修改,写回时互相覆盖,用户看到的最终版可能是A的标题加B的正文加C删掉的一段,混乱到无法修复。

我的建议是:所有共享写操作都走版本号控制。每个代理写回时必须携带它读取时获取的版本号,存储端用乐观锁做CAS校验,版本不一致就拒绝写入并返回“状态已变更,请重新拉取”。更进一步,代理写回时应该声明自己负责的字段,而不是整块覆盖,防止无意识的互相踩踏。

4.5 权限边界的显式化

多用户场景里,权限绝不能依靠“让AI自己判断能不能看”。模型不具备可靠的数据权限判断能力,这在工程上已经是被反复验证过的结论。正确做法是:在消息入口处由网关代理完成身份识别,再由总线上的权限过滤组件根据用户角色和资源标签决定消息是否允许流转。模型只能看到过滤后的数据。

也就是说,安全边界在架构层完成,而不是在提示词层完成。这个边界要设计成“默认拒绝”:新用户、新代理加入时,没有显式授权就无法接触任何数据。

5. 基础设施选型:编排层、本地模型接入与分布式部署

5.1 编排层和代理运行时不能混为一谈

我见过不少团队把调度逻辑写在某个代理的内部,也就是“让主代理自己决定要不要调用别的代理”。短期看灵活,长期看整个系统变成了一团无法测试、无法替换的黑洞。

正确做法是把编排层和代理运行时分开。编排层是纯确定性组件,负责状态机管理、路由、重试、超时、版本切换,里面没有大模型的概率输出;代理运行时则专门承载模型推理、工具调用、提示词组装。编排层可以被单元测试覆盖,代理运行时则只需要保证接口契约稳定。

这两个东西分开之后,系统的确定性边界就清晰了:控制逻辑是确定的,智能逻辑是概率的。排障时先查控制逻辑,不要一上来就怀疑模型。

5.2 AI代理助手接入本地模型的架构要点

多人多AI协同的架构里,很多用户会要求“我的AI代理助手必须接入本地模型”,原因往往是数据不出内网、成本控制或者特定行业的合规要求。这个诉求在架构上完全成立,但要注意几个问题。

建议在代理运行时之上再抽象一层“模型网关”,统一接入OpenAI、vLLM、Ollama、本地Triton等推理后端。上层代理只面向网关接口编程,这样同一个代理可以随时切换远端模型和本地模型,而不需要改业务逻辑。判断一个请求该走本地模型还是远端模型,可以按规则来:敏感数据任务强制走本地,非敏感且需要强推理能力的任务可以跑到远端大模型,成本敏感的任务优先本地小模型。

本地模型接入还有一个实际门槛:模型并发能力有限。一个本地7B模型在高并发下延迟会急剧上升,所以网关层必须做排队和流控,不能让几十个代理同时把请求压到同一个推理进程上。

5.3 多机部署里的网络与硬件隔离设计

当参与协同的代理数量多到单机扛不住时,就得拆成多机部署。这时的网络拓扑,我在设计里参考了分布式交换机的思路:所有节点所连接的网络结构都需要保持节点间配置的一致性,并支持控制面/数据面分离。换成协作语言就是:代理之间的业务流量走数据面,调度命令和策略下发走控制面。数据面的高吞吐和大规模交换最好交给专门的传输通道,控制面的配置变更则需要保证各节点能看到同一个策略版本。

硬件加速设备方面,如果多个代理共享GPU资源,必须考虑隔离强度。在Linux系统上,IOMMU软件架构决定了设备能否安全直通给某个容器或虚拟机。做GPU直通前先确认IOMMU分组是否完整:如果本应属于同一设备的PCI功能被分到了不同IOMMU组,直通就会失败或带来安全风险。开机后在日志里看分组情况,这是Linux上很基础但项目里总是有人忽略的一步。

5.4 部署环境检查清单

我每次搭多代理环境,都会过一遍这张清单:

  • 用uname -m确认当前系统架构是amd64还是arm64,再选择对应版本的推理运行时镜像,避免在树莓派这类ARM设备上装到x86包导致运行失败。
  • 启动前检查IOMMU开关,确认设备透传路径是否通畅。
  • 确认所有节点能访问同一个共享存储或状态服务,否则不同机的代理会看到漂移的视图。
  • 检查消息总线的Topic权限配置,新的执行代理只授予它订阅所需主题的权限。
  • 提前部署日志采集组件,让每个代理的标准化日志从第一天起就进入统一检索平台。
  • 给每个代理的模型版本、提示词版本、知识库版本做登记,部署时才能做到精确回滚。

6. 控制回路失控与排查:循环调用、雪崩、状态漂移

6.1 重试引发雪崩

多代理系统里有一条容易踩爆的路:代理调用外部工具超时后自动重试,重试再超时再重试,最后外部服务被打挂,连锁反应又拖垮模型网关。查下来“每个代理都只重试了两三次”,但几十个代理的重复请求汇聚起来就是一场流量雪崩。

解决办法是分层限流和退避:代理内部做基础重试时用指数退避,两次之间至少间隔1秒、2秒、4秒;网关层再设置全局并发限制,超限直接返回“忙,稍后重试”。关键是让代理学会读取“忙”信号,而不是机械地再次发起请求。

6.2 代理间循环调用:开放环路的噩梦

代理A产生了一个待确认问题,发给代理B;代理B不够确定,又把问题转发给A;两边都觉得对方应该先表态,于是互相等待,或者在非同步链路里无限互相触发。这不是理论风险,是多代理项目里最典型的失控模式。

我在消息信封里专门设计了两个机制来对抗:一是hop_count限制,任何链路超过5跳就强制终止;二是parent_message_id要求,每个消息必须指出它是因哪条消息而生。有了这两个字段,就可以在通信日志里画出完整的调用树,检测出形成环的路径,并在调度层配置“禁止同一对代理在短时间窗口内互相转发超过N次”。

更重要的设计原则是:普通执行代理不能自主转交任务。所有跨代理的转交必须回到调度协调者,由协调者根据全局任务DAG决定下一步给谁。代理之间有直接的“唤起”能力看似高效,实际上会把任务流变成随机游走。

6.3 状态漂移与不可复现

大模型本身就是非确定性的,同一个提示词两次调用可能得到完全不同的结果。这给协同系统带来了“状态漂移”问题:代理A执行完某个子任务之后,它自己知道结果,但调度者如果只保存了“完成”这个标记而没有保存关键字段,后续代理拿到的可能是已经被改写过的摘要,最终结论就会偏离原始结果。

我的应对是:每个执行代理的任务结果必须包含结构化字段“结论与依据”,关键执行结果禁止只存在于对话摘要里。同时在协同链路的关键节点,把模型版本号、提示词版本号、温度参数一起落库。复盘时如果发现结论异常,可以先确认是不是某一次链路使用了不同的模型版本。

6.4 可观测性建设必须前置,别等出事了再补

多代理系统排障最痛苦的一点是“不知道哪一步出了问题”。普通单体服务看一条调用链就够了,多代理协同则需要回放几十条链路交错的消息流。所以从第一天起,所有代理日志就必须按统一JSON格式输出,包含trace_id、agent_id、parent_message_id三个字段。缺这些字段,排查时只能对着时间戳猜。

具体做法是:给每个用户请求生成一个trace_id,所有代理处理该请求时都在日志里带上它;再配合消息信封中的parent_message_id,就能在日志平台里重建出完整的消息树。出问题时先顺着trace_id把消息树拉出来,看哪个节点没有产出、哪个节点发生了异常重试,定位效率能快一个数量级。

有一个成本比较高的经验是:早期为了省事没给日志加结构,等系统出了循环调用问题才去补,补的代价比写日志高得多。日志结构化这件事,必须在联调之前就完成,不要在队列之后。

7. 写在最后:如果只保留一条设计原则

如果问我在这个架构里最想强调哪一条原则,我会回答:消息是唯一事实来源。所有协同行为都通过结构化消息发生,所有共享状态都由消息事件投影生成,所有权限校验都在消息入口完成。你可能觉得这句话太抽象,但实际做下来,它会逼你在设计早期就把接口契约、存储边界、审计能力和重试机制都定下来,而不是等到问题爆炸后再打补丁。

具体到多人多AI协同的落地顺序,我也建议先别急着讨论“代理和代理之间怎么实现拟人化协作”,先把第一版跑通:用户网关进请求,调度者拆任务,两个执行代理各干一件事,消息全程留痕,状态落库可回放。这一步走稳之后,再去加协商机制、加长期记忆、加更复杂的策略版本管理。

架构研究最忌讳的就是在纸面上设计一个完美系统,AI代理的引入放大了不确定性,但架构的价值恰恰在于把不确定性约束在可控的边界内。希望这份拆解能让你在设计自己的多人多AI系统时,少走几条弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询