Files
DevFlow/docs/02-架构设计/专项设计/多Agent并行执行与仲裁合并设计-2026-07-01.md
绝尘 eff1522ec3 新增: 多Agent并行执行与仲裁合并设计文档(P0 Batch 37-38 实施依据)
- 数据模型:混合方案(messages加FK列 + Plan/SubTask/Conflict独立表)

- 并行策略:层间串行+层内并行,写SubTask降级串行,token预算池

- 上下文隔离:子Agent独立ContextManager+fork快照,合并产出落回主对话

- 事件协议:4个新事件(AiPlanCreated/AiSubTaskStatusChanged/AiMergeCompleted/AiConflictResolved)

- UI交互:PlanProgress发送即展示+工具卡分组+冲突徽章非阻塞

- 状态机:Plan/SubTask/Conflict各自状态流转图

- 前端类型:PlanRecord/SubTaskRecord/ConflictRecord TypeScript定义

- INDEX.md注册 + Batch.md同步批次明细
2026-07-01 15:18:02 +08:00

16 KiB
Raw Blame History

多 Agent 并行执行与仲裁合并设计 — 2026-07-01

性质:架构设计 / 数据模型 / 事件协议 / UI 交互 关联: AI-Native方向与路线图-2026-06-29.mdP0 智能体人设 + 多 Agent 协作) 关联: 单对话并行多轮-设计-2026-06-20.mdPlan DAG 结构与分层调度) 关联: Agent架构说明-2026-06-14.md(当前 Agent 能力边界) 关联: 三层模型-流程模板与人设体系-2026-06-28.md(模板/工作流/人设三层抽象) 用途:Batch 37-38 的实施依据


一、设计目标

DevFlow 的定位是 AI Native——AI 是系统的主要操作者,人的角色是「决策者 + 政策制定者」。

多 Agent 并行执行要解决的核心问题:

