![Pi Subagent Cluster Dashboard](assets/subagent-cluster-dashboard.png)

# Pi Subagent Cluster

这是一个 Pi 扩展包，用于把复杂任务拆成任务图，交给隔离的 Pi 子进程执行，并通过同模型审核器自动重试和升级 worker 等级。

## 使用建议

为了让主 agent 更稳定地判断任务复杂度、拆解任务并提交可审核的任务图，建议将本仓库 `AGENTS.md` 中的内容添加到你自己的 `AGENTS.md` 中。这样 Pi 在处理项目任务时可以遵循统一的任务分流、验收标准和协作约束，通常能获得最佳效果。

## 安装

当前 Pi CLI 的 npm 包源使用 `npm:` 前缀：

```bash
pi install npm:@0xbb2b/pi-subagent-cluster
```

安装后重启 Pi。扩展会自动注册 `subagent_cluster` 工具、`/subagent-cluster` dashboard 命令和 `/subagent-settings` 设置命令。

## 从 Git 克隆后本地安装

将 `<仓库地址>` 替换为实际 Git 仓库地址，将扩展克隆到本地：

```bash
git clone <仓库地址> "$HOME/Projects/pi-subagent-cluster"
```

Pi 的包根目录必须是包含 `package.json` 的目录。可以全局安装这个本地包：

```bash
pi install "$HOME/Projects/pi-subagent-cluster"
```

如果只希望某个项目使用本地版本，请在目标项目目录执行：

```bash
cd /path/to/your-project
pi install -l --approve "$HOME/Projects/pi-subagent-cluster"
```

`-l` 会把配置写入当前项目的 `.pi/settings.json`。本地路径安装不会复制文件，Pi 会直接读取 Git 克隆目录，因此修改代码后无需重新安装，也不需要先执行 `npm publish`。当前包只使用 Pi 已提供的核心依赖，无需在扩展目录单独执行 `npm install`。

## 二次开发

进入本地克隆目录后，直接修改 `extensions/` 下的 TypeScript 文件：

```bash
cd "$HOME/Projects/pi-subagent-cluster"
# 修改 extensions/index.ts、extensions/scheduler.ts 等文件
```

正在运行 Pi 时可以输入 `/reload` 重新加载扩展；如果当前 Pi 版本或运行模式不适合热重载，退出后重新启动 Pi 即可。也可以不写入任何安装配置，临时加载入口进行验证：

```bash
pi --no-extensions \
  --extension "$HOME/Projects/pi-subagent-cluster/extensions/index.ts"
```

发布前可以在无需联网的情况下运行回归测试，并检查 npm 包实际会包含哪些文件：

```bash
cd "$HOME/Projects/pi-subagent-cluster"
npm test
npm pack --dry-run
```

查看或移除本地安装：

```bash
pi list
pi remove "$HOME/Projects/pi-subagent-cluster"

# 如果使用了项目级安装：
cd /path/to/your-project
pi list
pi remove -l "$HOME/Projects/pi-subagent-cluster"
```

## 配置

项目配置放在 `.pi/subagent-cluster.json`，也可以放在 `~/.pi/agent/subagent-cluster.json` 作为全局配置。配置会从当前工作目录向上查找，项目配置优先于全局配置。

当前只支持标准 JSON：配置文件中不能写 `//`、`/* ... */` 注释，也不能使用尾逗号。下面的示例可以直接复制到 `.pi/subagent-cluster.json`；参数解释见后面的参数速查表。

```json
{
  "levels": {
    "high": {
      "model": "openai/gpt-5.6",
      "thinkingLevel": "high",
      "tools": ["read", "grep", "find", "ls", "bash", "edit", "write"],
      "timeoutMs": 1800000
    },
    "medium": {
      "model": "openai/gpt-5.6",
      "thinkingLevel": "medium",
      "tools": ["read", "grep", "find", "ls", "bash", "edit", "write"],
      "timeoutMs": 1200000
    },
    "low": {
      "model": "openai/gpt-5.6",
      "thinkingLevel": "low",
      "tools": ["read", "grep", "find", "ls", "bash", "edit", "write"],
      "timeoutMs": 900000
    }
  },
  "reviewer": {
    "thinkingLevel": "medium",
    "tools": ["read", "grep", "find", "ls"],
    "timeoutMs": 180000
  },
  "maxConcurrency": 4,
  "maxTasks": 8,
  "maxRetriesPerLevel": 1,
  "learning": {
    "enabled": true,
    "taskTypeUpgradeThreshold": 2,
    "historyRetentionDays": 30
  }
}
```

