From d5b1e2c3af6e522b047f996651697471c281c081 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sun, 9 Aug 2026 18:15:42 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AF=B9=E8=AF=9D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E9=87=8D=E6=9E=84=E6=96=B9=E6=A1=88=EF=BC=88?= =?UTF-8?q?=E6=B6=88=E9=99=A4=E5=A4=9A=E6=99=BA=E8=83=BD=E4=BD=93=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=85=A8=E5=B1=80=E7=8A=B6=E6=80=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/conversation_type_unification_plan.md | 214 +++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 docs/conversation_type_unification_plan.md diff --git a/docs/conversation_type_unification_plan.md b/docs/conversation_type_unification_plan.md new file mode 100644 index 00000000..dbd6cf69 --- /dev/null +++ b/docs/conversation_type_unification_plan.md @@ -0,0 +1,214 @@ +# 对话类型统一重构方案:消除「多智能体模式」全局状态 + +> 状态:方案已确认(v2,含用户设计修正),实施中 +> 撰写日期:2026-08-09 +> 关联记忆:`multi_agent_mode_design`(本方案实施后将取代其前端部分) + +--- + +## 1. 背景与问题 + +当前「多智能体模式」是一个**全局标签页状态**,由三样东西共同维持: + +- 路由前缀:`/multiagent/` vs `/` +- 全局布尔:`multiAgentMode`(app 级状态,37 处引用、18 个文件) +- 列表过滤:所有对话列表请求携带 `multi_agent_mode=0|1`,两种模式的对话互相不可见 + +这套设计在双模式并行使用时暴露了一串耦合 bug(2026-08 实录): + +1. 多智能体标签页(工作区1)+ 传统标签页(工作区2)互拽视图——socket 用户级广播无条件跟随。 +2. 多智能体模式下,侧边栏「按项目分组」的项目旁新建按钮把对话建成了传统模式——每个新建入口都要手动"继承模式",漏一个就是一个 bug。 +3. 修复过程中发现同类耦合点越修越多(共享 session 工作区、创建落点、URL 前缀拼接……)。 + +**根因**:模式分裂本身制造了双标签页使用需求(两类对话只能在各自页面看到),而全局模式状态与共享 session、用户级广播交织出 N 个耦合点,修补式修复无法穷尽。 + +**产品决断**(用户拍板):多智能体是**对话的属性**,不是应用的模式。单标签页、单当前工作区是设计如此(后端对话级隔离已保证同/跨工作区多对话并行);两类对话进入同一个页面后,双标签页场景自然消失。 + +## 2. 目标 / 非目标 + +### 目标 + +- 删除全局「多智能体模式」状态;对话类型(`normal` | `multi_agent`)**创建时确定、之后不可变**,权威存储于对话 `metadata.multi_agent_mode`(现有字段,零迁移)。 +- 侧边栏两类对话**左右切换显示**(单类型视图 + 滑动动画),平铺列表与按项目分组两种视图套用同一规则。 +- 输入栏底行(发送按钮所在行)`+` 按钮**左侧**新增「智能体/多智能体」类型选择按钮。 +- URL 统一为 `/`;`/multiagent/`、`/multiagent/new` 做兼容重定向。 +- 打开对话时从 metadata 恢复类型(现有 `enterConversation` 机制),渲染/terminal/上下文走对应路径。 + +### 非目标(明确不做) + +- **不改多智能体运行时**:Team Leader、子智能体管理、消息池派发、多智能体消息渲染(`isMultiAgentMessage` 按消息级 metadata 判断)全部保留。 +- **不做类型转换**:普通对话不能"升级"为多智能体。 +- **不改分开存储**:多智能体对话仍由独立 conversation manager 存储。 +- **不做多标签页/多设备视图跟随**:各视图独立是设计前提。 +- **不改后端**:列表继续用现有 `multi_agent_mode=0|1` 服务端过滤(此时它是用户显式控制的视图过滤器,语义正确),端点全部保留。 + +## 3. 已确认的设计决策 + +| # | 决策点 | 结论 | +|---|--------|------| +| 1 | 类型选择器位置 | 输入栏**最下面固定行**(发送按钮所在行),`+` 按钮**左侧**的独立按钮;**不在 + 菜单内部** | +| 2 | 侧边栏呈现 | **左右切换**(一次只显示一个类型),控件位置:平铺模式(单工作区)放在「搜索对话」下面;分组模式(多工作区)放在**工作区标题行、新建对话等按钮的左边** | +| 3 | 切换动画 | 点击后对话记录显示区域**整体向左滑出**,多智能体列表**从右侧滑入**;切回时动画反向 | +| 4 | URL 方案 | 统一 `/` 为主路由;`/multiagent/` 重定向到 `/` | +| 5 | 类型可变性 | 创建时确定,**不可变**;打开对话从 metadata 恢复 | +| 6 | 工作区模型 | 单当前工作区维持设计,session 权威 | + +**实施细节默认**(用户可否决): + +- 侧边栏类型过滤器:`sidebarConversationType`(`'normal' | 'multi_agent'`),单一全局状态、localStorage 持久化、默认 `'normal'`,同时驱动平铺/分组两种视图的列表请求(`maParam` 由它生成)与切换动画方向。 +- 侧边栏新建按钮(含项目分组旁按钮):创建**当前过滤器显示的类型**(看着多智能体列表点新建,就建多智能体对话,立即出现在当前视图中)。 +- /new 页首条消息创建:按输入栏选择器的 `newConversationType`。 +- 空过滤器下(某工作区无该类型对话):列表区域显示该类型的空态。 + +## 4. 技术地基(已验证,2026-08-09 代码核对) + +| 验证点 | 结论 | 位置 | +|--------|------|------| +| 聊天执行的模式判定 | ✅ 已是**对话 metadata 驱动**:加载对话时 `self.multi_agent_mode = bool(meta.get("multi_agent_mode", False))`,chat_flow 全部读 terminal 实例标志 | `core/web_terminal.py:438-443`、`server/chat_flow_task_main.py` 多处 | +| 列表服务端过滤 | ✅ `multi_agent_mode=0|1` 过滤已支持且分页正确(每个 manager 各自分页);侧边栏切换器继续用它,**后端零改动** | `utils/context_manager/conversation_mixin.py:388`、`server/conversation.py:524` | +| 按 ID 定位对话归属 | ✅ 按文件存在性跨两个存储目录解析,类型无关 | `conversation_mixin.py:86` | +| 对话元数据前端恢复 | ✅ `enterConversation` 统一加载协议已读 `meta.multi_agent_mode`(重构后写入对话类型) | `bootstrap.ts:66` | +| 前端模式状态规模 | 37 处引用、18 个文件,绝大多数是 `isMultiAgent ? A : B` 分支 | 见 §6 改动地图 | +| 数据迁移 | ✅ 零迁移;旧对话缺字段时后端有 `/api/multiagent/rebuild-index` 补全机制(当前在 `/multiagent/new` bootstrap 触发,重构后改为 `/new` 首次进入或应用启动时触发一次) | `route.ts bootstrapRoute` | + +## 5. 总体设计 + +### 5.1 状态模型:从「全局模式」到「对话类型 + 两个局部选择状态」 + +**删除**: +- `app/state.ts` 的 `multiAgentMode` 全局布尔 +- `watchers.ts` 的 multiAgentMode → conversation store 同步 +- `stores/conversation.ts` 的 `multiAgentMode` patch + +**新增**(三个语义单一的状态,替代原来一个语义混杂的全局模式): + +| 状态 | 语义 | 生命周期 | +|------|------|----------| +| `currentConversationType: 'normal' \| 'multi_agent' \| null` | 已打开对话的类型 | `enterConversation` 从 `meta.multi_agent_mode` 落地;空对话态为 `null` | +| `newConversationType: 'agent' \| 'multi_agent'` | 输入栏选择器的待创建类型 | localStorage 持久化,默认 `'agent'` | +| `sidebarConversationType: 'normal' \| 'multi_agent'` | 侧边栏过滤器当前显示的类型 | localStorage 持久化,默认 `'normal'` | + +**派生规则**:打开对话后所有「是否多智能体」判断读 `currentConversationType`;/new 空态的创建判断读 `newConversationType`;侧边栏列表/新建按钮读 `sidebarConversationType`。三者互不复用。 + +### 5.2 路由统一 + +- 主路由:`/`(`stripConversationPrefix` 的 `conv_` 前缀美化保留,与类型无关)。 +- `/multiagent/new` → `replaceState` 到 `/new`。 +- `/multiagent/` → 正常 `enterConversation` 加载(类型从 meta 恢复),成功后 `replaceState` 到 `/`。 +- `isExplicitNewConversationRoute()` 收缩为只判 `new`。 +- `enterConversation` 的 `urlPrefix` 参数删除。 + +### 5.3 输入栏类型选择器(新 UI) + +- **位置**:输入栏最下面固定行(发送按钮所在行),`+` 按钮**左侧**,独立于 + 菜单。 +- **形态**:参考 `permission-switcher`(批准方式菜单)——按钮显示当前值 + caret,点击展开下拉: + - 「智能体」——单智能体对话(默认) + - 「多智能体」——Team Leader + 子智能体协作 +- **空对话(/new)**:可选,选中写 `newConversationType` + localStorage。 +- **已有对话**:显示该对话类型,**禁用态**(类型不可变)。 +- QuickMenu 内现有 `toggle-agent-mode` 入口(`@toggle-agent-mode` 事件链)移除。 +- 样式遵守 AGENTS.md §5.5:无 glow、语义 token、固定高度、菜单不透明、内部滚动。 + +### 5.4 侧边栏左右切换 + +**控件**(分段切换,两项:普通对话 / 多智能体对话,或反向顺序): + +- 平铺模式(单工作区):放在「搜索对话」输入框下面。 +- 分组模式(多工作区):放在工作区标题行,新建对话等按钮的**左边**(所有工作区分组共享同一个过滤器状态,控件在每个分组标题行重复出现或仅当前展开组——实施时按 DOM 结构定,优先每行都有,保证所见即所得)。 + +**动画**:点击切换后,对话记录显示区域**整体向左滑出**,目标类型列表**从右侧滑入**;切回时反向。实现:两个列表 pane 包一层 `overflow:hidden` 轨道,`transform: translateX` + transition(普通→多智能体方向向左,反向向右);Vue `` 自定义类或双 pane 轨道均可,以不引起高度跳动为准。 + +**列表请求**:继续携带 `maParam`,但来源从「全局模式」改为「侧边栏过滤器状态」;切换时重置 offset 重新加载。分组视图 `loadWorkspaceConversations` 同理。 + +### 5.5 创建链路 + +| 入口 | 类型来源 | 端点 | +|------|----------|------| +| /new 首条消息(send.ts) | `newConversationType` | `'multi_agent'` → `/api/multiagent/conversations`(`preserve_mode:true`),否则 `/api/conversations` | +| 侧边栏「新建对话」(blank 行为) | `sidebarConversationType` | 同上 | +| 项目分组旁新建按钮 | `sidebarConversationType` | 同上 + 该项目 `workspace_id` | + +创建成功后统一 `/` 跳转。 + +### 5.6 后端改动 + +**零改动**。列表过滤、创建端点、类型恢复全部复用现有能力。(原计划的合并列表分页修正取消——不合并,改为显式过滤。) + +## 6. 改动地图 + +### 6.1 前端(18 个文件,多数为删除分支) + +| 文件 | 改动要点 | +|------|----------| +| `app/state.ts` | 删 `multiAgentMode`;加 `currentConversationType`、`newConversationType`、`sidebarConversationType` | +| `app/watchers.ts` | 删 multiAgentMode watch 及路由模式推导 | +| `app/methods/ui/route.ts` | bootstrapRoute 删 multiagent 模式分支,改为重定向;`isExplicitNewConversationRoute` 收缩;rebuild-index 触发时机迁移 | +| `app/methods/ui/mode.ts` | `toggle-agent-mode` 相关方法清理 | +| `app/methods/conversation/bootstrap.ts` | meta → `currentConversationType`;删 `urlPrefix` 与模式前缀拼接 | +| `app/methods/conversation/load.ts` | `maParam` 改由 `sidebarConversationType` 生成 | +| `app/methods/conversation/action.ts` | 创建按 `sidebarConversationType` 选调端点;删 URL 模式前缀 | +| `app/methods/message/send.ts` | 首条消息创建按 `newConversationType`;URL 统一 `/` | +| `app/methods/search.ts` | 搜索过滤来源改为 `sidebarConversationType`(或全类型,实施时定) | +| `app/methods/taskPolling/sync.ts` | 删 URL 前缀拼接 | +| `app/methods/ui/hostWorkspace.ts` | 工作区切换后统一跳 `/new` | +| `stores/conversation.ts` | 分组加载 `maParam` 改来源;删 `multiAgentMode` patch | +| `stores/subAgent.ts` | 核对模式依赖(验证点 V3) | +| `composables/useLegacySocket.ts` | `conversation_resolved` 等处 URL 前缀删除 | +| `components/input/InputComposer.vue` | 底行 `+` 左侧新增类型选择器;删 `multi-agent-mode` prop 透传 | +| `components/input/QuickMenu.vue` | 移除 `toggle-agent-mode` 入口 | +| `components/sidebar/ConversationSidebar.vue` | 切换控件(两处位置)+ 双 pane 滑动动画 + 按类型渲染 | +| `App.vue` | 删模式相关 props/事件透传 | + +### 6.2 文档与记忆 + +- `AGENTS.md` §11 多智能体模式章节重写为「对话类型」模型 +- 项目记忆 `multi_agent_mode_design` 标注被本方案取代(前端部分);新增 `conversation_type_model` 记忆 + +## 7. 历史修复的取舍(2026-08-09 评估) + +| 批次 | 结论 | 理由 | +|------|------|------| +| `be061bec` stop_flags 任务级隔离 | **保留** | 真 bug(任意并行对话互相停止),与模式架构无关,新世界同样需要 | +| `165c771c` 网络权限实例级快照 + 快捷窗口残留 | **保留** | 真 bug(进程级 env 污染后台指令网络),与模式架构无关 | +| 未提交的广播过滤批(6 文件:socket 防拽视图/工作区本地化/显式 workspace_id/新建模式继承) | **已回退** | 错误路线:给双标签页耦合打补丁;新模式下该场景被设计消除,且新建继承等改动将被重构重写 | + +回退后基线 = `165c771c`(即"第一次说这个问题"时的状态 + 两个独立真修复)。 + +## 8. 风险与缓解 + +| 风险 | 缓解 | +|------|------| +| `enterConversation` 删 `urlPrefix` 后调用方未对齐 | 全仓搜索 `urlPrefix` 逐一核对;手测刷新恢复 | +| 切换动画引起列表高度跳动/滚动位置丢失 | 双 pane 轨道 + 固定容器高度;切换时保留各自滚动位置 | +| 分组视图每行都放控件导致拥挤 | 实施时评审 DOM 结构,必要时仅当前展开组显示 | +| `/multiagent/*` 旧链接(App 历史、书签)失效 | 重定向保留;Android App 下次发版同步 | +| 暗藏的的模式依赖(subAgent store、监控动画等) | 实施收尾时全仓 grep `multiAgentMode`/`multi_agent` 清零 | + +## 9. 实施时验证点 + +- **V3**:`stores/subAgent.ts` 的 `multiAgentMode` 具体用途 +- **V4**:QuickMenu 内 `toggle-agent-mode` 的现有处理链(App.vue → mode.ts) +- **V5**:`App.vue` 中 `multiAgentMode` 的 props 透传链 +- **V6**:分组模式工作区标题行的按钮组构成(确定切换控件插入点) + +## 10. 实施阶段与验证计划 + +- **阶段 A(状态与路由)**:状态层三态替换 + 路由统一/重定向 → 构建通过 +- **阶段 B(输入栏选择器)**:底行 `+` 左侧按钮 + 创建链路 → 构建通过 +- **阶段 C(侧边栏切换)**:控件 + 滑动动画 + 列表过滤来源切换 → 构建通过 +- **阶段 D(清理)**:残留 grep 清零 + 文档/记忆更新 + 冒烟 + +**手测清单**(完成后请用户实测): +1. /new 页输入栏左侧选「多智能体」发首条消息 → 创建多智能体对话,URL 为 `/` +2. 侧边栏切换控件:普通 ↔ 多智能体,列表滑动动画方向正确 +3. 平铺/分组两种视图切换控件位置符合 §3-#2 +4. 打开多智能体对话 → 选择器显示「多智能体」禁用态,Team Leader 路径正常 +5. 刷新页面 → 对话类型正确恢复;旧 `/multiagent/` 链接重定向正常 +6. 切工作区 → 列表随过滤器类型正确加载;分组旁新建按钮创建的类型 = 当前过滤器类型 + +## 11. 提交策略 + +1. 回退错误路线未提交改动(已完成,无痕) +2. **本方案文档 commit**(`docs:` 单独一个) +3. 重构实施:阶段 A-C 一个 `refactor` commit(或按需拆分),阶段 D 文档更新并入 +4. 工作区存在用户自己的未提交改动(`tools_execution.py`、`modules/multi_agent/tools.py`、`toolRenderers.ts`、`PersonalizationDrawer.vue`、`personalization.ts`、`agent_context.py`),任何 commit 不得混入