问题 现状(单 Agent 多 Agent 后
复杂任务拆解 LLM 自行在单轮内拆,上下文窗口受限 Coordinator 拆为 SubTask各自独立上下文
并行产出 串行,无冲突 多 Agent 同时改文件 → 数据竞争
用户感知 单条流式输出 多 Agent 产出交织,用户分不清谁是谁
错误恢复 一个工具失败 → LLM 下一轮决策 一个 SubTask 失败 → Coordinator 决定是否继续

二、数据模型(方案 C混合

2.1 设计原则

  1. messages 不膨胀 — 只加 subtask_id 外键列,不把 Plan/Conflict 字段塞进去
  2. 重实体独立 — Plan / SubTask / Conflict 各自独立表,各自演进
  3. 历史兼容subtask_id = NULL 表示单 Agent 时期消息,零回归
  4. FK 三层链路 — messages → SubTask → Plan 可追溯完整生命周期

2.2 ER 图

erDiagram
    ai_conversations ||--o{ ai_plans : "1:N 触发"
    ai_plans ||--o{ ai_subtasks : "1:N 拆解"
    ai_plans ||--o{ ai_conflicts : "1:N 冲突"
    ai_subtasks ||--o{ ai_messages : "1:N 产出(subtask_id FK)"
    ai_subtasks ||--o{ ai_tool_executions : "1:N 工具调用(subtask_id FK)"
    ai_conflicts }o--|| ai_subtasks : "subtask_a"
    ai_conflicts }o--|| ai_subtasks : "subtask_b"

    ai_plans {
        TEXT id PK
        TEXT conversation_id FK
        TEXT user_message_id "触发 Plan 的用户消息"
        TEXT status "planning/executing/merging/done/error"
        INTEGER subtask_count
        TEXT created_at
        TEXT completed_at "可空"
    }

    ai_subtasks {
        TEXT id PK
        TEXT plan_id FK
        TEXT persona_id "coder/reviewer/architect/..."
        TEXT intent "子任务意图描述"
        TEXT status "pending/running/done/error"
        INTEGER layer "DAG 层级(0起)"
        TEXT deps "JSON 数组: 依赖的 SubTask id 列表"
        TEXT created_at
        TEXT completed_at "可空"
    }

    ai_conflicts {
        TEXT id PK
        TEXT plan_id FK
        TEXT file_path "冲突文件路径"
        TEXT subtask_a FK
        TEXT subtask_b FK
        TEXT diff_a "Agent A 对该文件的改动摘要"
        TEXT diff_b "Agent B 对该文件的改动摘要"
        TEXT resolution "pending/a/b/merged/manual"
        TEXT resolved_by "reviewer/user"
        TEXT created_at
        TEXT resolved_at "可空"
    }

2.3 现有表改动

-- V36 迁移
ALTER TABLE ai_messages ADD COLUMN subtask_id TEXT;
ALTER TABLE ai_tool_executions ADD COLUMN subtask_id TEXT;

-- 新建 3 张表(见 ER 图字段)
CREATE TABLE ai_plans (...);
CREATE TABLE ai_subtasks (...);
CREATE TABLE ai_conflicts (...);

2.4 三层关联查询示例

-- 查某 Plan 的全部产出(消息 + 工具)
SELECT m.* FROM ai_messages m
JOIN ai_subtasks s ON m.subtask_id = s.id
WHERE s.plan_id = ?;

-- 查某 Plan 的所有冲突
SELECT c.* FROM ai_conflicts c WHERE c.plan_id = ? AND c.resolution = 'pending';

-- 查某对话的全部 Plan 历史
SELECT p.* FROM ai_plans p WHERE p.conversation_id = ? ORDER BY created_at DESC;

三、事件协议

3.1 新增事件

事件 时机 载荷 前端响应
AiPlanCreated Coordinator.decompose 完成后 { plan_id, layers: [[{id, persona, intent, status}],...] } PlanProgress 展示 DAG
AiSubTaskStatusChanged SubTask 状态变更pending→running→done/error { subtask_id, plan_id, status, persona_id } PlanProgress 更新节点状态 + 工具卡分组徽章
AiMergeCompleted Coordinator.merge 完成后 { plan_id, merged_output, conflicts: [{id, file, subtask_a, subtask_b}] } 展示合并结果 + 冲突徽章
AiConflictResolved 用户/reviewer 解决冲突后 { conflict_id, resolution, resolved_by } 冲突徽章消失 + 最终输出更新

3.2 事件流时序

用户发送消息
  ↓
AiPlanCreated { plan_id, layers }
  ↓ (PlanProgress 立即展示)
Layer 0 启动
  ↓
AiSubTaskStatusChanged { subtask_0, running }
AiSubTaskStatusChanged { subtask_1, running }   ← 层内并行
  ↓ (工具卡片按 subtask_id 分组,带 persona 徽章)
AiSubTaskStatusChanged { subtask_0, done }
AiSubTaskStatusChanged { subtask_1, done }
  ↓
Layer 1 启动 ...
  ↓
AiMergeCompleted { plan_id, merged_output, conflicts }
  ↓ (有冲突 → 冲突徽章;无冲突 → 直接展示合并输出)
用户点击冲突 → 展示 diff_a/diff_b → 选择
  ↓
AiConflictResolved { conflict_id, resolution }
  ↓
最终输出展示

四、并行执行策略

4.1 调度规则

Plan.to_layers() → [[subtask_0, subtask_1], [subtask_2], [subtask_3, subtask_4]]
      Layer 0并行        Layer 1串行   Layer 2并行
规则 说明
层间串行 上层全部 done 才进下一层DAG 依赖保证)
层内并行 同层 SubTask 用 JoinSet 并发执行
写 SubTask 降级串行 同层内如有 write_file/patch_file 工具的 SubTask降级为串行执行防 concurrent write
只读 SubTask 并行 read_file/search_files/list_directory 等只读工具的 SubTask 可安全并行

4.2 Token 预算管控

struct TokenBudgetPool {
    total: AtomicU64,        // 全局预算(来自设置项)
    consumed: AtomicU64,     // 已消耗
}

impl TokenBudgetPool {
    fn try_reserve(&self, estimate: u64) -> bool {
        // CAS 循环consumed + estimate <= total
    }
}
  • 每个 SubTask 启动前向预算池申请估算额度
  • 超限时 Coordinator 拒绝启动新 SubTask降级为串行顺序执行剩余任务
  • 预算来源:设置项 df-ai-plan-token-budget(默认 100k tokens

4.3 错误传播

场景 策略
SubTask 执行失败 记录 error 状态,不中断其他同层 SubTask(容错)
全部 SubTask 失败 Coordinator 标记 Plan 状态为 error前端展示错误
部分 SubTask 失败 merge 时跳过失败 SubTask 的产出,只合并成功的
子 Agent 超时 单 SubTask 超时(默认 120s→ 标记 error不影响其他

五、UI 交互设计

5.1 PlanProgress 组件(发送即展示)

┌─────────────────────────────────────────┐
│ 📋 执行计划                          2/4 │
├─────────────────────────────────────────┤
│ Layer 1                                 │
│  ▶ 🔵 [coder] 重构代码     running      │
│    🟢 [architect] 分析结构  done        │
│              ↓                          │
│ Layer 2                                 │
│    ⏸ [tester] 补测试        pending     │
│              ↓                          │
│ Layer 3                                 │
│    ⏸ [reviewer] 审查        pending     │
└─────────────────────────────────────────┘
  • 发送即展示:用户发消息后 Coordinator 分解完成立即展示
  • 实时更新AiSubTaskStatusChanged 驱动节点状态变化
  • 折叠工具卡:每个 SubTask 下的工具卡片折叠归组(点击展开)
  • 层间箭头DAG 层级关系可视化

5.2 工具卡片分组subtask_id 归组)

┌─ 🔵 [coder·重构代码] ──────────────┐
│  ▸ read_file main.rs        ✓ done │
│  ▸ patch_file utils.rs      ✓ done │
└────────────────────────────────────┘
┌─ 🟢 [architect·分析结构] ──────────┐
│  ▸ list_directory           ✓ done │
│  ▸ 分析结论: 模块耦合度偏高...      │
└────────────────────────────────────┘
  • 每个 SubTask 一个折叠容器,带 persona 颜色徽章
  • 工具卡片归入对应 SubTask 容器
  • 默认折叠,有审批/错误时自动展开

5.3 冲突展示(徽章非阻塞)

┌─────────────────────────────────────────┐
│ ✅ 执行完成              ⚠ 2 处冲突待处理 │
├─────────────────────────────────────────┤
│  合并输出:                              │
│  ...                                   │
├─────────────────────────────────────────┤
│  ⚠ main.rs — Agent A vs Agent B        │
│    [查看 diff] [接受 A] [接受 B] [手动] │
│  ⚠ utils.rs — Agent A vs Agent B       │
│    [查看 diff] [接受 A] [接受 B] [手动] │
└─────────────────────────────────────────┘
  • 冲突用徽章提示,不打断阅读流
  • 点击展开 diff 对比 + resolution 按钮
  • reviewer Agent 可自动给出推荐(resolved_by=reviewer),用户确认即可

六、取消与中断

操作 行为
用户点停止 停止所有并行 SubTask整体取消
单 SubTask 超时 只标记该 SubTask error不影响其他
会话切换 后台 SubTask 继续执行F-09 并发语义一致)
会话删除 所有关联 SubTask 停止 + Plan 标记 error

七、实施分批

Batch 37 — 数据层 + 并行执行

# 任务 文件
1 V36 迁移3 新表 + 2 ALTER migrations.rs
2 PlanRepo / SubTaskRepo / ConflictRepo CRUD 新 repo 文件
3 Coordinator.dispatch JoinSet 层内并行 coordinator.rs
4 写 SubTask 降级串行 + token 预算池 coordinator.rs
5 4 个新事件类型 AiChatEvent
6 PlanProgress 接入真实状态 PlanProgress.vue
7 工具卡按 subtask_id 分组 MessageList.vue

Batch 38 — 仲裁合并 + UI 完善

# 任务 文件
1 Coordinator.merge 冲突检测(按文件路径) coordinator.rs
2 Reviewer Agent 仲裁persona.rs 扩展) persona.rs + coordinator.rs
3 冲突 diff 展示 + resolution 按钮 新 ConflictResolver.vue
4 AiConflictResolved 事件闭环 AiChatEvent
5 技术债扫尾(编译警告 / unused import 各文件

八、子 Agent 上下文隔离

8.1 问题

多 Agent 并行执行时,每个 SubTask 需要独立的上下文(messages / currentText / tool_results)。 如果共享主对话的 ContextManager,多个 SubTask 的产出会交织污染。

8.2 方案:子对话快照 + 独立 ContextManager

struct SubTaskContext {
    /// 从主对话 fork 的快照(只读父上下文 + 用户原始消息)
    parent_snapshot: Vec<ChatMessage>,
    /// 独立的 ContextManager(不写回主对话)
    messages: ContextManager,
    /// 分配的人设
    persona: AgentPersona,
    /// 子任务 id(产出消息标记 subtask_id)
    subtask_id: String,
}

隔离规则:

维度 主对话 子 Agent
messages 用户消息 + 合并产出 fork 快照 + 独立执行轨迹
ContextManager 主对话的 每个 SubTask 独立实例
pending_approvals 主对话的 各 SubTask 独立(无并发写竞争)
tool_results 写入子 Agent 的 messages 合并时提取摘要写入主对话

8.3 合并产出落回主对话

SubTask 完成 → ExecutionResult { output, success }
                                       ↓
Coordinator.merge(results) → MergeResult { merged_output, conflicts }
                                       ↓
主对话 push 一条 assistant 消息(merged_output) + N 条冲突消息(conflicts[i])
  • 合并输出 = 单条 assistant 消息 — 不是把所有子 Agent 的消息都堆进主对话
  • 冲突 = 单独消息 — 每个冲突一条,带 conflict_id 供前端定位
  • 子 Agent 执行轨迹不落主对话 — 只在 ai_messages 表中保留(带 subtask_id),前端按需展开查看

九、前端类型定义

// api/types.ts 新增

/** Plan 执行状态 */
export type PlanStatus = 'planning' | 'executing' | 'merging' | 'done' | 'error'

/** SubTask 执行状态 */
export type SubTaskStatus = 'pending' | 'running' | 'done' | 'error'

/** 冲突解决状态 */
export type ConflictResolution = 'pending' | 'a' | 'b' | 'merged' | 'manual'

/** Plan 记录 */
export interface PlanRecord {
  id: string
  conversation_id: ConvId
  user_message_id: MessageId
  status: PlanStatus
  subtask_count: number
  created_at: string
  completed_at: string | null
}

/** SubTask 记录 */
export interface SubTaskRecord {
  id: string
  plan_id: string
  persona_id: string
  intent: string
  status: SubTaskStatus
  layer: number
  deps: string[]  // JSON 解析后的数组
  created_at: string
  completed_at: string | null
}

/** 冲突记录 */
export interface ConflictRecord {
  id: string
  plan_id: string
  file_path: string
  subtask_a: string
  subtask_b: string
  diff_a: string
  diff_b: string
  resolution: ConflictResolution
  resolved_by: 'reviewer' | 'user' | null
  created_at: string
  resolved_at: string | null
}

/** PlanProgress 展示用 DAG 层结构 */
export interface PlanLayer {
  items: Array<{
    id: string
    persona: string
    intent: string
    status: SubTaskStatus
  }>
}

十、状态机

10.1 Plan 状态流转

stateDiagram-v2
    [*] --> planning: Coordinator.decompose()
    planning --> executing: Plan 写入 DB + emit AiPlanCreated
    executing --> merging: 所有 SubTask done/error
    merging --> done: merge 完成 + 无冲突
    merging --> done: 所有冲突已解决
    merging --> error: merge 失败
    executing --> error: 全部 SubTask 失败
    done --> [*]
    error --> [*]

10.2 SubTask 状态流转

stateDiagram-v2
    [*] --> pending: Plan.decompose 创建
    pending --> running: dispatch 分配 + JoinSet 启动
    running --> done: agentic loop 收敛(无 tool_calls)
    running --> error: 超时 / 全部工具失败
    done --> [*]
    error --> [*]

10.3 Conflict 状态流转

stateDiagram-v2
    [*] --> pending: merge 检测到冲突
    pending --> resolved: reviewer 推荐 + 用户确认
    pending --> resolved: 用户手动选择 a/b/merged
    resolved --> [*]

十一、事件总线集成

4 个新事件均走现有双写机制(app_handle.emit + ai_event_bus.publish_event), 与 AiToolCallStarted/AiCompleted 等关键状态变更事件一致:

let ev = AiChatEvent::AiPlanCreated { ... };
let _ = app_handle.emit("ai-chat-event", ev.clone());
let _ = app_handle.state::<AppState>().ai_event_bus.publish_event(ev);
  • emit:桌面端前端监听
  • publish_event:tunnel subscriber 透传小程序

十二、不做的方向

方向 放弃理由 可能的时机
工具级并行(同轮多 tool_call JoinSet 并发写 pending_approvals + LLM 对乱序 tool_result 行为不可预测 永不LLM 已能单轮多 tool_call
单 SubTask 取消 MVP 简化,整体取消即可 P1 以后按需
双栏 diff 对比 UI 违背 AI Native 定位(让用户做 AI 该做的合并) 永不reviewer Agent 仲裁替代)
SubTask 级审批门控 审批在工具级RiskLevel已足够 P2 审批政策配置