> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openkova.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 子代理

> Task 四件套、四层发现、主代理自建定义、独立上下文与 token 结算，以及活动过程的持久化回放。

子代理让 agent 把大任务拆开并行处理，而不是在一个上下文里串行做完。
每个子代理是一个**独立的 Agent 实例**：独立上下文、独立工具集、独立记账。

<Note>
  一份子代理定义能碰到什么，由五个能力维度（工具、技能、MCP、知识源、记忆）
  逐个声明、逐个生效。详见[子代理能力模型](/features/subagent-capabilities)。
</Note>

## 四个调度工具

<Steps>
  <Step title="Task">派发一个子代理，带独立上下文与工具集，立即返回。</Step>
  <Step title="TaskWait">等待某个子代理完成并取回结果。</Step>
  <Step title="TaskList">列出当前存活的子代理及其状态。</Step>
  <Step title="TaskStop">中止一个正在跑的子代理。</Step>
</Steps>

并行规则很直白：**一条消息里发多个 Task 就是并行**；一旦和其他工具混在同一条消息里，
就退化成一个个串行跑。并发上限 8。子代理跑着的时候你不必干等——继续干活或继续聊天，
报告会在完成时自己回到对话里。

## 主代理自己就能建定义

除设置页之外，主代理手里还有三个管理工具：

<Steps>
  <Step title="subagents_list">列出全部定义（工具、轮次、模型、开关、来源文件、各层存储目录）。</Step>
  <Step title="subagents_save">新建或更新一份系统级 / 工作区级定义。</Step>
  <Step title="subagents_delete">删除一份自定义定义。</Step>
</Steps>

它们与设置页走**同一套语义**：schema 校验、层目录解析、内置重名拒绝、跨层查重、
改名时清理旧文件——所以 agent 建出来的定义和你手写的没有区别。

这组工具挂在 Task 组旁边，**不在基础工具表里**：子代理按定义取工具时结构性拿不到它们。
委派不能继续委派，也不能改定义——这是第二道闸门（第一道是可授予工具白名单）。

保存后协议层重排工具组，**下一个 turn 即生效**，不需要重启应用。

## 四层发现

定义是一份纯 YAML 文件，按四层加载：

<Columns cols={2}>
  <Card title="内置" icon="box">
    随应用分发：Explorer、Code-reviewer、Test-runner、Fixer。
    设置页只读——可开关、可复制为系统级，永不被写回。
  </Card>

  <Card title="系统级" icon="gear">
    应用数据目录下的 `subagents/*.yml`，全局生效。
    内置之外的个人定义放这一层。
  </Card>

  <Card title="工作区级" icon="folder">
    `<工作区>/.kova/subagents/*.yml`，跟着 git 走，只对该工作区生效。
    同名的工作区层与内置层互不影响。
  </Card>

  <Card title="插件提供" icon="puzzle-piece">
    插件清单里的 `subagents` 目录贡献自己的定义，
    开关与模型覆盖按 pluginId 命名空间隔离。
  </Card>
</Columns>

同名时后加载的遮蔽先加载的。一份坏文件只降级成一条诊断，不赔掉整份清单——
更不能赔掉整个 turn。

**开关与模型覆盖不写进定义文件。** 定义文件会进 git，内置更是只读常量，
所以「启用了没」「用哪个模型」是本机决定，整包存在 SQLite 里。
模型覆盖因此也是内置定义的**唯一可调项**。生效优先级：

`Task 参数` > `本机覆盖` > `定义自带` > `会话当前模型`

## 给它什么能力，由定义说了算

一份子代理定义可以声明五个正交维度。**未声明即不可达**——
没写的东西不会出现在它的工具表和提示词里。

| 维度 | 声明什么 | 没声明时 |
| - | - | - |
| `tools` | 基础工具白名单（编码工具 / 网络 / `use_skill` 等） | 只有定义必须的最少工具集 |
| `skills` | 技能白名单（按名） | 不注入技能目录，`use_skill` 不可用 |
| `mcp` | 允许访问的 MCP 服务器 | 网关根本不挂载 |
| `knowledge` | 声明式知识源（名称 + 工作区相对 glob） | 只有 `kb_search` 也不存在 |
| `memory` | 记忆档位：无 / 私有 / 共享 | 每次委派都是冷启动 |

细节与设计取舍见[子代理能力模型](/features/subagent-capabilities)。

## 为什么活动过程要落盘

这是子代理系统里最实际的一课。

**改动前的问题**：子代理干活的过程（它想了什么、调了什么工具、写了什么字）
只活在内存里，上限 400 条、满了还会优先裁掉思考与正文的增量。
应用一重启，打开「子智能体」面板就只剩一句「记录已过期」——
只有最终报告还留在主对话里。

**设计**就三件事：

<Columns cols={3}>
  <Card title="存哪">
    每个子代理一个文件：
    `sessions/subagents/<它的id>.jsonl`，一行一条记录，append-only。
  </Card>

  <Card title="怎么存">
    边干边写，但**攒一批再写**（250ms 批次），
    所以又快又不伤硬盘，也不阻塞 agent 主循环。
  </Card>

  <Card title="丢了怎么办">
    重启后从文件读回来，面板照常显示，
    前端 store 逻辑零改动。
  </Card>
</Columns>

### 崩溃丢失率

先定义才能谈保证——**丢失率 = 崩溃时丢失的记录数 / 该委派已产出的记录数**。

* 崩溃最多丢失**当前未刷批次**（≤250ms 的增量）；
* 终态（报告）**强制同步刷**，所以**结果永不失**；
* 推论：委派跑得越久，边界损失占比越低。**短任务不承诺这个指标**——
  它记录本来就少，且短任务的过程价值低，刻意接受。

### 恢复与「已中断」

读取是懒加载：只在内存未命中时读盘，不做启动期全量预热。
持久化时还在 running 的委派，恢复后标成 `interrupted`——
进程没了，它不可能还在跑，但它的完整过程（思考、工具调用、正文、报告）都还在。

### 保留与护栏

* 单文件 > 5MB 时截尾保留 1MB，防止长任务把磁盘打满；
* 文件数量按**目录盘点**（不是按内存——重启后内存是空的，必须扫盘），
  超上限删最旧的；
* **只在无委派在跑时清理**，running 的文件永不淘汰。

## token 结算：子代理自己记账

子代理是独立 Agent，用量从不写进父会话转录。如果只读父转录，
「目标靠子代理干完了大半活」时读数会严重少报。

所以子代理自己记账（`SubagentRun.tokens`），结算时汇给父 run；
重复结算被 `record.status` 守卫挡住，同一笔不会记两次。

<Note>
  这也是[目标模式](/features/goal-mode)把 token 账从「读转录」改成
  「现累增量」的原因之一：看不见子代理的账，目标模式的消耗读数就是错的。
</Note>

## 明确不做的

<Warning>
  设计里写明了边界，这些是刻意的取舍：

  * **不做续跑**：重启后不恢复执行链，未完成的委派只是「可回放的历史」，
    不自动接着干；
  * **不写进父会话转录**：父转录会被整份读取，写进去会污染它；
  * **不引入新存储引擎**：复用 JSONL 与现有写入纪律；
  * **不嵌套委派**：子代理拿不到 Task 组，也拿不到子代理管理工具组，
    它不能再往下派活；
  * **不让子代理决定主记忆**：它可以选「共享」目录直接写工作区记忆，
    但没有「把它的私有记忆导入主记忆」这种动作。
</Warning>

<Card title="下一步" icon="arrow-right" href="/features/subagent-capabilities">
  一份定义怎么拿到知识库、技能和记忆——子代理能力模型。
</Card>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.