Date: 2025-11-21
本设计旨在为 neovate-code 项目实现完整的 subagent(子代理)技术方案,参考 Claude Code 的 Explore Subagent 和 Task Tool 实现。目标是让主 Agent 能够将复杂的多步骤任务委托给专门的子 Agent 处理,每个子 Agent 具有特定的能力和工具访问权限。
核心需求:
- 完整复刻 Claude Code 的 subagent 能力
- 优先实现 Explore 和 Plan 两个核心子代理
- 建立完整的 Agent 系统架构(而非简单集成)
关键决策:实现范围
- 选择方案 A(完整复刻)而非轻量实现或定制化方案
- 优先实现 Explore + Plan 子代理,跳过 General-Purpose 和 Statusline-Setup
技术基础评估
- 现有工具系统部分完成:已有统一的 Tool 接口和基础工具实现
- 缺少:工具权限过滤机制、子代理调用机制、上下文 Fork 机制
评估了三个方案后选择 方案 B(标准 Agent 系统方案):
方案对比:
- 方案 A(轻量集成):改动小但扩展性弱
- 方案 B(标准 Agent 系统):架构清晰,完全对标 Claude Code ✓
- 方案 C(渐进式混合):风险可控但总周期长
选择理由:
- 一次到位,避免反复重构
- 架构优雅,符合"完整复刻"目标
- 可维护性好,便于后续扩展
- 长期收益最高
关键设计点:
-
工具名称规范
- Task 工具名称:
task(小写) - 编辑工具:
edit,write(小写)
- Task 工具名称:
-
模型配置策略
- 采用工厂函数模式传入 context
- Explore Agent:
context.config.smallModel || context.config.model - Plan Agent:
context.config.planModel
-
上下文 Fork 优化
- 参考
normalizeMessagesForCompact处理工具消息 - 智能过滤不支持的工具调用,转换为摘要
- 避免子 Agent 看到不可用工具的消息
- 参考
采用标准的 Agent 系统架构,建立清晰的分层设计:
src/
agent/
index.ts # AgentManager - 注册和调用入口
types.ts # 类型定义
executor.ts # AgentExecutor - 执行引擎
toolFilter.ts # ToolFilter - 工具权限过滤
contextFork.ts # ContextFork - 上下文继承
builtin/
index.ts # 内置 Agent 导出
explore.ts # Explore Agent 工厂函数
plan.ts # Plan Agent 工厂函数
common.ts # 公共配置
tools/
task.ts # Task 工具实现
用户输入
↓
runLoop (主 Agent)
↓
调用 Task 工具
↓
AgentManager.executeTask()
↓
├─ 查找 Agent 定义
├─ 过滤工具权限 (ToolFilter)
├─ Fork 上下文 (ContextFork, 可选)
└─ 执行子 Agent (AgentExecutor)
↓
runLoop (isSubAgent=true)
↓
返回结果
-
工具权限过滤
- 支持通配符
["*"]表示所有工具 disallowedTools优先级高于tools- 静态方法设计,无状态,易测试
- 支持通配符
-
上下文 Fork 机制
- 过滤孤立的 tool_use(没有对应 tool_result)
- 将不支持的工具调用转换为摘要
- 添加明确的上下文分隔标记
-
工厂函数模式
- Agent 定义使用工厂函数:
createExploreAgent(opts) - 通过
opts: { context: Context }传入上下文 - 在函数内部访问
context.config.smallModel等配置
- Agent 定义使用工厂函数:
export interface AgentDefinition {
agentType: string;
whenToUse: string;
systemPrompt: string;
model: string;
source: 'built-in' | 'plugin' | 'user';
tools?: string[];
disallowedTools?: string[];
forkContext?: boolean;
color?: string;
}
export interface TaskToolInput {
description: string;
prompt: string;
subagent_type: string;
model?: string;
resume?: string;
}
export interface AgentExecutionResult {
status: 'completed' | 'failed';
agentId: string;
content: string;
totalToolCalls: number;
totalDuration: number;
usage: {
inputTokens: number;
outputTokens: number;
};
}核心逻辑:
- 构建禁用工具集合
- 处理通配符(未定义 tools 或
["*"]) - 显式工具列表处理
- disallowedTools 优先过滤
关键方法:
static filterTools(allTools: Tool[], agentDef: AgentDefinition): Tool[]核心功能:
prepareForkMessages(): 准备 fork 的上下文消息normalizeMessagesForSubAgent(): 规范化父级消息- 过滤 assistant 消息中不支持的 tool_use
- 转换 tool 消息中不支持的工具结果为摘要
- 混合处理:支持的保留,不支持的摘要
buildContextSeparatorMessage(): 构建上下文分隔标记
消息处理策略:
- Assistant 消息:保留 text/reasoning,过滤不支持的 tool_use
- Tool 消息:支持的保留原样,不支持的转为 user 消息摘要
- 添加明确的工具列表说明
执行流程:
- 验证 Agent 定义
- 过滤工具(ToolFilter)
- 准备消息(ContextFork 或简单消息)
- 解析模型(优先级:调用时指定 > Agent 定义)
- 执行 runLoop(标记 isSubAgent=true)
- 处理结果和错误
错误处理:
- 工具过滤后为空 → 抛出友好错误
- 模型未指定 → 验证并抛出错误
- 所有异常 → 捕获并返回失败结果(不抛出)
职责:
- 注册和管理所有 Agent
- 查找 Agent 定义
- 执行 Task 工具调用
- 提供 Agent 描述(用于系统提示)
关键方法:
constructor(opts: { context: Context })
registerAgent(definition: AgentDefinition)
getAgent(agentType: string): AgentDefinition | undefined
executeTask(input: TaskToolInput, context: {...}): Promise<AgentExecutionResult>Explore Agent (src/agent/builtin/explore.ts):
export function createExploreAgent(opts: { context: Context }): AgentDefinition {
return {
agentType: 'Explore',
model: context.config.smallModel || context.config.model,
disallowedTools: ['task', 'edit', 'write'],
forkContext: false,
color: 'blue',
// ...
};
}特点:
- 快速模型(smallModel)
- 只读工具(禁用 edit/write)
- 禁用 task 工具(防止递归)
- 支持彻底性级别(quick/medium/very thorough)
Plan Agent (src/agent/builtin/plan.ts):
export function createPlanAgent(opts: { context: Context }): AgentDefinition {
return {
agentType: 'Plan',
model: context.config.planModel,
disallowedTools: ['task', 'edit', 'write'],
forkContext: false,
color: 'purple',
// ...
};
}特点:
- 强大模型(planModel)
- 深度分析和规划能力
- 相同的工具限制(只读 + 禁止递归)
Schema 定义(严格对标 Claude Code):
{
name: 'task',
description: 'Launch a new agent to handle complex, multi-step tasks autonomously',
parameters: {
description: 'A short (3-5 word) description of the task',
prompt: 'The task for the agent to perform',
subagent_type: 'The type of specialized agent to use for this task',
model: 'Optional model to use...',
resume: 'Optional agent ID to resume from...',
}
}执行逻辑:
- 获取当前对话历史(forkContext)
- 调用
agentManager.executeTask() - 格式化返回结果(包含 Agent ID、工具调用次数、耗时等)
批准机制:
- category: 'command'
- yolo 模式:无需批准
- 其他模式:需要用户批准
扩展 src/tool.ts:
- 添加
agentManager和getCurrentMessages可选参数 - 条件性创建 Task 工具
扩展 src/context.ts:
- 添加
agentManager: AgentManager属性 - 在
create()方法中初始化 AgentManager
扩展 src/loop.ts:
- 添加
isSubAgent?: boolean参数 - 用于日志区分主/子 Agent(可选)
核心测试用例:
-
ToolFilter 测试
- 验证 disallowedTools 过滤功能
-
ContextFork 测试
- 验证上下文分隔符和任务消息添加
-
集成测试
- 验证内置 Agent 正确注册
- 验证 Agent 类型和配置
测试目标:确保主流程正常工作,无需覆盖所有边界情况。
用户可以在配置文件中设置 subagent 使用的模型:
{
"model": "claude-3-5-sonnet-20241022",
"planModel": "claude-3-5-sonnet-20241022",
"smallModel": "claude-3-5-haiku-20241022"
}✅ 应该使用 Task 工具:
- 需要多轮搜索和探索的开放式任务
- 不确定第一次尝试就能找到正确结果
- 需要专门的快速搜索能力(Explore)
- 需要深度架构分析(Plan)
❌ 不应该使用 Task 工具:
- 读取已知的特定文件 → 直接使用 read 工具
- 搜索特定的类/函数名 → 直接使用 grep 工具
- 在 2-3 个已知文件中搜索 → 直接使用 grep 工具
未来可以支持:
- 自定义 Agent 注册(通过
agentManager.registerAgent()) - Agent 优先级系统(类似 Claude Code 的 source 优先级)
- 插件系统集成
- 异步执行(background execution)
explore-subagent-analysis.md: Claude Code 的 Explore Subagent 技术实现详解Task-Tool-Analysis.md: Claude Code Task Tool 技术实现分析src/utils/messageNormalization.ts: 消息规范化参考实现src/slash-commands/builtin/init.ts: 工厂函数模式参考
- 实现核心类型定义 (
src/agent/types.ts) - 实现工具过滤系统 (
src/agent/toolFilter.ts) - 实现上下文 Fork 机制 (
src/agent/contextFork.ts) - 实现 Agent 执行引擎 (
src/agent/executor.ts) - 实现 Agent 管理器 (
src/agent/index.ts) - 实现内置 Agent (
src/agent/builtin/) - 实现 Task 工具 (
src/tools/task.ts) - 集成到现有系统 (
tool.ts,context.ts,loop.ts) - 编写核心测试用例
- 更新文档 (
AGENTS.md, 用户指南)