从一个 RL 类比出发,搭建面向日常 Java 开发的轻量 Pi Workflow
摘要
这个项目最初来自一个与主流 LLM Agent 叙事略有不同的观察:对于不训练基础模型的 Agent 开发者,LLM 并不是一个可以直接更新参数的 policy,而更像一个通过 API 访问、行为带有随机性、只能用输入影响输出的黑盒组件。开发者真正能调整的是 prompt、memory、context selection、tool policy、阶段路由和失败恢复。因此,与其把全部注意力放在“如何训练 LLM”,不如先把外层 harness/controller 变成一个可观测、可评估、可迭代的对象。
这个观察有工程价值,但需要收紧表述。将 LLM 直接等同为 RL environment 并不严谨,因为真实环境还包括代码仓库、工具、外部服务、人类决策和工作区状态;LLM 更适合被视为环境中的一个冻结随机组件。真正可学习的对象则是外层 controller policy:它决定下一步调用哪个 Skill、加载哪些证据、使用什么 prompt、何时请求人工确认,以及哪些 ticket 可以并行。
基于这一边界,我选择 Pi 作为执行内核,将 Matt Pocock 的 engineering skills 作为工程动作,在一个 Java 仓库中实现了轻量状态机、研究门禁和单 ticket TDD 约束。第一次真实运行已经证明了这条路线的价值,也暴露了当前原型最关键的缺口:阶段状态虽然持久化了,但阶段产物还没有自动成为下一阶段的显式输入。research-gate 与 grill-with-docs 即使位于同一个 Pi session,仍不能只依赖聊天历史完成可靠交接。
本文记录这套思路的来路、研究坐标、当前实现、真实运行证据,以及从规则驱动 workflow 走向反馈优化的合理顺序。
一、最初的想法:把可控边界从模型参数移到外层系统
在常见的 LLM Agent 类比中,LLM 被视为 policy,上下文是 observation,工具调用是 action,工具和外部世界共同形成 environment。这个描述适合讨论模型如何根据 observation 选择 action,也适合研究模型参数的 RL 训练。
但日常 Agent 工程面对的是另一种控制边界。使用 DeepSeek、Claude 或 GPT API 的开发者通常不能更新模型权重,只能:
- 选择模型和推理等级;
- 组织 system prompt、task prompt 和 few-shot 示例;
- 决定从仓库、知识库和历史记忆中加载什么;
- 定义工具、权限、工作流状态和人工审批点;
- 根据运行结果修改上述配置。
因此,从工程师的视角看,一次模型调用确实很像一个带随机性的状态转移:给定当前任务状态和输入,冻结模型产生候选输出,随后工具执行、人类反馈和外部系统共同把任务推进到下一个状态。这个类比将优化目标从“训练模型参数”转向“优化复合 Agent 系统”。DSPy 把 LM pipeline 表达为可优化的参数化模块,并针对指标编译 prompt 和 demonstrations;TextGrad 使用 LLM 产生文本反馈,优化复合系统中的文本变量;ADAS 更进一步,把 prompt、tool use 和 control flow 都纳入可搜索的 agent code 空间;Agent Lightning 则强调 Agent 执行与 RL 训练的解耦,并通过统一轨迹接口处理复杂工作流的 credit assignment。1234
这些工作说明,“冻结或外部提供的模型 + 可优化外层系统”并不是空白方向。真正需要保留的创新点不是重新命名 RL 四元组,而是选择一个具有实际约束的工程优化对象:
| 概念 | 本项目中的定义 |
|---|---|
状态 S_t | 需求、仓库事实、已批准产物、当前阶段、未决问题、分支和工作区状态 |
Controller action A_t | 选择下一 Skill、context pack、prompt 版本、研究预算、模型配置和 ticket frontier |
| 环境 | 冻结 LLM、代码仓库、工具、Citadel、测试系统和人类审批共同组成的执行环境 |
| 转移 | 一次 Pi 执行产生的新 artifact、代码变更、工具结果和 workflow 状态 |
| 反馈 | 验收、测试、review 返工、阻塞、token、轮数、耗时和人工评价 |
这里最重要的修正是:LLM 不是整个 environment,而是 environment 中最难预测的一个组件;外层 workflow/controller 才是当前可以训练或搜索的 policy。 这使研究问题从概念类比落到可复现实验对象,也避免把文件写入、Git 状态和人工批准错误地归入“LLM 输出状态”。
二、为什么先做 Workflow,而不是马上做 RL
一个系统只有先稳定记录状态、动作和结果,才有资格谈反馈优化。否则,reward 只能对最终文本打分,无法回答哪个阶段、哪条 prompt 或哪次上下文选择造成了结果变化。
我对 Java 日常开发的真实需求不是让 Agent 自由探索,而是让它可靠走完以下链路:
需求输入
-> 仓库与内部知识研究
-> 只针对未决事项澄清
-> Spec 与测试 seam 审批
-> 垂直切片 Tickets 与 blocker 审批
-> 每个 Ticket 独立会话和 worktree
-> TDD 实现
-> Maven 验证与独立 Review
-> 验收证据回写
Matt Pocock Skills 已经提供了其中的高质量工程动作:grill-with-docs、to-spec、to-tickets、implement、tdd 和 code-review。但这些 Skill 有意把路由权留给使用者。官方说明中,grill-with-docs 会在当前对话中逐个澄清问题,并把术语和难以逆转的决定写进 CONTEXT.md 与 ADR;主链仍需要使用者显式推进到 to-spec -> to-tickets -> implement -> code-review。5
这恰好为轻量 controller 留出了位置。它不替模型写业务方案,也不把每个推理步骤编码成流程图,只负责五件事:
- 当前处于哪个阶段;
- 进入下一阶段需要什么证据;
- 哪些转换需要人工批准;
- 新会话必须加载哪些产物;
- 失败后回到哪个可恢复状态。
这也是为什么第一阶段不应该直接上 RL。当前没有足够的真实任务轨迹,没有校准过的 reward,也没有稳定的 action space。此时训练只会把偶然偏好固化成策略。更合理的顺序是:先用确定性规则跑通闭环,再积累 accepted/blocked、测试、返工、耗时和 token 数据,之后才判断需要普通搜索、contextual bandit,还是长时序 RL。
三、为什么选择 Pi:薄执行内核与显式扩展边界
Pi 将自己定位为 minimal terminal coding harness。它提供模型调用、Agent loop、工具、交互界面和 session 持久化,同时把 plan mode、subagent 和团队工作流留给 Extension、Skill 或第三方 Package。官方仓库明确区分了 coding agent CLI、带工具与状态管理的 agent runtime,以及统一多模型 API。6
这与本项目的目标一致:不构建一个重新实现 Coding Agent 的重框架,而是在 Pi 外围增加很薄的工程控制。
当前本地原型的运行快照如下:
| 项目 | 当前状态 |
|---|---|
| Java 仓库 | /Users/lingdu/workspace/harness/expri/v3 |
| Pi | 本地安装 0.74.2 |
| 冻结模型 | deepseek/deepseek-v4-flash,已完成真实请求验证 |
| Matt Skills | 固定在 commit 2ab958093e83e0ec752e6c1c5932da465bf23e0c |
| 项目配置 | .pi/settings.json |
| 状态机 Extension | .pi/extensions/java-workflow.ts |
| 研究门禁 | .pi/skills/research-gate/SKILL.md |
| Ticket 执行约束 | .pi/skills/java-ticket-execution/SKILL.md |
| Run 产物 | .pi/workflow/runs/<feature>/ |
状态机只保存阶段和证据路径:
researching
-> grilling
-> spec-review
-> ticket-review
-> ready
-> implementing
-> reviewing
-> accepted
任一非终态 -> blocked -> 返回最早可解决问题的阶段
对应的三个自定义命令也保持最小:
/flow-start <feature-slug>
/flow-status <feature-slug>
/flow-record <feature> <next-stage> <artifact-path>
/flow-record 只允许状态机中合法的下一跳,并验证 artifact 位于当前仓库且真实存在。它不替用户批准需求,不自动修改 Java 代码,也不把一个 Skill 的输出伪装成验收完成。
Pi 的 Session 与 Skill 机制也适合这类轻编排。Session 自动保存为 JSONL 树;同一个交互式 Pi 进程中的连续输入属于同一个 session。/new 会开启新 session,/fork 和 /clone 会创建新的 session 文件,退出后用普通 pi 启动通常也会进入新 session,而 pi -c、pi -r 或 --session 才会恢复已有 session。7 /skill:name 本身不会开启新 session;它只是按需加载 Skill 内容,并将命令参数追加为本轮用户输入。8
这意味着 research-gate -> grill-with-docs -> to-spec -> to-tickets 可以在同一 session 中运行,而每张 ticket 的实现应主动使用 fresh session 和独立 worktree。前者需要共享讨论语境,后者需要隔离实现噪声和写入冲突。
四、第一次真实运行:MRD 问题不是“另开了会话”
第一次运行使用了 asset-return-reminder 作为 feature slug。实际顺序是:
/flow-start asset-return-reminder
/skill:research-gate asset-return-reminder 在员工离职时增加资产归还提醒功能……
/flow-record asset-return-reminder grilling \
.pi/workflow/runs/asset-return-reminder/evidence.md
/skill:grill-with-docs
当前 run 已经处于 grilling,并登记了 evidence.md。对 Pi session JSONL 的只读核对也确认:Research Gate Skill、grill-with-docs Skill、evidence 写入和“MRD 说了什么”的提问都出现在同一个 session 文件中。因此,这次现象不是因为两个 Skill 自动创建了不同会话。
research-gate 实际完成了大量有效工作:它读取当前 Java 仓库、相关测试和远端参考分支,识别出两套尚未合入的参考实现,梳理了现有离职提醒能力与新需求的边界,也列出了依赖和设计冲突。但读取两份内部 MRD 时,Citadel 被 CIBA 身份配置阻塞,无法取得文档正文。于是 evidence.md 明确把“MRD 内容”列在 Unknowns 第一项,并把“请提供 MRD 内容或摘要”列为 Questions for the user 第一项。
随后 grill-with-docs 提问:
MRD 说了什么?这是唯一只有你持有的事实,我无法从环境查到。
从 workflow 规则看,这个问题是合理的。研究阶段已经穷尽了当前可用证据,MRD 又是决定产品范围、境内外边界、提醒轮次和渠道的原始事实;grill-with-docs 本来就应该只追问无法由环境回答的事项。真正需要改进的是表达与交接机制:它应该明确引用 evidence.md 中的检索失败和两个 MRD 标识,而不是笼统地说“只有你持有”。事实上,MRD 并非天然只有用户持有,只是当前 Agent 没有通过 Citadel 鉴权。
这次运行暴露出三层不同的问题:
| 层次 | 实际情况 | 结论 |
|---|---|---|
| Session 连续性 | 两个 Skill 位于同一 Pi session | 没有发生会话丢失 |
| 事实可达性 | Citadel 因身份配置失败,MRD 未读 | 需要用户提供内容或恢复只读检索能力 |
| Artifact handoff | controller 记录了路径,但不会自动把 artifact 注入下一阶段 | 当前实现仍依赖聊天历史和模型主动读文件 |
第三点是原型的真正缺口。当前 flow-record 只更新 run.json,没有在下一阶段开始前强制读取已登记 artifact。相同 session 下,模型可能从聊天历史记得研究结论;经过 compaction、resume 或 fresh session 后,这种依赖就不可靠。Pi 官方也明确说明 compaction 是有损的,即使完整历史仍保存在 JSONL 中,当前模型上下文只保留摘要和最近消息。7
因此,下一版 controller 不应通过加长 system prompt 修补,而应增加显式 handoff:
flow-next(feature)
-> 读取 run.json
-> 校验当前阶段及批准状态
-> 读取该阶段登记的 artifact
-> 生成受长度约束的 handoff packet
-> 将 artifact 路径、关键事实、未知项和下一步注入当前轮
对于同一 session,handoff 防止模型只凭近期对话继续;对于 fresh session,bootstrap 命令应从 artifact 重建最小上下文,而不是复制完整聊天记录。这样,文件才是阶段契约,session 只是执行容器。
五、从 Spec 到并行 Tickets:fresh session 不能等于丢失上下文
计划阶段适合同一 session,因为 grill-with-docs、to-spec 和 to-tickets 依赖已经对齐的语言和决策。实现阶段则相反:Matt 的 to-tickets 要求每张票是可在一个 fresh context 内完成、能够独立验证的垂直切片,并声明 blocking edges。9
每张 ready ticket 的正确执行单位应是:
Ticket = 独立 ticket 文件
+ 已批准 spec
+ 相关 evidence/ADR/CONTEXT
+ 独立 Git worktree
+ 独立 Pi session
+ 一个拥有完整 RED-GREEN 循环的 writer
+ 独立 read-only review
这里不应把 TDD 拆成“测试 subagent”和“实现 subagent”两个并发 writer。RED 测试定义了下一小步的行为契约,GREEN 实现必须立即消费这个反馈;拆给不同 writer 会产生竞态和对测试意图的二次解释。可以委托 read-only scout 查资料,也可以在完成后让另一个 reviewer 对 spec 和 diff 评审,但一个 ticket 的写入权应始终只有一个 owner。
多个 ticket 只有同时满足以下条件才能并行:
- blockers 已全部验收,而不只是“代码写完”;
- 修改的 Maven module、数据库 schema、生成代码、共享测试资源和构建配置不重叠;
- 每张 ticket 都有独立 worktree 和 session;
- 后继 ticket 只根据验收证据解除阻塞;
- 合并顺序与冲突处理规则已经明确。
当前原型还没有完成这个闭环。首先,.pi/ 在 Java 仓库中仍是未跟踪目录,新 worktree 不会自动继承 Extension 和本地 Skills。其次,当前 run.json 只记录 feature 级阶段,无法同时表达 ticket-01=reviewing、ticket-02=implementing、ticket-03=blocked。再次,run 目录被忽略,各 worktree 之间没有共享或聚合状态。
所以,当前版本已经可以运行研究、澄清、Spec 和 ticket 拆分,也可以指导单 ticket 的 TDD,但还不能宣称具备真正的多 ticket session 调度。要实现这一目标,最小数据模型需要从单一 feature stage 扩展为:
FeatureRun
specStatus
ticketGraph
tickets[]
id
blockers[]
ownership[]
worktree
branch
sessionId
stage
evidence[]
这里仍不需要中央“智能调度 Agent”。一个确定性的 frontier 计算器、worktree 创建器和 session bootstrap 就足够;重叠 ownership 或高风险变更继续交给人确认。
六、从可观测 Workflow 到反馈优化
当 workflow 能稳定产出轨迹后,最初的“自进化”想法才可以重新进入工程计划。第一步应优化外层 controller,而不是 DeepSeek V4 Flash 的模型权重。
可调整参数可以分为三档:
| 风险 | 可优化内容 | 例子 |
|---|---|---|
| 低 | 表达和上下文选择 | research query、handoff 摘要、prompt variant、context pack 排序 |
| 中 | 预算与路由 | 是否进入 Citadel、研究深度、thinking level、review 强度、是否 compact |
| 高 | 交付结构 | ticket 拆分、并行 frontier、阶段回退、是否允许自动实施 |
第一版只应开放低风险参数。产品范围、权限、数据库变更、ticket 验收和部署决策不能因为 reward 更高而绕过人工 gate。
Reward 也不能只用一个 LLM judge 的“输出质量分”。LLM judge 已被观察到存在位置偏差等系统性偏差;一旦 controller 针对 judge 长期优化,还会产生 reward hacking 风险。10 对 Java 开发,更稳妥的做法是先设置硬门槛,再计算软分数:
Hard gates:
spec 已批准
无越权/越范围修改
指定测试通过
mvn compile/test 通过
review 无阻塞问题
Soft reward:
R = 交付正确性
+ 证据完整度
- review 返工次数
- 无效提问轮数
- token 成本
- wall-clock 时间
- blocked 恢复成本
Token、轮数和耗时只能在 hard gates 通过后比较。否则 controller 很容易通过少查资料、少写测试或提前结束来获得更高分。
建议的演进顺序是:
- 规则基线:固定模型、固定 Skill 版本、固定状态机,收集真实运行数据;
- 离线回放:用已完成任务比较不同 handoff、prompt 和 context selection,不修改生产流程;
- 候选搜索:使用网格搜索、贝叶斯优化或 DSPy/TextGrad 类方法优化低风险文本变量;
- Contextual bandit:针对任务特征选择研究预算或 prompt variant,并保留人工审批;
- 长时序 RL:只有当多阶段 credit assignment 明确、样本量足够、reward 经人工校准后再考虑。
这条路线与 Agent Lightning 的“执行与训练解耦”在架构上相近,但优化目标不同:Agent Lightning 的主要目标是训练 Agent 中的 LLM;本项目首先优化冻结模型之外的 controller policy。与 ADAS 相比,本项目的搜索空间也更窄,暂不允许元 Agent 任意生成 Agent 代码,而是在受审查的 prompt、context、route 和 scheduling 参数中搜索。这个收缩不是能力不足,而是内部 Java 开发对可解释性、权限和回滚的现实要求。
结论
最初的想法并不需要因为“LLM 不是严格意义上的 environment”而放弃。更有研究与工程价值的重述是:当基础模型被冻结且只能通过 API 访问时,把 Agent 看成一个可观测的复合系统,并学习或搜索其外层 controller policy。
Pi 提供了合适的薄执行内核,Matt Skills 提供了可读、可替换的工程动作。当前 Java workflow 已经证明了最小状态机、research gate 和 artifact gate 可以在很少提示词下工作。第一次真实运行也给出了比概念讨论更重要的结论:
/skill:research-gate与/skill:grill-with-docs可以并且实际运行在同一 session;- MRD 提问源于 Citadel 访问失败后留下的真实未知项,不是自动换会话造成的失忆;
- 同一 session 并不能替代 artifact handoff,阶段切换必须显式加载证据;
- fresh ticket session 必须由 spec、ticket、context pack 和验收证据重建上下文;
- 多 ticket 并行需要 ticket 级状态、worktree 隔离和 frontier 调度,当前原型尚未完成;
- 在这些轨迹稳定以前,不应训练 controller,更不应把 LLM judge 分数直接当作唯一 reward。
因此,下一步不是增加更多 Skill 或更长 system prompt,而是补齐两项最小运行时能力:artifact-driven stage handoff 与 ticket-level session/worktree registry。完成后,这套系统才会从“可以手动推动的 Skill workflow”变成“能够积累训练数据的轻量 harness”。
参考资料
本地实现证据
/Users/lingdu/workspace/harness/expri/v3/.pi/settings.json/Users/lingdu/workspace/harness/expri/v3/.pi/extensions/java-workflow.ts/Users/lingdu/workspace/harness/expri/v3/.pi/skills/research-gate/SKILL.md/Users/lingdu/workspace/harness/expri/v3/.pi/skills/java-ticket-execution/SKILL.md/Users/lingdu/workspace/harness/expri/v3/.pi/workflow/README.md/Users/lingdu/workspace/harness/expri/v3/.pi/workflow/runs/asset-return-reminder/run.json/Users/lingdu/workspace/harness/expri/v3/.pi/workflow/runs/asset-return-reminder/evidence.md
本文状态核对时间:2026-08-03。版本、模型目录、仓库分支和外部文档可能变化;后续实验应固定 Pi、Skills、模型和数据集版本后再比较结果。
参考资料
Omar Khattab et al. DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines, 2023. ↩
Mert Yuksekgonul et al. TextGrad: Automatic “Differentiation” via Text, 2024. ↩
Shengran Hu, Cong Lu, Jeff Clune. Automated Design of Agentic Systems, ICLR 2025. ↩
Xufang Luo et al. Agent Lightning: Train ANY AI Agents with Reinforcement Learning, 2025. ↩
Matt Pocock Skills.
grill-with-docs,to-spec,to-tickets. ↩Earendil Works. Pi Agent Harness and Pi Coding Agent. ↩
Pi Coding Agent. Sessions and Compaction. ↩ ↩
Matt Pocock Skills.
to-ticketsvertical slices and blocking edges. ↩Lin Shi et al. Judging the Judges: A Systematic Study of Position Bias in LLM-as-a-Judge, 2024. ↩