Skip to main content

Kimi CLI 子 Agent 调度拆解

前置01 MAST 失效模式。看得懂 Python。

本篇回答:「派一个子 Agent 去干活」这句话落到代码上,具体是什么 —— 子 Agent 是一个进程、一个线程,还是一个文件?主 Agent 怎么把活交出去,又怎么把结果收回来?

上一篇说答案要去源码里读,麻烦是大部分产品根本没源码可读:

产品能读到什么
Manus · Devin只有泄露的提示词 —— 能看出有哪些零件,看不出零件怎么装
Claude Code核心闭源,只有官方文档和 SDK
Kimi CLI五万行 Python 全部 Apache-2.0,子 Agent 调度每一行可核对

所以这是全专题唯一一次直接读代码,后面几篇都拿它当基线。

一、把代码拉下来

git clone --depth 1 https://github.com/MoonshotAI/kimi-cli.git
仓库Star协议版本体量Python快照
MoonshotAI/kimi-cli11,218Apache-2.01.49.052,049 行≥3.122026-08-19

同族仓库:kimi-code(6,903★,TypeScript,MIT)、kimi-agent-sdk(571★,三语言)。本篇拆 Python 版,subagents/ 实现最完整。

1.1 能力面

src/kimi_cli/ 下 40 个子模块,按职责归类:

