PiAgent 系列 6:pi-subagents 自定义子智能体
上篇介绍了 pi-subagents 的概念、8 个内置角色、fork/fresh 机制与模型/技能继承。本文继续深入:如何定制 agent,以及 pi-subagents 为什么要这样设计——理解这些底层约束,是后续编排工作流的前提。
自定义 Agent
内置的 8 个角色已经覆盖了大多数场景,但实际项目中往往要微调:让 reviewer 用更强的模型、给 scout 关掉网络搜索、或者加一个只负责安全审计的专属角色。pi-subagents 提供了两类自定义方式:一类是通过配置项 agentOverrides 覆盖内置 agent 的元数据;另一类是通过 .md 文件创建或覆盖 agent 定义,同名文件会覆盖内置角色,新名字则作为补充角色。
通过配置覆盖:agentOverrides
agentOverrides 是 Pi settings 中 subagents 下的一个配置对象,写在项目 .pi/settings.json 或用户 ~/.pi/agent/settings.json 里。它用来覆盖内置 agent 的元数据,不需要新建 agent 文件。其特点是:不改 prompt,只调模型、thinking、默认上下文等配置。适合快速对齐不同项目的需求。
{
"subagents": {
"agentOverrides": {
"reviewer": { "model": "deepseek-v4-pro", "thinking": "high", "defaultContext": "fork" },
"scout": { "model": "deepseek-v4-flash" },
"researcher": { "disabled": true }
}
}
}
disabled: true 隐藏不需要的内置 agent。subagents.disableBuiltins: true 一次性禁用所有内置 agent(如果只用自定义角色)。
通过 .md 文件创建或覆盖
在项目或用户目录创建 .md 文件即可定义 agent。如果文件名与内置 agent 同名,就会覆盖内置角色;如果取新名字,就会新增一个补充角色。
项目 .pi/agents/reviewer.md ← 最高优先级(跟项目走,适合团队统一规范)
用户 ~/.pi/agent/agents/reviewer.md ← 次优先级(个人偏好,跨项目复用)
内置 reviewer.md ← 最低优先级
示例——为一个 Web 项目定制审查规则的 reviewer:
---
name: reviewer
description: 我的项目审查员
tools: read, grep, find, ls, bash, edit, write
model: deepseek-v4-pro
thinking: high
systemPromptMode: replace
inheritProjectContext: true
inheritSkills: false
defaultContext: fork
defaultReads: plan.md
skills: security-checklist
---
你是此项目的专属代码审查员。
审查规则:
1. 所有 SQL 必须用参数化查询,不得拼接字符串
2. API 返回格式必须统一为 { code, data, message }
3. 密钥、token、手机号、身份证不得出现在日志中
4. 新增接口必须有入参校验
5. 数据库迁移必须有回滚方案
同名覆盖示例已经展示了一个 Web 项目的专属 reviewer。如果要新增角色,则取一个不与内置 agent 重名的文件,比如 security-auditor.md——只盯安全,不看性能或风格,适合内置角色覆盖不到的垂直职责。
两种机制由浅入深:agentOverrides 适合做轻量参数调整,.md 文件适合做角色重写或新增角色。但无论哪种方式,好的 agent 设计都遵循一些共同规律。
设计一个好 Agent 的几个原则
基于 8 个内置 agent 的设计,可以总结出几条规律:
-
系统提示要窄不要宽。 写清楚"你不做什么"比"你能做什么"更重要。planner 明确说"do not make code changes",oracle 明确说"do not silently become a second decision-maker"这些是角色隔离的核心。
-
工具集给最少。 如果一个 agent 不需要 edit,就别给 edit。每个多余的工具都是一扇可能被误用的门。
-
用
systemPromptMode: replace。 除非有明确的理由(比如 delegate 需要完整 Pi 提示),否则都该用 replace。子 agent 是专注角色,不需要知道 Pi 怎么运作。下一节会深入解释为什么这是角色隔离的基石。 -
产出格式要固定。 worker 的输出模板(Implemented X. Changed files: Y…)不是啰嗦,固定格式让主 agent 能稳定解析结果,做出下一步决策。
这些原则的共同目标是把角色边界做实:子 agent 越专注,主 agent 越容易编排。
下面这个速查表汇总了 agent 文件里可用的 Frontmatter 字段,供写自定义 agent 时参考。
Frontmatter 字段速查
| 字段 | 说明 |
|---|---|
name |
agent 名,同名覆盖内置 |
description |
给 LLM 看的描述 |
tools |
工具白名单。省略则继承全部。subagent 可声明以允许子 agent 内再调子 agent |
model |
默认模型 |
fallbackModels |
主模型失败时的备用(如配额耗尽、超时、认证失败) |
thinking |
off / minimal / low / medium / high / xhigh / max |
systemPromptMode |
replace(替换 Pi 提示)或 append(追加) |
inheritProjectContext |
是否继承 AGENTS.md |
inheritSkills |
是否继承 Pi 技能列表 |
defaultContext |
fork 或 fresh |
skills |
显式指定的技能列表 |
defaultReads |
启动前自动读的文件 |
defaultProgress |
是否维护 progress.md |
completionGuard |
false 可跳过实现完成检查(给只用 bash 的验证类 agent 用) |
设计约束
在学编排之前,有必要先把三个底层约束理解清楚,它们决定了你能怎么编排、不能怎么编排,以及为什么 chain 必须靠文件交接。
systemPromptMode: replace——角色隔离的基石
delegate 以外的所有内置 agent 都用 systemPromptMode: replace。子 agent 拿到的是一个完全替换掉 Pi 默认系统提示的 prompt。
它不知道自己在一个叫 Pi 的工具里,不知道 Pi 怎么运作,不知道 Pi 有哪些自定义扩展。它只知道我是 scout/worker/reviewer,我有这些工具可以做这些事。这比其他 agent 框架常见的「追加一条角色指令」要激进得多,换来的是更强的角色边界。
这种隔离直接决定了编排方式:子 agent 拿不到 Pi 主提示,所以不会主动读文件、不会主动建上下文、不会自我纠正偏离的任务,只能靠你给的 task 和文件引用来工作。这就是 Chain 需要显式文件流转、而不是简单把上一步输出塞进下一步提示的根本原因。
由于 replace,子 agent 看不到 Pi 的默认基础系统提示(以及你在其中写的全局规则)。但 AGENTS.md 不受影响——内置 agent 全部设了 inheritProjectContext: true,项目指令仍然会注入。如果需要子 agent 也带上 Pi 基础系统提示,只能用 systemPromptMode: append(只有 delegate 这么干)。
子 Agent 原则上不能再调子 Agent
子 session 的系统提示明确说「你不是父编排者,不能提议或运行子 agent」。默认不注册 subagent 工具。即使某个 agent 显式声明了 tools: subagent,拿到的也是受限版本,只能执行父 agent 分配的扇出任务,不能自由创建 agent。全局默认最多允许 2 层嵌套(主 session → 子 agent → 子子 agent),maxSubagentDepth 可以收紧,但不能放宽。单个 agent 还可以设更紧的 maxSubagentDepth 限制自己的孩子。
设计意图: 避免树状嵌套爆炸。如果允许子 agent 自由建子 agent,一个审查任务可能不知不觉套三层,token 消耗失控。
contact_supervisor:子 Agent 的反向通道
当 worker 在实施中遇到计划没覆盖、但又必须拍板的决策时,它不会猜测,也不会擅自决定——而是通过 contact_supervisor 联系父 session。流程如下:
worker: contact_supervisor(reason="need_decision", message="需要确认:API 返回格式用 snake_case 还是 camelCase?")
主会话回复: 用 camelCase,跟现有风格一致
worker: 收到回复,继续执行
在 Pi 主会话中,你会收到一条 supervisor 消息,确认或回复后 worker 才会继续。
这是一个受控的升级通道。子 agent 的系统提示强调:不要把它当作常规完成汇报的通道,只在被阻塞或需要决策时才用。 reason 有三个选项:need_decision(阻塞性决策)、interview_request(结构化输入)、progress_update(非阻塞更新)。
为什么要先理解这些
这三个约束共同解释了 pi-subagents 的编排风格:
- 因为
replace,子 agent 不维护上下文,所以多步骤协作要靠文件交接,而不是靠对话历史。 - 因为禁止自由嵌套,复杂流程必须由父 session 显式编排,不能指望某个子 agent 自己再拆一层。
- 因为
contact_supervisor是受控通道,子 agent 遇到阻塞可以安全升级,而不是擅自猜测。
理解了这个设计前提,下一篇就可以放心地进入编排本身:如何并行运行多个 agent,如何用 Chain 串联它们,以及如何保存和复用这些工作流。