从零搭建自己的 AI Agent:harness 工程方法论 + Pi 实践
🚀 2026 年,我们已经很少“手写”代码了。
我们的角色正在发生根本转变——从传统程序员,升级为 AI Agent 团队的架构师、Product Owner 和高级 Reviewer。
逐行编写、调试和审查这些活儿可以放手了,精力转去这几件事:
- 定义清晰的目标与验收标准
- 设定边界条件和架构约束
- 在关键节点进行高阶审查与决策
分析任务、编写代码、运行测试、修复 Bug、持续迭代,这些几乎全部交给 Agent 自主完成。
本文将从 AI 开发模式的演变、Harness 工程核心原理、主流架构对比、Loop 工程与共识记忆,以及 Pi 框架的落地实践 五个维度,系统分析如何从零搭建一个可靠、可审查、可长期运行的 AI Agent 系统。我会配合大量简洁图表,帮助你快速建立直观认知。

AI 开发模式演变:从“AI 辅助写代码”到“Agent 接管执行回路”
传统的 IDE + Copilot 模式,本质是人类驱动:开发者主导整个流程,AI 主要提供代码建议和补全,最终由人类判断、确认和执行。
而随着 Agent 技术成熟,我们正在转向代理驱动(Agentic)的回路模式。人类从“主驾驶”转变为“产品经理 + 架构师 + 终审官”,重点负责设定目标、定义规则和审查结果,让 Agent 在受控循环中自主推进。
这两种模式的本质差异,可以通过下面这张图直观对比:

左侧(传统模式):人类始终掌握执行权,AI 是聪明助手。
右侧(Agentic Loop):人类定义目标与约束后,Agent 自主执行 写代码 → 运行测试 → 读取错误 → 自我修复 → 回归测试 的回路。
真正的核心转变:执行权从人类手中,部分转移到了被严格工程约束的 Agent 循环中。
截至 2026 年 6 月,主流实践已明显分为三类:
1. 桌面端全功能 Agent 平台
代表工具:Claude Code Desktop、OpenAI Codex App、Qoder 等。
这类工具在强大 Agent Runtime 基础上,叠加了丰富的图形化管理能力:多会话侧边栏、任务追踪、Git worktree 隔离、集成终端、diff 审查、应用预览、自动化工作流等。
适合人群:需要同时协调多个 Agent、长期跟踪复杂项目、频繁审查变更、或偏好图形界面的开发者。你只需扔进一个完整功能、Bug 或用户故事,Agent 就会自动创建隔离分支、分析代码、实现、测试并生成 PR。你主要在关键节点把控方向和决策。

2. 终端原生 CLI/TUI Agent(本文重点)
代表工具:Aider、Crush、Pi、Claude Code CLI、Codex CLI、Amp 等。
这类工具极致轻量,几乎零 GUI 负担,天然适合嵌入 tmux、iTerm2、WSL、SSH、CI/CD 或远程服务器环境。
其中 Pi 最为特别。它极简到近乎苛刻,默认只暴露 read、write、edit、bash 四个基础工具,刻意不内置 plan mode、sub-agent、MCP 或复杂工作流,这些能力全部留给开发者通过 extensions、skills、prompt templates 和 packages 自行扩展。
正因为它小、可读、可彻底 hack、可完全自托管,Pi 成为搭建个性化、高度可控 Harness 的最佳底座。

3. AI-native IDE 的 Solo 工作模式
代表工具:**Cursor、Windsurf、Trae等。
在这类 IDE 中切换到“Solo 工作视图”后,AI 从代码补全助手变成了围绕明确目标自主操作整个项目的执行者:读取代码库、调用终端、跨文件修改、根据错误持续自我修复。
人类依然是最终责任人,但工作范式已彻底转变为:“我设定目标与约束,AI 推进执行回路,我审查结果与方向”。
传统 IDE vs 独立 Agent/Harness 的本质区别
| 维度 | 传统 IDE + Copilot 模式 | 独立 Agent / Harness 模式 |
|---|---|---|
| 工作范式 | Human-in-the-loop(人类主导) | Agentic Loop(人类定目标,AI 回路执行) |
| 执行循环 | 建议 → 人工确认 → 人工执行 | 自主循环:写代码 → 测试 → 读错 → 自我修复 |
| 并发能力 | 基本单线程,聚焦当前文件 | 原生支持 3–5+ 个 Agent 并行(不同分支/上下文) |
| 上下文管理 | 当前 buffer + 向量检索 | 跨文件、跨会话的结构化长期记忆与状态管理 |
![]() |
听起来像降维打击?
但现实远没有这么理想。
当任务从“改一个函数”升级到“连续几个小时实现完整特性”时,独立运行的 Agent 脆弱性会迅速暴露。在长期、多迭代、全栈项目中,常见系统性问题包括:
- 自我肯定偏差:模型写完代码后常自我表扬“架构优雅”,但隐藏的 Bug、缺失的边界测试和技术债并不会消失。
- 上下文漂移:对话越长,越容易忘记最初的架构约束和产品目标,几轮后可能已严重偏离方向。
- 幻觉正反馈循环:一个小错误若未被外部验证打断,模型会基于错误假设越修越复杂。
- 长期记忆不可靠:项目状态只存在于聊天记录,难以被多个 Agent、新会话或回滚稳定继承。
- 可观测性差:人类很难判断 Agent 真实进度、决策逻辑,以及它是否只是“看起来很忙”。
这些问题的根源在于:单纯的 Model-in-the-Loop 缺少强大的外部工程控制系统。
Martin Fowler 在《Harness Engineering》中给出了清晰定义:Harness 是 Agent 中 Model 之外的所有部分,即 Agent = Model + Harness。
Addy Osmani 进一步将 Harness 分析为提示工程、工具系统、上下文策略、Hooks、沙箱、Sub-agents、反馈循环、恢复机制等一系列工程组件。
这篇文章,正是我从零开始系统分析 Harness Engineering 核心原则,并以 Pi 这个极简、可完全自托管的开源 Terminal Harness 为主要实践载体,分享一套可长期运行、可审查、可回滚、可扩展的 AI Agent 工作流落地方法论。
💡 什么是 Harness Engineering
核心公式:Agent = Model + Harness。

Model 负责理解需求、生成内容和制定计划。它是创造力的引擎。但它无法可靠地自我评估完成度。
Harness 则是工程“外骨骼”。它为模型提供结构、约束和可观测性,包括结构化提示、工具集成、上下文注入、沙箱执行、自动化测试、独立评估器、状态持久化、Git 回滚、详细日志、人工审批门和调度机制。
没有 Harness,Model 很容易陷入自我肯定偏差和幻觉循环。有了 Harness,Agent 才真正具备生产力。

Harness Engineering 的核心原则
-
Generator 可以生成,但 Evaluator 必须完全独立。这是最重要的一条铁律。模型存在强烈的自我偏好偏差(self-preference bias),倾向于肯定自己的输出,导致幻觉循环反复出现。
这个偏差有实证研究支撑,机制也已经被定位。一篇 2024 年的研究发现 GPT-4 这类模型能以不低的准确率认出自己写的文本,且自我识别能力越强、自我偏好越严重,两者呈线性相关。另一篇分析进一步定位了原因:LLM 评估时系统性偏爱困惑度(perplexity)更低的文本——也就是它自己“觉得顺”的表达。自己生成的内容天然就是自己概率分布下最顺的,所以哪怕匿名化处理,模型依然会给自己的输出打高分。这跟内容质量无关,是概率分布层面的结构性偏差,提示词修不掉,只能靠架构隔离:让评估者和生成者用不同的模型,或者至少让评估者拿不到生成者的对话历史。
-
Sprint Contract(冲刺合约):每个迭代都必须先定义结构化的合约文件,放在
.web-builder/目录。这些文件包括sprint_plan.json、sprint_01_contract.md、eval_report.md、progress.md、feature_list.json、spec.md和architecture.md。每份合约文件都是结构化的验收标准,不是随意的装饰。 -
Default-FAIL(默认失败):所有检查默认标记为失败。只有出现明确硬证据(测试通过、lint 干净、评估器明确批准)时才转为通过。这避免了“看起来差不多”的幻觉。
-
Fresh-Context Evaluation(新鲜上下文评估):评估器运行在隔离环境中。它只读取当前 diff、sprint contract 和最新日志,绝不接触生成器的完整对话历史,防止上下文污染。
-
Consensus Memory(共识内存):文件系统 + Git 是唯一的 Single Source of Truth。所有重要状态都落地为文件,通过
checkpoints/目录实现可靠回滚。Checkpoint 的原理说穿了就是数据库快照思想搬到 Agent 工程:在每个已验证的稳定点(测试通过、Evaluator 给出 PASS),把代码状态(Git commit)和任务状态(
state.json、progress.md的副本)一起冻结存档。两者必须成对保存——只回滚代码不回滚任务状态,Agent 会以为活已经干完了;只回滚状态不回滚代码,Agent 会在脏代码上重复劳动。之后任何一轮迭代搞砸了,恢复操作就是机械的:git reset到对应 commit 加恢复对应状态文件,Agent 从上一个确认正确的世界继续,错误的中间过程整体丢弃。这也是为什么 checkpoint 只能在验证通过后打:在未验证的点存档,等于把 bug 也固化进了“安全点”。 -
Outer Loop 责任分离:用 Bash while 循环把 Planner、Generator、Deterministic Checks、Evaluator、Git 操作和 Human Approval 彻底分开。
外层 Bash While 循环:把上面几条原则串起来跑
上面这些原则单独看都好理解,但怎么落地?最朴素的实现就是一个 Bash while 循环。它的价值在于:每个角色各占循环里的一步,谁也越不了界。Generator 想给自己打 PASS?做不到,评分在 Evaluator 那一步。Evaluator 想偷看 Generator 的思考过程?也做不到,它只拿得到 diff 和日志。
while true; do
# 1. Generator(使用 Pi 生成或修改代码)
pi -p "根据 sprint_01_contract.md 实现下一部分..."
# 2. 确定性检查:lint 和测试不过直接打回,连 Evaluator 都不用惊动
npm run lint && npm test || continue
# 3. Independent Evaluator(隔离评估,只看证据)
pi -p "仅读取 diff、contract 和日志,客观判断是否真正完成..." > eval_report.md
# 4. 共识内存与回滚:PASS 则 commit 存档,FAIL 则回滚重来
if grep -q "PASS" eval_report.md; then
git add . && git commit -m "sprint 01 complete"
break
else
git reset --hard HEAD~1
fi
done
四步拆开看:第 1 步生成,第 2 步用确定性工具拦截低级错误(零 token 成本),第 3 步独立评估,第 4 步要么 commit 形成 checkpoint、要么 hard reset 回滚。失败的代价被限制在一轮循环之内。

