首页
/ 用 pi 的 subagent 链式工作流实现 scout → planner → worker 全流程代码实施

用 pi 的 subagent 链式工作流实现 scout → planner → worker 全流程代码实施

2026-09-06 19:13:51作者:卓炯娓

implement.md 是 pi(coding-agent)示例扩展目录中提供的一份"工作流预设(workflow preset)":它本身不是一段直接执行的代码,而是一份写给模型看的分步指令,指导模型通过 subagent 工具的 chain 参数,把一次大型代码变更拆成"侦察上下文 → 制定计划 → 动手实现"三个接力阶段。读完本文,你将掌握该预设的完整语义、它背后 {previous} 占位符与链式执行的源码机制、以及 scout / planner / worker 三类子代理的角色划分,能够直接把它安装进本机并二次定制出自己的多智能体实施流水线。

一、implement.md 是什么:一条可斜杠调用的三阶段实施流水线

以仓库根目录为起点,该文件的完整路径为 packages/coding-agent/examples/extensions/subagent/prompts/implement.md,正文只有两段,却定义了完整的端到端实现流程:

  1. 第一步:用 "scout" 代理找出与任务($@)相关的所有代码;
  2. 第二步:用 "planner" 代理,基于上一步产出({previous})为任务制定实现计划;
  3. 第三步:用 "worker" 代理,基于上一步的计划({previous})落地实现。

文件开头明确要求以链(chain)方式执行,阶段之间通过 {previous} 传递上一阶段的输出。其 frontmatter 中的 description 字段(Full implementation workflow - scout gathers context, planner creates plan, worker implements)会在斜杠命令自动补全时展示给用户。

这份文件与同目录下的另外两份预设共同构成了可复用的流水线模板族:

预设文件(仓库相对路径) 流水线 说明
implement.md scout → planner → worker 完整的"调研—规划—实施"闭环
scout-and-plan.md scout → planner 只产出计划,明确禁止动手实现
implement-and-review.md worker → reviewer → worker 实现后交 reviewer 审查并回收反馈,二次落盘

为什么它能在编辑器里用 /implement <需求> 触发

