@chthub/pi-subagents0.4.1
A minimal, complete sub-agent framework for pi: three tools, isolated contexts, parallel execution, automatic results, resumable sessions, and native UI.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-subagents
为 pi 提供极简、完整的子代理框架。
三个操作——启动、发消息、停止——配上独立上下文、并行执行、自动结果交付、可恢复会话和原生子代理窗口。生命周期完整,操作接口极简:你定义任务,框架管理代理。
作为 pi 扩展运行,除 pi 宿主提供的包外,没有额外运行时依赖。已在 pi 1.1.0 上测试。
你: 调研一下这个项目的数据库 schema
→ pi 调用 agent_spawn,spawn 一个常驻的 `pi --mode rpc` 子进程
→ 子 agent 在独立上下文窗口里独立工作
→ 结果返回;你继续聊天
为什么需要它?
Pi 没有内置 sub-agent。于是重活、并行的活、吃上下文的活,全都挤进你那一个窗口。pi-subagents 把活交给子 pi:噪音留在那边,只有答案回来。
特性
- 安静上下文 — 日志、搜索结果、测试输出留在子窗口里。你拿到最终结果,不是一片杂乱。
- 真正并行 — 一次起多个后台 agent,就绪结果在主代理下一个 turn 边界一起交付,每份保留独立卡片。无任务排队、无取结果工具。
- 全程可控 —
agent_send给运行中的 agent 发消息重定向(也能唤醒空闲的),agent_stop停止进程。最终回复自动回传,承载阻塞问题和最终汇报;默认深度下子代理另有发往@parent的agent_send,只用于非阻塞问题。 - 追问保留上下文 —— 子代理完成后默认常驻(空闲,零 token;每个约 180 MB 内存),进程已退出,或失败/停止后仍活着时,
agent_send都会从保存的会话自动恢复。子代理缺信息或需要决策时,把问题作为最后的回答结束;父代理用agent_send回答,子代理接着原来的上下文继续。非阻塞问题在轮中用agent_send发给@parent,子代理接着做。 - Pi 原生 — 渲染、会话、attach 全复用 pi 自己的机制;卡片看起来像内置工具——因为它们就是。
- 继承或覆盖 — 子 agent 默认继承你的模型与推理强度,也可逐个覆盖。便宜模型做侦察,强模型做实现。
- 无隐藏限制 — 默认无 token 上限、无超时、无并发上限。需要时用可选
timeoutMs加一道护栏。 - 可复盘 — 每个会话持久化、永不删除;任何结果都能
pi --session <path>回看。 - 按需嵌套 — 默认子代理不能再派子代理;在配置文件里调高
maxDepth才允许。 - Token 经济 — 系统侧保持克制:
- 系统提示 — 工具描述只解释操作,协调规则放在按需读取的 skill 中。
- 通知 — LLM 只看到最小结构化数据;装饰(label、用量)留在渲染层,永不进入 LLM 上下文。
- 结果 — 自动截尾(2000 行 / 50KB);展开任何卡片仍见全量。
设计理念
Pi 是一个极简、可扩展、由你定制的代理运行框架。pi-subagents 延续这个思路:提供完整的委派机制,不替你规定工作方式。
- 接口小,生命周期完整。
agent_spawn、agent_send、agent_stop覆盖委派、追问和取消。结果自动送回,会话持久保存,代理可见、可查看。 - 提供机制,不做工作流引擎。 不预设角色,不内置调度器,不强制编排。通过 prompt 定义任务,按需组合工具。协调建议放在可选 skill 中,而非运行时策略里。
- 复用 pi,而非另造一套基础设施。 子代理是真正的 pi 进程。RPC、会话、渲染和模型配置沿用 pi 的能力,不引入另一套代理运行框架。
- 不用就不承担额外负担。 代理工具按需启用,详细指导按需读取;UI 元数据不占模型上下文。嵌套和超时由你明确选择,不设隐藏默认限制。
极简是减少需要理解的概念,不是省掉代理生命周期。设计约定见 SPEC.md,UI 层边界见 docs/ui.md。
安装
# npm(推荐)
pi install npm:@chthub/pi-subagents
# git
pi install git:github.com/chthub/pi-subagents
# 本地目录(开发)
pi install /path/to/pi-subagents
只在一次会话里试用:pi -e /path/to/pi-subagents/index.ts。
重启 pi 后直接说"让子 agent 去…"。
快速上手
发起一个任务
让一个子 agent 分析 src/ 下的认证逻辑
交互会话中,agent_spawn 默认后台运行:子代理在独立上下文里工作,最终回复自动送回。需要工具调用等待并内联返回答案时,指定 run_in_background: false。
后台并行跑多个
同时起三个子 agent 分别看 auth 模块、数据库层和 API 路由
Pi 在同一块里发三次 agent_spawn。子代理各自独立工作,后台就绪结果在下一个交付边界自动送回;前台调用同样并行执行,答案内联返回。无需轮询,也不需要额外的取结果工具。
发消息或停止
那个数据层子 agent——方案行不通,改用组合式重写
Pi 调用 agent_send 向运行中的 agent 注入 steering(当前模型响应及工具批次结束后、下一次模型调用前送达)。要停掉失控的 agent:“干掉那个后台子 agent” → agent_stop。两者对运行中的 agent 都适用;persistent: true 的话,完成后依然可寻址——agent_send 唤醒空闲的 agent 继续追问,agent_stop 结束。
启用方式
主会话里三个 agent 工具注册时默认不激活:不派子代理的对话里不带它们的参数说明。子代理里的 agent_send 一开始就是激活的。无参数的 subagent_enable 启用工具,下一次模型请求起生效。启用记录写进会话,resume 后仍然有效。
协调 skill
包内自带 pi-subagents-workflow,是任务边界、并行、文件修改分工、后续工作和检查规则的唯一维护位置。不定义预设代理身份,也不在运行时强制工作流。
加载整个包后,新窗口默认就能看到 skill 的名称、触发描述和路径,不需要先调用 subagent_enable。模型读取 skill 或用户调用 /skill:pi-subagents-workflow 时,全文才进入上下文。包的 skill 过滤配置和 --no-skills 可以禁用发现。只用 -e 加载 index.ts 不会同时加载包内 skill。不能再派生子代理的子代理(默认深度下所有子代理都是如此)看不到这个 skill。
工具描述解释调用会发生什么,skill 说明主代理怎么组织工作。子代理即使没有读取 skill,也会收到一段解释最终回复自动回传的简短任务附言。
底部列表与子代理窗口
输入框下方列出本对话的所有子代理,一行一个(运行中 / 空闲 / 完成 / 失败 / 停止):
⠋ @jude 只读目录检查 12s bash: ls -la
‖ @yara keeper idle
✓ @max 分析 auth 模块 1m3s
点击进入 · /agents
- 点击某一行(pi 默认的全屏模式下可用),或输入
/agents(/agents max直接打开),进入该子代理的全屏窗口:用 pi 原生组件显示它的完整对话,运行中实时更新。 - 在窗口里直接打字和它对话。 这是侧聊:回答只留在子代理自己的会话里,不会通知主 agent。已结束的子代理在你发第一条消息时用
pi --mode rpc --session <路径>复活,之后常驻(空闲)到主会话结束或被agent_stop。失败/停止的旧进程仍活着时,这条新消息会先关闭旧进程,再恢复同一会话;额度耗尽不再导致切换账号后仍永久无法投递。 - 用
/continue继续任务,并把结果回传父代理。 在子代理窗口输入/continue,或/continue 已切换账号,请重试。这不是私聊:必要时恢复失败/停止的子代理,并将这一轮最终回复(或失败)自动通知父代理。运行中或空闲的直接子代理也能使用。普通消息仍是侧聊;/continue由窗口本地处理,不代表支持完整的 Pi/命令。 - 鼠标悬停时高亮当前行。在 tmux、zellij、screen 里 pi 不开启鼠标移动上报,所以没有悬停高亮,但点击照常可用。
- 窗口底部是这个子代理自己的 pi 底边栏:工作目录和 git 分支、它的 token 用量、费用和上下文占用、模型和推理强度,以及它的扩展设置的状态。窗口自己的按键提示放在最后一行。
- 连按两次 Ctrl+X 停掉正在运行或常驻的子代理(第一次只是确认提示,中间按别的键就取消)。会话文件保留,之后发消息仍可复活。如果主 agent 还在等它这一轮的结果,会收到一条 stopped 结果,说明是用户停掉的。
- Esc 回到主对话;PgUp/PgDn 或滚轮滚动;全屏鼠标模式下可拖动右侧滚动条滑块、点击轨道跳转,拖到底部后重新跟随新输出。悬停或拖动时滑块从
┃加宽为█,不改变正文换行;悬停依赖终端上报鼠标移动。在正文上拖动可选中文字(双击选词、三击选行),松开后由 pi 复制,与主对话一致。ctrl+o 展开工具输出。 - resume 或
/reload后列表会从会话记录重建(进程不会保留,会话文件会)。/new、/resume、/fork也会停掉当前会话的子代理。
配置
配置文件
<agentDir>/pi-subagents.json(默认 ~/.pi/agent/pi-subagents.json,跟随 PI_CODING_AGENT_DIR),也可以用 PI_SUBAGENT_CONFIG 指定路径。可选;启动和 /reload 时读取。
{ "maxDepth": 1 }
| 键 | 默认 | 含义 |
|---|---|---|
maxDepth |
1 |
主会话之下允许几层子代理。1:主会话能派子代理,子代理不能再派。2:子代理还能再派一层。以主会话的值为准,整棵树生效;到达上限的子代理只保留 agent_send,且只能发给 @parent。 |
值不合法时退回默认值,并在 TUI 里提示。
环境变量
| 变量 | 默认值 | 含义 |
|---|---|---|
PI_SUBAGENT_SESSION_DIR |
<agentDir>/subagent-sessions/ |
子 agent 会话存放目录;设置可迁移位置。<agentDir> 为 pi 的 agent 目录(默认 ~/.pi/agent,遵循 pi 的 PI_CODING_AGENT_DIR)。 |
目录遵循 pi 的 agent-dir 约定,刻意放在 pi 标准会话树之外,让 pi -r(resume)保持干净。会话文件永不删除。
工具参考
agent_spawn — spawn 一个隔离的 sub-agent
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
prompt |
string | 必填 | 子 agent 的自包含任务描述。 |
label |
string | 必填 | 短标识,显示在工具卡、通知卡、widget 行和会话名上。 |
model |
string | 继承 | 覆盖子 agent 的模型。指定但注册表中找不到 → 报错,不静默降级。 |
thinking |
"off"…"max" |
继承 | 覆盖推理强度;省略则用当前会话的级别。 |
tools |
string[] | 全部 | 子 agent 可见的工具白名单——白名单之外全部不可见。agent_send 总会保留,不用列出。 |
run_in_background |
boolean | true |
后台(默认)立即返回 agent_id,完成时投递携带最终输出的通知;false 阻塞到结果就绪。pi -p / json 模式下省略时走前台(宿主答完即退出,后台结果会丢)。子代理里没有这个参数,只能前台。 |
timeoutMs |
number | 无 | 可选的单轮时限(毫秒),作用于任务本身和之后每一轮由 agent_send 发起的对话。触发时扩展打断这一轮、等它 settled(等不到就强制停止),并按 stopped 报告。 |
persistent |
boolean | true(根会话)/ false(子代理内) |
完成后常驻(空闲,零 token),追问时不用重启;agent_stop 结束它。false 时完成即退出,agent_send 仍会从会话文件复活它。 |
最终回复自动回传,承载阻塞问题和最终汇报。默认 maxDepth=1 时子代理唯一的 agent 工具是发往 @parent 的 agent_send,只用于非阻塞问题,协调由父代理完成。
agent_stop — 取消代理任务
| 参数 | 含义 |
|---|---|
agent_id |
必填 — agent_spawn 返回的 agent id。取消正在执行的任务并停止进程;明确要求停止空闲代理时只关闭进程。优雅终止(stdin EOF);不生成新的完成通知。 |
停止不会删除保存的对话、撤销文件修改或取消已提交作业。空闲进程不消耗模型 token,随宿主会话退出而关闭。
agent_send — 树内发消息
| 参数 | 含义 |
|---|---|
to |
必填 — agent_spawn 给到的 agent id(短人名,如 max),或 "@parent" 发给 spawn 你的会话。 |
message |
必填 —— 消息文本;在目标当前轮结束后送达,唤醒空闲子代理,或从会话文件复活已结束的子代理(回答以通知送回)。 |
向空闲子代理发消息会启动新一轮模型调用;向已退出的子代理发消息会恢复保存的会话。最终回复自动返回。
工具只能寻址直接子代理,或者在子代理里寻址 @parent(它的直接父代理;到达深度上限的子代理只能发给 @parent)。父代理收到的是 {kind: "non_blocking_question", agent_id, question},完成通知则带 kind: "final_reply"。发给 @parent 的消息不结束子代理这一轮:父代理在当前回复及工具批次结束后读到,回答在子代理当前这批工具调用结束后、或这一轮结束后出现在它的对话里。父代理若正阻塞在前台 agent_spawn 里,要等这次调用返回后才读到。不能直接投递给兄弟代理,跨层协调需要父代理转发。
进阶
指定模型
用 claude-sonnet 起一个子 agent 分析数据库设计
不指定模型 → 继承当前会话模型。thinking 同理:省略时继承当前推理强度,传 "off"…"max" 可覆盖。
模型指定但注册表中找不到 → 报错,不静默降级。
限制工具
让子 agent 调研项目结构,但只允许用 read 和 grep
子 agent 看不到其他任何工具。只读探索 + 便宜模型是调研任务的推荐模式。
子进程启动集成事件
其他扩展可通过事件让子 RPC 进程加入自己的集成,不必依赖 pi-subagents,也不必另设 agent 目录。每次新建或复活 AgentProcess 前,pi-subagents 同步发出 pi.events.emit("pi-subagents:child-launch:v1", launch),载荷是全新的可变对象:
{ env: Record<string, string>, extensions: string[], error?: string }
env、extensions 初始分别为 {}、[]。监听器必须同步完成,不能用 async、Promise 或延迟修改。例如,在扩展工厂中:
const unsubscribe = pi.events.on("pi-subagents:child-launch:v1", (data) => {
const launch = data as {
env: Record<string, string>;
extensions: string[];
error?: string;
};
try {
launch.env.MY_CHILD_SYNC = "1";
launch.extensions.push("/absolute/path/to/child-bridge.ts");
} catch {
launch.error = "Child integration unavailable";
}
});
pi.on("session_shutdown", () => unsubscribe());
- 环境变量补充项覆盖继承的进程环境,但 pi-subagents 的子代理身份与深度字段优先。没有监听器时,环境继承行为不变。此机制本身不修改
PI_CODING_AGENT_DIR,也不传递凭据;请只注入非敏感协调信息,不要注入 token。 - 扩展路径去重后作为
--extension <path>参数传入,新会话和带--session的复活均适用,不替代 Pi 原有扩展发现机制。建议使用绝对路径;相对路径以子进程工作目录为基准。 - 构造进程前会校验并复制载荷;pi-subagents 不把它写进 roster 或会话记录。环境变量、扩展字段的类型或字符串无效时拒绝启动,不做隐式类型转换。
- 只要提供了
error就拒绝启动,非字符串等畸形值同样拒绝。请使用不含路径或 token 的通用错误文字;pi-subagents 只报告通用拒绝原因,不回显原文。事件发出本身的异常继续向上传递。 - Pi 会在内部捕获并记录监听器异常。 此钩子无法检测这些异常,也不等待监听器的异步工作。监听器必须自行捕获准备阶段的失败,并同步设置
error,才能确保失败时不启动子进程。 - 订阅清理由监听器所属扩展负责;pi-subagents 不注销其他扩展的处理器。
工作原理
每个 sub-agent 都是带持久化 session 的常驻 pi --mode rpc 子进程:
- 前台 —
agent_spawn等待子进程 settled,内联返回最终输出。非常驻子代理随后退出,常驻子代理保留以供追问。 - 后台结果 —
agent_spawn立即返回;子代理 settled 后,结果进入会话级收件箱。主代理忙碌时,所有就绪结果在当前回复及工具批次结束后、下一次模型调用前一起送达,也就是 steer 的交付时机;通过turn_end实现,agent_before_settle兜底。不打断生成或工具,不进入 follow-up 队列,不依赖全局 steering 队列的逐条/全部消费设置。空闲时只合并一个事件循环 tick 内已就绪的结果,保留独立卡片、只启动一次模型处理,不等待尚未完成的子代理。这防止排队造成的延迟,不保证报告在后续文件修改后仍然代表最新状态。 - 发消息/停止 —
agent_send双向均用 steering(当前响应及工具批次结束后、下一次模型调用前),空闲子代理会被唤醒;agent_stop关闭 stdin 优雅退出,不新增完成通知,但不删除已排队的有效结果。 - 常驻空闲 — 子代理默认常驻,完成后保持 idle(零 token);之后
agent_send可唤醒继续同一上下文,agent_stop结束。完成通知与 widget 行带idle标记。 - 恢复/失败后继续 — 通过
agent_send或子代理窗口发送一条新消息时,进程已退出,或仍活着但状态为failed/stopped的直接子代理,都会从保存的会话恢复。先等旧进程退出,再启动pi --mode rpc --session <路径>,不重启健康的运行中/空闲进程。新消息开始下一轮,不自动重发之前失败的请求。旧进程清理后重新收集启动联动信息,因此已接入的 Codex 账号切换也作用于替代进程。并发恢复共用一次启动;明确停止或主会话关闭会取消待恢复操作。主代理追问仍回传结果,用户侧聊仍保持私有;已经尝试投递但失败的消息不会被盲目重试。 - 提问与交付 — 每个子任务末尾说明两条向上的通道怎么用。最终回复承载最终汇报和阻塞问题:被卡住时以问题结束本轮回复,等父代理用
agent_send回答。发往@parent的agent_send只承载非阻塞问题:发出问题后接着做;不用它发阻塞问题、进度汇报或结果。完成后直接返回交付,结果自动回传,不额外发送一份完成报告。前台 spawn 的说明另加一句:父代理要等这次回复结束才读到轮中消息,所以非阻塞问题也写进回复。agent_send默认对每个子代理开放,tools白名单里不用列出。 - Attach / 复盘 — sub-agent 会话存储在
<agent 目录>/subagent-sessions/(见配置)。在主会话里找到 session 路径(agent_spawn 调用结果或完成通知卡片),执行pi --session <path>——通知里也带 path,直接问 LLM 也行。 - Graceful turn limits(默认不限,opt-in) — 不设隐藏 deadline:子 agent 一直跑到完成或被停,除非你传
timeoutMs。不做 token 限制——用量仅通知卡统计。
嵌套 sub-agent
默认关闭(maxDepth: 1)。调高 maxDepth 之后:
子 agent 是完整 pi 实例——全局安装本扩展后,它天然能再 spawn 子 agent。每一层都是独立进程、独立上下文;深度倍增启动时间与 token 成本。是否值得,由你(或模型)判断。
机制是纯点对点投递器:agent_send 按 id(短人名,如 max)投给直接子,或投给 "@parent"——其他目标显式报错。路由由 LLM 自行完成:每个 agent 只寻址自己被给到的 id,逐跳转发(孙联系祖 = 每层父 LLM 决定用 @parent 上转)。信息披露是唯一限制——父持有它 spawn 的 agent 的 id;没被告知的 id 谁也发不到。
成本与注意
- Headless 下子 agent 随宿主退出(
pi -p)。主进程在响应结束时退出,后台子 agent 经 stdin EOF 被清理——从不留孤儿。后台流程(等通知、发消息、stop)面向常驻的 TUI 会话。 - 一子 agent 一进程。 前台后台同是常驻 rpc 子进程。开多了 = 进程多了——请节制。
- 结果一次性投递。 按进程及 settlement 的
completion_id在当前运行会话内去重。同一代理不同轮次均保留,即使文本相同。主代理中断或报错后,结果只保存、不强行重启模型,供下一轮使用;若主会话先崩,结果仍在子代理 session 文件里(pi --session <path>)。 - 常驻代理占用内存。 每个空闲代理仍是一个 pi 进程(典型扩展配置下约 180 MB RSS),直到主会话结束或被
agent_stop。一次性任务可设persistent: false,之后仍能从会话恢复追问。 - 恢复依赖保存的会话。 恢复的是对话上下文,不是被中断工具的内存或执行位置。
清理
pi 退出时,子 agent(运行中或 idle)都收到优雅的 stdin-EOF 关闭。session 留在磁盘供 attach/复盘;清理不会删除会话文件。
开发
开发、版本更新与发布规范统一见 AGENTS.md。
许可证
MIT。版权声明和第三方来源说明保留在 LICENSE。