模块提供什么
Agent 内核soul/主循环、上下文、自动压缩、审批注入
子 Agent 编排subagents/工种注册、实例构建、前后台 runner、磁盘持久化
工具集tools/19 个内置工具(下表)
扩展mcp_oauth.pyplugin/skill/ skills/MCP + OAuth、插件管理器、Skill 机制(内置 skill-creator
协议acp/Agent Client Protocol 0.8.0,可被 Zed 等编辑器直接驱动
运行时background/hooks/approval_runtime/后台任务、生命周期钩子、审批
会话session.pysession_fork.pyshare.py会话状态、fork、分享
接口cli/ui/web/wire/vis/终端 TUI、内置 FastAPI + WebSocket 服务、事件线、可视化
可观测telemetry/遥测

19 个内置工具(agents/default/agent.yaml 声明):

工具
文件ReadFile ReadMediaFile Glob Grep WriteFile StrReplaceFile
执行Shell
后台TaskList TaskOutput TaskStop
网络SearchWeb FetchURL
规划SetTodoList EnterPlanMode ExitPlanMode
交互AskUserQuestion
委派Agent ← 本篇主角
未启用Think SendDMail(YAML 中被注释掉)

依赖侧能看出几个取向:ripgrepy(搜索走 ripgrep 而非 Python 遍历)、trafilatura+lxml(网页正文提取而非全 HTML 入上下文)、kosong(Moonshot 自研的模型抽象层)、keyring(凭据不落明文)。

行号会随版本漂移,按文件名 + 函数名定位

二、委派要解决的问题

单 Agent 执行「搞清楚鉴权模块并修复登录超时 bug」,动手改代码前的上下文构成:

token性质
系统提示词 + 用户请求2K常驻
grep 输出8K一次性
12 个文件全文145K有效信息约 30 行
git log15K一次性
待修改代码0.5K目标
剩余可用<30K——
后果机制
成本上下文每轮重发;后续 20 轮交互 → 145K 计费 20 次
质量衰减lost-in-the-middle:中段注意力显著下降,早期信息在后期等同不存在(对应 MAST FM-2.3 任务跑偏 7.4%
硬中断满窗后只能截断或有损压缩(对应 FM-1.4 丢失对话历史 2.8%
A · 单 Agent 自己探索B · 委派给 explore 子 Agent主 Agent 上下文窗口 200K主 Agent 上下文窗口 200K系统提示词 + 用户请求 2Kgrep 输出 8K12 个文件全文 145K有效信息约 30 行git log 15K待改代码 0.5K剩余可用 <30K系统提示词 + 用户请求 2K子 Agent 回传结论 3K剩余可用 ~195K委派145K 探索上下文随子实例销毁探索垃圾(可回收)有效载荷剩余空间
委派的本质是上下文管理:探索过程产生的 145K 中间态留在子实例里,随其销毁;主 Agent 只吸收 3K 结论。代价是子 Agent 看不到主对话历史,因此 prompt 参数必须自包含。

委派的本质是上下文管理技术,不是「多智能体协作涌现」:探索过程产生的中间态不进入执行上下文。

边界:子任务若需写同一份产物,隔离会导致隐式决策冲突,此时委派有害——判据见 08

三、源码里的四个核心对象

subagents/ 之前先对上名字,否则会被 SoulLaborMarket 这些命名绕晕。

源码符号是什么关键属性
Runtime一次运行的全局环境模型、工作目录、配置、会话、roleroot / 子 Agent)
Agent配置,不是运行实体系统提示词、工具白名单、模型、可雇工种
SoulAgent 配置上岗的运行实体持有 Context,跑主循环
Context一个 Soul 的完整对话历史落盘为 context.jsonl
LaborMarket工种注册表(源码原文命名)dict[str, AgentTypeDefinition]

关系:Runtime 派生 Agent 配置 → 实例化成 Soul → 每个 Soul 一份独立 Context子 Agent 复用与主 Agent 完全相同的 Soul 循环,仅配置不同。

隔离契约写在每个子工种的提示词首段:

All the user messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task.

即:子 Agent 看不到主对话历史,父 Agent 只能看到子 Agent 的最后一条消息。 这句话必须明确告诉模型——它决定了模型会不会认真写总结。

四、主循环与阈值

src/kimi_cli/soul/kimisoul.py:996-1020
step_no = 0
while True:
step_no += 1
# ── 2a. Step Guard ──
if step_no > self._loop_control.max_steps_per_turn:
raise MaxStepsReached(self._loop_control.max_steps_per_turn)
self._current_step_no = step_no
wire_send(StepBegin(n=step_no))
...
# ── 2c. Context Compaction ──
if should_auto_compact(
self._context.token_count_with_pending,
self._runtime.llm.max_context_size,
trigger_ratio=self._loop_control.compaction_trigger_ratio,
reserved_context_size=self._loop_control.reserved_context_size,
src/kimi_cli/config.py:75-95
class LoopControl(BaseModel):
max_steps_per_turn: int = Field(default=1000, ge=1, ...)
max_retries_per_step: int = Field(default=3, ge=1)
max_ralph_iterations: int = Field(default=0, ge=-1)
reserved_context_size: int = Field(default=50_000, ge=1000)
compaction_trigger_ratio: float = Field(default=0.85, ge=0.5, le=0.99)
参数默认作用横向对照
max_steps_per_turn1000单轮工具调用上限Manus 官方均值 ~50;Mini-Agent 默认 50;DeerFlow 子 Agent 150/60
max_retries_per_step3单步重试——
compaction_trigger_ratio0.85压缩触发水位不等满窗,为压缩动作自身留操作空间
reserved_context_size50,000输出预留与上一条取或触发

步数上限只挡「无限跑」,挡不住「原地打转」(同一动作序列重复)——DeerFlow 用 loop_detection_middleware 补这一层;单步卡死则由第七节的墙钟超时兜底。

五、子 Agent 编排

5.1 工种注册表

src/kimi_cli/subagents/registry.py:8-28
class LaborMarket:
"""Registry of built-in subagent types."""
def __init__(self) -> None:
self._builtin_types: dict[str, AgentTypeDefinition] = {}
def add_builtin_type(self, type_def: AgentTypeDefinition) -> None:
self._builtin_types[type_def.name] = type_def
def require_builtin_type(self, name: str) -> AgentTypeDefinition:
type_def = self.get_builtin_type(name)
if type_def is None:
raise KeyError(f"Builtin subagent type not found: {name}")
return type_def
src/kimi_cli/subagents/models.py:25-33
@dataclass(frozen=True, slots=True, kw_only=True)
class AgentTypeDefinition:
name: str
description: str
agent_file: Path
when_to_use: str = "" # 拼进工具描述,供模型选型
default_model: str | None = None # 工种级模型绑定 → 成本杠杆
tool_policy: ToolPolicy = field(default_factory=lambda: ToolPolicy(mode="inherit"))
supports_background: bool = True

注册的是工种而非实例(LaborMarket 为源码原始命名)。default_model 允许 explore 绑小模型——探索类调用频次通常是编码类的数倍,是最有效的单点成本优化。

5.2 三工种权限矩阵

src/kimi_cli/agents/default/agent.yaml:27-36
  subagents:
coder: { path: ./coder.yaml, description: "Good at general software engineering tasks." }
explore: { path: ./explore.yaml, description: "Fast codebase exploration with prompt-enforced read-only behavior." }
plan: { path: ./plan.yaml, description: "Read-only implementation planning and architecture design." }
工种权限矩阵 · 继承 agent.yaml 后逐级做减法三个子工种均 extend agent.yaml,只删不增;新增工具时自动继承,不会漏配ShellReadGlob/GrepWriteWebAgentAskUserTodo主 Agentagent.yamlcodercoder.yamlexploreexplore.yaml只读planplan.yaml可用受限可用已剥离(exclude_tools)
只读工种走双保险:提示词声明「无编辑工具」+ exclude_tools 物理剥离。仅靠提示词约束不住模型;仅靠剥离会让模型转用 Shellsed -i 绕过。plan 连 Shell 都不给,是三者中最保守的一档。

三个子工种均 extend: ./agent.yaml 后做减法(新增工具时主 Agent 自动获得、子工种自动继承,不会漏配):

工种ShellReadGlob/GrepWriteWebAgentAskUserTodo用途
主 Agent全权
coder改代码
explore✅ 只读摸代码库
plan出方案
src/kimi_cli/agents/default/explore.yaml(节选)
agent:
extend: ./agent.yaml
system_prompt_args:
ROLE_ADDITIONAL: |
You are now running as a subagent. All the `user` messages are sent by the
main agent. The main agent cannot see your context, it can only see your
last message when you finish the task. ...
You are a codebase exploration specialist. Your role is EXCLUSIVELY to
search, read, and analyze existing code. You do NOT have access to file
editing tools.
allowed_tools: [Shell, ReadFile, ReadMediaFile, Glob, Grep, SearchWeb, FetchURL]
exclude_tools: [Agent, AskUserQuestion, SetTodoList, ExitPlanMode, EnterPlanMode,
WriteFile, StrReplaceFile]
subagents: # 空
机制实现单独使用会怎样
只读双保险提示词声明「无编辑工具」+ exclude_tools 物理剥离仅提示词:约束不住;仅剥离:模型转用 Shellsed -i 绕过
身份声明三个工种 ROLE_ADDITIONAL 首段完全一致不声明则模型不知「只有末条消息可见」,交差敷衍
递归堵死subagents: 为空 + exclude_toolsAgent配置层第一道,另有工具层与运行时层

5.3 工具描述:数字判据

src/kimi_cli/tools/agent/description.md:22-41(节选)
prefer `subagent_type="explore"` over doing the search yourself. Use it when:
- Your task will clearly require more than 3 search queries
- You need to understand how a module, feature, or code path works
- You want to investigate multiple independent questions — launch multiple
explore agents concurrently

thoroughness: "quick" targeted lookups / "medium" understand a module /
"thorough" cross-cutting analysis

**When Not To Use Agent**
- Reading a known file path
- Searching a small number of known files
- Tasks that can be completed in one or two direct tool calls

「>3 次搜索」是模型可执行的阈值;「适当时候使用」不可执行。正反清单都给——只给正面时模型会把一切外包(每次都是完整 LLM 会话)。

工种能力表由代码动态拼入描述:

src/kimi_cli/tools/agent/__init__.py:92-95
lines.append(
f"- `{name}`: {type_def.description} "
f"(Tools: {tool_names}, Model: {model}, Background: {background}).{suffix}"
)

5.4 工具参数与三重递归禁令

src/kimi_cli/tools/agent/__init__.py:17-57
MAX_FOREGROUND_TIMEOUT = 60 * 60  # 1 hour
MAX_BACKGROUND_TIMEOUT = 60 * 60 # 1 hour

class Params(BaseModel):
description: str # 3-5 词,UI 用
prompt: str # 子 Agent 唯一输入
subagent_type: str = "coder"
model: str | None = None # 覆盖工种默认
resume: str | None = None # 复活已有实例
run_in_background: bool = False # 默认前台
timeout: int | None = Field(default=None, ge=30, le=MAX_BACKGROUND_TIMEOUT)

prompt 是子 Agent 的唯一输入,主 Agent 对话历史零传递——任务描述的自包含程度直接决定产出质量(Anthropic 将此列为多 Agent 系统首要经验)。run_in_background 默认 False,描述中写明「除非任务可独立进行且提前交还控制权有明确收益,否则用 false」:后台引入并发写冲突风险。

src/kimi_cli/tools/agent/__init__.py:119-130
@override
async def __call__(self, params: Params) -> ToolReturnValue:
if self._runtime.role != "root":
return ToolError(
message="Subagents cannot launch other subagents.",
brief="Agent unavailable",
)
if params.model is not None and params.model not in self._runtime.config.models:
return ToolError(message=f"Unknown model alias: {params.model}", ...)
位置手段失效表现
配置子工种 YAMLsubagents: 为空无可雇工种
工具子工种 YAMLexclude_toolsAgent模型看不到该工具
运行时__call__ 首行role != "root"返回 ToolError

一行布尔判断把委派树压成两层。对照 Claude Code 默认允许 3 层(深度计数器 + 到顶撤销工具 + Agent(type1, type2) 限定可派工种),本专题分布为三家禁止、一家带刹车放开

5.5 spawn 六步流水线

src/kimi_cli/subagents/core.py:36-86
async def prepare_soul(spec, runtime, builder, store, on_stage=None):
# 1. Build agent from type definition
agent = await builder.build_builtin_instance(
agent_id=spec.agent_id, type_def=spec.type_def, launch_spec=spec.launch_spec)
# 2. Restore conversation context
context = Context(store.context_path(spec.agent_id))
await context.restore()
# 3. System prompt: reuse persisted prompt on resume, persist on first run
if context.system_prompt is not None:
agent = replace(agent, system_prompt=context.system_prompt)
else:
await context.write_system_prompt(agent.system_prompt)
# 4. For new (non-resumed) explore agents, prepend git context
prompt = spec.prompt
if spec.type_def.name == "explore" and not spec.resumed:
git_ctx = await collect_git_context(runtime.builtin_args.KIMI_WORK_DIR)
if git_ctx:
prompt = f"{git_ctx}\n\n{prompt}"
# 5. Write prompt snapshot (debugging aid)
store.prompt_path(spec.agent_id).write_text(prompt, encoding="utf-8")
# 6. Create soul
soul = KimiSoul(agent, context=context)
return soul, prompt
要点
3resume 复用存盘提示词而非当前代码版本:防止 CLI 升级导致续跑中途行为漂移,同时保住 KV-cache 前缀
4唯一的工种特例硬编码(仅 explore 且非 resume 时注入 git 状态)
5prompt.txt 运行时无用,纯排查——提示词动态拼接,无快照则无法复盘模型实际所见
src/kimi_cli/subagents/builder.py:12-42
async def build_builtin_instance(self, *, agent_id, type_def, launch_spec) -> Agent:
effective_model = self.resolve_effective_model(type_def=type_def, launch_spec=launch_spec)
llm_override = clone_llm_with_model_alias(...)
runtime = self._root_runtime.copy_for_subagent(
agent_id=agent_id, subagent_type=type_def.name, llm_override=llm_override)
return await load_agent(type_def.agent_file, runtime, mcp_configs=[])

@staticmethod
def resolve_effective_model(*, type_def, launch_spec) -> str | None:
return launch_spec.model_override or launch_spec.effective_model or type_def.default_model

mcp_configs=[]:子 Agent 无 MCP 工具。代价——「派子 Agent 查数据库」不可行;收益——省去随用随建的连接开销与工具描述的上下文占用。对照 Claude Code 按子 Agent 粒度配 mcpServers:更灵活,但配错即重现 Kimi 规避的问题。

模型优先级:调用时指定 > 实例创建时记录 > 工种默认。

六、结果回收与失败处理

6.1 摘要下限

src/kimi_cli/subagents/runner.py:40-48
SUMMARY_MIN_LENGTH = 200
SUMMARY_CONTINUATION_ATTEMPTS = 1
SUMMARY_CONTINUATION_PROMPT = """
Your previous response was too brief. Please provide a more comprehensive summary
that includes:
1. Specific technical details and implementations
2. Detailed findings and analysis
3. All important information that the parent agent should know
""".strip()
src/kimi_cli/subagents/runner.py:158-173
final_response = soul.context.history[-1].extract_text(sep="\n")
remaining = SUMMARY_CONTINUATION_ATTEMPTS
while remaining > 0 and len(final_response) < SUMMARY_MIN_LENGTH:
remaining -= 1
failure = await run_soul_checked(soul, SUMMARY_CONTINUATION_PROMPT, ...)
if failure is not None:
return None, failure
final_response = soul.context.history[-1].extract_text(sep="\n")

子 Agent 探索上下文随实例销毁,父 Agent 仅得末条消息——回「已完成」等于整段工作作废。重写仅 1 次:多轮追逼会使模型转入凑字数状态。

DeerFlow 治相反的一头

Kimi CLIDeerFlow
病症太短太长
阈值<200 字符>2000 字符
手段再跑一轮 LLM头 2/3 + 尾 1/3 确定性截断
确定性(注释明写 This is not an LLM summary.
成本多一次调用

生产实现应两头都做。

6.2 异常翻译

子 Agent 出错时的两条路,走错一条就出事子 Agent 抛异常run_soul_checked 捕获是取消类吗?RunCancelled / CancelledError是 → raise 上抛终止整个任务Ctrl-C 语义是「整体停」,翻译成失败会让主 Agent 继续跑翻译成 SoulRunFailure 文字消息,作为工具返回值回传子任务失败不中断主流程,转为父 Agent 上下文里的可决策信息四类异常各自的消息MaxStepsReached → 附「split into smaller subtasks」APIStatusError → 带 HTTP 状态码ChatProviderError → 供应商层错误Exception → 兜底,同时 logger.exception 留栈错误消息的读者是模型:除「哪儿错了」还必须写「接下来怎么办」另有一道结果合法性校验:末条消息若不是 assistant 角色,直接判失败,防止拿到半截状态
取消类异常必须继续上抛,是这段代码里最容易写错的一处:若也翻译成「子任务失败了」,主 Agent 会认为只是子任务出问题而继续执行,用户会以为 Ctrl-C 没生效。
src/kimi_cli/subagents/runner.py:88-139(节选)
except MaxStepsReached as exc:
return SoulRunFailure(
message=(f"Max steps {exc.n_steps} reached when {phase}. "
"Please try splitting the task into smaller subtasks."),
brief="Max steps reached")
except RunCancelled: raise
except asyncio.CancelledError: raise
except APIStatusError as exc:
return SoulRunFailure(message=f"LLM API error (HTTP {exc.status_code}) ...", ...)
except ChatProviderError as exc:
return SoulRunFailure(message=f"LLM provider error when {phase}: {exc}", ...)
except Exception as exc:
logger.exception("Subagent soul run failed when {phase}", phase=phase)
return SoulRunFailure(message=f"Unexpected error when {phase}: {exc}", ...)

context = soul.context
if not context.history or context.history[-1].role != "assistant":
return SoulRunFailure(message="The agent did not produce a valid assistant response.",
brief="Invalid agent result")
规则原因
一般异常 → 消息回传,不上抛子任务失败不应中断主流程,转为主 Agent 上下文中的可决策信息
取消类必须 raiseCtrl-C 语义是「整体停止」;翻译成「子任务失败」会导致主 Agent 继续执行
错误消息附行动建议"Please try splitting the task into smaller subtasks."——错误消息的读者是模型,除「哪儿错了」还需「接下来怎么办」
结果合法性校验末条非 assistant 消息即判失败,防止拿到半截状态

七、持久化与生命周期

7.1 磁盘布局

src/kimi_cli/subagents/store.py:68-92
@property
def root(self) -> Path: return self._session.dir / "subagents"
def context_path(self, agent_id): return self.instance_dir(agent_id) / "context.jsonl"
def wire_path(self, agent_id): return self.instance_dir(agent_id) / "wire.jsonl"
def meta_path(self, agent_id): return self.instance_dir(agent_id) / "meta.json"
def prompt_path(self, agent_id): return self.instance_dir(agent_id) / "prompt.txt"
def output_path(self, agent_id): return self.instance_dir(agent_id) / "output"
<session_dir>/subagents/a3f9c1d2/
├── context.jsonl 完整对话历史(与主 Agent 物理隔离)
├── wire.jsonl 事件流(UI)
├── meta.json 状态机
├── prompt.txt 启动 prompt 快照
└── output/ 产出物

隔离由文件系统保证而非约定。ID 生成 f"a{uuid.uuid4().hex[:8]}"——短标识符为模型而设,它需准确回填至 resume 参数;完整 UUID 36 字符易抄错。

7.2 状态机与并发防护

src/kimi_cli/subagents/models.py:9-16
type SubagentStatus = Literal[
"idle", "running_foreground", "running_background",
"completed", "failed", "killed"]
src/kimi_cli/tools/agent/__init__.py:175-182, 220-245
if params.resume:
record = self._runtime.subagent_store.require_instance(params.resume)
if record.status in {"running_foreground", "running_background"}:
return ToolError(message=f"Agent instance {record.agent_id} is still "
f"{record.status} and cannot be resumed concurrently.", ...)
...
# Mark running_background synchronously before dispatching the
# async task so that concurrent resume attempts see the guard
# immediately (asyncio.create_task only queues the coroutine).
self._runtime.subagent_store.update_instance(agent_id, status="running_background")
try:
view = self._runtime.background_tasks.create_agent_task(...)
except Exception:
self._runtime.subagent_store.update_instance(agent_id, status="idle")
if created_instance:
self._runtime.subagent_store.delete_instance(agent_id)
raise

asyncio.create_task 仅入队不执行:状态若在协程内更新,两次紧邻 resume 会同时通过检查并写同一 context.jsonl。故派发前同步落状态;失败时回滚(改回 idle + 删除新建实例),否则实例永久卡在 running。

resume 使子 Agent 从无状态函数调用变为有身份的长期实体:同一 explore 实例可被反复追问,无需重新遍历代码库。

7.3 墙钟超时

MAX_FOREGROUND_TIMEOUT = 60 * 60  # 1 hour
MAX_BACKGROUND_TIMEOUT = 60 * 60 # 1 hour

步数上限对「单步卡死」无效(步数恒为 1,时间无限),必须叠加墙钟超时。1 小时上限 + 主循环 1000 步共同界定了小时级自动运行的时间尺度。

7.4 后台返回值即行动指引

src/kimi_cli/tools/agent/__init__.py:246-261
lines = [
f"task_id: {view.spec.id}",
f"status: {view.runtime.status}",
f"agent_id: {agent_id}",
"automatic_notification: true",
"next_step: You will be automatically notified when it completes.",
("next_step: Use TaskOutput with this task_id for a non-blocking status/output "
"snapshot. Only set block=true when you intentionally want to wait."),
f'resume_hint: Use Agent(resume="{agent_id}", prompt="...") to continue this '
"instance later.",
]

next_step: / resume_hint: 非程序解析字段,是给模型的行动指引。置于工具返回值而非系统提示词:仅在真正 spawn 后才占用上下文,不进常驻前缀。

八、会在哪儿翻车:症状 → 原因 → 对策

把前面所有「在防什么」汇总成一张排查表。你自己实现时可以直接对照:

你观察到的症状真正的原因对应的防护
子 Agent 干了半天,回一句「已完成」只有最后一条消息会回传,模型不知道要写详细摘要设字数下限,不够就打回重写一次(Step 6)
账单莫名其妙翻了十几倍子 Agent 套子 Agent,指数级展开三重保险禁止递归(Step 8)
说好只读,结果代码被改了光靠提示词约束不住模型提示词 + 工具剥离双保险(Step 4)
升级之后,老的续跑任务行为变了续跑时用了新版系统提示词续跑复用存盘的提示词(第五节第 3 步)
同一个子 Agent 的历史文件乱了并发 resume,两个协程同时写派发前同步改状态(6.3)
子 Agent 加载特别慢 / 上下文一上来就很满MCP server 的连接开销和工具描述mcp_configs=[],子 Agent 不给 MCP(第五节)
模型撞到步数上限后反复重试同样的事错误消息只说了「错了」,没说「该怎么办」错误消息里附行动建议(Step 7)
任务一直在跑,步数却没涨单步卡死,步数上限拦不住墙钟超时,上限 1 小时(6.1)
用户按了 Ctrl-C 但任务还在跑取消异常被当成普通失败吞掉了取消类异常必须 raise 上抛(Step 7)
出错后这个子 Agent 再也启动不了状态卡在 running,没回滚异常时把状态改回 idle 并删除新建实例(6.3)

这张表比任何架构图都能说明「生产级」三个字的含义。 上面每一行,都是有人在真实环境里踩过之后才写进代码的。

九、全局定位:Kimi CLI 在这个坐标系的哪儿

把整套机制画成一张总图:

01 那篇的五个维度 打分:

维度Kimi CLI 的答案一句话解释
D1 隔离单位一个 context.jsonl 文件 + 独立工具白名单同进程内的协程,隔离靠文件系统而非操作系统
D2 通信拓扑严格星型子 Agent 之间零通信,全部经主 Agent 中转
D3 结果回收只回传最后一条消息,低于 200 字强制重写一次治的是「太短」这一头
D4 递归深度单层,三重保险硬禁止没有孙子 Agent
D5 生命周期前台 / 后台 / 可 resume 复活,单次最长 1 小时子 Agent 是有身份的实体,不是一次性函数

一句话总结:Kimi CLI 的多 Agent 是保守的星型单层结构。它没有在拓扑上玩花样(没有群聊、没有兄弟通信、没有递归),而是把全部工程力气花在了两件事上——隔离要彻底结论要可用

十、小结与自查清单

如果你要自己实现一套子 Agent 委派,照着下面这张表走一遍:

基础机制

  • 子 Agent 有自己独立的对话历史吗?是物理隔离(不同文件/进程)还是只靠约定?
  • 主 Agent 能拿到子 Agent 的什么?只有最后一条消息,还是全部过程?这件事告诉模型了吗
  • 派活的任务描述是自包含的吗?子 Agent 看不到主对话,缺的信息补上了吗?

权限与成本

  • 不同工种的工具权限分开了吗?只读工种是提示词和代码各堵了一遍,还是只堵了一遍?
  • 便宜的活(搜索、阅读)能不能绑个便宜的模型?
  • 子 Agent 要不要给 MCP?想清楚连接开销和工具描述的上下文占用了吗?

防炸

  • 步数上限有吗?墙钟超时有吗?(两个都要,挡的不是同一种卡死)
  • 子 Agent 能不能再派子 Agent?如果不能,是在几层堵的?
  • 子 Agent 的异常会不会炸掉主流程?取消类异常有没有被误吞?

结果质量

  • 摘要有没有下限?不够会怎么办?
  • 摘要有没有上限?太长会不会撑爆主上下文?(Kimi 只治了下限,DeerFlow 治的是上限,理想是两头都治)

给模型的信息

  • 「什么时候该派活」有没有给一个可执行的数字判据,而不是「适当时候」?
  • 「什么时候不该派活」的反面清单写了吗?
  • 报错消息里除了「哪儿错了」,有没有写「你接下来该怎么办」?
  • 给模型看的 ID 够短吗?(它要能准确抄回来)

可排查

  • 每个子 Agent 启动时的 prompt 有没有存快照?
  • 状态机的状态变更是同步写的吗?失败会回滚吗?

十一、参考