根据 packages/coding-agent/docs/prompt-templates.md 的约定:prompt template 是会被展开为完整提示词的 Markdown 片段,在编辑器输入 /name 即可调用,name 即去掉 .md 后的文件名。~/.pi/agent/prompts/*.md、项目级 .pi/prompts/*.md、包内 prompts/ 目录、pi.prompts 配置项等位置都会被加载。

模板内支持占位符:$1$2 为位置参数,$@(或 $ARGUMENTS)表示拼接后的全部参数。所以 implement.md 中的 $@ 就是用户在 /implement add Redis caching to the session store 这类命令中输入的剩余文本,模板展开后该文本会进入 scout 的任务描述;而 frontmatter 中未写 argument-hint 时,补全列表仅显示 description

值得强调的是:这份预设交付给主模型的是一段"操作剧本"——真正干活的是 subagent 工具。也就是说,模型读到模板正文后,需要自己发起一次携带 chain 参数的 subagent 工具调用,才把三段流水线跑起来。

二、理解 {previous}:链式阶段之间的"交接棒"机制

{previous} 是链(chain)模式下最重要的语义。它不是一个通用模板变量,而是 subagent 工具内部约定的"上一步最终输出"占位符,其替换逻辑可以直接在源码中看到:

index.ts 的链式分支中,工具会对 chain 数组中的每个 step 顺序执行,并在把任务交给子进程前完成替换:

// index.ts
const taskWithContext = step.task.replace(/\{previous\}/g, previousOutput);

previousOutput 来自 getFinalOutput(result.messages)——它倒序遍历消息,取出最后一条 assistant 消息中最后一个 text 类型内容(index.tsgetFinalOutput 实现)。这意味着:下游代理拿到的是上游"总结性的最终文本输出",而不是完整会话、也不是所有工具调用明细。这正是流水线各角色要把输出格式固定下来的原因(详见第三节):scout 的发现、planner 的计划,都必须以结构化 Markdown 文本收尾,才能被下一棒高效消费。

这一机制让三份预设拥有了统一的设计语言:

  • implement.md:planner ... using the context from the previous step (use {previous} placeholder)
  • implement-and-review.md:reviewer 审查"上一阶段的实现"、worker 应用"审查反馈";
  • scout-and-plan.md:planner 基于 scout 的上下文产出计划。

角色名称硬编码在预设文案中(scout / planner / worker / reviewer),因此调用这些预设前,本机必须已安装同名子代理定义。

三、三个角色的 Agent 定义与职责契约

子代理(agent)是带 YAML frontmatter 的 Markdown 文件:frontmatter 声明元信息,正文即该代理的系统提示词。与 implement.md 同目录的四个角色定义位于 agents/ 下。

scout:快速侦察并压缩上下文

agents/scout.md 的定位是"侦察兵"——它不实现任何功能,而是快速摸清代码库并返回压缩后的结构化发现,供一个完全没有看过这些文件的后续代理直接使用。其 frontmatter 声明:

---
name: scout
description: Fast codebase recon that returns compressed context for handoff to other agents
tools: read, grep, find, ls, bash
model: claude-haiku-4-5
---

侦察的详细程度(Thoroughness)由任务推断:Quick 只做针对性查找;Medium 跟随 import 并阅读关键片段(默认);Thorough 则要追踪全部依赖并检查测试与类型。其输出契约被固定为四段 Markdown,正是为了便于被 {previous} 机制完整传递:

  1. Files Retrieved:精确到行号范围的文件清单,例如 path/to/file.ts (lines 10-50)
  2. Key Code:关键类型、接口与函数(附真实代码片段);
  3. Architecture:各片段如何衔接的简要说明;
  4. Start Here:建议优先阅读的文件及理由。

planner:只读分析与计划产出

agents/planner.md 明确声明"You must NOT make any changes. Only read, analyze, and plan",工具集只含 read, grep, find, ls(没有 write/edit/bash),从能力上就禁止它越权改码。它接收 scout 的发现与原始需求,输出固定结构的计划:Goal(一句话目标)→ Plan(编号的小步操作,每步指向具体文件/函数)→ Files to ModifyNew FilesRisks,并强调"计划要足够具体,worker 将逐字执行它"——这是链式交接对输出质量的核心要求。

worker:拥有完整能力的执行者

agents/worker.md 是全能力、无工具限制的通用执行代理(frontmatter 中不写 tools 字段,即继承全部默认工具),运行在隔离上下文窗口中,避免污染主会话。完成后的输出格式为:Completed(做了什么)、Files Changed(变更文件与内容)、Notes(主代理需要知道的事项);若还要交给下一个代理(如 reviewer),必须附上精确的变更文件路径与涉及的关键函数/类型清单——这一约定直接服务了 implement-and-review 这类"worker → reviewer → worker"的回环流程。

frontmatter 与发现机制(agent 如何被读到)

从源码看,代理定义的文件格式与目录语义是确定性的:

  • agents.ts 中的 parseToolList 同时接受 tools: read, bash 字符串与 tools: [read, bash] 数组两种写法,坏文件会被跳过而不是拖垮同目录其他代理;
  • loadAgentsFromDir 只读取目录下 .md 文件(普通文件与符号链接均可),用 parseFrontmatter 解析 YAML,要求 namedescription 必须是字符串才纳入;
  • 代理来源有两类:用户级 ~/.pi/agent/agents/*.md 与项目级 .pi/agents/*.md(沿 cwd 向上查找最近的 .pi/agents 目录)。scope 为 both 时项目级同名代理覆盖用户级;
  • 每次工具调用都会重新执行 discoverAgentsagents.ts),因此会话中途修改 agent 定义也能立即生效。

从源码结构可以推断:如果本机没有 scout / planner / worker 这三个名字,runSingleAgent 会在调用前就返回错误(stderr 中给出可用代理列表),因此流水线预设与角色定义必须配套安装。

四、subagent 工具的三种执行模式与 chain 的运行语义

承载该预设的底层是 subagent 工具本身(扩展入口 index.ts)。它的参数使用 TypeBox 声明,支持三种互斥模式:

模式 参数结构 语义
Single { agent, task } 单代理执行单任务
Parallel { tasks: [{ agent, task }] } 多任务并发(上限 8 个任务、4 并发),互不依赖
Chain { chain: [{ agent, task }] } 顺序执行,任务文本中 {previous} 会被替换为上一步最终输出

每个调用必须恰好命中一种模式(源码中通过 modeCount !== 1 做参数校验),chain 数组的每个元素还支持可选的 cwd 字段来指定该步骤的工作目录。

Chain 模式在 index.ts 中的运行语义包括:

  • 按数组顺序逐个执行 runSingleAgent,执行完一步就把该步的最终文本输出存入 previousOutput,供下一步替换;
  • 遇到失败立即中断:当某步 exitCode !== 0stopReason"error"/"aborted" 时,返回 Chain stopped at step N (agent): 错误信息,并标记 isError: true——这正是"只把侦察结果交给计划者、而不是把残缺输出继续往后传"的保障;
  • 全部成功时,返回最后一步(worker)的最终输出作为整个工具对主模型的结果。

每个子代理其实是一个独立的 pi 进程

runSingleAgent 通过 spawn 派生一个真正的 pi CLI 子进程,从而实现"隔离上下文窗口"。构建出的子进程参数包括(index.ts):

  • --mode json -p --no-session:以 JSON 模式、无交互、无会话方式运行,便于解析结构化事件流;
  • --model <model> 与(在继承场景下)--thinking <level>:若 agent frontmatter 未声明 model,则继承当前会话的活动模型与思考级别(即 agent.model 为空时走继承分支);
  • --tools read,grep,...:按 frontmatter 声明的工具白名单传递(worker 不写 tools,则不加该参数、使用默认全套);
  • --append-system-prompt <临时文件>:agent 的系统提示词正文被写入一个权限为 0600 的临时 Markdown 文件后追加给子进程,运行结束即删除;
  • 任务文本通过 Task: ${task} 追加在末尾。

子进程的 stdout 按行解析 JSON 事件:message_end 累加 turns、token、缓存读写与 cost 等用量统计,tool_result_end 汇聚工具结果消息;stderr 被收集为诊断信息。因此 implement.md 描述的三段流水线,真实发生时是三次独立的模型推理:scout 子进程读完代码并总结 → planner 子进程基于总结出计划 → worker 子进程基于计划改代码,各自上下文互不串扰。

五、把 implement 工作流装进你的 pi

示例扩展目录 subagent/ 的 README 给出了完整的安装方式,核心思路是把扩展与资源软链接到用户级配置目录(以下为 README 中步骤的要点,完整命令请查阅该 README):

# 1) 软链接扩展本体(必须放在含 index.ts 的子目录中)
mkdir -p ~/.pi/agent/extensions/subagent
ln -sf "<仓库>/packages/coding-agent/examples/extensions/subagent/index.ts" ~/.pi/agent/extensions/subagent/index.ts
ln -sf "<仓库>/packages/coding-agent/examples/extensions/subagent/agents.ts" ~/.pi/agent/extensions/subagent/agents.ts

# 2) 软链接角色定义到用户级 agents 目录
for f in <仓库>/packages/coding-agent/examples/extensions/subagent/agents/*.md; do
  ln -sf "$PWD/$f" ~/.pi/agent/agents/"$(basename "$f")"
done

# 3) 软链接工作流预设到用户级 prompts 目录
for f in <仓库>/packages/coding-agent/examples/extensions/subagent/prompts/*.md; do
  ln -sf "$PWD/$f" ~/.pi/agent/prompts/"$(basename "$f")"
done

目录结构对应关系:

~/.pi/agent/extensions/subagent/   # 扩展(index.ts + agents.ts),注意必须带 index.ts
~/.pi/agent/agents/                # scout.md / planner.md / worker.md / reviewer.md
~/.pi/agent/prompts/               # implement.md / scout-and-plan.md / implement-and-review.md

安装完成后即可在编辑器中使用:

  • /implement add Redis caching to the session store:触发三阶段完整实施;
  • /scout-and-plan refactor auth to support OAuth:只调研与规划;
  • /implement-and-review add input validation to API endpoints:实现—审查—修订回环。

也可以不依赖预设,直接在对话中提出自然语言任务让模型组合 subagent 工具,例如"先让 scout 找到 read 工具的实现,再让 planner 基于它的发现给出改进建议"。

配置注意:四个示例角色的 frontmatter 分别指定了 claude-haiku-4-5(scout,快速省钱)与 claude-sonnet-4-5(planner / worker / reviewer)等模型名,这些是仓库示例的默认值;实际使用时若本机可用模型列表不同,需自行调整;当省略 model 字段时,子代理会自动继承主会话的活动模型与思考级别(该逻辑在 runSingleAgentinheritsDispatchConfig 分支中),这是让子代理模型跟随当前会话最省心的做法。

六、运行效果与结果解读

子代理执行过程是流式的:父模型可以实时看到每个阶段的工具调用与进展。从 index.ts 的渲染逻辑看,chain 模式会按步展示状态:折叠视图下每步显示 ─── Step N: agent ✓/✗、最近若干条工具调用与文本,以及格式化的用量统计行(例如 3 turns ↑input ↓output RcacheRead WcacheWrite $cost ctx:contextTokens model,数值超过 1000 自动缩写为 k/M);展开视图(Ctrl+O)则展示完整任务文本、全部工具调用、按 Markdown 渲染的最终输出,以及"每步用量 + 总计用量"。

对 implement 工作流而言,最有价值的观察点是每步的 token 与 cost:scout(Haiku)的侦察开销、planner(Sonnet)的规划开销、worker(Sonnet)的实施开销会被分别统计,便于定位流水线中的成本热点。

并行模式的流式 UI 会展示每个任务的状态与"2/3 done, 1 running"这样的进度行,且每个任务返回给父模型的输出被限制在 50 KB 以内(完整结果保留在工具详情中);当子进程在产出前退出时,会回传 stderr/错误信息作为失败诊断。这些能力同样来自 subagent 工具本身,供编排更复杂的并行流水线时参考。

七、错误处理、限制与安全边界

失败语义

  • 子进程 exitCode !== 0:工具返回错误并附 stderr/输出;
  • stopReason === "error":透传 LLM 错误信息;
  • stopReason === "aborted":用户中断(Ctrl+C)会传播到子代理进程(先 SIGTERM,5 秒未退出再 SIGKILL)并抛出错误;
  • Chain 模式在首个失败步骤处停止,明确报告失败的是第几步、哪个 agent。

已知限制

README 与源码常量:折叠视图只保留最近 10 条展示项(可展开查看全部);并行模式单任务可见输出上限 50 KB;并行任务上限 8、并发 4;agent 每次调用都重新发现(代价是允许会话中编辑,换来即时生效)。

安全边界:为什么要给 worker 全能力保持警惕

该扩展会在独立 pi 子进程中执行,并携带委派好的系统提示词与工具/模型配置,因此子代理拥有真实执行能力(worker 可读写文件、跑 bash)。默认只加载用户级代理 ~/.pi/agent/agents;若要启用项目级 .pi/agents/*.md(这些文件由仓库控制、可能指示模型读取文件或执行命令),必须显式传入 agentScope: "project""both"——交互模式下,对不受信任的项目会在运行项目级代理前弹出确认框(可用 confirmProjectAgents: false 关闭确认),仅受信任项目免去二次确认。该逻辑对应 index.ts 中的项目代理确认分支。reviewer 的系统提示词中也有对应的防护约定:bash 仅限 git diff/git log/git show 等只读命令,并明确声明"不要假定工具权限可以完美约束行为"。

在集成 implement 这类三阶段流水线时,应遵循同样的原则:scout/planner 是只读角色(工具白名单已约束),worker 是全能力角色——务必在可信仓库中、或在对项目代理进行过审阅后才运行完整实施链。

结语

implement.md 展示的不只是一条"scout → planner → worker"的快捷指令,更是一套值得复用的流水线设计范式:用不同模型与工具白名单切分角色(侦察、规划、实施、审查各司其职)、用固定的输出契约配合 {previous} 占位符做结构化交接、用独立的 pi 子进程隔离上下文、用 chain 的失败即停语义保证接力质量。理解这套机制后,你可以照猫画虎地把 scout-and-plan、implement-and-review 组合进自己的工作流,甚至基于相同的 chain 参数自定义出"安全审计 → 修复 → 复核"等更多专业化流水线。

登录后查看全文
热门项目推荐
相关项目推荐