
前阵子我们在内部搭了一套“多AI协同”工具把几个大模型、一个本地模型接进同一个项目协作环境里让不同AI代理替人去跑代码审查、写检查清单、做技术调研。做之前以为最难的是选模型、调prompt真正跑起来才发现最让人头疼的其实是架构问题多个AI之间要通信、要交接、要避免上下文打架还要做到审计可追溯而这一切不能全指望靠提示词解决。后来我们把设计重心放在“代理代为交互”这条主线上——用户通过AI代理去和其他AI代理、工具、接口打交道而不是直接频繁地手动切换各家模型。这篇文章就把这套多人多AI协同系统架构的完整拆解、落地过程和踩坑经验写清楚适合正在考虑做Agent类产品、多模型聚合平台或者准备给自己的团队搭一套AI协作基座的人参考。1. 为什么“代理代为交互”会比“直接调用多个大模型”更值钱1.1 先看清问题多人多AI协同到底卡在哪里我最初以为把多个大模型API拼在一起给一段用户问题各自推理一次再把结果拼起来就能算“多AI协同”。实际上这个思路很快就废了。举个例子我们有个会议室里同时挂了三类AI代理代码审查代理、文档撰写代理、测试用例生成代理。用户丢进来一个问题“我这个模块改了接口帮我看看影响范围顺便补一个测试说明。”如果只是单纯调用三次API会发生什么呢代码审查代理说“接口改了影响五个文件”文档代理说“我写了接口变更说明”测试代理说“建议补充单元测试”。三条结果各自为政没有人去裁决谁的话更关键没有代理去追问上下文整个会话看起来是一堆答案互相碰撞而不是一次有效的协作。更麻烦的是如果这个会话是多人参与的用户A还在追问用户B又丢了一个新任务两个请求之间上下文如何衔接就完全失控了。真正卡住我们的不是模型能力而是缺少一个能让“多个AI之间沟通、协商、执行事务”的中间层。这个中间层就是“代理代为交互”的核心每个AI代理不是一个孤立的API端点而是一个拥有身份、状态、权限和沟通能力的交互单元它可以代表用户去参加协同会话、调用其他代理的能力、决策由谁响应、并把结果按结构化方式回写。1.2 “代为交互”的本质把用户意图转化为可委托的任务“代为交互”这四个字我理解的本质是委托和授权。用户不再需要知道“这个难题该找哪个模型”他只需要向系统表达意图。系统里有一个代理管理器维护了一份“能力注册表”比如“代码分析”、“文档生成”、“测试脚本编写”你只要说出自己的目标编排层会把这个目标拆解成一项或多项任务然后分派给对应能力的代理去执行。每个代理在执行任务时名义上是“以用户委托的身份”去和其他代理交互的。它手里攥着一个会话级授权令牌令牌里包含它被允许访问的资源范围、可执行的动作类型、以及其他代理需要知道的项目上下文标识。这种设计看起来多绕了一层但价值非常大一是在多人场景下用户不必每轮都重新解释背景二是代理之间作为平行的协作单元会主动做任务交接而不是依靠用户拷贝粘贴这个交互模式和“人指挥多个机器人”很像——用户不直接控制每个机器人的动作而是向调度中心下指令各个机器人之间自己协商配合。1.3 哪些场景最需要这种架构研发团队场景代码审查、变更影响分析、自动测试说明生成多个AI代理围绕同一个MR/PR做协作。知识工作流水线比如市场调研、内容撰写、配图建议、渠道分发建议整个流程拆成多个环节由不同代理完成。复杂客服场景用户问题需要多个专业知识域协作普通AI助理撑不住需要“路由到多个专精代理再由一个总代理综合裁决”。如果你只是在个人工具里单条对话式使用大模型那这套架构确实有点重但凡是进入多人、多角色、多任务协同的环境这套“代理代为交互”的架构就是必需品不是花活。2. 系统整体分层编排层、代理层、路由层、状态层四件套2.1 交互编排层感知输入、生成意图、分配任务整个系统入口是交互编排层它负责感知用户的自然语言输入并迅速转换成结构化意图。我在这层里放了一个“意图路由器”它不是靠纯规则匹配而是先做一次轻量级分类可以用小模型也可以本地模型兜底输出意图标签比如“分析影响范围”、“生成测试说明”、“写接口文档”。然后编排层根据意图标签去能力注册表里找匹配的代理。这里有个容易踩的坑不要一上来就让大模型自由决定“下一步该调用哪个代理”。自由调用看起来灵活但生产环境里非常难控制模型可能为了显摆能力跑去调用一个根本不相关的文档代理。我们后来给意图路由增加了一个白名单控制只有注册过的、且当前授权范围内的代理才会出现在候选列表里模型只能在候选列表中做选择不允许自创调用目标。编排层还需要管理“人的介入点”。多人协同场景里人和AI不是单向流水线人可能会在中途插话、撤回任务、改变优先级所以编排层的消息结构里必须有“用户干预”这个入口。2.2 代理管理层注册、能力声明、生命周期代理管理层是独一无二的“核心账本”。每个AI代理在上线前都要注册注册内容包括唯一代理ID比如agent-code-review。能力声明这是给编排层做匹配的关键比如capabilities: analyze_code, review_diff, suggest_fixes。可访问资源列表比如repo: project-name、document-store: docs-wiki。模型后端配置比如用的哪个API、是否是本地模型、并发上限多少。生命周期状态空闲IDLE、处理中BUSY、暂不可用DEGRADED。我曾经在早期原型里没有做能力声明全靠一个非常长的if-else路由表做代理匹配结果每一次接新代理都要去改路由代码极其痛苦。后来改成能力匹配之后新代理上线只需要提交注册表编排层自动就能把它纳入候选。能力声明的写法也有讲究不要写“我很擅长分析”要写具体动词对象方便做结构化匹配语义化标签加动词短语比如code-review|python|type-safe。2.3 消息路由与通信层请求-响应之外的事件流多个AI协同仅仅靠“请求-响应”是不够的。一个代码审查代理分析完后需要主动触发“测试代理去生成新测试”、或者“文档代理去更新接口说明”。如果这些动作都靠用户重新发指令系统就又退化成人工编排了。所以在通信层里除了消息队列还必须有一套事件总线。事件总线的核心消息类型包括request主动向某个代理发起调用期待响应。reply针对请求的响应。broadcast向所有代理广播一个上下文变化比如“接口定义已更新”并不指定接收者。event代理主动产生的事件比如“发现一处疑似高风险代码”、“测试用例生成完毕”。control系统级别指令暂停、降级、终止某个代理任务。一开始我们图省事把事件总线直接做成轮询某张数据库表几秒一次后果是延迟高、消息顺序混乱。后来换成了基于消息队列的发布订阅才稳定下来。事件总线的另一个作用是让每个代理只需要关心自己订阅的话题比如测试代理订阅subject.testing代码审查代理订阅subject.code-review。这样双方不会互相看对方无关的日志也减少了token浪费。2.4 状态与记忆层会话快照与协作时间线最后是状态层它负责保存两类东西会话快照和协作时间线。会话快照记录的是“当前多人多AI协作进展到哪一步、每个代理在等待什么、已经产出了什么”放在Redis里过期时间设成和会话生命周期一致。协作时间线是每一条消息的审计记录包括谁在什么时候向谁发过什么、结果如何放在持久化数据库里用于故障排查、重放和复盘。状态与记忆层有几个组件配合Redis存活跃会话快照PostgreSQL存最终时间线向量库存长期记忆。这个分层我放在第五节详细展开。下面这张表总结了我们系统里四个层承担的核心职责层次核心职责主要组件交互编排层意图识别、任务拆解、人机交互入口意图路由器、候选代理选择器代理管理层代理注册、能力声明、生命周期代理注册表、状态管理消息路由与通信层请求响应、广播事件、控制消息消息队列、事件总线状态与记忆层会话快照、协作时间线、长期记忆Redis、PostgreSQL、向量库四个层不是各干各的它们之间通过统一消息信封串联起来这就是下一节要讲的通信协议设计。3. 消息与通信协议设计让多个AI之间说同一种语言3.1 统一消息信封类型、来源、目标、时间线字段多AI协同里最怕的就是每个代理的消息格式都不一样A说自己丢了JSONB回了一个Markdown表格C又抛出一个奇怪的对象。所以从第一天起我就强制所有代理之间传输的消息使用统一信封。信封的JSON结构大致是这样的{ message_id: msg_9f2c3a1e, trace_id: trace_7a8f21c4, turn_id: turn_5b1d23e9, parent_turn_id: turn_a12b34cd, type: request, from: agent-coordinator, to: agent-code-review, priority: normal, created_at: 2025-06-18T10:24:00Z, payload: { task: analyze_impact, target_module: payment_service, context_slice: [接口变更点, 关联测试文件] }, meta: { idempotency_key: key_88b7f0e2, model_hint: local-mini, deadline_ms: 15000 } }几个关键字段值得展开说。trace_id贯穿一次用户请求引发的全部协作过程只要排查问题就靠它把所有消息串起来。turn_id和parent_turn_id构建了消息之间的父子关系用来表达“这条消息是响应哪一条请求的”。idempotency_key是幂等键用来防止重试导致代理重复执行动作后面第六节还会细讲。所有字段统一小写、对齐时间格式是减少解析Bug最便宜的方式。3.2 基于MCP约定的工具调用标准纯消息信封解决的是“打包格式”但代理之间怎么互相调用对方的能力呢我们采用的是MCP风格的工具调用约定MCP的意义在于它把文件读取、数据库查询、API调用这些外部能力统一成“工具协议”代理不需要知道对方底层是什么语言、什么框架只要按工具协议声明和调用即可。在我们的系统里代理与代理的互相调用也被抽象成“工具”比如代码审查代理调用测试代理的“生成测试用例”能力在测试代理的能力声明里这个动作就是一个工具generate_test_cases它有输入参数的定义和输出的结构约定。调用方代理不需要直接写代码去操作测试框架它只需要发出一个工具调用请求测试代理内部再映射到具体实现。如果接入的外部平台和你选用的MCP实现不一致不要强行硬接。我们后来加了一个协议网关它负责把外部非MCP的方言翻译成系统内部统一协议。协议网关本身不执行业务逻辑只做字段映射、单位归一化、时间格式化。这样将来想替换某个代理实现不需要改协议层。3.3 把“自由会话”和“强制工具调用”分开处理大模型在做自由对话时很容易跑偏比如你要它调用工具获取文件内容它可能给我返回一堆分析性废话就是不触发工具。我们的做法是把消息类别拆开request类和reply类走LLM语义解析通道大模型可以自由发挥负责理解和生成而broadcast类、event类、control类消息只走调度器和事件总线绝不允许大模型自由生成。换句话说大模型在这个系统里只负责两类事一是理解用户意图二是生成专业分析内容。至于“谁该给谁发消息”“什么时候降级”“该暂停哪个代理”这类控制逻辑必须由代码决策不能把控制权交给LLM。一旦把控制消息也交给模型轻则行为不可复现重则消息风暴。还有一个小细节当代理A调用工具的时候我们要求调用方在payload里带上足够上下文片段的引用而不是让被调用方翻遍全库去找这样既节省token也降低误判率。3.4 超时、重试与幂等协作系统最容易死在细节里如果代理之间通信没有超时概念一次网络抖动就能让整个链路互相等待。我们在一开始没有做全局超时结果本地模型偶发卡住时整个编排任务挂在那边过了很久才发现卡在一个没有超时上限的HTTP请求里。后来订了一套超时阶梯第一级2秒用于本地小模型、快速工具调用。第二级5秒用于普通代理的语义分析。第三级15秒用于要查询外部知识库的重型任务。超过15秒的编排层直接降级处理而不是继续无限等待。重试机制也要配合幂等键使用。我们曾遇到过一次消息重试风暴请求超时后编排层自动重发了一次调用结果触发代理重复执行了一遍写文件操作最终生成了两条重复记录。这是因为我们没有在meta里放幂等键。加上idempotency_key并做过期去重之后再多的重试都不会导致副作用重复。提示幂等键的生成规则必须是稳定且唯一我们用的是trace_id action target_id的组合放在meta字段中接收方先查去重表再决定是否真正执行。4. 多人多AI会话的状态机与上下文治理4.1 从“会话”到“协作回路”的状态机单用户单AI的聊天里状态机很简单就是“接一条消息回一条消息”。但在多人多AI协同系统里一个会话可能同时存在多个代理在并行干活有的在等待用户输入有的在等待下游代理返回必须明确区分两类状态维度。第一类维度是会话总状态描述整个协作任务的进展等级状态说明IDLE会话空闲等待用户发起指令ENGAGED编排层已介入正在拆解任务WAITING至少一个代理进入等待等待外部响应或用户确认RESPONDING至少一个代理正在输出分析结果HANDOFF任务正在从当前代理移交给下一个代理CLOSED会话结束清理资源第二类维度是单个代理的参与状态比如它当前是空闲IDLE、处理中PROCESSING、等待下游BLOCKED、还是已经移交HANDED_OFF。两个维度同时维护才能判断“整个会话卡住了”和“某个代理卡住了”。如果不区分往往要等到整个会话超时才发现是一个特定的代理出了问题定位效率极低。4.2 用“三层上下文切片”避免多AI上下文撕裂多AI协同最严重的工程问题之一是上下文撕裂。不同代理共享同一份庞大上下文时要么互相污染要么上下文很快超出token上限。我们采用的方案是把上下文切成三层切片全局上下文Global所有代理都需要知道的最小信息比如项目名称、目标模块路径、本次协作的整体目标。这个切片刻意做得短只放必要项。代理私有上下文Agent-Scope每个代理自己维护的专业信息比如代码审查代理记住“已检查文件清单”文档代理记住“已更新章节”。这些信息对其他代理透明避免互相干扰。场景上下文Scene-Scope当前协作回合的片段包含用户最新消息、最近一次代理产出、待决策问题。场景上下文会随回合推进而滚动淘汰只有被标记为“持久结论”的内容才提升到全局上下文。这个切片的模型很像是多人会议的白板、个人便签和会议纪要的关系。白板大家共用贴的是当前议题个人便签只给自己看记录自己查到的小抄会议纪要沉淀的都是最终决议。没有这个分层所有东西都糊在一张桌子上不用多久就乱套了。实践中还要给每个切片的token做预算。我们一般把全局控制在500个token以内场景控制在2000到4000个token之间代理私有上下文视代理类型灵活设置。超出预算时优先丢弃对话流水保留决策结论这个优化显著减少了上下文超限报错。4.3 冲突仲裁多个AI同时想改同一份状态怎么办多人多AI协同里多个代理同时想修改的状态一定会发生。比如代码审查代理发现“某个接口应该废弃”文档代理同时写入“该接口仍然可用”如果双方都直接改全局上下文就会出现版本互相覆盖甚至把协作搞回退。我们实现了一个轻量级的冲突仲裁表核心字段包括资源名、版本号、申请者、操作类型、申请时间。处理逻辑是优先判断版本号和操作类型如果是追加型操作比如补充备注执行后递增版本号如果是覆写型操作必须走仲裁。仲裁优先级至少有三条规则按顺序判断第一具有coordinator权限的代理意见优先第二相同权限下时间戳靠后的请求获胜因为通常更反映最新决策第三如果双方都不具备明显优先级系统进入“待人工确认”状态而不是擅自采纳任何一方。这里最关键的设计是不允许“静默覆盖”任何一次覆写都要在协作时间线里生成一条conflict_resolution事件。事后想复盘“为什么最终是这个方案”直接翻审计日志即可不用靠猜。4.4 记录协作审计时间线时间线是我们调试和复盘最重要的工具。每条消息的关键字段包括turn_id, parent_turn_id, from_agent, to_agent, action, resource, status, version, timestamp有了这条时间线你能重建某个任务在任意时刻的完整状态。一次真实的排查经历用户在会话里连续追问了两次“取消上次的计划”结果系统生成了完全相反的两个决策。我们通过时间线回放发现是文档代理和编排代理对“取消”动作的执行时机不同导致先到的新请求反而晚被执行。随后我们在时间线上增加了动作序列号校验才彻底修掉这类问题。5. 记忆分层短期工作记忆、长期记忆、知识库三权分立5.1 短期工作记忆会话内的上下文窗口短期工作记忆对应的是当前活跃会话里的临时上下文。它存在Redis里以会话ID为键里面存放的是最近几轮的关键消息、当前状态切片、待办决策项。短期记忆不追求完整只追求“当下够用”所以定期滚动清除陈旧对话流水是必要操作。在处理消息时我们会为每条消息分配一个token权重比如用户最新指令权重最高、代理之间协商消息其次、早期流水权重最低。当短期工作记忆接近上下文预算上限时系统会优先淘汰低权重的流水片段。这个策略实施后我们明显感觉到长会话里各代理的“走神”现象变少了因为模型每次看到的都是最相关的一小段内容而不是一团互相矛盾的旧消息。5.2 长期记忆会话结束时的结构化沉淀长期记忆的设计原则是“不沉淀流水只沉淀结论”。每次会话结束时系统会调用一个记忆整理器把整场协作中的关键决策提炼成结构化条目。格式是三元组项目事实、用户偏好、未解决问题。项目事实比如“支付服务模块当前接口定义已更新原order接口已废弃”。用户偏好比如“该用户偏好返回精简答案要求带代码示例”。未解决问题比如“支付回调测试尚未覆盖并发场景”。提取出来的条目向量化之后写入向量库。下次新会话开始前编排层会从向量库中召回与当前任务相关的历史记忆并把它们作为全局上下文的一部分注入。这个机制给系统带来了明显的连续性用户隔天再问相关问题时代理能够自动记起上次的讨论结论不需要再解释一遍背景。5.3 知识库的注入要显式不要隐式知识库RAG不能一股脑全塞。把整个文档库塞进上下文既有token浪费又会让代理被无关段落带偏。我们采用显式注入策略只有当任务类型标记为需要精确事实的时候才触发检索增强流程。比如“查询某个接口的字段定义”就需要而“给这个接口写一个通俗解释”不一定要检索完整文档可以依赖模型已有知识。检索增强流程分三步先按文件名和章节标题缩小候选范围再对候选段落做向量相似度排序最后用一个本地小模型做rerank过滤。这个本地模型不负责生成只负责判断“该段落是否真的与问题相关”这样既能提升召回准确率又把昂贵的LLM调用留给了最终生成环节。5.4 记忆写入的边界记忆层虽然方便但不能什么都往里存。我们把记忆写入分成三个权限等级权限等级内容示例可访问范围公开级项目模块结构、接口名、通用技术结论所有代理可读项目级特定项目的业务规则、用户偏好同项目代理可读受限级涉及敏感信息的脱敏前数据、个人隐私仅指定代理、经授权可读每次记忆写入长期库之前都要过一遍脱敏检查脚本把疑似邮箱、手机号、内部账号等字段自动打码。这项规则看似麻烦但能避免很多后续的安全隐患尤其是系统里接入了本地模型存储的场景一旦敏感数据被写入长期记忆库再想清理就非常被动。6. 容错、过载与安全审计生产环境才见真章6.1 单Agent故障时的降级策略任何一个依赖多个外部模型的系统都必须假设单个代理随时会出问题可能是API限流、可能是模型服务挂了、也可能是代理内部逻辑报错。我们的降级策略分三层第一层是“快速失败开关”。每个代理在响应超过自己超时阈值时编排层会把它标记为DEGRADED后续请求不再等待它第二层是“本地小模型兜底”。主模型挂了可以用本地模型临时顶上做基础分类虽然生成质量会下降但至少系统不彻底瘫痪第三层是“部分成功降级”。一开始我追求所有代理都要成功返回才给用户一个最终结果但实际多AI协同里某个代理慢病极容易拖垮整个会话。后来改成只要关键路径上的代理成功返回就先把阶段性结果汇报给用户次级代理的结果晚到再补上。6.2 队列与限流防止多AI高并发互相踩踏多用户接入之后高并发问题会迅速暴露。多个会话同时触发同一个代码审查代理可能瞬间把它压垮。我们在编排层和每个代理之间各放了一层队列并做两级限流入口限流负责控制“新会话进入系统的速率”代理级队列负责控制“某个代理同时在处理的任务数”。队列的大小必须是有界的。一开始我没设置最大长度结果某个模型API被限流后队列无限堆积延迟越来越大。后来给每个代理队列设置了最大长度比如10个任务达到上限的新请求直接走“忙碌重试”分支通知用户稍后再试或改走其他代理。这个策略虽然会拒绝一部分请求但比所有人一起卡死强得多。另外代理自身的并发限制也要在注册表里声明比如“本地模型最大并发4”编排层会按这个值去分配任务不会把一个模型后端塞到并发几十路。6.3 最小权限与审计日志多AI协同系统里权限不能按“模型品类”来划分按“能力资源”来划分更合理。比如本地模型后端可以访问共享代码库但不能发送对外通知文档代理可以读写Wiki但不能操作部署密钥。每个代理的授权令牌在会话开始时获取有效期只跟随当前会话过期自动失效。所有涉及数据访问、文件修改、对外发送消息的动作都会走一遍审计服务。配置里我必须显式声明“哪些动作需要审计”默认是所有control和event类操作都记录request和reply类则记录摘要不记录完整payload这样平衡审计密度和存储成本。6.4 可观测性trace_id贯穿全链路多AI协同调试的难度比单Agent大一个量级。因为一个结果可能经历过五个代理的接力传递任意一环出问题都会导致最终结果怪异。我们的可观测性设计围绕trace_id展开从用户请求进入系统的那一刻起生成一个trace_id它会被写入所有消息信封、代理日志、Redis会话快照、PostgreSQL时间线记录、向量库记忆条目里。排查问题的时候我们用这个trace_id轻松拉出整条协作链条的所有日志直接重组出某次任务从开始到结束的完整过程。这个动作比看散落的日志效率高太多强烈建议任何打算做这类系统的人第一天就把trace_id体系设计进去不要等出了事故再补。7. 落地实测我用不到三千行代码跑通的最小副本与教训7.1 最小副本的模块划分为了验证这套架构我写了一个最小副本总计不到三千行Python代码。模块结构是这样的app/ agent_service.py # 代理生命周期管理、能力注册 coordinator.py # 编排层意图拆解和任务分配 message_bus.py # 事件总线、路由、超时调度 context_store.py # 三层上下文切片的读写 memory_writer.py # 长期记忆提炼与向量化 audit_store.py # 协作时间线的持久化 agents/ code_review_agent_gpt.py doc_agent_gpt.py test_agent_local.py # 本地模型代理 mock_agent.py # 用于联调和压测这个最小副本挂了一个外部GPT代理、一个本地模型代理、一个Mock代理跑通了一个典型链路用户说“分析支付服务接口变更影响”编排层拆成代码审查任务派给GPT代理审查完产生一条事件路由层转给测试代理让它生成关联测试清单同时文档代理收到通知去更新接口说明。整个过程用户只需要在入口发一次指令。7.2 真实踩坑记录第一坑能力匹配不稳定。最初的注册表能力声明写得像人话“擅长分析代码影响”模型经常匹配得非常随机。后来我把能力改成标签组合比如code-review|diff-analysis|python|critical-path匹配率立刻稳定多了。第二坑上下文超限。我们第一版把整场会话的所有消息都塞进全局上下文结果跑不到第十轮就爆了。改成三层切片之后问题基本消失。第三坑重试风暴导致的重复写盘。加幂等键之前发生过加之后彻底解决。第四坑一次性编排太脆弱。前几版要求所有代理全部成功才汇报用户结果一个小代理的偶发失败会让整个会话显示“失败”。改成部分成功降级后可用性明显提升。第五坑把控制权放给大模型。第一版让GPT代理自己判断要不要通知测试代理它经常漏通知。后来改成规则路由只有事件总线确定匹配到目标代理才转发稳定多了。控制权收起是多方实践换来的教训。7.3 还能继续扩展的点这套架构目前覆盖的是文本协作后面可以继续扩展的方向包括多模态对象协作让代理能处理图片、音频片段定时任务代理让代理按周期自动执行巡检以及和外部系统域打通比如通过类似ROS域的知识把真实设备状态作为外部系统桥接进来。架构上这些扩展只需要在代理注册表、事件类型、能力声明三个地方加对应条目不需要改动底层消息骨干。在我个人看来做多AI协同系统最难的不是接多少个模型而是你有没有能力把一个复杂的协作过程稳定地结构化。如果你也准备做类似的事我的建议是不要急着上重型中间件先把消息信封、状态机、三层上下文切片这三件事做扎实它们决定了系统的底线。最后想分享一个实操小习惯比起盯着大模型的最终答案多观察日志里代理之间的turn_id接力是否顺畅排查问题的速度会快很多。