# 多智能体模式消息路由机制 > 消息路由由**接收方**决定:根据接收方当前状态,选择 inline 插入工具结果后,或作为 user 消息触发新一轮任务。 > 参考现有后台任务通知池机制实现,保证消息不丢失、不错乱、可批量处理。 --- ## 1. 核心思想 ### 1.1 接收方决定插入方式 当一条消息需要发送给某个子智能体(或主智能体)时,路由层只关心接收方当前处于什么状态: | 接收方状态 | 插入方式 | 效果 | |-----------|---------|------| | 正在运行中,且当前在某次工具调用中阻塞等待 | 把消息作为该工具调用的结果返回 | 子智能体在当前轮次继续执行,立即看到消息 | | 正在运行中,但不在阻塞等待状态 | 把消息 inline 插入到当前对话上下文末尾 | 子智能体下一轮输出时自然看到 | | 空闲状态(本轮已自然结束) | 把消息作为普通 user 消息插入 | 触发子智能体新一轮运行 | ### 1.2 参考现有后台通知池 现有后台任务通知机制: - **运行期间(inline)**:`server/chat_flow_tool_loop.py` 中 `execute_tool_calls` 末尾调用 `process_sub_agent_updates(..., inline=True)`,把已完成的子智能体任务一次性插入当前 `messages`,不触发新任务。 - **停止输出后(polling)**:`server/chat_flow_task_main.py` 中 `handle_task_with_sender` 结尾启动 `run_completion_poll`,统一轮询子智能体 + 后台命令完成通知,批量插入 user 消息,只触发一个后续任务。 多智能体模式的消息路由借鉴此机制: - 每个子智能体有自己的「待处理消息队列」。 - 子智能体每次进入可接收消息的状态时,从队列中取出所有消息,按顺序处理。 - 避免逐条消息触发多次「工作 → 停止 → 再工作」循环。 --- ## 2. 消息路由状态机 ### 2.1 子智能体状态 ``` ┌─────────────┐ │ idle │ 空闲 └──────┬──────┘ │ create_sub_agent / send_message_to_sub_agent ▼ ┌─────────────┐ │ running │ 运行中 │ (normal) │ 正常输出 └──────┬──────┘ │ 调用 ask_master / ask_other_agent ▼ ┌─────────────┐ │ waiting │ 阻塞等待回答 │ (asking) │ └──────┬──────┘ │ 收到 answer / 自然结束 ▼ ┌─────────────┐ │ completed │ 本轮结束(上下文保留) └─────────────┘ ``` ### 2.2 路由决策 ```python def route_message(target_agent_id, message): agent = get_agent(target_agent_id) if agent.state == "waiting" and agent.pending_tool_call: # 目标正在阻塞等待回答:直接返回到工具结果 agent.resolve_pending_tool_call(message) return "resolved" if agent.state == "running": # 目标正在运行:inline 插入到当前对话上下文末尾 agent.inject_inline_message(message) return "injected_inline" if agent.state in ("idle", "completed"): # 目标空闲:作为 user 消息插入,触发新一轮运行 agent.inject_user_message(message) return "triggered_new_turn" ``` --- ## 3. 关键场景分析 ### 3.1 子智能体正在输出,主智能体要引导 场景: - UI Operator_1 正在运行,输出了「接下来我会创建新 API...」 - Team Leader 要立即干预:「先不要创建 API,先确认现有 auth 模块是否可复用。」 处理: - UI Operator_1 状态为 `running`(正常输出,不在阻塞等待) - `send_message_to_sub_agent` 的消息 inline 插入到 UI Operator_1 的 `messages` 末尾 - UI Operator_1 下一轮模型调用时会看到这条 user 消息 ### 3.2 子智能体正在等待回答 场景: - Full-Stack Engineer_1 调用了 `ask_master`,等待 Team Leader 回答 - Team Leader 调用 `answer_sub_agent_question` 处理: - Full-Stack Engineer_1 状态为 `waiting` - 回答直接返回到 `ask_master` 工具结果中 - Full-Stack Engineer_1 继续执行 ### 3.3 子智能体已经完成,主智能体追加任务 场景: - Full-Stack Engineer_1 已经自然结束输出,状态为 `completed` - Team Leader 要追加新任务 处理: - `send_message_to_sub_agent` 的消息作为普通 user 消息插入 - 触发 Full-Stack Engineer_1 新一轮运行 ### 3.4 边界情况:子智能体正在进行最后一轮输出 场景: - UI Operator_1 正在输出最后一段话,后面没有工具调用了 - Team Leader 此时调用 `send_message_to_sub_agent` 处理: - 如果消息到达时 UI Operator_1 还在运行:尝试 inline 插入 - 但由于这是最后一轮,后面没有工具调用了,inline 的消息不会被模型看到 - 因此需要在 UI Operator_1 本轮任务结束后,把这条消息作为触发新一轮任务的 user 消息发送 实现要点: - 路由层维护每个子智能体的「待处理消息队列」 - 子智能体任务自然结束时,检查队列 - 如果有待处理消息,立即作为 user 消息触发新一轮运行 ### 3.5 子智能体 A 问 B,B 正在运行 场景: - UI Operator_1 调用 `ask_other_agent(target=2)` 问 Full-Stack Engineer_1 - Full-Stack Engineer_1 正在运行中 处理: - 如果 Full-Stack Engineer_1 处于 `running` 状态:inline 插入 user 消息 - Full-Stack Engineer_1 下一轮输出时看到问题 - 如果 Full-Stack Engineer_1 调用 `answer_other_agent`:回答返回到 UI Operator_1 的 `ask_other_agent` 工具结果 --- ## 4. 待处理消息队列 每个子智能体维护一个待处理消息队列 `pending_messages`。 ```python @dataclass class PendingMessage: id: str source_display_name: str source_agent_id: Optional[int] target_agent_id: int message_type: str # task / output / ask / message / answer content: str question_id: Optional[str] # 用于 answer 匹配 created_at: float ``` ### 4.1 队列消费时机 1. 子智能体每次模型调用前,先检查队列,把待处理消息合并到 `messages` 中 2. 子智能体从 `waiting` 状态恢复时,优先消费回答类消息 3. 子智能体自然结束时,如果有剩余待处理消息,立即触发新一轮运行 ### 4.2 批量消费 参考现有通知池,每次消费时尽可能一次性取出所有可消费消息: ```python def consume_pending_messages(agent): messages = agent.pending_messages.drain_all() for msg in messages: formatted = format_message(msg) agent.messages.append({"role": "user", "content": formatted}) return len(messages) ``` --- ## 5. 与现有通知池的对比 | 维度 | 现有后台通知池 | 多智能体消息路由 | |------|--------------|----------------| | 触发源 | 子智能体/后台命令完成 | 子智能体间/主智能体向子智能体发消息 | | 接收方 | 主智能体对话 | 子智能体对话或主智能体对话 | | 插入方式 | inline / 触发新任务 | inline / 返回到工具结果 / 触发新任务 | | 批量处理 | `_collect_pending_completion_notices` 一次性取多条 | 每个子智能体维护自己的待处理队列 | | 持久化 | 直接插入对话历史 | 先进入队列,再按状态消费并持久化 | --- ## 6. 消息路由实现位置 - 核心路由逻辑:`modules/multi_agent/message_router.py` - 待处理队列:`MultiAgentSubAgentTask.pending_messages` - 状态管理:`MultiAgentSubAgentTask.state` - 工具调用等待:`MultiAgentSubAgentTask.pending_tool_calls` --- ## 7. 防丢失机制 1. 每条消息都有唯一 `id` 2. 消息进入队列时立即持久化到子智能体 metadata 3. 消费完成后从队列移除并持久化 4. 子智能体恢复时从 metadata 加载未消费消息 5. 回答类消息通过 `question_id` / `message_id` 精确匹配 --- ## 8. 关键代码参考 - `server/chat_flow_task_support.py`:`inject_runtime_user_message`、`process_sub_agent_updates` - `server/chat_flow_task_main.py`:`_collect_pending_completion_notices`、`poll_completion_notifications`、`_dispatch_completion_user_notice` - `server/chat_flow_tool_loop.py`:`execute_tool_calls` 末尾的 `process_sub_agent_updates(..., inline=True)`