# 发言顺序调度调研 > 问题:多 Agent 会议室里的**发言顺序**应该怎么定? > > **本项目的最终决定(在调研之后由需求方拍定)**: > 设一个**主持人**,它的模型用**召集者会话的模型**,但它**没有任何与会者的上下文**—— > 只拿到群聊记录、成员名单与"谁还没说话",因此不会被任何人的私有记忆带偏。 > 主持人逐步决定"下一个谁发言"或"宣布散会";会议发言内容**不做结构化模板**, > 让 AI 自然讨论收敛;上限(`maxRounds`)只作为主持人坏掉时的兜底。 > 落地见 `src/core/moderator.ts`、`src/adapters/moderators.ts`、 > `src/core/room-orchestrator.ts`。 > > 下面保留调研原文,作为这个决定的依据与备选方案记录。 > > 证据分级:**【实证】**= 一手文档/论文原文;**【存在】**= 页面确认存在但正文未完整取证; > **【未找到】**= 无证据。 ## 1. 主流方案(5 类) | # | 方案 | 代表实现 | 算法 | 缺点 | |---|---|---|---|---| | A | **固定轮转** | AutoGen `RoundRobinGroupChat` | 参与者按列表循环,`UserProxyAgent` 也按传入顺序被调用【实证】 | 不感知上下文,与议题无关的轮次纯浪费 | | B | **中心主持/模型选择** | AutoGen `SelectorGroupChat` | LLM 依据各 agent 的 `name`+`description` 与历史,在 `{participants}/{roles}/{history}` 提示下选下一个发言者;默认**禁止同一人连续发言**;可用 `selector_func` 完全接管、【`candidate_func`】先缩小候选集【实证】 | 每轮多一次 LLM 调用;选择不稳定、对提示敏感 | | C | **对等交接** | AutoGen `Swarm` | 发言者由**最近一条 `HandoffMessage`** 决定,依赖模型 tool calling;并行 tool call 会一次产生多个 handoff,需 `parallel_tool_calls=False`【实证】 | 局部决策,易互相踢皮球 | | D | **规划者 + 双账本** | `MagenticOneGroupChat` | Orchestrator 维护 Task Ledger(外环重规划)与 Progress Ledger(内环自省),连续无进展则重规划【实证】 | 重且贵,适合开放式任务 | | E | **流程 / SOP 驱动** | `GraphFlow` / MetaGPT / CrewAI | MetaGPT 把 SOP 编码进 prompt 序列、流水线式角色分工【实证,摘要级】;CrewAI `Process.sequential` vs `hierarchical`【存在】 | 流程僵化,不适配临时议题 | 来源: - [AutoGen Selector Group Chat](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/selector-group-chat.html) - [AutoGen Swarm](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/swarm.html) - [AutoGen Magentic-One](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/magentic-one.html) / [arXiv:2411.04468](https://arxiv.org/abs/2411.04468) - [AutoGen RoundRobin / HITL](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/human-in-the-loop.html) - [AutoGen GraphFlow](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/graph-flow.html) - [MetaGPT arXiv:2308.00352](https://arxiv.org/abs/2308.00352) - [CrewAI Processes](https://docs.crewai.com/en/concepts/processes) - [LangChain Multi-agent](https://docs.langchain.com/oss/python/langchain/multi-agent) - [CAMEL NeurIPS 2023](https://papers.nips.cc/paper_files/paper/2023/file/a3621ee907def47c1b952ade25c67698-Paper-Conference.pdf) ## 2. 默认推荐:首轮固定 + 后续主持人 **有先例,但没有统一学名。** AutoGen 官方的 `selector_func` / `candidate_func` 示例正是这个模式: 强制"user 之后必须是 planning agent"、"planning agent 必须先指派任务"、 任务完成后把球交回 planning agent 收尾。 术语上,人机多方对话文献称 **moderator / chair**;框架里它落成"自定义 selector 函数"。 **未找到**学术界为"首轮固定 + 后续主持"命名的固定术语, 也**未找到**直接定量对比 round-robin vs LLM 选主持的论文。 最接近的是固定总调用数下消融"轮数 vs agent 数"的 [arXiv:2510.05611](https://arxiv.org/abs/2510.05611), 以及人机多方 turn-taking 的系统化研究 [Frontiers in AI 2025, doi:10.3389/frai.2025.1582287](https://doi.org/10.3389/frai.2025.1582287)。 **本项目选择它作为默认(`brief-then-moderated`)**,理由: 1. 第 1 轮"人人有输出"是信息量最大的一轮(每人说出自己的进度/障碍/需求),不能省; 2. N 人 × R 轮的全连接会产生 O(N·R²) 广播成本——Anthropic 实测多 Agent 系统约 **15×** chat token, 必须靠主持人收敛; 3. 第 1 轮按 id 的 code-unit 排序 → 可复现、可审计。 ## 3. 终止条件的成熟做法 AutoGen 内建 11 种可组合的 `TerminationCondition`【实证】 ([文档](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/termination.html)): `MaxMessageTermination`、`TextMentionTermination`、`TokenUsageTermination`、`TimeoutTermination`、 `HandoffTermination`、`SourceMatchTermination`、`ExternalTermination`、`StopMessageTermination`、 `TextMessageTermination`、`FunctionCallTermination`、`FunctionalTermination`;另有 team 级 `max_turns`。 **"所有 agent 都说没补充了"没有内建条件**,需自定义。 本项目实现的三层叠加: 1. `maxRounds` 硬顶(默认 3); 2. **主持人主动收束**:`selectSpeakers` 返回空数组即结束 (注意:必须区分 `undefined`=主持人不可用→回退启发式,与 `[]`=明确宣布没人要发言→收束。 早期版本把两者混为一谈,导致主持人宣布收束后又被启发式强行续命); 3. `reactive` 模式下没有候选人即结束。 ## 4. 避免广播风暴 - `max_turns` 硬顶【实证】; - 终止条件组合(`max | text` / `max & text`)【实证】; - token 预算 / 超时兜底【实证】; - `allow_repeated_speaker=False`(AutoGen 默认)防同一人连续刷屏【实证】; - Magentic-One"连续无进展 N 步即重规划"【实证】。 本项目额外做了**召集节流**(`minCallIntervalMs`,默认 60 秒): 任何成员都能召集,但同一成员短时间内不能反复召集,且**被拒绝时理由明确带回发起者**。 同时停滞触发(priority 1)**穿透**节流——刚开完会就卡死恰恰最需要介入。 ## 5. 人类参与 AutoGen 把 `UserProxyAgent(input_func=...)` 放进团队:RoundRobin 中按顺序轮到、 Selector 中由 selector 决定何时轮到;**运行中阻塞**,官方建议仅用于短交互(审批/警报)【实证】。 Transcript 表示为 `UserInputRequestedEvent` + `TextMessage(source='user_proxy')`。 更稳的做法是**在两次 run 之间插入**:`max_turns=1` 或 `HandoffTermination(target="user")`, 恢复时把用户话作为 `HandoffMessage`/`TextMessage` 传回。 **本项目的做法**:人作为 `human` 参与者进入 transcript,用独立 `kind: 'human'` 标记, **不占轮次也不占发言名额**——会议是一群 Agent 的循环,人不该被轮询机制排队。 刻意**不采用**阻塞式输入:那会让整场会议卡在等人上。 ## 6. 会后摘要:全局总结 ≠ 参与者 takeaways **这是本项目的关键区分点。** - 学术侧:Re-FRAME(EMNLP 2025 Findings)把会议摘要当作"基于问题的富化 + personalization", 是**个性化纪要**的可引用工作【存在】 ([ACL Anthology](https://aclanthology.org/2025.findings-emnlp.1094/)); - Anthropic 的做法:阶段完成后**总结并写入外部记忆**,上下文将满时**开新 subagent 带干净上下文**, 大产出**写文件只回传引用**,以避免"传话游戏"【实证】 ([multi-agent research system](https://www.anthropic.com/engineering/multi-agent-research-system)); - AutoGen 有 `SocietyOfMindAgent`(内部小组会后再对外产出)【存在】; CrewAI 有短期/长期/实体记忆【存在】。 **未找到**任何框架内建的"逐参与者个性化纪要"组件。 所以本项目自己实现了 `src/core/minutes.ts`: 1. 给出**可操作的"相关性"判据**(不给定义只说"总结与你相关的",模型大概率写一份通用会议摘要); 2. 刻意包含**反面指令**(不要复述别人完整发言、不要写旁白式回顾、不要客套); 3. 用 `validateMinutes` **硬校验压缩结果**:必须短于会议记录本身,否则拒绝 (一条"越压越长"的纪要说明模型在抄会议而不是在压缩)。 ## 7. 成本与上下文 - **调用次数【推导】**:每发言 1 次 + 主持人选择 1 次/轮 → R 轮约 `N·R + R` 次; - **实测倍率【实证】**:Anthropic 数据——普通 agent ≈ **4×** chat token,多 Agent 系统 ≈ **15×**; - **上下文爆炸【实证】**:Selector/Swarm 都显式"broadcast 给所有其他成员"→ 每人持有完整 transcript, 成本随轮数近似二次增长【推导】; - **缓解【实证/存在】**:`max_turns`、token 预算;滚动窗口;分层摘要; 大产出外置为文件引用;主持人选择改用小模型。 **本项目的上下文控制**:会议室**不给每人完整 transcript**。 `MeetingRoom.transcriptProjection()` 用分层投影—— 第 1 轮的结构化发言**永远完整保留**(信息密度最高),讨论轮只保留最近窗口, 并显式标注"已省略 N 条"。总长恒在 `transcriptBudgetChars` 内,不随轮数线性增长。 ## 8. 对本项目的落地清单 | # | 调研建议 | 落地情况 | |---|---|---| | 1 | 首轮固定顺序 → 后续主持人选择 | ✅ 首轮 `MeetingRoom.firstRoundOrder()`(确定性顺序保证人人发言);之后**每一步**都由主持人决定(比调研建议的"每轮一次"更细,是需求方要的"主要靠主持人控场") | | 2 | 发言格式 SOP 化(进度/障碍/需求) | ❌ **刻意不做**。需求方明确"没必要做结构化的东西,AI 会自动讨论出合理结果" | | 3 | 三层终止条件 | ✅ `maxRounds` 硬顶 + 主持人宣布散会 + 主持人不可用时安全回退 | | 4 | 不给每人完整 transcript | ✅ `transcriptProjection()` 滚动窗口 + 显式标注省略条数 | | 5 | 会后两段式(先全局纪要,再按人定向) | ⏳ 目前直接从 transcript 为每人压缩;两段式是下一步 | | 6 | 人类用显式 `source=human`,不阻塞 | ✅ `appendHumanTurn`,独立 `kind: 'human'`,不占轮次也不占发言名额 | | 7 | 规模 N 建议 5–7、硬上限 10 轮 | ✅ `maxRounds` 可配(默认 6,偏保守) | | 8 | 主持人选择用小模型 | ⚠️ 本项目用**召集者会话的模型**(需求方指定),与"用廉价模型"的建议不同;理由是风格一致 + 不引入额外模型依赖 | ### 8.1 主持人的"无上下文"是本项目独有的约束 调研过的所有框架(AutoGen `SelectorGroupChat`、LangGraph supervisor、CrewAI hierarchical、 Magentic-One orchestrator)里,**选择者都能看到完整的对话历史甚至成员描述**—— AutoGen 甚至把各 agent 的 `name`+`description` 塞进选择 prompt。 本项目的约束不同:主持人**只拿到群聊记录 + 名单 + 谁没说话**, **拿不到任何成员的私有记忆投影**。这是需求方明确要求的 ("不会被任何人的上下文带偏"),也是对多 Agent 系统一个真实失效模式的防御: **如果控场者能看到 who 的上下文,它就会系统性地偏向"看起来做得更多"的成员。**