这个循环里其实藏着几个角色:写代码的(第 1 步)、验代码的(第 2、3 步)、管状态的(第 4 步)。再加上循环开始前得有人把需求拆成 sprint_01_contract.md——规划者。这些角色怎么组织、拆到什么精细度,业界给出了好几种答案。
主流 Agent 架构对比
要决定怎么组织这些角色,先看看市面上有哪些选项。
| 架构 | 灵活性 | 可靠性 | 协调成本 | 适用场景 |
|---|---|---|---|---|
| ReAct | 高 | 低 | 低 | 简单探索任务、短时交互 |
| Plan-and-Execute | 中 | 中 | 低 | 目标明确的短期任务 |
| PGE | 中 | 高 | 低 | 长时程自主编码 |
| Reflexion | 中 | 中高 | 中 | 需要反思迭代的场景 |
| Graph-based (LangGraph) | 高 | 高 | 高 | 复杂持久化状态机 |
| Multi-agent | 极高 | 低 | 极高 | 需要强并行但成本高 |
表里的 Multi-agent 严格说是一类协作模式的统称,不算单一架构,它有两种差别很大的形态:
- Orchestrator-Worker(主从式):一个 Main Agent 分析任务、派发给多个 Subagent,Subagent 之间互不通信,只把结果交回协调者。Claude Code 的 subagent、各家的 delegate/task 工具都属于这个模式。拓扑是星型的,协调成本可控,可靠性风险主要出在任务拆分质量上。
- Peer-to-Peer(对等协作式):多个 Agent 各有独立上下文窗口,通过消息系统互相通信、共享任务列表。Claude Code 的 Agent Teams(实验性功能,默认关闭)就是这种:Lead Agent 当负责人,Teammate 之间可以直接发消息。(Agent Teams 介绍)
表里“可靠性低、协调成本极高”指的主要是后者:Agent 互发消息,每条通信都可能引入误解,且没有任何一方掌握全局状态。一个简单的判断标准——需要“跑腿拿结果”用主从式就够,真需要“开会讨论”才考虑对等式,而绝大多数工程任务用不上开会。

我的选择:PGE——但有前提
这张表不是说 PGE 全面胜出。真实情况是生产环境里跑的多数 Agent 就是 ReAct 循环——够简单、社区工具好,对短任务完全够用。PGE 的优势要到长时程任务里才能兑现,而且代价不小:Anthropic 用同一个任务做过对照,单 Agent 跑 20 分钟花 9 美元,完整 PGE harness 跑 6 小时花 200 美元,贵了 20 倍。差别在结果上——单 Agent 的成品做好的关卡没法实际游玩,PGE 版本交付了 16 个特性、10 个 sprint,全部通过真实行为测试。(具体实验细节下一节展开。)
所以选架构,本质是在判断任务值不值得为可靠性付溢价:
- 十分钟内能人工验收的活(改函数、查 bug)→ ReAct 就够了,上 PGE 是杀鸡用牛刀
- Agent 独立跑几小时、出错成本高(长时程自主开发)→ 这正是 PGE 的设计场景,失败模式随时长放大,角色隔离的价值才显出来
- 流程有复杂分支和状态依赖 → LangGraph 这类状态机更合适,PGE 的线性流水线表达不了
这篇笔记的主题恰好是第二种,所以 PGE 是合适的主线。换个主题结论就得重新算。
为什么选择 Pi 作为实践载体?
架构定了,还差一个执行引擎。前面 Bash 循环里那句 pi -p "...",理论上换成任何支持命令行单次调用的 coding agent 都能跑。但工具自身的设计哲学会反过来影响 Harness 的形状:如果工具自带 plan mode、自动记忆、内置 sub-agent,这些“贴心功能”会和你自己搭的 Planner、共识记忆、评估循环打架——出了问题你分不清某个行为是你的 Harness 给的,还是工具偷偷干的。
Pi 的极端极简主义正是它的优势。它默认只提供 read、write、edit、bash 四个基础工具,没有预置的 MCP、子代理、计划模式或复杂记忆系统。
这迫使我们亲手从零搭建 Harness,框架的“魔法”指望不上。扩展方式有这几种:
- 自定义 skills 和 extensions
AGENTS.md和CLAUDE.md注入持久上下文- Session 以 JSONL 树状结构存储完整历史
- 深度集成 Git 实现 checkpoint 和一键回滚
Pi 就像一张干净的白纸,让 Harness Engineering 的每一块拼图都清晰可见、可控、可审计。
小结
Harness Engineering 的本质,是用工程的确定性包裹模型的创造力。它把“模型容易幻觉”这个事实转化为可管理的、可观测的、可回滚的系统——靠更长的 prompt 祈祷模型变聪明没有出路。
讲到这里,Harness 的「为什么」基本说清了:模型要约束、要独立评估、要把状态落盘。但「怎么分工」还没展开——前面 Bash 循环里那几个角色(规划、生成、评估)只是雏形。
这套角色拆分不是我拍脑袋想的,Anthropic 在长时程自主编码实验里把它跑成了一套成型的架构,也就是下一节要拆的 PGE。看完它怎么分工、怎么协作,我们再回到 Pi,把这套机制真正搭起来。
🎯 PGE 三角色架构:Anthropic 的长期 Agent Harness 实践
如果说 Harness Engineering 解决的是“为什么不能让模型自己回路”,那么 Anthropic 的 Planner / Generator / Evaluator 三代理架构,就是一个非常清晰的落地样板。为了方便表述,下面我把它简称为 PGE。这不是官方标准协议,是 Anthropic 在长期自主编码实验中公开的一种 three-agent architecture。
Anthropic 在 2026 年 3 月发布的《Harness design for long-running application development》中,把问题定义得很明确:他们想让 Claude 在更长时间内构建完整应用、尽可能减少人工干预,超越生成单个页面或局部代码的水平。文章开头就指出,Harness design 是推动 agentic coding 前沿表现的关键;他们的目标包括高质量前端设计,以及让 Claude 在没有人类中途介入的情况下构建完整应用。
Anthropic 最终采用的做法,是把长期任务拆成三个职责清晰的 agent persona:
| 角色 | 主要职责 | 运行时机 |
|---|---|---|
| Planner(规划者) | 把用户的一两句话需求扩展成完整产品规格,拆出功能范围、产品方向和 sprint 结构 | 启动阶段运行 |
| Generator(生成者) | 根据当前 sprint contract 写代码、改文件、运行测试、修复问题,并交付候选实现 | 每个 sprint 中循环运行 |
| Evaluator(评估者) | 像 QA / Reviewer 一样独立检查运行中的应用,测试 UI、API、数据库状态,并按标准打分 | 每次 Generator 交付后运行 |
Planner在项目启动时将简短需求扩展为完整规格、sprint_plan.json以及冲刺划分。Generator针对每个sprint的合同开展实现工作,包括编写代码、修改文件、运行测试并交付候选版本。Evaluator在Generator交付后立即作为独立QA,使用Playwright对UI、API和数据库行为进行测试,并依据合同中的明确标准给出评分。