整体省略 `learning` 时会自动使用默认值；显式提供时必须是对象，且 `enabled` 必须是布尔值，`taskTypeUpgradeThreshold` 和 `historyRetentionDays` 必须是正整数，缺少字段或类型不正确时配置会被拒绝并提示错误。

### 参数速查

| 参数 | 类型 | 作用 |
|---|---|---|
| `levels` | 对象 | 配置三个 worker 等级；必须包含 `low`、`medium`、`high`。 |
| `levels.<level>.model` | 字符串 | worker 使用的模型，推荐使用 `provider/model` 格式。 |
| `levels.<level>.thinkingLevel` | 字符串 | worker 的推理强度；`/subagent-settings` 会根据当前模型能力过滤可选值。 |
| `levels.<level>.tools` | 字符串数组 | worker 可调用的工具；写入类工具会允许 worker 修改项目。 |
| `levels.<level>.timeoutMs` | 正整数 | 该等级 worker 的单次超时时间，单位毫秒。 |
| `reviewer.model` | 字符串 | 审核器使用的固定模型；省略时跟随主 agent。 |
| `reviewer.thinkingLevel` | 字符串 | 审核器推理强度；省略时跟随主 agent，固定模型时按模型能力选择。 |
| `reviewer.tools` | 字符串数组 | 审核器工具，只能是 `read`、`grep`、`find`、`ls`。 |
| `reviewer.timeoutMs` | 正整数 | 单次审核超时时间，单位毫秒。 |
| `maxConcurrency` | 正整数 | 同时运行的最大 worker 数量。 |
| `maxTasks` | 正整数 | 单次集群允许的最大任务数量。 |
| `maxRetriesPerLevel` | 非负整数 | 自动升级前的同等级重试次数。 |
| `learning.enabled` | 布尔值 | 是否启用全局跨运行学习；默认 `true`。关闭后不读取也不写入学习证据。 |
| `learning.taskTypeUpgradeThreshold` | 正整数 | 全局共享的同一 `taskType` 触发提升所需的不同运行有效升级证据数量；运行以 `projectKey` 和 `runId` 的组合去重，默认 `2`。 |
| `learning.historyRetentionDays` | 正整数 | 学习证据的有效天数；默认 `30`。 |

`reviewer.model` 可配置固定审核模型；省略时审核器使用当前主 agent 的模型。`reviewer` 只控制审核器的模型、thinking level、只读工具和超时。

## 设置命令

使用 `/subagent-settings` 打开 subagent-cluster 专属设置页。它不会覆盖 Pi 内置的 `/settings`：

- `/settings`：Pi 自身设置。
- `/subagent-settings`：subagent-cluster 设置。

设置页主菜单按分组显示：

```text
HIGH
MEDIUM
LOW
REVIEWER
CLUSTER
```

进入 HIGH、MEDIUM 或 LOW 二级菜单后，可以配置模型、thinking level、worker tools 和 timeout。进入 REVIEWER 二级菜单后，可以配置固定模型、thinking level、只读 tools 和 timeout；当 Model 选择“跟随主 agent”时，Thinking level 也可以选择“跟随主 agent”。reviewer tools 可在 `read`、`grep`、`find`、`ls` 中多选。CLUSTER 二级菜单配置最大并发数、最大任务数、同等级重试次数、学习开关、同类型提升阈值和证据有效天数，也可以选择 `Clear learning evidence` 清除所有项目共享的全部全局学习证据。清除前会明确要求确认；未确认不会删除任何记录。

二级设置页使用 `←/→` 双向循环切换 thinking level、学习开关和数字参数；模型与工具仍使用 `Enter` 打开选择页，清除学习证据使用 `Enter` 执行。每次模型、thinking、tools、学习开关或数字参数切换后都会自动保存，不需要单独点击保存。

首次打开 `/subagent-settings` 且全局配置不存在时，会立即创建并保存完整默认配置；之后每次修改也会自动写入同一文件：

```text
~/.pi/agent/subagent-cluster.json
```

项目配置仍然可以放在 `.pi/subagent-cluster.json`，并且优先于全局配置；`/subagent-settings` 修改的是全局配置，便于多个项目共享同一套模型和调度参数。如果当前项目存在 `.pi/subagent-cluster.json`，它会覆盖全局配置；要让当前项目使用全局设置，需要删除或移走项目级配置。

