子代理与 swarm
agent/coder/explore/plan 四个内置 profile、Agent/AgentSwarm 两个工具、swarm 模式的三种触发与提醒注入,以及防止无限委派的白名单。
四个内置 profile
v0.29 时代 profile 是 profile/default/ 下的 YAML;现在是 TS 代码里的 registerAgentProfile()。agent、coder、explore 在 packages/agent-core-v2/src/session/agentLifecycle/profile/profiles.ts,plan 由 plan Feature 在 features/plan/profile/plan.ts 注册。主代理的注册(profiles.ts:90):
registerAgentProfile({
name: 'agent',
description: 'Default agent',
tools: AGENT_TOOLS,
subagents: ['coder', 'explore', 'plan'],
renderSystemPrompt: (context) =>
renderSystemPromptResult('', context, { skillActive: skillActiveFor(AGENT_TOOLS) }),
});| profile | 用途 | 工具面 |
|---|---|---|
agent | 主代理 | 全套,含 Agent、AgentSwarm 与 mcp__* |
coder | 写代码 | 唯一带 Write/Edit 的子代理 |
explore | 探索代码库 | 靠提示词约束只读,可用 Bash |
plan | 规划 | 只读,没有 shell,也不能改文件 |
一个 profile 决定 system prompt(共用 app/agentProfileCatalog/system.md 模板,子代理再加一段「父代理只看得到你最后一条消息」的角色前缀)、工具白名单与可委派的子代理。用户还能在 ~/.kimi-code/agents/、.kimi-code/agents/ 及对应的 .agents/agents/ 下放自定义 agent 文件,插件也能带 agent。
README 里对应的描述:
Subagents for focused, parallel work. Dispatch built-in
coder,explore, andplansubagents in isolated contexts while keeping the main conversation clean.
关键词是 isolated contexts——explore 可能要读 50 个文件才找到答案,这 50 次工具往返不该进主上下文。
两个协作工具
| 工具 | 位置 | 作用 |
|---|---|---|
Agent | agent/tools/agent/ | 派发单个子代理,可 resume 旧实例、run_in_background 转后台 |
AgentSwarm | features/swarm/tools/agent-swarm/ | 用 prompt_template 加 items 一次派发一群,上限 128 个 |
Agent 的工具描述是现拼的:基础说明之后,按 TaskList、TaskOutput、TaskStop 是否可用,接上 agent-background-enabled.md 或 agent-background-disabled.md——后台能力开关会换掉工具描述,不能用的能力就不出现,否则模型会尝试并失败。实验开关 subagent_fork 打开时再追加 fork 参数的说明,最后列出当前可委派的 agent 类型与可选模型。
AgentSwarm 还有一条硬约束:它必须是一次回复里唯一的工具调用。v1 用 agent-swarm-exclusive-deny.ts 权限策略实现,现在是 features/swarm/agent/swarmService.ts:47 在工具执行前否决:
toolExecutor.onBeforeExecuteTool((event) => {
const agentSwarmCount = event.toolCalls.filter(
(toolCall) => toolCall.name === 'AgentSwarm',
).length;
if (agentSwarmCount === 0 || (agentSwarmCount === 1 && event.toolCalls.length === 1)) {
return;
}Swarm 模式
features/swarm/agent/swarm.ts:3:
export type SwarmModeTrigger = 'manual' | 'task' | 'tool';三种触发来源:用户持久开启(/swarm on)、一次性任务(/swarm <task>)、模型主动调用 AgentSwarm。后两种在回合结束时自动退出。手动审批模式下开 swarm,TUI 会先询问是否切到 auto 或 yolo。
只有非 tool 触发才注入提醒。 agent/injection/swarmInjection.ts:43:
const trigger = this.options.getTrigger();
const active = trigger !== null && trigger !== 'tool';
const rendered = this.renderedState(ctx);模型自己调工具进的 swarm 模式,它已经知道自己在干什么,再注入「你现在处于 swarm 模式」是浪费 token;只有用户从外部切换时才需要告知。模式结束时再对应注入一段「Swarm Mode Ended」。
状态走事件溯源。 v1 的 SwarmMode 类要手写 logRecord,并另备一个不写记录的 restoreEnter() 供回放;现在进出都是持久事件 swarm_mode.enter/swarm_mode.exit,状态 swarmKey 由事件归约而来并声明为 replayable,回放时自然恢复,不再需要特判。enter 依旧幂等。
批次与限流
一群子代理由 features/swarm/session/agentRunBatch.ts(648 行)调度:前 5 个立即启动,之后每 700ms 再放一个;遇到 provider 限流就把子代理挂起重排、指数退避并收缩并发,也可用 KIMI_CODE_AGENT_SWARM_MAX_CONCURRENCY 设硬上限。子代理与 swarm 默认都有 2 小时超时(KIMI_SUBAGENT_TIMEOUT_MS、KIMI_CODE_SWARM_TIMEOUT_MS)。每次派发都注册为一个 SubagentTask,转到后台时就是一个后台任务。
权限继承与防嵌套
v1 靠 PermissionManager.parent 链;现在 session/subagent/subagentService.ts:184 在创建子代理时直接复制父代理的权限模式:
created.accessor
.get(IAgentPermissionModeService)
.setMode(caller.accessor.get(IAgentPermissionModeService).mode);子代理不会比父代理更宽松——否则「派个子代理去做」就成了提权后门。宿主运行时注册的自定义工具(user tool)也一并继承。
防止「子代理再派子代理」靠白名单。app/agentProfileCatalog/profile-shared.ts:75:
export function profileCanDelegate(
profile: Pick<AgentProfile, 'tools' | 'disallowedTools'>,
): boolean {
const possesses = (name: string) =>
(profile.tools === undefined || profile.tools.includes(name)) &&
!(profile.disallowedTools ?? []).includes(name);
return possesses('Agent') || possesses('AgentSwarm');
}调用方没显式声明 subagents 时,withoutDelegatingTargets() 会把自身还能委派的 profile 从候选里剔除;内置的 coder、explore、plan 本就没有 Agent、AgentSwarm 工具,委派链天然只有一层。
记录与状态
swarm 模式进出会发出 AgentStatusUpdated(带 swarmMode 字段),TUI 据此显示模式指示;transcript 包的 schema 里也有 swarm: { trigger } 与 goal 元数据字段,保证 Web UI 与 TUI 看到一致的模式状态。
下一篇:Goal 与 Plan。