ARTICLE DETAIL

资讯详情

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

多人多AI协同系统架构:从概念拆解到最小系统落地

多人多AI协同系统架构:从概念拆解到最小系统落地 最近在小范围技术圈里聊到一个很有意思的选题多人多AI协同。很多人第一反应是这不就是大家一块用ChatGPT嘛。还真不是。每个人有自己的AI代理代理之间自动交换消息、协调任务、汇总结果人只在关键节点做审批这种“AI代理代为交互”的模式才是多人多AI协同系统架构研究的真正核心。我基于这个方向做了一轮从概念拆解到最小系统落地的梳理下面把架构设计思路、关键机制、实操步骤和踩坑记录都整理出来希望能给正在做同类系统的团队省点弯路。先说清楚这套系统能解决什么问题。传统的AI协作本质上是“人发起、AI响应”当参与人数和AI数量增长到一定程度消息会变成全连接网状人和人都要转发、转述、确认效率反而比单人用AI更低。多人多AI协同则是把交互主体从“人-AI”变成“AI-AI”人只需要向自己的代理下达目标代理之间通过消息总线协商、分工、求解最后把结论聚合回给所有人。整个过程是异步的、可追溯的、可编排的这才能放大AI的并行处理能力。1. 理解“代理代为交互”的架构动机与核心需求1.1 从“人机直连”到“代理代交互”到底解决了什么问题先看一个很常见的场景一个五人的产品小组要产出竞品分析报告。如果走传统路径五个人各开一个AI对话窗口重复提问“帮我查一下XX产品的功能”“帮我整理竞品定价”……信息散落在不同会话里最后还是要靠人汇总、去重、协调冲突。这个过程的瓶颈根本不是AI生成内容的能力而是信息通道的混乱。引入AI代理之后系统架构会产生一个关键变化每个人不再直接面对大模型而是面对一个属于自己的“AI代理实例”。这个代理知道你的角色、你的偏好、你的上下文你只需要给它一条高层次的指令比如“下周要完成竞品分析重点是定价策略”。代理会把任务拆解成子任务通过协同消息总线分发给其他代理比如数据分析代理负责爬价格、文案代理负责整理卖点、主管代理负责汇总审阅。整个过程中人看到的是代理返回的阶段性结果而不是原始API响应。这种“代交互”带来三个直接的红利。第一是异步化人不需要守在电脑前等AI逐个回答代理之间可以并行处理结果到了再通知人。第二是上下文隔离每个代理维护独立的会话记忆避免多人混用同一个大模型上下文导致的“串味”。第三是可编排系统可以把任务执行过程录制下来什么时候谁访问了哪个工具、谁改动了文档都变成可审计的日志。对于企业场景而言第三点往往比模型本身的推理能力更重要。1.2 多AI协同与多代理系统的三个层次很多文章把“多AI协同”当成一个笼统概念其实落地时必须区分层次否则架构设计会陷入混乱。我习惯把协同拆成三层。第一层是编排层解决的是“谁来做、按什么顺序做”。一个主代理拿到用户请求之后需要调用规划器把任务分解成DAG有向无环图结构确定每个节点的依赖关系再用调度器把节点分配给合适的代理执行。第二层是协作层解决的是“代理之间怎么交换信息”。这里包括消息路由、协议定义、状态同步。比如任务A完成后结果要传给任务B是通过共享存储、还是通过消息队列推送或者两者结合需要在架构上提前定清楚。第三层是协商层解决的是“多个代理意见不一致怎么办”。比如数据分析代理认为产品定价应该下调市场代理认为应该维持这时候谁来仲裁。协商层通常需要一个仲裁代理或者一组合约规则甚至可以引入人在回路做最终决策。这三层对应到系统架构上分别是任务编排器、消息总线和决策仲裁模块。很多团队上来就写代码把多个Agent丢到一个循环里互相调用结果发现任务卡死、上下文错乱、意见冲突没人解决就是因为跳过了层次设计。先划分层次再确定实现多代理系统才不会变成多线程事故现场。1.3 系统设计前的三个关键问题动手设计之前建议团队先回答三个问题。第一个问题人到底参与多深。有的场景希望全自动代理之间自由协商人只看最终报告有的场景则要求关键节点必须由人确认比如对外发消息、修改订单、发布文章。我推荐的做法是引入“审批闸门”在任务流中预设多个检查点代理完成任务后不直接进入下一跳而是先进入待审批队列。这样既能保留自动化的高效又不会让系统失控。第二个问题协同范围是封闭的还是开放的。封闭场景指代理数量固定、角色固定适合用中心化编排开放场景指代理可以动态加入退出比如外部合作伙伴的AI代理要接入你们的系统那消息协议必须标准化注册发现机制也必须支持动态更新。这直接决定选择消息总线的技术形态。第三个问题状态存储在哪里。多代理协同最忌讳的就是把全部状态放在每个代理的本地上下文里一旦某个代理崩溃整个任务状态就丢了也无法回放。可行的做法是引入共享状态存储按任务ID分命名空间保存每个代理的执行快照代理之间只传轻量级结果引用真正的大体量数据放对象存储或向量库里。这是很多从原型走向生产的系统最需要补的课。2. 多人多AI协同系统的总体架构与分层设计2.1 五层架构从接入层到记忆与存储层经过多轮迭代我最终沉淀出一套五层架构它适用于大多数中小规模的多人多AI协同应用也方便后续横向扩充。第一层是接入层。要能同时处理人和系统的输入输出。人可以通过Web客户端、IM机器人、API网关跟自己的代理交互其他系统则通过Webhook或消息队列接入。接入层需要做身份认证和消息整形把不同来源的请求统一封装成标准任务对象。第二层是代理层也就是每个用户或每个业务角色对应的AI代理实例。代理实例内部通常包含角色提示词、工具列表、短期记忆和决策逻辑。这里要注意代理实例并不是简单地封装一个LLM调用它需要具备使用外部工具的能力比如检索知识库、读写表格、调用企业内部API否则代理之间沟通得再多也无法真正解决业务问题。第三层是编排层这是整个架构的中枢。任务编排器负责解析用户目标、拆解子任务、生成DAG、分派给代理。编排器内部还要维护一张任务状态表记录每个子任务处于pending/running/succeeded/failed哪个阶段并对超时任务做重试或回滚。目标越大任务拆解的粒度就越重要拆得太粗代理干不了拆得太细编排器自己先成了瓶颈。第四层是协同决策层也可以叫协商层。当多个代理的结果需要合并或冲突时这层负责调用仲裁模型、规则引擎或者发起人工审批。它不直接执行任务但决定结果是否可以进入下一阶段。第五层是记忆与存储层。这里的记忆分三种全局记忆存团队共享的领域知识任务记忆存当前协同任务的全过程上下文私有记忆存单个代理与用户的偏好和沉淀。存储层需要同时支撑结构化数据数据库、非结构化数据对象存储和向量数据向量数据库三种存储各有用途缺一个都会在特定场景下卡壳。2.2 核心组件注册中心、任务路由器、上下文总线与冲突消解器五层架构落地成具体代码需要提炼出四个核心组件。代理注册中心是代理们“报到”的地方。每个代理启动后向注册中心声明自己的身份标识、角色描述、能力标签、可用工具、权限级别和状态。注册中心维护一张能力索引表任务路由器在分配任务时先查这张表只把任务分给声明了对应能力的代理。我习惯用“能力标签置信度”的方式描述代理能力例如data_analysis:0.9这样路由器可以根据置信度结合当前负载打分。任务路由器负责匹配“子任务”与“代理”。它的核心是一个评分函数通常考虑三个维度能力匹配度、当前负载、历史完成质量。比如一个涉及数据分析的子任务路由器会优先选择能力标签匹配度高、当前队列短且历史任务通过率高的代理。复杂场景下路由器还需要感知数据依赖如果子任务B强依赖子任务A的输出那么A和B不应被分给两个不共享存储的代理。上下文总线解决的是“代理们如何共享知识”的问题。实现方式可以是一个Redis Stream也可以是一套Kafka Topic关键不在于消息中间件选型而在于消息必须携带命名空间和血缘信息。每条消息需要带上task_id、correlation_id和from_agent、to_agent字段这样上下文总线才能帮助代理判断哪些消息与自己相关、哪些消息需要忽略。我见过很多团队把总线当成普通聊天室所有消息所有代理都收结果上下文越来越大token开销和噪音一起暴涨。冲突消解器是协同决策层的关键执行者。它不只是一个调用仲裁LLM的prompt而是一套规则引擎加上仲裁模型的组合。常规冲突比如两个代理给出了不同的数字结论消解器先根据数据来源可信度做判定如果还分不出高下再调用仲裁模型并把冲突过程和消解依据记录到日志方便人后续审查判断。2.3 架构选型中心化编排、去中心化协商与混合模式我在研究阶段对比了三种主流架构形态各有取舍这里整理成表格方便参考。架构模式核心特点优势短板适用场景中心化编排单一编排器统一下发任务、收集结果状态可控、流程清晰、便于调试编排器易成瓶颈单点风险高团队角色固定、流程标准的内部协同去中心化协商代理之间直接协商没有统一调度者扩展性好代理自治性强调试困难状态一致性难保证开放生态、代理动态加入的跨组织协同混合模式主流程中心化编排局部决策去中心化兼具可控性和灵活性架构复杂度高需要额外设计边界大多数企业级场景推荐起点就现阶段落地难度来看我建议大多数团队优先选择混合模式。具体做法是主任务流用中心化编排器控制保证整体流程稳定而代理在执行子任务时允许它自主调用其他代理的轻量能力比如主代理让数据分析代理去问前端代理“页面埋点数据表结构”这种局部交互不需要走完整编排。这有点像操作系统设计里的权限划分思路关键资源统一管理非关键路径放权系统才能在稳定和灵活之间取得平衡。3. 核心机制详解多用户映射、任务分解与代理通信3.1 M×N身份映射与上下文隔离“多人多AI”意味着用户集合与代理集合不是简单的1对1而是一个M×N的映射。一个用户可能拥有多个代理比如产品经理同时拥有“需求分析代理”和“竞品调研代理”一个代理也可能被多个用户共享比如团队共用的“数据查询代理”。这种映射关系必须由独立于代理实例的身份管理层维护。我实现时会在数据库里设计三张表用户表、代理表、绑定关系表。绑定关系表除了记录user_id和agent_id之外还会记录角色上下文模板和权限级别。代理实例在接收任务时必须根据当前操作的用户身份加载对应的上下文而不是使用统一的全局上下文。这就是上下文隔离的本质。上下文隔离做不好会出现一个很尴尬的现象用户A让代理查竞品定价用户B紧接着让同一个代理写周报总结然后B收到的报告里混入了A的数据。我见过不止一次这种事故。解决的思路是在消息对象和上下文存储中都打上namespace标识命名空间由“用户ID任务ID”拼接而成代理在处理每一条消息前都先校验namespace是否匹配同时在做向量检索时必须把namespace作为过滤条件彻底切断跨用户的数据泄漏。3.2 基于能力矩阵的任务分解与动态路由任务分解是多代理系统里最能体现架构水平的部分。初级做法是把用户请求原封不动丢给其中一个代理让它自己决定怎么干活这种做法在正式架构里不可取因为大模型的“自我规划”通常不够稳定容易出现重复劳动和遗漏环节。更稳妥的做法是由编排器中的规划模块强行执行任务拆解生成结构化计划。我常用的拆解策略是“目标-里程碑-子任务”三级结构。比如用户目标是“发布一份AI行业周报”规划模块先判断这需要四个里程碑信息采集、数据整理、内容撰写、排版发布。每个里程碑再拆成若干原子子任务例如信息采集里程碑包括“抓取科技媒体TOP10新闻”“筛选与AI相关的3条重点事件”“为每条事件生成摘要”。原子子任务的粒度以“一个代理调用一个工具、返回一个结果”为宜。子任务生成之后进入动态路由阶段。每个子任务都会携带一个required_capability字段路由器去注册中心筛选候选代理再根据负载打分。我自己的实现里给路由评分设了权重能力匹配度0.6当前队列积压量0.3历史通过率0.1。注意历史通过率需要周期性回刷否则老代理永远优先新代理永远得不到试用机会系统能力会退化。3.3 代理间通信协议与消息格式设计代理间通信协议是整个系统架构的血脉。早期原型我直接用自然语言让代理之间互发消息效果非常差原因是大模型生成的自由文本无法被程序稳定解析代理A说了“好的我马上处理”代理B却以为它已经处理完了。后来我改用结构化消息协议形式上类似一个受限的JSON-RPC。消息格式至少要包含这些字段{ version: 1.0, task_id: t_20250210_001, correlation_id: c_8f3a9b, from_agent: agent_planner, to_agent: agent_analysis, type: task_delegate, status: pending, payload: { action: query_database, arguments: { sql: SELECT product, price FROM products WHERE categoryAI } }, timestamp: 2025-02-10T10:00:00Z }这里最有价值的是correlation_id它可以把一整条任务链上的所有消息串起来排查问题的时候按correlation_id搜索就能看到完整的消息轨迹。type字段建议采用枚举值比如task_delegate、task_result、tool_call、conflict_report、approval_request程序可以直接按类型路由大模型只需要填充payload部分的内容即可。在“AI代理加本地模型”的落地场景里通信协议更应该收敛。本地模型对工具调用的遵循能力参差不齐如果协议太复杂模型输出的JSON格式很容易崩坏。我的经验是尽量让payload只包含“动作和参数”不要包含长篇自然语言解释解释类内容放到独立的notes字段即使解析失败也不影响主流程执行。3.4 记忆分层与状态管理私有、共享与全局多代理系统不能把所有信息都塞进大模型的上下文窗口否则token成本会失控而且记忆之间会互相干扰。我在架构里强制实行三层记忆体系。私有记忆归属于某个用户和某个代理的组合主要存用户偏好、历史决策、常见操作习惯。共享记忆归属于当前任务所有参与该任务的代理都能读写这部分通常用Redis或者关系型数据库实现按task_id做键保存任务执行过程中的中间结果和状态快照。全局记忆是团队的领域知识库用向量数据库存储供所有代理在回答问题时检索。更新记忆的时机也非常关键。如果每次子任务执行完都立刻把内容写入全局记忆会让知识库快速膨胀并且质量失控。我设置了一个“沉淀闸门”只有经过人工确认或者仲裁模型判定高质量的结果才会从任务记忆写入全局记忆其余过程数据只保留在任务命名空间内。这样做牺牲了一点自动化程度但换来了知识库的干净和可信长期看更值。4. 实操参考从零搭建一个可运行的多人多AI协同最小系统4.1 技术选型与环境准备理论讲再多不如跑通一个最小系统。我搭建的这个参考实现完全可以在普通开发机上运行目标是复现“三人三AI协同调研并输出报告”的核心流程。技术栈选择上我用了Python 3.10作为主语言FastAPI提供API网关Redis充当消息总线和状态缓存SQLite做代理注册与任务状态持久化。大模型接入部分留了接口可以接云端的模型服务也可以用本地模型跑推理关键是在封装层统一成OpenAI兼容的调用接口。这样做的好处是切换模型供应商时不用改业务代码。我额外用了一个轻量级的向量存储来做全局记忆检索开发环境下可以用chromadb生产环境再换更专业的向量数据库。整套系统里只有FastAPI和Redis是硬依赖SQLite和向量库都可以替换所以参考实现的可移植性比较好。目录结构建议这样组织multi_agent_system/ ├── app/ │ ├── agents/ │ │ ├── base_agent.py │ │ └── workers.py │ ├── core/ │ │ ├── orchestrator.py │ │ ├── router.py │ │ ├── message_bus.py │ │ └── registry.py │ ├── memory/ │ │ ├── local_memory.py │ │ └── vector_memory.py │ ├── api/ │ │ └── gateway.py │ └── config.py ├── tests/ └── requirements.txt环境准备只需要一条pip命令安装依赖加上本地的Redis服务。我建议先把Redis用Docker起起来指定端口6379方便后面统一清理数据。如果电脑配置够本地模型可以用量化版本并开启GPU加速如果只想验证逻辑直接接云端API会更省心。4.2 核心代码骨架代理封装、消息总线与编排器代码实现我尽量简化到能展示架构思想为准先看代理基类。class BaseAgent: def __init__(self, agent_id, role, capabilities, llm_client): self.agent_id agent_id self.role role self.capabilities capabilities self.llm llm_client self.tool_registry {} def register_tool(self, name, func): self.tool_registry[name] func def handle_message(self, message): if message[type] task_delegate: return self._execute_task(message) if message[type] approval_result: return self._on_approval_result(message) raise ValueError(funknown message type: {message[type]}) def _execute_task(self, message): payload message[payload] tool_name payload.get(action) if tool_name in self.tool_registry: raw self.tool_registry[tool_name](**payload.get(arguments, {})) else: raw self.llm.chat(payload.get(prompt, )) return { task_id: message[task_id], correlation_id: message[correlation_id], from_agent: self.agent_id, status: succeeded, data: raw }这里最关键的一点是工具注册机制。代理的所有能力都以“工具”的形式挂载而非把工具描述写在系统提示词里让模型自由发挥。虽然前者实现起来稍微多一点代码但好处是稳定程序知道代理确认具备执行某个函数的能力不会出现模型生成了一个工具名称而代码里根本没有这个函数的情况。4.3 核心代码骨架消息总线和编排器消息总线我直接用Redis Stream实现封装成class。class MessageBus: def __init__(self, redis_client, stream_nameagent_events): self.redis redis_client self.stream stream_name def publish(self, message, to_agent): message[to_agent] to_agent self.redis.xadd(self.stream, { data: json.dumps(message, ensure_asciiFalse) }) def subscribe(self, agent_id): # 简化逻辑轮询当前代理未读消息生产环境应该用消费者组 pending self.redis.xrange(self.stream) for item in pending: msg json.loads(item[1][bdata].decode()) if msg.get(to_agent) agent_id: yield msg编排器负责把高层请求变成计划并分派任务。class Orchestrator: def __init__(self, planner, router, bus, memory): self.planner planner self.router router self.bus bus self.memory memory def submit(self, user_id, user_request): task_id ft_{uuid4().hex[:8]} plan self.planner.plan(user_request) self.memory.create_task_context(task_id, user_id, plan) for step in plan[steps]: agent_id self.router.route(step) message { task_id: task_id, correlation_id: fc_{uuid4().hex[:6]}, from_agent: orchestrator, type: task_delegate, payload: step } self.bus.publish(message, to_agentagent_id) return task_id def collect_results(self, task_id): results self.memory.get_task_results(task_id) if not results: return None return self.planner.merge_results(results)这套代码跑起来之后你会看到一个很直观的现象多个代理各自消费自己的消息任务结果回到编排器统一收集。整个执行过程是异步的这就体现了“代交互”与“同步调用”的本质区别。4.4 演示场景三人三AI协同调研并输出报告我们模拟三个角色产品经理A、数据分析师B、内容编辑C分别对应三个AI代理。用户给系统提交一个任务“调研三款主流AI编程助手的定价输出对比报告”。编排器的planner会生成如下简化计划数据采集子任务能力标签web_search分给数据分析师代理B。数据分析子任务能力标签data_analysis分给数据分析师代理B。内容撰写子任务能力标签content_write分给内容编辑代理C。汇总审阅子任务能力标签report_merge分给产品经理代理A。执行过程中代理B先用网页搜索工具抓取官网定价把结果写入Redis缓存再调用数据分析工具生成对比表。代理C从共享任务记忆中读取对比表生成一份可读性更强的分析文档。代理A最后把文档和对比表合并成最终报告发回给用户。这个流程演示了“多代理协同”最关键的价值每个代理只做自己擅长的事并且通过共享任务记忆交换中间产物避免了反复传递大段原始文本。整个流程中用户只发起了一次请求收到了一份最终报告其余过程全部由代理代交互完成。5. 常见问题与排查技巧实录5.1 上下文串扰多个代理共享记忆引发的脏数据这是我在实际运行中遇到的问题。一开始所有代理都从同一张表读取上下文结果数据分析代理在回答问题时把内容编辑代理的历史写作偏好也带了进去产出的数据报告里出现了“本报告文风活泼”这种奇怪表述。排查思路是从消息总线里把该任务的所有消息拉出来按correlation_id回放发现是共享记忆没有加namespace过滤。修复方法就是在所有记忆读写接口里强制校验namespace标识并在向量检索时增加命名空间过滤条件。这个坑提醒我共享记忆虽然共享但不能“无差别开放”。5.2 任务环路A调用B、B又调用A第二个常见故障是代理之间互相委托形成死循环。比如编排器把数据清洗任务分给分析代理分析代理觉得自己缺数据又委托采集代理采集代理处理到一半发现需要分析代理的模型判断又回调分析代理如果双方都在等待对方任务就卡死了。解决办法是两层措施并用。第一层在消息对象里增加trace_path字段每经过一个代理就追加自己的agent_id编排器检查到路径中出现重复ID时直接拒绝继续转发。第二层在编排器里设置最大跳数默认10跳超过之后任务自动进入failed状态并触发告警。很多“莫名其妙卡住”的任务追查到最后都是环路导致这两层措施能兜住大多数情况。5.3 响应超时、失败重试与降级策略本地模型推理速度波动大云端API有时也会超时多代理系统的整体执行时间会因此不可控。如果调度器对超时任务不做处理用户就会收到一个长时间无响应的“坏请求”。我采用三重超时策略。每级等待设置超时例如代理调用工具等待3秒代理间消息等待30秒整体任务等待5分钟。超时后先做一次重试重试采用指数退避间隔1秒、2秒、4秒递增。如果重试两次都失败就把任务标记为degraded降级为规则引擎处理或者转交人工。降级策略虽然不够“智能”但至少保证流程不会永久卡死这在多人协同场景里比追求完美自动化更重要。注意不要让一个代理的失败无限拖累整个协作链路。在架构上引入熔断开关当某个代理连续失败超过5次编排器会把它临时下线并从路由候选池中移出避免任务反复被分配给故障代理。5.4 多代理意见冲突的消解规则三个代理对同一份数据得出不同结论时谁说了算我在原型里实现了一版简单的消解器先给每个代理配置一个来源可信度权重数据代理0.5市场代理0.3文案代理0.2。冲突时先按权重计算加权平均值如果各结论差距超过阈值比如20%进入仲裁模式。仲裁模式调用仲裁模型让它基于原始数据进行再次判断但要求它引用数据来源。如果仲裁结果仍不明确触发人工审批把冲突详情和各方理由是提交给用户决策。这套规则不会让系统做出越权决定又能自动消解大多数低风险冲突。关键是要把触发人工审批的门槛放低宁可多一点审批请求也不要让系统在关键业务上自作主张。5.5 幂等设计与消息去重消息总线在重试机制下很容易产生重复投递如果代理执行的步骤是非幂等的比如重复写库、重复扣费、重复发送通知后果很严重。我在消息结构里预留了correlation_id就是为了实现幂等。代理在执行业务操作之前先检查该correlation_id是否已经处理过如果处理过则直接返回上次的结果不再重复执行。对写操作我在状态表里增加唯一约束用correlation_id作为唯一键防止重复插入。这里的排查技巧是当发现某个代理的行为“多做了一次”时不要急着怀疑业务逻辑先去看消息队列里是不是有重复消息往往问题出在投递层。表格汇总一下我刚才提到的几个高频问题问题现象可能原因解决思路代理回答中包含其他用户信息共享上下文未隔离增加namespace隔离向量检索加过滤条件任务长时间卡住不返回代理间形成调用环路增加trace_path查重和最大跳数限制整体响应越来越慢消息队列积压失败重试无退避引入超时熔断和指数退避策略代理之间结论冲突缺少仲裁机制设置可信度权重分层自动仲裁与人工审批重复执行同一操作消息重复投递未做幂等用correlation_id做幂等键状态表加唯一约束我在实际使用中的体会是多人多AI协同最大的难点其实不是模型能力而是架构层面的秩序感。AI代理越自由系统就越需要明确的边界、协议和纪律。先把消息格式定死把任务状态管住把记忆隔离做好再逐步放宽代理的自治权这条路比一开始就让所有代理“自由协商”要稳得多。最后分享一个小技巧调试阶段可以在编排器里加一个“上帝视角”接口定期把当前所有代理的任务状态、消息队列长度、token消耗都拉取出来渲染成文本日志。这套系统越复杂这个接口就越值得保留它能帮你在一堆并发消息里快速定位是哪一环掉了链子。
返回列表