这种架构形成sprint回路。每个sprint开始前先产出结构化的Sprint Contract JSON,作为可验证的外部事实依据。
{
"goal": "在关卡编辑器中实现矩形填充工具",
"scope": [
"支持拖拽创建矩形",
"填充纯色或渐变",
"实时预览"
],
"out_of_scope": [
"圆形工具",
"旋转功能",
"保存到后端"
],
"acceptance_criteria": [
"用户能通过拖拽定义矩形区域",
"填充操作在50ms内完成",
"工具在Undo栈中正确记录"
],
"required_checks": [
"lint通过",
"单元测试覆盖率>80%",
"Playwright E2E测试全部通过"
]
}
Evaluator始终使用全新上下文,其提示仅关注当前合同、代码diff、运行日志和可观察行为,从不参考Generator的历史对话,采用cold acceptance方式。
所有检查采用Default-FAIL机制,默认状态均为FAILED,只有提供明确外部证据(lint返回0、测试全部通过、Evaluator分数≥90并附验证证据)才能翻转为PASS。状态示例如下:
{
"checks": {
"lint": {
"status": "FAILED",
"evidence": null
},
"tests": {
"status": "FAILED",
"evidence": null
},
"evaluator": {
"status": "FAILED",
"score": 0,
"verification": null
}
}
}
系统使用.web-builder/文件系统作为共识记忆,而非对话历史。目录结构如下:
.web-builder/
├── sprint_plan.json
├── current_sprint/
│ ├── contract.json
│ ├── state.json
│ ├── checkpoints/
│ └── progress.md
├── artifacts/
│ ├── diffs/
│ └── evaluations/
└── memory/
└── evaluation_context.md
progress.md采用OKR风格跟踪进度。Generator与Evaluator之间形成GAN-inspired的建设性对抗循环:Generator负责创造性实现,Evaluator负责严格阻断式反馈,由Orchestrator作为外层状态机负责编排、检查点和升级。
这组对比数据来自 Anthropic 原文的真实实验。任务是同一句 prompt:“做一个 2D 复古游戏制作器,包含关卡编辑器、精灵编辑器、实体行为和可玩的测试模式”,模型都是 Opus 4.5,分别交给单 Agent 和完整 harness 跑:
| 模式 | 耗时 | 成本 | 结果 |
|---|---|---|---|
| Solo 单 Agent | 20 分钟 | 9 美元 | 界面看着像样,但做好的关卡没法实际游玩——核心功能挂了 |
| Full harness | 6 小时 | 200 美元 | Planner 把一句话扩成 16 特性、10 个 sprint 的规格,最终交付含精灵动画系统、音效音乐、AI 辅助生成器、可分享导出链接 |
贵了 20 多倍,换来的差距是“演示品”和“能用的产品”的差距。Anthropic 自己的表述是:成本差异立竿见影,输出质量的差异同样立竿见影。
PGE 的主要目的就是把容易互相污染的职责拆开
如果只有一个 Agent,它会同时扮演产品经理、工程师、测试、代码审查员和项目经理。短任务里这没什么问题,但长任务里很容易出事:它会忘记最初需求,会对自己的实现过度乐观,会绕过边界条件,也会把“解释得通”误以为“真的完成”。
Anthropic 也观察到,当 agent 评估自己写出来的东西时,经常会自信地赞美自己的工作,即使在人类看来质量明显一般;即便任务有可验证结果,agent 仍然可能因为判断力不足而影响完成质量。
PGE 的价值就在这里:Planner 负责定义方向,Generator 负责推进实现,Evaluator 负责冷酷验收。
PGE 的关键设计机制
PGE 有了角色分工,但谁来驱动这些角色循环跑起来?外层调度器(Orchestrator)是一个轻量确定性状态机,负责按顺序启动 Planner、驱动 Generator 和 Evaluator 来回迭代、判定成败、执行回滚。完整的 Loop Engineering 机制在后面的章节展开,先看 PGE 内部的几个设计模式:
关键设计机制
1. Sprint Contract:先定义“完成”,再开始写代码
PGE 里最值得借鉴的一点,是 Sprint Contract。
Anthropic 的 full-stack harness 中,每个 sprint 开始前,Generator 和 Evaluator 会先协商当前 sprint 的 contract:Generator 说明自己准备构建什么、如何验证成功;Evaluator 审核这个方案,确认它确实对应产品规格,并且具备可测试的完成标准。双方反复迭代,直到对“done”达成一致,Generator 才真正开始实现。
这一步解决的是 Agent 开发里最常见的问题:
写代码之前没有定义完成条件,最后就只能靠模型自己宣布“我完成了”。
一个好的 sprint contract 至少包含这几项:
{ "sprint_id": "sprint_03", "goal": "实现关卡编辑器中的矩形填充工具", "scope": [ "支持鼠标拖拽选择矩形区域", "释放鼠标后批量填充当前 tile", "支持 undo/redo" ], "out_of_scope": [ "不重构整个 tilemap 数据结构", "不修改 sprite editor" ], "acceptance_criteria": [ "用户可以点击拖拽生成矩形选区", "松开鼠标后矩形区域全部被填充", "undo 后地图恢复到填充前状态", "相关单元测试和 e2e 测试通过" ], "required_checks": [ "npm run lint", "npm test", "npm run test:e2e" ]}
这个文件的意义是把任务边界变成可引用、可检查、可回滚的外部事实。
2. Independent Evaluation:让评估者独立于生成者
Anthropic 这篇文章最核心的经验,是把做事的 agent 和判断结果的 agent 分开。
他们一开始在前端设计任务里尝试 generator / evaluator 结构:Generator 负责生成页面,Evaluator 使用明确评分标准判断设计质量、原创性、工艺和功能性。随后,他们把这个 GAN-inspired pattern 扩展到 full-stack development,因为软件开发里的 code review 和 QA,本质上也扮演着 evaluator 的结构性角色。
这不是说 Evaluator 一定完美。Anthropic 也很诚实地指出,Evaluator 仍然是 LLM,默认也可能偏宽松;但调教一个独立 evaluator 变得更挑剔,要比让 generator 对自己的作品保持批判容易得多。一旦外部反馈存在,Generator 就有了可以迭代的具体目标。
所以在我们自己的 Harness 里,Evaluator 的问题模板是这样:
请只根据 sprint contract、测试输出、运行日志、diff 和实际页面行为判断:当前实现是否满足验收标准?不满足时,列出 blocker、复现步骤和建议修复方向。
这才是 Independent Evaluation 的核心。
3. Default-FAIL:没有证据通过,就默认失败
你原文里的 Default-FAIL 值得保留,但要把它写成我们自己的工程实践,别强行归因给 Anthropic。
Anthropic 原文提到,他们为每个 criterion 设置 hard threshold,如果任何一个标准低于阈值,sprint 就会失败,Generator 会收到详细反馈。 这和 Default-FAIL 的精神一致:不要让模糊解释替代明确证据。
在自己的 Harness 中,可以把所有检查项初始设为失败:
{ "contract_review": "FAILED", "lint": "FAILED", "unit_tests": "FAILED", "e2e_tests": "FAILED", "manual_review": "FAILED", "final_verdict": "FAILED"}
只有当外部证据出现时,状态才能变成 PASS:
npm run lint → PASSnpm test → PASSplaywright e2e → PASSevaluator verdict → PASS
这样做的目的,是防止模型用一句“实现已经完成,整体逻辑清晰”绕过真正的验证。
4. Structured Artifacts:用文件系统传递上下文
长任务 Agent 最大的问题之一,是上下文会断。Anthropic 在这篇文章里明确说,他们从早期 long-running harness 中继承了两个经验:把构建任务拆成可处理的小块,以及用 structured artifacts 在 session 之间传递上下文。
在 full-stack harness 中,agent 之间的通信也是通过文件完成的:一个 agent 写文件,另一个 agent 读取并回应,可能直接在同一个文件中回复,也可能写新文件。这种文件式通信让工作更忠于 spec,同时又不会过早把实现细节写死。
这就是我们前面说的“共识内存”:不要把项目状态藏在聊天记录里,要把状态写进仓库。
例如:
.web-builder/├── spec.md├── sprint_plan.json├── sprint_03_contract.md├── sprint_03_generator_notes.md├── sprint_03_eval_report.md└── progress.md
这样做有三个好处:
第一,新 agent 或新上下文启动时,不需要猜之前发生了什么;它读文件就行。
第二,Evaluator 不需要相信 Generator 的口头总结;它可以直接读 contract、diff、日志和测试结果。
第三,Git 可以把这些文件和代码一起 checkpoint,失败时可以整体回退。
5. GAN-inspired Loop:制造建设性对抗,不是让两个模型聊天
Anthropic 明确提到,他们从 GAN 获得启发,设计了 generator / evaluator 结构。这个类比非常适合放在文章里,但要讲清楚:这个结构借用了 GAN 里“生成者—判别者”之间的对抗关系,但跟训练 GAN、做反向传播是两码事。
Generator 的任务是产出更好的实现;Evaluator 只管找问题、打分、给出可执行反馈,不用去鼓励 Generator。Generator 根据反馈继续迭代。Anthropic 在前端设计实验中还提到,评分标准里的语言会强烈影响输出方向,例如“最好的设计具有博物馆级质量”这类表达,会把模型推向特定的视觉收敛方向。
放到 Coding Agent 里,这个对抗循环可以理解成:
Generator:我实现了这个 sprintEvaluator:你没有满足第 7、12、18 条验收标准Generator:我根据反馈修复Evaluator:重新测试,仍有 2 个 blockerGenerator:继续修复Evaluator:全部通过,进入下一个 sprint
这比“模型写完后自己总结一下”可靠得多。
这些设计机制加在一起,就是 PGE 的完整工程落地方式。但 PGE 仍然只是角色分工——谁来定时触发?谁来管状态文件?谁来决定重试几次后放弃?这些调度层面的问题,就是下一节 Loop Engineering 的内容。
🔄 Loop Engineering:Agent 时代的自动化调度层
上一节讲的 PGE,解决的是角色怎么分工:Planner 规划,Generator 生成,Evaluator 评估。
但一个更现实的问题是:
这些角色由谁启动?什么时候启动?失败了谁重试?通过了谁推进?状态写到哪里?下一轮怎么知道上一轮发生了什么?
这就是 Loop Engineering 要解决的问题。
Loop Engineering 这个词确实带一点 AI 行业“重新命名旧工程实践”的味道。Addy Osmani 在 2026 年 6 月写过一篇《Loop Engineering》,里面把它定义成:你设计一个系统,让这个系统去提示 Agent、分配任务、检查结果、记录状态,并决定下一步,人类从一轮轮手动提示中解放出来。换句话说,人从每一轮 prompt 的执行者变成了循环系统的设计者。(Addy Osmani)
这听起来很新,但对软件工程师来说其实并不陌生。传统 DevOps / CI/CD 早就在做类似的事:代码 push 之后触发流水线,创建隔离环境,安装依赖,运行测试,成功就部署,失败就报警。Loop Engineering 把循环中间的执行者从固定脚本换成了 Agent——这才是和传统 CI/CD 真正的区别。
所以更准确地说:
DevOps 是固定脚本驱动的软件交付循环;Loop Engineering 是 Agent 驱动的软件工作循环。
两者都依赖触发器、隔离环境、验证机制、状态记录和失败处理。区别在于:DevOps 的中间步骤通常是确定性的 shell script;Loop Engineering 的中间步骤则允许 Agent 根据上下文做判断、改代码、修 bug、补测试、读日志、决定下一步。
从 DevOps 到 Agent Loop:到底变了什么?
把两边的流程摆在一起看,会发现一件好笑的事。
传统 DevOps:
触发机制:开发 push 了代码,触发 Webhook
隔离环境:为了不污染宿主机,起一个干净的 Docker 容器
执行任务:在容器里跑 npm install
验证反馈:跑 ESLint 查代码规范,跑 Jest 过单元测试
全绿:打个 Tag,飞书群通知完工
红了:飞书里直接骂那个提交代码的人,让他滚回去改
AI 仙人所谓的 Loop 工程新法开发呢,也是一样的:
触发机制:CronJob 定时任务,或者 GitHub 上的新 Issue
隔离环境:哦,他们造了个新词,叫 Worktree 沙箱,其实还是个 Docker 容器
执行任务:不再跑固定的 CI 脚本,而是烧 Token 叫 Agent 读 Skills 执行
验证反馈:依然是 ESLint / Jest——这一步完全没有 AI,看来还是有点脑子的,
我还以为会叫 AI 一行一行检查呢
全绿:AI 打个 Tag,飞书群通知完工
红了:编辑器里直接骂大模型,让它滚回去改
骂的对象从人换成了模型,其他环节几乎原封不动。真正变化的只有中间这一层:
过去:脚本只能执行预设步骤
现在:Agent 可以根据反馈调整路径
但这不代表我们可以把所有事情都交给 Agent。恰恰相反,Agent 越能自主执行,越需要 Harness 把它包住。OpenAI Codex 的最佳实践也强调,建议让 Codex 不只是“做一个改动”,还要让它在需要时创建测试、运行相关检查、确认行为、审查 diff;Codex 可以做这个 loop,但前提是它知道什么叫“好”。(OpenAI 开发者)
这句话非常关键:Loop Engineering 的核心:把“好”的定义写进循环里。
一个 Loop 至少需要哪些组件?
一个能长期运行的 Agent Loop,通常至少需要六个组件。

