302 lines
10 KiB
Markdown
302 lines
10 KiB
Markdown
# 核心模块设计 — 多代理协调器 (Coordinator)
|
||
|
||
> 所属项目: free-code .NET 10 重写
|
||
> 文档类型: 核心模块设计
|
||
> 原始代码来源: `../../src/coordinator/`、`../../src/assistant/`
|
||
> 原始设计意图: 将单轮 LLM 交互扩展为多代理编排,负责 worker 生成、消息路由、团队管理与协调模式切换
|
||
> 上级文档: [核心模块设计总览](核心模块设计.md)
|
||
> 交叉参考: [工具系统](核心模块设计-工具系统.md) | [后台任务管理](../基础设施设计/基础设施设计-后台任务管理.md)
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
多代理协调器是 free-code 中用于“把工作拆给多个 worker”的编排层。它不是简单的工具调用器,而是面向复杂任务的调度中枢:当用户问题可被拆分为研究、实现、验证等多个并行分支时,协调器负责创建 worker、下发上下文、接收回传结果,并在需要时继续同一 worker 的上下文。
|
||
|
||
原始源码中,`../../src/coordinator/coordinatorMode.ts` 承担了本模块的核心语义:定义 coordinator mode 的开关、worker 可用工具集合、系统提示词、session mode 匹配逻辑,以及 worker 结果的消息协议。`../../src/assistant/` 则提供与会话历史、会话筛选和辅助选择相关的配套能力,可视为协调器在“恢复旧会话”和“读取历史事件”场景下的支撑模块。
|
||
|
||
在 .NET 10 重写中,本模块应被抽象为独立服务层,避免与 QueryEngine、工具系统、后台任务管理直接耦合,同时保留原有行为:
|
||
|
||
- 允许 coordinator 生成 worker 并并发分发任务
|
||
- 允许 worker 通过消息队列继续执行
|
||
- 允许在会话恢复时自动匹配 coordinator/normal 模式
|
||
- 允许读取会话历史以重建协作上下文
|
||
|
||
---
|
||
|
||
## 设计职责
|
||
|
||
### 1. 多代理任务编排
|
||
|
||
协调器负责将一个高层目标拆解为多个子任务,并为每个子任务创建独立 worker。worker 适合执行以下类型工作:
|
||
|
||
- 代码库研究
|
||
- 定向实现
|
||
- 验证与测试
|
||
- 失败修复
|
||
|
||
### 2. worker 生命周期管理
|
||
|
||
协调器需掌握 worker 的创建、继续、停止与结果归档。worker 不是一次性调用;其价值在于保留已加载上下文后继续推进。
|
||
|
||
### 3. 代理间消息路由
|
||
|
||
worker 的结果以结构化任务通知返回,协调器再据此向用户总结,或把后续指令发回同一个 worker。
|
||
|
||
### 4. 团队协作管理
|
||
|
||
协调器支持 team create / delete / send-message 这类“代理团队”操作,用于将多个 worker 视为一个协作单元管理。
|
||
|
||
### 5. 会话模式同步
|
||
|
||
当用户恢复旧会话时,协调器需要根据保存的 session mode 自动切换当前环境变量,确保继续运行时处于正确的 coordinator/normal 模式。
|
||
|
||
---
|
||
|
||
## 原始实现要点
|
||
|
||
### `../../src/coordinator/coordinatorMode.ts`
|
||
|
||
该文件是本模块的语义核心,包含以下能力:
|
||
|
||
#### coordinator 模式开关
|
||
|
||
`isCoordinatorMode()` 通过 feature gate `COORDINATOR_MODE` 和环境变量 `CLAUDE_CODE_COORDINATOR_MODE` 判断当前是否进入协调模式。
|
||
|
||
#### 会话模式匹配
|
||
|
||
`matchSessionMode(sessionMode)` 会比较当前模式与已恢复会话记录的模式:
|
||
|
||
- 若一致,直接返回
|
||
- 若不一致,自动翻转环境变量
|
||
- 同时记录 analytics 事件
|
||
|
||
这说明 coordinator 是“会话状态敏感”的,而不是一个纯工具注册器。
|
||
|
||
#### worker 工具上下文
|
||
|
||
`getCoordinatorUserContext(mcpClients, scratchpadDir)` 会为 worker 注入可用工具说明:
|
||
|
||
- 简化模式下仅暴露 Bash / Read / Edit
|
||
- 常规模式下暴露 async agent 允许工具集
|
||
- 追加 MCP 服务名
|
||
- 在 scratchpad gate 开启时暴露 scratchpad 目录
|
||
|
||
#### coordinator 系统提示词
|
||
|
||
`getCoordinatorSystemPrompt()` 描述了 coordinator 的核心职责:
|
||
|
||
- 直接回答能答的问题
|
||
- 用 worker 处理适合并行化的工作
|
||
- 只能把新信息总结给用户,不能把 worker 当作对话对象
|
||
- 通过 `Agent`、`SendMessage`、`TaskStop` 管理 worker 生命周期
|
||
|
||
同时它还明确了 worker 结果的 `<task-notification>` 协议格式。
|
||
|
||
### `../../src/coordinator/workerAgent.ts`
|
||
|
||
当前快照中该文件为占位实现,但从模块命名可知,它原本承载 worker agent 的启动入口或 worker 代理适配层。在 .NET 重写里,这一职责应由 worker 启动器与任务执行器拆分承接。
|
||
|
||
### `../../src/assistant/*`
|
||
|
||
`../../src/assistant/sessionHistory.ts` 提供了会话事件分页读取:
|
||
|
||
- 先构造 OAuth 请求上下文
|
||
- 再按 `anchor_to_latest` / `before_id` 拉取分页事件
|
||
- 返回历史事件、游标与是否还有更旧内容
|
||
|
||
`sessionDiscovery.ts`、`gate.ts`、`index.ts`、`AssistantSessionChooser.tsx` 在本快照中多为占位或轻量 UI 适配,但它们体现出 assistant 模块的定位:用于发现、筛选、恢复与展示历史会话。
|
||
|
||
---
|
||
|
||
## .NET 10 接口设计
|
||
|
||
### ICoordinatorService
|
||
|
||
```csharp
|
||
public interface ICoordinatorService
|
||
{
|
||
bool IsCoordinatorMode { get; }
|
||
|
||
string? MatchSessionMode(SessionMode? sessionMode);
|
||
|
||
string BuildCoordinatorSystemPrompt(CoordinatorPromptContext context);
|
||
|
||
CoordinatorUserContext BuildWorkerContext(
|
||
IReadOnlyList<McpClientDescriptor> mcpClients,
|
||
string? scratchpadDirectory = null);
|
||
|
||
Task<WorkerHandle> SpawnWorkerAsync(
|
||
SpawnWorkerRequest request,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
Task SendMessageAsync(
|
||
string workerId,
|
||
string message,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
Task StopWorkerAsync(
|
||
string workerId,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
Task<WorkerResult?> GetWorkerResultAsync(string workerId);
|
||
|
||
Task<TeamHandle> CreateTeamAsync(CreateTeamRequest request,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
Task DeleteTeamAsync(string teamId,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
Task SendTeamMessageAsync(string teamId, string message,
|
||
CancellationToken cancellationToken = default);
|
||
}
|
||
```
|
||
|
||
### IAgentWorker
|
||
|
||
```csharp
|
||
public interface IAgentWorker
|
||
{
|
||
string WorkerId { get; }
|
||
string Description { get; }
|
||
WorkerStatus Status { get; }
|
||
|
||
Task StartAsync(CancellationToken cancellationToken);
|
||
Task ContinueAsync(string message, CancellationToken cancellationToken);
|
||
Task StopAsync(CancellationToken cancellationToken);
|
||
Task<WorkerSnapshot> CaptureSnapshotAsync();
|
||
}
|
||
```
|
||
|
||
### 支撑模型
|
||
|
||
```csharp
|
||
public enum SessionMode { Normal, Coordinator }
|
||
|
||
public sealed record CoordinatorPromptContext(
|
||
string WorkingDirectory,
|
||
string? ScratchpadDirectory,
|
||
IReadOnlyList<string> AllowedTools,
|
||
IReadOnlyList<string> McpServerNames);
|
||
|
||
public sealed record CoordinatorUserContext(
|
||
string WorkerToolsContext,
|
||
string SystemPrompt);
|
||
|
||
public sealed record SpawnWorkerRequest(
|
||
string Description,
|
||
string Prompt,
|
||
string WorkerType,
|
||
string? Model = null,
|
||
string? TeamId = null);
|
||
```
|
||
|
||
这些类型遵循 .NET 命名习惯,使用 PascalCase,并把“提示词构建”和“worker 运行实例”分离,避免协调器直接依赖底层 LLM 调用细节。
|
||
|
||
---
|
||
|
||
## Worker 生成与生命周期
|
||
|
||
### 生成流程
|
||
|
||
1. 协调器接收高层目标
|
||
2. 判断是否适合并行拆分
|
||
3. 为每个分支构造自包含 prompt
|
||
4. 通过 worker 工厂创建 `IAgentWorker`
|
||
5. 记录 worker 到会话/任务存储
|
||
|
||
### 生命周期阶段
|
||
|
||
| 阶段 | 说明 |
|
||
|------|------|
|
||
| Created | worker 已创建但未启动 |
|
||
| Running | worker 正在执行 |
|
||
| Waiting | worker 暂停等待继续指令 |
|
||
| Completed | worker 已完成任务 |
|
||
| Failed | worker 执行失败 |
|
||
| Stopped | worker 被协调器主动停止 |
|
||
|
||
### 生命周期原则
|
||
|
||
- worker 应保留自己的上下文快照
|
||
- 继续 worker 时优先复用旧上下文,而不是重新启动新 worker
|
||
- 停止 worker 应保留最后状态,供后续诊断或继续执行
|
||
|
||
---
|
||
|
||
## 消息路由
|
||
|
||
### 结果回传协议
|
||
|
||
原始实现定义了 `<task-notification>` XML 作为 worker 结果消息格式。.NET 重写可保留相同语义,但建议使用结构化模型解析后再渲染给终端:
|
||
|
||
```csharp
|
||
public sealed record WorkerNotification(
|
||
string TaskId,
|
||
string Status,
|
||
string Summary,
|
||
string? Result,
|
||
WorkerUsage? Usage);
|
||
```
|
||
|
||
### 路由规则
|
||
|
||
- worker → coordinator:上报任务完成、失败、暂停、进度
|
||
- coordinator → worker:发送续作指令、修正指令、停止指令
|
||
- coordinator → 用户:仅输出总结,不暴露内部通知原文
|
||
|
||
### 设计约束
|
||
|
||
- 不能用一个 worker 检查另一个 worker
|
||
- 不能把低价值文件内容回读任务交给 worker
|
||
- 继续 worker 时必须使用其原始 task id
|
||
|
||
---
|
||
|
||
## Agent Team 管理
|
||
|
||
team 机制用于把多个 worker 归入一个协作单元,常见场景是“研究组 + 实现组 + 验证组”。
|
||
|
||
### TeamCreate
|
||
|
||
创建 team 时应生成团队 ID,并维护成员 worker、目标、状态与消息历史。
|
||
|
||
### TeamDelete
|
||
|
||
删除 team 应清理团队元数据,但不能无条件销毁已完成 worker 的结果记录。
|
||
|
||
### SendMessage
|
||
|
||
team message 应广播到 team 成员,或按策略路由给主 worker。 .NET 设计应把广播规则显式化,避免隐式分支。
|
||
|
||
---
|
||
|
||
## 关联 `../../src/assistant/` 的 .NET 设计
|
||
|
||
`../../src/assistant/` 更适合映射为“会话访问与恢复辅助服务”,建议拆为:
|
||
|
||
- `IAssistantSessionHistoryService`:读取会话事件分页
|
||
- `IAssistantSessionDiscoveryService`:发现可恢复会话
|
||
- `IAssistantSessionChooser`:会话选择 UI 适配层
|
||
|
||
其中 `sessionHistory.ts` 的分页逻辑适合直接迁移为可测试的基础设施服务,而非放进协调器主服务中。
|
||
|
||
---
|
||
|
||
## 设计要点
|
||
|
||
- coordinator 是编排层,不是 worker 执行层
|
||
- worker 适合做局部自治,coordinator 负责全局收敛
|
||
- 会话模式必须可恢复、可切换、可记录
|
||
- worker prompt 必须自包含,不能依赖隐式上下文
|
||
- 协调器输出面向用户,内部通知面向系统
|
||
|
||
---
|
||
|
||
## 参考资料
|
||
|
||
- [核心模块设计总览](核心模块设计.md)
|
||
- [工具系统](核心模块设计-工具系统.md)
|
||
- [后台任务管理](../基础设施设计/基础设施设计-后台任务管理.md)
|
||
- [总体概述与技术选型](../总体概述与技术选型/总体概述与技术选型.md)
|