## 使用方式

主 agent 会根据任务复杂度决定是否调用 `subagent_cluster`。调用时必须提交结构化任务图。每个任务必须提供非空 `taskType`；它使用 `<技术栈>/<任务性质>/<作用范围>` 格式，例如 `typescript/backend-api/cross-module`、`go/bugfix/local` 或 `database/migration/schema`，使跨项目证据保持可比。系统会去除首尾空白并统一为小写。任务图中的 `task` 描述本次需要完成的精确任务，`taskType`、`title`、`task` 和 `acceptanceCriteria` 共同决定精确任务指纹；其中 `taskType` 也用于把不同但同类的任务归入同一学习类别：

```json
{
  "goal": "实现用户管理后台",
  "tasks": [
    {
      "id": "backend",
      "title": "实现后端接口",
      "taskType": "typescript/backend-api/cross-module",
      "task": "实现用户列表、创建、编辑和删除接口",
      "acceptanceCriteria": [
        "接口遵循项目现有路由约定",
        "包含输入校验",
        "测试全部通过"
      ],
      "level": "low",
      "dependsOn": []
    },
    {
      "id": "frontend",
      "title": "实现管理页面",
      "taskType": "typescript/frontend-ui/cross-module",
      "task": "实现用户列表和编辑表单页面",
      "acceptanceCriteria": [
        "页面可以加载用户列表",
        "表单错误可见",
        "构建和测试全部通过"
      ],
      "level": "medium",
      "dependsOn": ["backend"]
    }
  ]
}
```

这个任务图体现两级学习范围：相同项目中相同任务指纹的有效升级证据可以在下一次直接提高最低起始等级；不同任务但相同 `taskType` 的证据会在所有项目间共享，只有达到 `learning.taskTypeUpgradeThreshold` 个不同运行后才会提高该类型的最低起始等级。运行以 `projectKey` 和 `runId` 的组合去重，因此同一项目的不同运行会累计，不同项目的同名运行也会分别累计。学习结果只会提高主 agent 请求的等级，不会降低请求等级。

执行集群后任务会在后台运行，不会占用输入栏。输入栏下方会显示一行集群状态栏：

- 输入栏为空时按 `↓`：选择集群状态栏
- 按 `Enter`：打开集群管理 dashboard，等价于 `/subagent-cluster`
- 也可以随时输入 `/subagent-cluster` 打开 dashboard
- 状态栏只在集群运行或暂停（包括等待用户决策）时显示；完成、取消或不可恢复失败后消失
- 集群结束后，结果会作为后台消息自动返回主 agent

## Dashboard 操作

- `↑↓` 或 `j/k`：选择任务
- `Enter`：展开或收起选中任务的输出
- `p`：暂停或继续集群
- `r`：重试等待用户决策的任务
- `e`：手动提升等待用户决策的任务等级
- `a`：接受当前结果
- `x`：放弃当前任务
- 集群列表中的 `d` 或 `Delete`：确认后删除选中的非活动集群历史；活动集群不能删除
- `Escape`：退出 dashboard，返回输入栏，不会取消集群
- `Ctrl+C`：取消整个集群

任务需要用户决策时，问题会回到主对话区，并暂停任务调度及正在运行的 worker/reviewer，避免等待期间消耗超时时间。请在主输入框回复选项编号或动作（例如 `1`、`retry`、`升级`）；答复会直接恢复对应任务，不会再触发一轮无关的主 agent 对话。

底部快捷键会按状态动态显示：运行中显示暂停和取消；手动暂停时显示继续；等待用户决策时显示重试、升级、接受和放弃；集群列表选中非活动历史时显示删除；完成、失败或取消后只保留适用操作。删除历史前会显示包含运行 ID 的二次确认，确认后会删除该运行的状态快照和任务输出，且不可撤销。

暂停会终止 worker/reviewer 当前尚未完成的网络请求，并冻结累计运行 timeout；恢复后通过隔离的临时 Pi 会话和当前工作区状态继续，避免挂起请求在恢复瞬间超时。Dashboard 展示的集群和任务运行时长也会扣除暂停区间。取消会终止所有 worker 和审核器子进程。

## Token、执行历史与瞬时 API 重试