| 组件 | 作用 | 工程实现 |
|---|---|---|
| Trigger 触发器 | 决定什么时候启动循环 | cron、GitHub Issue、PR 评论、人工命令、Codex Automation |
| Isolation 隔离环境 | 防止 Agent 破坏主工作区 | Git worktree、Docker、sandbox、临时分支 |
| Context 上下文 | 告诉 Agent 要做什么、不能做什么 | AGENTS.md、SKILL.md、spec、sprint contract |
| Agent Executor 执行者 | 读取上下文、修改代码、运行命令 | Pi、Codex、Claude Code、Cursor、Windsurf |
| Verification 验证器 | 判断结果是否可信 | lint、test、typecheck、Playwright、Evaluator |
| State & Recovery 状态与恢复 | 记录进度、失败时回滚 | .web-builder/、Git commit、checkpoint、eval report |
这六件事组合起来,才是一个真正的 Loop。否则所谓“循环”,很可能只是:
模型没做完 → 再让模型继续 → 继续失败 → 继续补救
这只是延长版聊天,算不上工程系统。
为什么现在大家开始认真谈 Loop?
因为主流工具已经开始把这些原语产品化。
OpenAI Codex App 官方文档里,Automations 可以让你选择项目、prompt、cadence 和执行环境,并在后台按计划运行;对于 Git 仓库,automation 还可以运行在独立 background worktree 中,以避免和你当前正在编辑的工作互相冲突。(OpenAI 开发者) Codex 的 worktree 文档也明确说明,worktree 的目的就是让 Codex 在同一个项目里并行运行多个独立任务,彼此不干扰。(OpenAI 开发者)
Codex 的 /goal 则更接近“目标驱动循环”:官方文档说,当任务需要 Codex 跨多轮持续工作,直到达到一个可验证的停止条件时,就可以使用 /goal;好的 goal 会定义目标、不修改的范围、验证进度的方式和停止条件。(OpenAI 开发者)
Claude Code 这边也在提供类似的控制点。官方 Hooks 文档把 hook 定义为用户自定义的 shell 命令、HTTP endpoint 或 LLM prompt,可以在 Claude Code 生命周期中的特定点自动执行;hook 事件包括每轮一次的 UserPromptSubmit、Stop、StopFailure,以及工具调用前后的 PreToolUse、PostToolUse 等。(Claude Code)
这些产品功能背后其实是同一个趋势:
Agent 被放进了一个可触发、可隔离、可验证、可恢复的循环系统里,告别了被动回应 prompt 的模式。
OpenAI 在一篇关于长时程 Codex 任务的文章中也用了类似表述:软件开发正在从 single-shot prompts 和紧密 pair-programming loop,走向 long-running teammates;人类主要在 milestone 上 steering,代码级别的细节交给 Agent 自行处理。(OpenAI 开发者)
一个最小可用的 Agent Loop 长什么样?
不需要一开始就上复杂平台。一个最小可用 Loop,甚至可以是一段 bash:
#!/usr/bin/env bash
set -euo pipefail
MAX_ATTEMPTS=5
ATTEMPT=0
while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do
ATTEMPT=$((ATTEMPT + 1))
echo "== Attempt $ATTEMPT =="
# 1. 让 Agent 根据当前 sprint contract 工作
pi -p "Read .web-builder/sprint_plan.json and implement the current sprint."
# 2. 运行确定性检查
npm run lint
npm test
# 3. 让独立 Evaluator 根据证据审查
pi -p "Evaluate the latest diff against .web-builder/current_contract.md. Write verdict to .web-builder/eval_report.md."
# 4. 根据评估结果决定推进或回滚
if grep -q "VERDICT: PASS" .web-builder/eval_report.md; then
git add .
git commit -m "Complete current sprint"
echo "Sprint passed."
break
else
echo "Sprint failed. Reverting to last safe checkpoint."
git reset --hard HEAD
fi
done
这个脚本当然很粗糙,但它已经包含 Loop Engineering 的核心:
目标来自 sprint contract
执行交给 Agent
验证交给测试和 Evaluator
状态写入 .web-builder/
成功就 commit
失败就回滚
最多重试 MAX_ATTEMPTS 次
这比“让 Agent 一直继续”可靠得多,因为它有明确边界、有失败条件、有退出条件,也有回滚点。
哪些任务适合 Loop?哪些不适合?
Loop Engineering 并不是所有任务都适合。适合放进 Loop 的任务通常有三个特点:
第一,目标清楚。比如“把项目从 Jest 迁移到 Vitest,并保证所有测试通过”。
第二,验证方式明确。比如 lint、test、typecheck、e2e、benchmark、截图对比、eval suite。
第三,允许迭代修复。Agent 可以先做一版,跑检查,读失败,再修复。
典型适合的任务包括:
依赖升级
大规模重构
测试补齐
CI failure 修复
文档同步
代码迁移
安全扫描结果修复
重复 PR review
release note 生成
不适合直接放进无人值守 Loop 的任务也很明确:
需求本身不清楚
没有验收标准
会改生产数据
涉及高风险权限
需要产品判断或审美判断
失败成本过高且不能自动回滚
这些任务让 Agent 跑没问题,但不能直接跑完整回路。这些任务需要加上人工审批点、只读模式、dry run、draft PR 或更严格的 sandbox。
重新理解“AI 行业造词”
所以,Loop Engineering 确实不是从零发明的新东西。它借用了很多 DevOps、CI/CD、工作流编排、测试自动化和任务队列里的老方法。
但它也不是完全没价值的“造词”。因为 Agent 加入之后,循环里的执行单元发生了变化:
传统自动化:脚本执行固定路径
Agent 自动化:模型根据上下文选择路径
这个变化会带来更高的弹性,也会带来更高的风险。
所以光把 Agent 塞进 cron 里不够,要把它放进有 Harness 的 Loop 里:
Trigger → Worktree/Sandbox → Agent → Test/Eval → Checkpoint/Retry → Human Review
这就是 Loop Engineering 真正值得讨论的地方。
💾 共识记忆:文件系统即上下文
在前面我们讲的 Loop Engineering 中,每轮循环都会让 Agent 读写文件、跑测试、Evaluator 打分、状态更新、回滚。问题来了:
Agent 如何知道上一轮做了什么?下一轮该从哪继续?
答案是 共识记忆(Consensus Memory)。简单来说,就是 把项目状态、任务边界、评估结果等核心信息落盘,让文件系统成为单一可信来源(Single Source of Truth, SSOT)。这样,循环里任何一个 Generator 或 Evaluator 都能读写同一份状态,不依赖模型的对话记忆或上下文窗口。
为什么文件系统比对话记忆可靠?
-
跨会话可持续:Agent 不会因为重启、长任务或新 session 而丢失上下文。
-
可审查、可追踪:每个 JSON/Markdown 文件都可以被人类审查,Git 提供历史版本和 checkpoint。
-
便于回滚:失败时只需回退 Git commit 或 JSON 文件,模型记忆靠不住。
-
支持多 Agent 协作:多个 Generator 或 Evaluator 可以同时读取相同状态,通过文件锁或事务控制一致性。
文件系统里的核心文件
典型目录结构可以如下设计:
.web-builder/
├── sprint_plan.json # 当前 sprint contract
├── progress.md # 当前进度
├── eval_report.json # 最新评估结果
├── feature_list.json # 功能注册表
├── spec.md # 产品规格
├── architecture.md # 架构约束
└── checkpoints/ # 关键快照记录
-
sprint_plan.json:定义当前任务目标、验收条件、依赖和范围
-
eval_report.json:Evaluator 输出的分数、问题列表、建议修复
-
progress.md:当前完成状态,便于 Loop 或人类查看
-
checkpoints/:存储 Git commit 或 JSON 快照,用于失败回滚
小结:任何 Agent 的决策都要基于这些文件。自身记忆和对话上下文都靠不住。
文件系统和 Loop/Harness 的关系
Loop Engineering
└── 决定什么时候运行 Generator / Evaluator
Harness Engineering
└── 控制每个 Agent 的操作、工具和评估
Consensus Memory
└── 保障跨循环、跨 Agent 的状态一致
Model
└── 实际生成和推理
换句话说:
-
Loop 决定循环节奏
-
Harness 决定单轮安全边界
-
文件系统提供长期记忆和可观测性
-
Model 做生成和工具调用
缺一不可。
典型实现方式
-
使用
.web-builder/目录或类似沙箱存储 JSON/Markdown 文件 -
Git 用于 checkpoint、回退和版本管理
-
每轮循环,Evaluator 写入
eval_report.json,Generator 读取最新sprint_plan.json并生成改动 -
可选:数据库或 KV store 存储大型项目状态,但核心原则仍是 单一可信来源
实践建议
-
尽量结构化:JSON / YAML / Markdown,让 Agent 能轻松解析和修改
-
分离读写:Generator 写自己的输出文件,Evaluator 写自己的报告文件
-
记录历史:用 Git 或 append-only 文件记录所有修改
-
定期 checkpoint:每个 sprint 或关键节点保存 snapshot,方便失败回滚
生产案例:Hermes Agent 的记忆系统架构
前面讲的共识记忆(文件系统 + Git)是一种设计原则。那真实的生产级 Agent 是怎么落地的?以 Hermes Agent 为例来看,它把记忆系统拆成了四层:

