10 KiB
核心模块设计 — 多代理协调器 (Coordinator)
所属项目: free-code .NET 10 重写
文档类型: 核心模块设计
原始代码来源:../../src/coordinator/、../../src/assistant/
原始设计意图: 将单轮 LLM 交互扩展为多代理编排,负责 worker 生成、消息路由、团队管理与协调模式切换
上级文档: 核心模块设计总览
交叉参考: 工具系统 | 后台任务管理
概述
多代理协调器是 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
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
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();
}
支撑模型
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 生成与生命周期
生成流程
- 协调器接收高层目标
- 判断是否适合并行拆分
- 为每个分支构造自包含 prompt
- 通过 worker 工厂创建
IAgentWorker - 记录 worker 到会话/任务存储
生命周期阶段
| 阶段 | 说明 |
|---|---|
| Created | worker 已创建但未启动 |
| Running | worker 正在执行 |
| Waiting | worker 暂停等待继续指令 |
| Completed | worker 已完成任务 |
| Failed | worker 执行失败 |
| Stopped | worker 被协调器主动停止 |
生命周期原则
- worker 应保留自己的上下文快照
- 继续 worker 时优先复用旧上下文,而不是重新启动新 worker
- 停止 worker 应保留最后状态,供后续诊断或继续执行
消息路由
结果回传协议
原始实现定义了 <task-notification> XML 作为 worker 结果消息格式。.NET 重写可保留相同语义,但建议使用结构化模型解析后再渲染给终端:
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 必须自包含,不能依赖隐式上下文
- 协调器输出面向用户,内部通知面向系统