- 每个 `TaskAttempt` 分别保存 worker 和审核器的完整执行结果，包括模型、输出、stderr、停止原因、错误、工具调用、消息历史、usage 与 API 重试记录。审核器执行中产生的历史和 usage 会实时进入当前 attempt，Dashboard 可以在审核尚未结束时展示。
- 单次执行从完整消息历史聚合 usage；任务统计包含所有同等级重试和等级升级 attempt 中的 worker 与审核器，集群统计等于所有任务统计之和。
- 总 Token 的固定口径是 `input + output + cacheRead + cacheWrite`。provider 上报的 `reasoning` 是 `output` 的子集，不会再次相加。界面显示总 Token、输入、输出、turns 和整体缓存命中率；缓存读写与成本仍保留在内部 usage 数据中，不在界面展开。
- 新运行写入 `executionDataVersion: 1`，表示 worker 与审核器的统计和历史完整。`version: 2` 且没有该标记的快照会显示“统计不完整，缺少审核器数据”和“完整审核器历史未记录”；扫描历史时会直接删除 `version: 1` 运行目录。
- worker 与审核器共用同一套瞬时 API 错误处理：初始请求失败后最多重试 3 次，固定等待 10 秒、30 秒、60 秒。每条记录保存发生时间、重试序号、等待时间和错误摘要。
- Pi 发出 `auto_retry_start` 时，集群会立即终止该子进程的内建 2/4/8 秒退避，并使用同一个临时 session 按集群策略恢复，避免两层退避叠加。Pi 自动重试关闭时，最终 assistant 错误由 Pi AI 的瞬时错误分类器判定后进入相同流程。
- API 重试发生在一次逻辑执行内部，不创建新的 `TaskAttempt`，不消耗 `maxRetriesPerLevel`。退避等待不扣减 worker/reviewer timeout；暂停会冻结剩余等待，取消会立即结束。认证、配置、上下文、取消、进程错误和其他非瞬时错误不会进入 API 退避。
- 每次失败响应已经产生的历史和 usage 都会保留；三次 API 重试全部耗尽后，最终失败才交回调度器，由正常的同等级重试和升级规则处理。

## Dashboard 布局

管理页采用分层 workflow navigator：

- 集群列表、概览页和状态栏显示集群累计 Token 及整体缓存命中率；概览页显示 input、output 和 turns。左侧任务列表显示完成标识、任务标题、状态、等级和尝试次数；右侧显示当前 subagent 的 Prompt、Result、总 Token 以及 worker/审核器分项，并展示 `taskType`、主 agent 请求等级、实际初始等级，以及等级选择说明；发生等级提升时，该说明显示为等级调整原因。
- 在任务列表按 `Enter`：进入当前 subagent 详情页，查看状态、`taskType`、请求等级、实际初始等级、等级选择说明（发生等级提升时显示为等级调整原因）、模型、Prompt、Result 和最近活动。
- 在 subagent 详情页再次按 `Enter`：进入完整历史 pager，按 attempt 查看 worker 执行、审核器完整执行和结构化审核结论；每次执行显示模型、退出码、停止原因、输出、stderr、Pi/provider 实际返回的 user、assistant 文本、公开 thinking、工具调用、工具结果、usage、API 重试与错误。每次审核器执行和结论紧跟对应 worker，位于下一次 worker 之前。结构化结论完整显示 `decision`、`reason`、`missingCriteria` 和 `nextInstruction`，不承诺隐藏推理。
- 历史 pager 默认使用终端可用的最大垂直空间，尽可能显示全部记录；历史超过屏幕高度时，右侧显示不可拖动的进度滚动条，并支持 `↑↓`、`j/k`、`PgUp/PgDn`、`Home/End` 翻阅全部上下文；`n/p` 可直接跳到下一个/上一个 worker 标题，到达首尾后停止并提示。
- `Escape` 按页面层级逐级返回；概览页按 `Escape` 退出 dashboard。
- 后台集群结束消息包含集群总 Token、输入、输出、整体缓存命中率以及每个任务的总量、worker Token 和审核器 Token；工具结果展开后也显示集群与任务的同口径统计。

## 执行和审核流程