分层存储:MEMORY.md + USER.md
Hermes 在磁盘上维护两个持久化文件:
- MEMORY.md — Agent 自己的笔记,记录环境事实、项目约定、工具技巧、踩过的坑
- USER.md — 用户画像,记录偏好、沟通风格、工作习惯
两个文件都被注入到每个新会话的系统提示中,内容以 § 分隔符组织,支持精确的增删改。(Hermes Agent Memory Tool)
Frozen Snapshot:既持久又不伤缓存
这是 Hermes 设计里比较巧的一点。每次会话启动时,她把 MEMORY.md 和 USER.md 的内容冻结成快照注入系统提示。会话中间的任何修改会立刻写入磁盘,但不更新系统提示。
好处很实在:前缀缓存(prefix cache)在整个会话期间保持不变。模型不需要重新处理前几轮的系统提示和上下文,缓存命中率更高、响应更快、成本更低。快照在下一轮会话开始时才会刷新。
Session Search:FTS5 SQLite 做跨会话回忆
如果 MEMORY.md 是长期记忆,那 session search 就是工作记忆。Hermes 用 SQLite FTS5 把过去会话全文索引,支持四种调用模式:
| 模式 | 作用 | 适用场景 |
|---|---|---|
| DISCOVERY | 关键词搜索,返回匹配会话片段 + 前后文窗口 | 查找以前讨论过的东西 |
| SCROLL | 在单个会话内上下滚动,查看完整上下文 | 回顾某个任务的完整过程 |
| READ | 读取整个会话的全部内容 | 深入理解讨论脉络 |
| BROWSE | 列出最近会话的标题和摘要 | 快速回顾最近在做什么 |
更重要的是,搜索结果附带自动截取的开始/结束段(bookend),让你不需要加载全文就能知道这个会话当初的目标和最后结论。(Hermes Session Search)
Skills:可执行的程序化记忆
记忆不一定只是文本。Hermes 把可复用的工作流包装成 Skills——带 YAML frontmatter 的 SKILL.md 文件,描述触发条件、执行步骤和避坑指南。这些 skills 被注入为工具描述,Agent 在需要时按需加载。
这其实就是 PGE 架构里 Planner / Evaluator 用 Pi Skill 实现的那个思路的工程版本。只是 Hermes 把它做成了更完整的体系——记忆分持久/会话两档,搜索用 SQLite 全文索引,工作流用 SKILL.md 标准化。
通过共识记忆,你的 Loop + Harness 就从“聊天式模型”升级为可持续、可审查、可回滚的工程化系统。
--
检索方式之争:grep/FTS vs 向量 RAG
共识记忆的检索方式看起来很"原始":grep 搜关键词、read 读文件、FTS5 做全文索引。那为什么不用向量数据库 + RAG?
先看生产级 coding agent 的实际选择:
| Agent | 记忆检索方式 |
|---|---|
| Hermes | FTS5 SQLite(会话搜索)+ 直接 read(MEMORY.md/USER.md) |
| Claude Code | ripgrep + read,无向量组件 |
| Pi | read + grep + find,纯文件操作 |
| Codex | 文件系统 + 精确搜索 |
| Cursor | 混合:向量索引做语义搜索 + 精确的 symbol search |
清一色选了精确匹配。为什么?
代码是精确的。 函数名、变量名、文件路径、错误信息都是确切字符串。grep handleAuthCallback 一搜一个准;向量 embedding 会把它和 processLoginRedirect 算成"语义相似"——但 agent 要找的就是那个具体函数。
文件系统天然是最新的。 代码改了 grep 立刻搜到新内容,RAG 需要等重新 embedding 同步索引。
确定性。 同样的 grep 查询永远返回同样的结果。向量相似度依赖 embedding 模型,可能版本升级后结果漂移。
可审计。 出了问题能追溯"agent 读了哪个文件的哪一行做了这个决定"。RAG 的 chunk 拼接很难审计。
那 RAG 完全没用吗?也不是。它擅长语义模糊检索——"鉴权怎么做的"能匹配到写 JWT 的文件,grep 搜不到"鉴权"这个词就没辙。Cursor 就是混合方案:精确搜索兜底,语义搜索辅助发现。Mem0、LangChain 社区工具里的 agent 也用向量存储做长期记忆,但那些更多面向"客服 bot 查文档"场景。
一句话总结:
精确匹配胜在确定性、零维护、对代码友好;RAG 胜在语义模糊检索。代码场景下精确性比相似度重要,所以生产 coding agent 普遍选了前者。
讲完了共识记忆的"为什么"和"怎么检索",下面进入实践部分——先明确我们要用的工具 Pi 的真实定位。
Pi 的真实定位
Pi 由 Mario Zechner(libGDX 作者)开发,是一个 最小化的 Agent SDK 和构建工具包:
- ✅ 系统提示词不超过 1000 token
- ✅ 只暴露 4 个原始工具:
read、write、edit、bash - ✅ 无 MCP、无后台 bash、无内置 to-do、无 plan mode、无子 agent
- ✅ 所有额外功能通过 package 引入
Pi 既是一个可以直接用的 CLI/TUI coding agent,也是一个专门用来构建自定义 Agent 的基座。
OpenClaw 就是基于 Pi 构建的
OpenClaw(原 Clawdbot/Moltbot)是构建在 Pi 架构之上的自主 AI 助手平台。
| 特性 | Pi Coding Agent | OpenClaw |
|---|---|---|
| 定位 | 核心引擎 / SDK / 基座 | 应用框架 / 编排平台 |
| 接口 | CLI / Terminal TUI / 代码 API | 聊天应用(Telegram、WhatsApp、Web UI) |
| 焦点 | 极简 LLM 执行 + 核心工具循环 | 身份、后台自动化(cron)、安全、集成 |
| 类比 | 引擎 + 变速箱 | 整辆车(仪表盘、座椅、轮子) |
当你通过 Telegram 给 OpenClaw 发消息「构建一个网站」,OpenClaw 的 Gateway 处理请求,路由到会话管理器,然后交给内嵌的 Pi 运行时框架执行。Pi 跑 bash 命令、读写文件、循环工具调用,直到任务完成。
📦 上手 Pi:从安装到能干活
Pi 装起来很简单,一行搞定。但有几个坑我帮你趟过了。
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
cd 你的项目目录
pi
Pi 支持 /login 用 Claude Pro/Max、ChatGPT Plus/Pro(Codex)、GitHub Copilot 登录,也可以直接配 API key。(Pi Coding Agent)
但别急着让 Pi 改代码。 先做一件事:
git status
git add . && git commit -m "checkpoint before pi"
Pi 会直接操作你当前目录的文件,没有沙箱。先打 checkpoint 再让它动手——这本身就是 Harness 的第一道安全线。
我实际在用的命令
装好之后,真正高频用的就这几条:
| 命令 | 我什么时候用 |
|---|---|
@文件名 | 让 Pi 看某个文件,模糊搜索就行 |
!npm test | 跑测试让模型看结果,然后自己修 |
!!npm run build | 自己看构建输出,不塞进模型上下文 |
/tree | 一个方案跑偏了,跳回之前的节点重新来 |
/fork | 从某个节点分叉出新会话,试另一个方向 |
/compact | 聊太长了压缩一下,省 token |
pi -c | 继续上次没干完的活 |
! 和 !! 的区别要记住:! 把输出喂给模型,!! 只给你自己看。长任务里不是所有终端输出都值得塞进上下文——编译警告、node_modules 路径、Webpack 的 verbose log,这些只会浪费 token。
Pi 内部是个 TypeScript monorepo,但你不需要关心它的分层——你只需要知道:Pi 默认只给模型 read / write / edit / bash 四个工具,其他能力全部靠下面要讲的 AGENTS.md、skills、extensions、packages 往上装。 这也是为什么它适合做 Harness 基座:核心够小,你才能完全控制往里面加什么。
🛠️ Pi 实践:把前面的理论落地
前面讲了 Harness、PGE、Loop、共识记忆这些抽象概念,现在需要一个具体的工具把它们串起来。我选的是 Pi——原因很简单:它足够小、足够透明、足够可改。工具自带的 plan mode、记忆系统、sub-agent 这些“贴心功能”会和你自己搭的 Harness 打架;而 Pi 默认只给四个工具(read、write、edit、bash),其他一切由你决定。

