diff --git a/AGENTS.md b/AGENTS.md index d7ccfab..832bbf4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -332,3 +332,86 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: - 调试开关(代码变量):`modules/approval_agent.py` 中 `DEBUG_SAVE_APPROVAL_AGENT_TRANSCRIPT` - 开启后写入:`logs/approval_agent/` - 记录以累积 `messages` 为主,便于对齐主/子智能体会话格式 + +## 11) 多智能体模式(multi-agent mode) + +> 对话级开关 `metadata.multi_agent_mode = true` 启用,URL 入口 `/multiagent/`,数据目录 `~/.astrion/astrion/host/mutiagents/`(保留原拼写)。完整设计与踩坑记录见项目记忆 `multi_agent_mode_design`,本节只列 Agent 改代码时必须知道的硬约束。 + +### 11.1 角色与实例 + +- 主智能体显示名固定为 `Team Leader`,不需要专门的预置角色文件。 +- 子智能体 = `role_id`(如 `ui-operator` / `full-stack-engineer` / `code-reviewer` / `researcher`)+ `agent_id`(同一 role_id 下从 1 递增)。显示名格式 `{Role Name}_{agent_id}`,例如 `UI Operator_1`,后缀永远带数字。 +- 主→子 / 子→主 / 子→子三种通信通过工具完成,工具签名见 `modules/multi_agent/tools.py`。 +- **`send_message_to_sub_agent` 与 `ask_sub_agent` 语义不同,必须保留两者**:前者插入引导消息不阻塞,后者阻塞等待一轮回答。 +- 子智能体间通信要求同时向主智能体输出汇报,不允许「偷偷沟通」。 + +### 11.2 子智能体执行机制 + +- 子智能体在主进程内 `asyncio.Task`,跑在独立后台事件循环线程里(避开 Flask-SocketIO threading 冲突)。工具调用复用主进程沙箱/容器链路,网络调用走 `utils.api_client.DeepSeekClient`。 +- 子智能体在多智能体模式下: + - `create_sub_agent` 强制 `run_in_background=False`,不触发 `sub_agent_waiting` 事件,不阻塞前端输入区。 + - 子智能体自然的 assistant 输出结束(无 tool_calls)即本轮任务结束,进入 `idle`,上下文保留,不算 failed。 + - 不能把 `output.success == null` 直接判为 `failed`—`_check_task_status()` 对多智能体任务 `status=running/idle` 视为正常态(见 `modules/sub_agent/state.py`)。 + - idle 等待必须用 `asyncio.Event`,不能用 `threading.Event`,跨线程唤醒用 `call_soon_threadsafe`。 + - 任务记录里 `multi_agent_mode` 字段必须显式写入,缺失会导致传统后台通知池把多智能体任务当后台任务处理。`reconcile_task_states()` 已对旧任务补回。 + +### 11.3 消息池与派发链路(重点) + +**输出端 ↔ 接收端分离**: +- 输出端(`SubAgentTask._forward_output_to_master`):子智能体每次 assistant 文本输出都封为标准格式消息并 `push_master_message` 到会话的 `MultiAgentState.pending_master_messages`,不再做事。 +- 接收端(`MultiAgentState` + dispatch):根据主智能体当前状态选择插入方式: + - **情况1(主运行中)**:在 `execute_tool_calls` 末尾 `process_multi_agent_master_messages(inline=True, after_tool_call_id=...)` 插入到下一轮模型 messages 列表里(详见 `server/chat_flow_tool_loop.py:1326`)。 + - **情况2(主空闲)**:`poll_multi_agent_notifications` spawn 出后台 poll,主对话空闲且 pool 有消息就 drain,调 `_dispatch_multi_agent_idle_messages` 创建 `task_type="notice"` 的后续 task,触发新一轮工作。完整顺序见项目记忆 multi_agent_mode_design 中的「情况2完整链路」图。 + - **情况3(主最后一轮无工具调用)**:主循环 `if not tool_calls:` 分支调 `process_multi_agent_master_messages(inline=False)` 后 `continue` 继续迭代,Team Leader 在同 task 续跑处理子智能体输出。(实现上与情况2同路径,即情况3通过后续 task 完成情况2)。 + +**情况2 硬约束(调试踩过坑)**: + 1. **Pool 优先,不能等所有 running 退出再 drain**:`ask_master` await 期间 status=running,但子智能体本身不会产生新输出,只有等主对话回答才能解套。pool 有消息就立即 drain,不管 running 状态。「对话处于运行状态」只决定前端显示态,不阻塞 pool 消费。 + 2. **`_dispatch_multi_agent_idle_messages` 持久化不要重复**:前置 N-1 条调 `inject_multi_agent_master_message` 绑定持久化。最后一条只 emit 给在线客户端、不持久化、在后续 `task_manager.create_chat_task` 走的 `handle_task_with_sender` 里才 `add_conversation`。否则历史里会有两条相同 user 消息,前端刷新会被渲染两遍(一条多智能体渲染、一条通知渲染)。 + 3. **Metadata `visibility` 必须显式为 `"chat"`**:`_user_message_ui_defaults("sub_agent")` 默认给 `{visibility: "compact"}`;在 `_dispatch_multi_agent_idle_messages` 构造 `auto_user_message_payload`时,如果有`**ui_defaults`,必须在它之**后**写 `"visibility": "chat"`(dict 字面量中后出现的key 胜出)。同样,在`handle_task_with_sender`处理多智能体消息时,`user_message_metadata.update(multi_agent_meta)`之后要显式`user_message_metadata["visibility"] = "chat"`。 + 4. **必须传 `auto_message_type`**:前端 `isMultiAgentMessage()` 只看 `auto_message_type.startsWith('multi_agent_')`。`auto_user_message_payload` 和 `preceding_user_notices[i].payload` 都必须显式写 `auto_message_type`,字段不能用空值,否则前端 fallback 起通知渲染。 + +### 11.4 通知池轮询器完全独立 + +- 传统后台子智能体 / 后台 `run_command` 通知:`poll_completion_notifications`,在 `handle_task_with_sender` 结尾的 `needs_completion_poll` 。 +- 多智能体通知:`poll_multi_agent_notifications`,在 `needs_ma_poll`。两者完全独立,避免 task_manager 单工作区互斥竞争。 +- `task_complete` 事件中: + - `has_running_sub_agents` 只算传统后台任务,不算多智能体。 + - `has_running_multi_agent` 多智能体专用字段,同时包含 _running_ 实例和 pending master 消息(详见 `server/chat_flow_task_main.py:2324`)。前端通过该字段走独立的 `startMultiAgentTaskProbe`(不启动 `sub_agent_waiting`)。 + +### 11.5 渲染与前端约定 + +- 多智能体消息渲染条件是**两个独立判断**: + - `isMultiAgentMessage()`:看 `auto_message_type.startsWith('multi_agent_')`。 + - `getMessageVisibility()`:看 `metadata.visibility`。两者都必须正确,消息才能走多智能体渲染分支。任一错都会 fallback 到通知渲染。 +- 多智能体消息不显示新的 assistant 回复头部(Astrion/工作时间),即 `metadata.starts_work=false`。但前端通过 `has_running_multi_agent` 在 `task_complete` 中单独处理恢复轮询。 +- 子智能体进度弹窗:输出与工具按真实时间线混排,默认 3 行,过长用省略号或内部滚动;颜色走 `--text-primary` 等语义 token。 +- 全局工具规范统一适用于多智能体权限:UI 不能引入 `compact` 以外的 fallback 样式习惯复制到多智能体渲染。 + +### 11.6 调试机制 + +- 调试日志统一走 `modules/multi_agent/debug_logger.py` 的 `ma_debug()` 函数,写入 `~/.astrion/astrion/host/logs/multi_agent_loop.log`。 +- 关键状态转换点必须打 `ma_debug`,便于复现 bug: + - `handle_task_with_sender_start`(含 `pending_master_messages_count`) + - `state_push_master_message` (交出 `queue_len_before`) + - `poll_ma_tick` (每 0.5s 一次,包含 `instance_count` / `statuses` / `pending_count` / `main_active` 的完整状态快照) + - `dispatch_ma_idle_enter` / `dispatch_ma_idle_before_create_task` / `dispatch_ma_idle_task_created` / `dispatch_ma_idle_sender_user_message` / `dispatch_ma_idle_exit_ok` + - `create_chat_task` 异常 / `provide_answer` 跨循环回写 + - `runtime_injected` 字段标记(metadata 里能区分多智能体 inline / idle / ask插入路径) + +### 11.7 已知坑 + +- 多智能体模式下根本不 `emit` `sub_agent_waiting` 事件,避免前端进入「等待后台子智能体」的输入区阻塞态。 +- `_announced_sub_agent_tasks` / `notified` 等标记仅适用于传统后台子智能体任务,多智能体任务不走这条通知路径。 +- 多智能体任务的 `output.json` 中 `status` 在子智能体自然进入 idle 时会被写为 `"idle"`;在 `_check_task_status` 中由 `_check_task_status_keep_alive` 跳过防止错误判定为 `failed`。 +- 传统后台通知池 `_collect_pending_completion_notices` 在 `task.get("multi_agent_mode")` 为真时跳过该 task;多智能体派出走独立的 `poll_multi_agent_notifications` 路径,不当混用。 +- `_has_pending_completion_work` 主动排除 `multi_agent_mode=True` 的任务;两者永远独立。 + +### 11.8 对话切换与状态保留 + +- 切换会话不清理 `_running_tasks` 和 `_sub_agent_instances`。`SubAgentManager` 的全局 tasks 字典按 `task_id` 保持,多智能体状态由 `conversation_id` 在 `get_multi_agent_state` 中查。 +- 子智能体对话存在 `~/.astrion/astrion/host/host/data/sub_agents/`。重启后走 `manager.restore_sub_agent` 恢复实例引用。 +- `MultiAgentState` 实例在 Manager 上常驻(dict by `conversation_id`);进程重启走 `from_snapshot` 恢复。 + +--- + +注:本节按「现有架构 + 多智能体分支」方案描述,与项目记忆 `multi_agent_mode_design` 同步;如两者冲突以代码为准并同步修订本节。 \ No newline at end of file