1. 主 agent 提交带 `taskType`、精确 `task`、验收标准和依赖关系的任务图。
2. 调度器校验任务 ID、依赖关系、任务数和验收标准。
3. 学习开关开启时，系统从全局证据目录聚合未过期且配置签名匹配的证据，再分别按当前项目 key 查找精确任务指纹证据和跨项目查找 `taskType` 证据，计算任务的实际初始等级；学习关闭时直接使用请求等级。
4. 没有未完成依赖的任务按配置并行执行。
5. worker 使用独立的 `pi --mode json` 子进程和进程级临时会话执行；消息历史和 usage 随 JSON 事件实时写入当前 attempt。
6. worker 或审核器遇到瞬时 API 错误时，在当前逻辑执行和临时 session 内按 10/30/60 秒最多重试 3 次；等待不占 timeout，也不增加 attempt。
7. 同模型只读审核器逐条检查验收标准，并把完整审核执行结果与结构化结论一并保存。
8. 审核通过后任务完成；审核重试达到当前等级上限后自动升级。
9. 高等级仍不能通过时进入 `paused_for_user`，问题回到主对话区等待用户决策；等待期间暂停任务调度及正在运行的子进程。
10. 审核器超时会将任务标记为 `timed_out`，审核器返回无效 JSON 会将任务标记为审核失败，不会误进入 `paused_for_user`。
11. Pi 会话退出或重载后，无法恢复的运行中快照会显示为已取消，而不是继续显示等待用户决策。
12. 每次运行会保存到 `~/.pi/agent/subagent-cluster/runs/--项目绝对路径编码--/<run-id>/`，包括状态快照、完整 worker/reviewer 统计与历史、API 重试记录和各任务输出。
13. 运行结束后，系统根据学习规则把每条有效升级证据独立写入 `~/.pi/agent/subagent-cluster/learning/evidence/`；文件名使用 UUID，先写入同目录临时文件再 rename，多个 Pi 进程不会重写同一个全局 JSON。每条证据包含项目 key。

### 跨运行学习规则

- **全局存储**：学习证据统一存放在 `~/.pi/agent/subagent-cluster/learning/evidence/`，每个有效升级证据是一个独立 JSON 文件，不创建项目级 `history.json`。
- **精确任务学习**：任务指纹由 `taskType`、`title`、精确 `task` 和验收标准组成；读取时还必须匹配当前项目 key，因此相同指纹不会跨项目套用。匹配证据存在时，下次直接把最低起始等级提高到证据中的等级。
- **任务类型学习**：不同精确任务只要规范化后的 `taskType` 相同，就会在所有项目间累计。每个目标等级独立统计，必须有 `learning.taskTypeUpgradeThreshold` 个不同运行都证明同一较低等级不足，才会提高到该目标等级；medium 的证据不会与单条 high 证据混合后误升到 high。去重键包含项目 key 和 `runId`，所以同一项目的不同运行会累计，不同项目即使使用相同 `runId` 也会分别计数；同一项目同一运行的多个同类任务对同一目标等级只计一次。默认阈值为 2。
- **跨项目示例**：项目 A 的 `typescript/backend-api/cross-module` 任务在运行 `run-7` 产生一次 low→medium 有效升级，项目 B 的同类型任务在也叫 `run-7` 的运行产生另一次 low→medium 有效升级；两条证据的项目 key 不同，会共同达到默认阈值并提升项目 C 的同类型任务，但 A 的精确任务指纹证据不会提升 B 的同指纹任务。
- **有效升级证据**：任务最终完成；每个 worker 正常退出且没有进程错误、错误停止或中止；先经审核给出 `retry` 或 `escalate`，随后由更高等级 worker 通过审核。跨越多个等级时会分别形成 low→medium 和 medium→high 证据，使每个目标等级独立学习。
- **无效失败**：worker 进程失败或中止、审核错误/超时、用户决策、取消、阻塞或未最终通过，都不会产生学习证据。损坏或结构无效的证据文件会被忽略，过期文件读取时可安全清理。
- **并发安全**：写入证据时使用 UUID 文件名，并在证据目录内完成临时文件 rename；不同 Pi 进程各写入自己的文件，不会因读改写同一个全局 JSON 而丢失更新。
- **配置与有效期**：证据绑定 low、medium、high 三个 worker 等级的模型、推理、工具和超时配置；这些配置任一变化后，旧证据不再参与学习。证据默认 30 天有效，超过 `learning.historyRetentionDays` 后失效。
- **清除全部证据**：打开 `/subagent-settings`，进入 `CLUSTER` 并选择 `Clear learning evidence`，在确认框中确认后清除证据目录中的全部全局学习证据，影响所有项目；未确认不会执行清除，不需要手工定位存储文件。

worker 子进程不会加载当前扩展，避免扩展递归启动；它会在任务目录中使用项目上下文和配置的工具权限。审核器只使用只读工具。
