Claude Code - Multi Agent
Claude Code - Multi Agent
Multi Agent
Sub-Agent
隔离
tool隔离
- 所有subagent通用黑名单
- 能派新sub agent的工具
- 能主动问用户问题的工具
- 能切换规划模式的工具
- 能停止其他任务的工具
- 自定义agent多套一层黑名单
- 后台异步agent走白名单
1 2 3 4 5 6 7 8 9 10
// src/tools/AgentTool/agentToolUtils.ts:70 export function filterToolsForAgent({ tools, isBuiltIn, isAsync, permissionMode }): Tools { return tools.filter(tool => { if (tool.name.startsWith('mcp__')) return true// MCP 工具全放行 if (ALL_AGENT_DISALLOWED_TOOLS.has(tool.name)) return false // 黑名单 if (!isBuiltIn && CUSTOM_AGENT_DISALLOWED_TOOLS.has(tool.name)) return false // custom agent 黑名单 if (isAsync && !ASYNC_AGENT_ALLOWED_TOOLS.has(tool.name)) return false return true }) }
context隔离
读文件的缓存要复制一份给sub agent改全局状态这件事对sub agent 直接关闭注册后台任务sub agent保留这个接口- 给每个sub agent发独立 ID、深度代代+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/utils/forkedAgent.ts:345 export function createSubagentContext(parentContext, overrides): ToolUseContext { return { // 决策一:文件读缓存克隆一份 readFileState: cloneFileStateCache(parentContext.readFileState), // 决策二:写全局状态直接设为空操作 setAppState: () => {}, // 决策三:但任务注册的通路例外保留 setAppStateForTasks: parentContext.setAppStateForTasks ?? parentContext.setAppState, // 决策四:独立 ID + 深度 +1 agentId: overrides?.agentId ?? createAgentId(), queryTracking: { chainId: randomUUID(), depth: (parentContext.queryTracking?.depth ?? -1) + 1, }, // ...其他字段略 } }
父子agent通信
父 -> 子
- 消息队列 + 异步通知
- sub agent 档案
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/tasks/LocalAgentTask/LocalAgentTask.tsx:116 export type LocalAgentTaskState = TaskStateBase & { type: 'local_agent'; agentId: string; // 子 agent 唯一 ID prompt: string; // 初始任务 agentType: string; status: TaskStatus; // pending/running/completed/failed/killed result?: AgentToolResult; // 完成后的结果 progress?: AgentProgress; // 进度 isBackgrounded: boolean; // 是否已转后台 pendingMessages: string[]; // 信箱:父 agent 扔进来的待处理消息 messages?: Message[]; };
- 消息投递
1 2 3 4 5 6 7 8 9 10
// src/tools/SendMessageTool/SendMessageTool.ts:800 const task = appState.tasks[agentId] if (isLocalAgentTask(task) && !isMainSessionTask(task)) { if (task.status === 'running') { queuePendingMessage(agentId, input.message, context.setAppStateForTasks) return { data: { success: true, message: 'Message queued...' } } } // 任务已停止,自动唤醒从 transcript 里恢复 const result = await resumeAgentBackground({ agentId, prompt: input.message, ... }) }
1
2
3
4
5
6
7
// src/tasks/LocalAgentTask/LocalAgentTask.tsx:162
export function queuePendingMessage(taskId, msg, setAppState): void {
updateTaskState<LocalAgentTaskState>(taskId, setAppState, task => ({
...task,
pendingMessages: [...task.pendingMessages, msg]
}));
}
子 -> 父
- 将完成通知拼成XML, 伪装成用户消息, 放进父agent的对话中 ```xml
1
2
3
4
5
6
7
8
9
```ts
// src/tasks/LocalAgentTask/LocalAgentTask.tsx:197
const message = `<${TASK_NOTIFICATION_TAG}>
<${TASK_ID_TAG}>${taskId}</${TASK_ID_TAG}>
<${OUTPUT_FILE_TAG}>${outputPath}</${OUTPUT_FILE_TAG}>
<${STATUS_TAG}>${status}</${STATUS_TAG}>
<${SUMMARY_TAG}>${summary}</${SUMMARY_TAG}>${resultSection}${usageSection}
</${TASK_NOTIFICATION_TAG}>`;
enqueuePendingNotification({ value: message, mode: 'task-notification' });
auto-background
- 父agent同步等待sub agent完成(一个阈值时间内), 超过阈值, sub agent转后台执行
1 2 3 4 5 6 7 8
// src/tools/AgentTool/AgentTool.tsx:72 function getAutoBackgroundMs(): number { if (isEnvTruthy(process.env.CLAUDE_AUTO_BACKGROUND_TASKS) || getFeatureValue_CACHED_MAY_BE_STALE('tengu_auto_background_agents', false)) { return 120_000; // 2 分钟 } return 0; }
Fork Agent
- 利用到prompt cache, fork sub agent和父agent的system prompt一致
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/utils/forkedAgent.ts:57 export type CacheSafeParams = { /** System prompt - 必须跟父完全一致 */ systemPrompt: SystemPrompt /** User context - 拼接在消息前,影响缓存 */ userContext: { [k: string]: string } /** System context - 拼接在 system prompt 后,影响缓存 */ systemContext: { [k: string]: string } /** 工具池、模型等所在的上下文 */ toolUseContext: ToolUseContext /** 父 agent 的消息前缀,用于缓存共享 */ forkContextMessages: Message[] }
- fork agent的system prompt和父agent一致, 不用自己生成
1 2 3 4 5 6 7 8 9 10
// src/tools/AgentTool/forkSubagent.ts:60 export const FORK_AGENT = { agentType: FORK_SUBAGENT_TYPE, tools: ['*'], // 用父的完整工具池 maxTurns: 200, model: 'inherit', // 继承父的模型 permissionMode: 'bubble', // 权限弹窗浮到父终端 source: 'built-in', getSystemPrompt: () => '', // 返回空串! } satisfies BuiltInAgentDefinition
- fork 和 coordinate互斥
1
2
3
4
5
6
7
8
9
// src/tools/AgentTool/forkSubagent.ts:32
export function isForkSubagentEnabled(): boolean {
if (feature('FORK_SUBAGENT')) {
if (isCoordinatorMode()) returnfalse// 互斥!
if (getIsNonInteractiveSession()) return false
return true
}
return false
}
Coordinator-Worker
- coordinate模式要显式打开
1
2
3
4
5
6
7
// src/coordinator/coordinatorMode.ts:36
export function isCoordinatorMode(): boolean {
if (feature('COORDINATOR_MODE')) { // 编译时的功能开关
return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE) // 环境变量 CLAUDE_CODE_COORDINATOR_MODE=1
}
return false
}
- 主agent只做
派 worker,收结果,合成答案; system prompt支持
1
2
3
4
5
6
7
8
9
10
11
12
You are Claude Code, an AI assistant that orchestrates software engineering
tasks across multiple workers.
## 1. Your Role
You are a **coordinator**. Your job is to:
- Help the user achieve their goal
- Direct workers to research, implement and verify code changes
- Synthesize results and communicate with the user
- Answer questions directly when possible, don't delegate work
that you can handle without tools
你的身份是协调者,你的工作是指挥 worker 去做研究、实现、验证,然后自己合成结果跟用户交流。能自己回答的问题不要派人去做。
- coordinator的builtin工具
1 2 3 4 5 6 7
// src/coordinator/coordinatorMode.ts:29 const INTERNAL_WORKER_TOOLS = new Set([ TEAM_CREATE_TOOL_NAME, // 创建 worker 团队 TEAM_DELETE_TOOL_NAME, // 解散团队 SEND_MESSAGE_TOOL_NAME, // 给 worker 发消息 SYNTHETIC_OUTPUT_TOOL_NAME, // 合成最终输出给用户 ])
- 支持并行创建worker, 部分system prompt ```md Parallelism is your superpower. Workers are async. Launch independent workers concurrently whenever possible, don’t serialize work that can run simultaneously and look for opportunities to fan out.
并行是你的超能力,worker 全是异步的,能并行的绝不串行,多找机会一口气派一堆出去。 ```
- 持续派活时, 是resume old agent 还是 spawn new agent
- 新任务跟 worker 现有上下文高度相关(比如刚查的文件现在要改),续命老 worker
- 新任务跟 worker 现有上下文没关系,或者之前 worker 的工作走偏了,派新 worker
This post is licensed under CC BY 4.0 by the author.