所有前面讲的抽象概念,在 Pi 里都有对应的落地方式:
| Harness 概念 | Pi 实现 | 一句话解释 |
|---|---|---|
| 项目规则注入 | AGENTS.md | 什么能做、什么不能做、完成后更新哪些文件 |
| Planner / Evaluator | Skills (.pi/skills/) | 按需加载的工作流,一个 skill = 一个角色 |
| 硬抦截 / 强约束 | Extensions (.pi/extensions/) | TypeScript 钩子,拦截工具调用、注入上下文、自动 checkpoint |
| 共识记忆 | .web-builder/ + Git | JSON/Markdown 状态文件 + commit 快照 |
| 外层循环 | bash while loop / cron | 驱动 Generator→测试→Evaluator→commit/reset |
| 探索历史 | Sessions (JSONL tree) | 对话树,支持 fork/branch/回溯 |
| 团队复用 | Packages (npm/git) | 把 skills + extensions + prompts 打包分发 |
深入机制:Pi 的每个模块怎么用
上面的映射表是全景图。下面逐个拆开看每个模块怎么落地。
AGENTS.md:把项目规则写进上下文入口
Agent 在陌生项目里出问题,通常不是代码能力不够——是不懂这个项目的规矩。测试命令是 npm test 还是 pnpm test?能不能改 .env?什么目录只能读?
Pi 启动时加载 context files。全局规则放 ~/.pi/agent/AGENTS.md,项目规则放当前目录的 AGENTS.md 或 CLAUDE.md,改完 /reload 重新加载。(Pi Coding Agent)
一个适合 Harness 的项目级 AGENTS.md:
# Project Agent Rules
## Before You Start
- 先读取 `.web-builder/progress.md`
- 当前任务范围以 `.web-builder/current_contract.md` 为准
- 不允许超出 current sprint 的 scope
## Checks
- 修改代码后运行 `npm run lint`
- 修改类型后运行 `npm run typecheck`
- 修改核心流程后运行测试
- 如果命令不存在,记录到 `.web-builder/progress.md`
## Safety
- 不允许修改 `.env`
- 不允许删除测试来让检查通过
- 不允许执行生产 migration
- 不允许大规模删除代码,除非 current_contract 明确要求
## Harness Protocol
- Generator 只能更新实现和 progress
- Evaluator 才能写最终 PASS / FAIL
- 没有证据通过时默认 FAIL
核心思路:把项目规则从脑子里搬到仓库里。 Agent 出问题的根源通常是不懂规矩,不是能力不行。
Skills:把 Planner / Evaluator 写成可复用工作流
如果 AGENTS.md 是项目规则,那 Skill 就是可复用的角色工作流。
Pi 把 Skills 定义为按需加载的包——启动时只暴露名称和描述,任务匹配时才读完整的 SKILL.md(progressive disclosure,省 token)。(Pi Coding Agent)

用 skills 实现 PGE 三角色:
sprint-planner skill → Planner
Pi 默认 coding agent → Generator
sprint-evaluator skill → Evaluator
目录结构:
.pi/skills/
├── sprint-planner/
│ └── SKILL.md
└── sprint-evaluator/
└── SKILL.md
sprint-planner/SKILL.md——约束它只做规划:
---
name: sprint-planner
description: Turn a user request into spec.md, sprint_plan.json, and current_contract.md.
---
# Sprint Planner
You are the Planner in a PGE Harness.
Read:
- user request
- README.md
- AGENTS.md
- existing .web-builder/progress.md
Write:
- .web-builder/spec.md
- .web-builder/sprint_plan.json
- .web-builder/current_contract.md
Rules:
- Do not write implementation code.
- Split work into small, testable sprints.
- Every sprint must include acceptance criteria and required checks.
sprint-evaluator/SKILL.md——约束它只做评估:
---
name: sprint-evaluator
description: Evaluate current code changes against current_contract.md.
---
# Sprint Evaluator
You are the Evaluator in a PGE Harness.
Read:
- .web-builder/current_contract.md
- .web-builder/progress.md
- git diff
- test output
- relevant source files
Write:
- .web-builder/eval_report.md
Rules:
- Default to FAIL.
- Do not trust Generator self-summary.
- Do not modify business code.
- Judge only by evidence: tests, diff, runtime behavior, acceptance criteria.
- Final verdict must be exactly one of:
- VERDICT: PASS
- VERDICT: FAIL
这两份文件把 Harness 角色定义变成了仓库里可 review、可复用、可迭代的工程资产。
Extensions:把软规则变成硬护栏
Skill 是给模型看的"工作手册",Extension 是直接改变 Pi 行为的代码。
Pi 的 Extension 是 TypeScript 模块,能订阅生命周期事件、注册自定义工具、拦截工具调用、注入上下文、管理状态。(Pi Coding Agent)

举个例子——"不要改 .env"这条规则:
弱实现:在 AGENTS.md 里写"不要改 .env"
强实现:写 extension,拦截 write/edit,发现目标路径是 .env 就拒绝
最小 path protection extension:
// .pi/extensions/path-guard.ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default function (pi: ExtensionAPI) {
pi.on("before_tool_call", async (event) => {
const path = String(event.params?.path ?? "");
if (
path.includes(".env") ||
path.includes("node_modules") ||
path.includes("dist/")
) {
throw new Error(`Blocked unsafe path modification: ${path}`);
}
});
}
再比如自动 checkpoint:
弱实现:提醒模型"开始前请 git commit"
强实现:写 extension,在 agent turn 开始前自动 git stash / git commit
能用代码强制的,不要只靠 prompt 约束。 这就是 Harness Engineering 里最关键的一步。
Sessions:对话树,不是线性聊天

Pi 的 session 是树结构。每条记录有 id 和 parentId,/tree 能跳回之前任意节点继续。(Pi Coding Agent)
实际编码里这很实用。比如你让 Agent 做状态管理重构,它提了三种方案:
A:继续沿用当前 store,只做局部重构
B:迁移到 Zustand
C:把状态上移到 server action
线性聊天里你选了 A 就很难回去试 B。Pi 的 /tree 允许你跳回分歧点,重新走另一条路。/fork 和 /clone 则适合从旧会话创建独立 session。
但要强调:Pi session 适合保存对话历史,不适合作为项目状态的唯一来源。 session 是"Agent 怎么想的",不是"项目现在是什么状态"。真正的共识记忆应该放 .web-builder/ + Git。
Pi session tree → 对话路径、探索分支、尝试过的方案
.web-builder/ → sprint 状态、规格、评估报告、长期共识
Git commit → 文件级 checkpoint、可回滚工程状态
三者不要混用。很多 Agent 项目跑乱,原因就是这三类状态混在聊天记录里。
Packages:把 Harness 封装成可复用工具箱
当你的 AGENTS.md + skills + extensions 稳定之后,就能打包。Pi packages 通过 npm 或 git 分发,可以在 package.json 的 pi 字段声明资源。(Pi Coding Agent)
一个团队级 Harness package:
my-team-pi-harness/
├── package.json
├── extensions/
│ ├── git-checkpoint.ts
│ ├── path-guard.ts
│ └── eval-runner.ts
├── skills/
│ ├── sprint-planner/
│ │ └── SKILL.md
│ └── sprint-evaluator/
│ └── SKILL.md
├── prompts/
│ └── code-review.md
└── themes/
└── team-theme.json
package.json 声明资源:
{
"name": "my-team-pi-harness",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"],
"themes": ["./themes"]
}
}
把 Planner、Evaluator、路径保护、Git checkpoint、审查模板打包,新项目一条 npm install 就复用。
❗ Pi packages 有完整系统访问能力,extensions 能执行任意代码,安装前必须审查源码。(Pi Coding Agent)
安全边界:Pi 不是沙箱
Pi 没有内置 sandbox,用它启动用户的权限跑。内置工具能读文件、写文件、编辑文件、运行 shell 命令;extensions 同权。真正的隔离来自操作系统、虚拟化或容器边界。(Pi Coding Agent)

别在主机裸奔无人值守任务:
| 场景 | 建议 |
|---|---|
| 本地小改动 | Git checkpoint + 人工审查 |
| 长时间自动循环 | Git worktree + Docker / VM |
| 不可信仓库 | 容器或远程 sandbox |
| 涉及密钥 / 生产配置 | 不给凭据,必须人工审批 |
| 第三方 package | 安装前读源码 |
Pi 的取舍很清楚:它不假装安全,它把本地权限交给你。你要自己设计好 Harness 的边界。
实战推荐:Pi Package 周边工具里的好东西
Pi 的 package 社区已经有不少实用的轮子可以直接装,这里按场景分类列一下我实际用过的:
子 Agent 与 MCP
- pi-subagents — 补全子 agent 能力
- pi-mcp-adapter — MCP 协议接入(不过很多 MCP 能力可以直接做成 package,不一定非要走 adapter)
上下文压缩
- DCP (Dynamic Context Pruning) — 常规的总结压缩,在用
- pi-observational-memory — 可配独立模型做总结,设计比 DCP 复杂,长会话防偏移
context-mode— 安装量第一但体验不好,它把工具输出拦截到沙箱只给摘要,模型经常判断错该不该展开,关键信息丢失,建议别装
/goal 持续执行
- pi-until-done — 目标完成前不停止,我在用
- pi-codex-goal — 追求 Codex /goal 手感的可以试这个
代码搜索
- pi-ace-tool — ace 代码搜索插件
- @ff-labs/pi-fff — Rust/SIMD 加速模糊 find + grep,替代原生,速度极快
- pi-fast-context — 快速上下文加载
安全与审查
- @juicesharp/rpiv-advisor — 请求强模型给第二意见,关键决策前多一层校验
- pi-simplify — 审查近期改动的清晰度和维护性
- @narumitw/pi-plan-mode —
/plan只读规划模式,禁止写操作,输出方案确认后才恢复权限
操作回退
- pi-rewind — 依赖 Git 的存档点回退,原生级体验
搜索与抓取
- pi-search — 集成了 grok-search、context7 和反检测 fetcher
UI 与交互
- pi-nano-context — 紧凑上下文占用条
- pi-tool-display — OpenCode 风格工具输出折叠 + diff 渲染
- pi-btw — Claude Code 同款
/btw旁路问题,不污染主对话
踩坑警告:pi-powerline-footer 太重,会接管编辑器布局和鼠标滚动,建议用 pi-nano-context 替代。
🧪 一个最小 Pi Harness 应该长什么样?
把上面这些拼起来,一个最小可用的 Pi Harness 项目可以这样组织:
my-project/
├── AGENTS.md
├── package.json
├── src/
├── tests/
├── .web-builder/
│ ├── spec.md
│ ├── sprint_plan.json
│ ├── current_contract.md
│ ├── progress.md
│ └── eval_report.md
└── .pi/
├── skills/
│ ├── sprint-planner/
│ │ └── SKILL.md
│ └── sprint-evaluator/
│ └── SKILL.md
├── extensions/
│ ├── path-guard.ts
│ └── git-checkpoint.ts
└── settings.json
然后用一个外层脚本驱动:
#!/usr/bin/env bash
set -euo pipefail
MAX_ATTEMPTS=5
ATTEMPT=0
while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do
ATTEMPT=$((ATTEMPT + 1))
echo "== Pi Harness Attempt $ATTEMPT =="
# 1. 让 Generator 实现当前 sprint
pi -p "
Read AGENTS.md and .web-builder/current_contract.md.
Implement the current sprint.
Update .web-builder/progress.md with what changed.
Do not mark the sprint as passed.
"
# 2. 跑确定性检查
npm run lint
npm run typecheck
npm test
# 3. 启动 Evaluator 独立评估
pi -p "
/skill:sprint-evaluator
Evaluate the latest git diff against .web-builder/current_contract.md.
Write the final verdict to .web-builder/eval_report.md.
"
# 4. 根据结果推进或回滚
if grep -q "VERDICT: PASS" .web-builder/eval_report.md; then
git add .
git commit -m "Complete current sprint"
echo "Sprint passed."
exit 0
else
echo "Sprint failed. Reverting to last checkpoint."
git reset --hard HEAD
fi
done
echo "Failed after $MAX_ATTEMPTS attempts."
exit 1
这个脚本并不复杂,但它已经具备 Harness 的基本骨架:
AGENTS.md → 项目规则
Skills → Planner / Evaluator 工作流
Pi 默认 agent → Generator
.web-builder/ → 共识记忆
npm run lint/test → 硬验证
git commit/reset → checkpoint 和回滚
bash while loop → 外层调度
也就是说,不需要一上来就引入复杂框架。
只要把角色拆开、状态落盘、验证外置、失败可回滚,你就已经拥有了一个最小可用的 Agent Harness。
这一节的结论
Pi 没有把工作流写死,这才是它适合做 Harness 的根本原因。
它提供最小的执行能力:
read / write / edit / bash
再提供可扩展入口:
AGENTS.md / Skills / Extensions / Packages / Sessions
剩下的部分由你决定:
任务怎么拆?
谁来评估?
什么算完成?
状态写哪里?
失败怎么回滚?
哪些路径不能动?
什么时候必须人工审批?
这正是 Harness Engineering 的核心精神:
模型负责生成,Harness 负责约束、评估、记忆和恢复。
💰 Agent Token 消耗:从成本角度看 Harness 设计
Harness 做得好不好,有一个很直接的量化指标:Token 消耗。
你可能注意到了,同样是改代码,有的 Agent 跑一段要烧掉几万 tokens,有的几千就能搞定。这背后的是缓存机制、上下文管理策略和工具设计理念的差距。
GenericAgent 的 2.9x–3.9x 优势
2026 年 4 月发布的 GenericAgent 论文明确展示了这一点。GA 的核心策略叫 Context Information Density Maximization(上下文信息密度最大化)——在有限上下文预算内塞进最多决策相关的信息。

GA 通过四个组件实现这个目标:
| 组件 | 怎么做的 | 效果 |
|---|---|---|
| 最小原子工具集 | 只暴露最少数量的原始工具,不堆砌功能 | 工具描述几乎不占上下文 |
| 分层按需记忆 | 默认只展示高层的抽象摘要,需要细节时才展开 | 80% 的时间上下文只带骨架 |
| 自进化机制 | 把验证过的成功路径编译成 SOP 和可复用代码 | 新手任务不需要从头推理 |
| 上下文截断与压缩 | 长执行过程中持续维持信息密度,丢弃冗余 | 会话增长时不会线性膨胀 |
结果很直观:在大部分场景下,GA 的 Token 消耗比传统 Agent 降低了 65%–74%,且性能反而更好。
为什么不同 Agent 的 Token 消耗差距这么大?
2026 年的 SWE-bench 研究出了一个很有意思的结论:同一种 Agent 跑同一任务,不同运行之间的 Token 消耗差距最高可达 30 倍。(How Do AI Agents Spend Your Money?) 原因是模型在推理路径上做了不同的选择——有的路径很快就收敛了,有的绕了半天。
不同 Agent 之间的差距则主要来自这几个维度:
1. 提示词设计:膨胀 vs 精简
传统 Agent 会在每个 prompt 里塞几百行系统指令、详细的工具描述、长串的 few-shot 示例。每个工具的描述可能长达几十到上百个 token,所有工具加起来轻松上千。
GA 和 Hermes 这类倾向于精简短 prompt 的 Agent,系统指令通常压到几十个 token,工具描述也只用一两句话。差距在一轮对话里不明显,但累积到几十上百轮后就非常可观。一篇 2026 年的优化指南提到,仅提示词精简一项就能节省 30%–70% 的成本。(Agent Token 优化完全指南)
2. 缓存策略:前缀缓存 vs 无缓存
这是最容易被忽略但影响最大的因素。不同模型提供商的缓存机制差异很大:
| 提供商 | 缓存单元大小 | 缓存时长 | 成本节省 |
|---|---|---|---|
| Anthropic Claude | Prompt caching(prompt 前缀匹配) | 5–10 分钟不活跃后失效 | 最多 90%(输入侧) |
| OpenAI | Context caching(更精细) | 自动管理 | 50%–80% |
| Gemini | Semantic cache(语义近似命中) | 根据策略配置 | 视情况而定 |
| 自部署模型 | 无 | — | — |
Hermes Agent 的 Frozen Snapshot 设计专门利用了这一点:系统提示在会话期间保持不变,意味着每次工具调用返回后,下一轮调用的前缀缓存都不会被破坏。如果每次写 memory 都会改 system prompt,缓存就会被清空,下一轮调用就得重新计算前几千个 tokens。
GA 的按需展开记忆也是同样的思路:默认只放少量高层摘要,细节只在实际需要时才注入,让缓存命中率尽可能高。
3. 工具设计与上下文污染:一个千万 Token 的教训
我亲眼见过一个真实案例。有人说 Hermes 消耗高,刚开始没在意——接的 DeepSeek V4 Flash,每天烧几千万 tokens 干些杂活也不贵。直到有一天让它只干一件事:安装部署一个项目。
结果那次部署跑了 一千万 tokens。
查原始请求记录,发现整个对话其实没几轮,但 Agent 一直在调用工具、一直在跑。原因很简单:部署过程中跑 npm install,终端的下载日志——包括下载进度条、依赖解压、网络重试——全部被当作工具调用的输出原样返回给模型。这时网络中断了,Agent 重试安装,历史日志+新日志叠加到上下文中。网络又断了,又重试。如此循环,上下文持续膨胀,重复请求堆积,最终装成功了——干了一千万 tokens。而且这个行为重复了两次。
Hermes 对工具输出的限制只有三层:stdout 上限 50KB、文件读取 2000 行、单行 2000 字符。它根本不管工具被调用了多少次、重复输出有没有被过滤。 而主流 Agent 在这方面的处理成熟得多。Claude Code 有至少八层筛选机制:流式过滤、输出截断、重复检测、无关信息丢弃……安装日志这种信息密度极低、对模型决策毫无帮助的内容,会在第一层就被筛掉。
这个案例说明了一个很简单的道理:工具调用的输出质量,比工具本身的数量更影响 Token 消耗。 一个没有输出过滤的 Agent,可以在一次部署里烧掉足够写整个项目的 tokens。设计 Harness 时,不仅要关注模型选型和 prompt 设计,还必须考虑每一轮工具调用的输出会不会成为下轮上下文的累赘。
🧩 我的推荐落地顺序

不要一上来就写 extension,也不要一上来就做多 agent。顺序错了,会很快把系统搭复杂。
我建议按这个顺序来:
第一步:先只用 Pi + AGENTS.md
目标是让 Pi 在项目里工作得“像熟悉规矩的工程师”。
先写:
AGENTS.md
里面只放四类内容:
项目怎么跑
代码怎么测
什么不能碰
完成后要更新什么文件
这一步完成后,你已经能显著减少 Agent 靠猜。
第二步:加 .web-builder/ 共识记忆
建立:
.web-builder/
├── progress.md
├── current_contract.md
└── eval_report.md
从这一刻开始,不允许 Agent 只在聊天里说“我完成了什么”。
它必须把状态写进文件。
这一步解决长期任务最核心的问题:状态不再依赖对话。
第三步:把 Planner / Evaluator 写成 skills
建立:
.pi/skills/sprint-planner/SKILL.md
.pi/skills/sprint-evaluator/SKILL.md
这一步让 PGE 真正落地:
Planner → skill
Generator → Pi 默认角色
Evaluator → skill
此时你还不需要多 agent。即便都是用同一个 Pi 跑,只要它们加载不同 skill、读取不同证据、写入不同文件,角色边界就已经清楚很多。
第四步:加 pi-rewind / Git checkpoint
等你开始让 Agent 连续工作,就必须加回滚。
可以先手动:
git add .
git commit -m "checkpoint before agent sprint"
之后再考虑用 pi-rewind 或自定义 extension 自动化。
这一步解决的是:Agent 写坏了以后能不能安全回来。
第五步:再上 pi-until-done / 外层 loop
只有当前四步稳定后,再让 Agent “跑到完成”。
否则你会得到一个很努力、很能烧 token、但方向不一定对的自动循环。
第六步:最后考虑 subagents / extension / package
当流程稳定后,再把它封装成:
pi-subagents
custom extensions
team pi package
这一步是积累,不是起点。
💭 写在最后
做 Agent 这件事,归根结底是放下对“更聪明的新模型”的幻想,把一个会犯错、会忘事、会自信解释的模型放进一套工程系统里。
Harness 给的是方法论:
角色分离、默认失败、独立评估、状态落盘、Git 回滚、外层循环。
Pi 给的是落地基座:
极简核心、四个工具、AGENTS.md、session tree、skills、extensions、packages。
两者结合之后,你搭出来的系统更像工程师的工作流:
有任务边界
有测试证据
有审查报告
有长期记忆
有回滚点
有人工审批
这比“让模型继续努力”可靠得多。
所以我对 Pi 的定位很明确:它不是最省心的工具,但它是最适合折腾 Harness 的工具之一。你可以从一个 AGENTS.md 开始,把项目规则写清楚;再加 .web-builder/,把状态落盘;然后把 Planner 和 Evaluator 写成 skills;最后用 Git、package、extension 和 loop 把流程逐渐固化。

