Harness Engineering实战:让 Agent 每次工作前先初始化—为什么初始化必须是独立阶段
Harness Engineering实战:让 Agent 每次工作前先初始化—为什么初始化必须是独立阶段
Harness Engineering 模块化指令实战:告别 600 行巨型 AGENTS:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/162702085
基于上述基础。
一、背景
使用 AI 编码 Agent 时,最常见的低效模式是:让 Agent 上来就写功能。它很快会发现测试框架没配好、环境有问题、目录不清晰,大量时间花在"搞清楚这个项目怎么运作",真正写功能的时间反而很少。
更好的做法是:在让 Agent 干活之前,先用一个独立的阶段把基础环境搭好、验证命令跑通、项目结构厘清。
一句话结论:初始化和实现是两种性质完全不同的任务,必须分阶段。初始化阶段只建"执行前提",不写业务代码。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、核心知识点讲解
2.1 两种不同的工作,优化目标天然冲突
| 阶段 | 优化目标 | 典型产出 |
|---|---|---|
| 实现阶段 | 最大化"已验证功能"的数量与质量 | 业务代码 |
| 初始化阶段 | 最大化"后续所有实现"的可靠性与效率 | 基础设施 |
当两者混在一起,Agent 面临多目标优化问题。在没有显式优先级时,它会自然倾向于"写代码"(直接可见产出),牺牲基础设施(价值在后续才体现)。结果:基础设施没搭牢,功能代码可靠性也打折。
2.2 初始化与实现混在一起的四类具体问题
- 基础设施搭不牢:Agent 花 80% 精力写功能,20% 随便搭基础设施。测试框架配了没验证、lint 太宽松、进度文件没建。缺陷在第二个会话才暴露——新 Agent 不知道怎么跑、怎么测、进度在哪。
- 未验证的累积(隐蔽代价):测试框架配好前就写的功能,回头补测试可能发现设计要推翻,前面写得越多,后面推翻重来越多。
- 上下文预算浪费:初始化消耗大量 token 预算,留给功能实现的预算不够。第一个会话只完成一半,第二个会话还得从头理解,两头没占着。
- 隐式假设埋雷:初始化的关键决策(测试框架、目录组织、依赖管理)不显式记录,后续会话可能做矛盾选择——例如第一会话选了 A 框架,第二会话又引入 B 框架,两套共存成本翻倍。
2.3 权威研究支撑
- Anthropic《长运行应用开发研究》:明确建议分离初始化与实现。实验数据——独立初始化阶段的项目,多会话场景功能完成率比混合方式高 31%;初始化投入时间在后续 3–4 个会话中完全收回。
- OpenAI Codex harness engineering 指南:强调"仓库即操作记录"原则——第一次运行就要建立清晰的操作结构,否则每次新会话都要重新推断约定。
2.4 核心概念术语表
- 初始化阶段:Agent 生命周期的第一个阶段,只建立后续实现所需的执行前提,不开发功能。产出是基础设施,不是业务代码。
- 启动就绪清单(初始化契约):项目能被"全新 Agent 会话"无歧义操作的条件——能启动、能测试、能看进度、能接手下一步(四条件缺一不可)。
- 从零开始 vs 从模板开始:从零开始时 Agent 自行推断结构效果差;从模板开始则基础设施就位效果好。能用模板就用模板(热启动)。
- 随时可接手:项目任何时刻处于"可被全新 Agent 接手"状态,不需口头解释,只看仓库就能干。
- 从开始到第一次测试通过:衡量初始化效率的核心指标,越短越高效。
- 后续会话的成功率:后续会话不需依赖隐式知识就能成功执行任务的比例,是初始化质量的最佳衡量标准。
2.5 初始化的正确做法(五大产出)
核心原则:把初始化当作独立阶段执行。第一个会话只做初始化,不写任何业务功能代码。
初始化的五大产出:
- 可运行的环境:项目能启动、依赖装好、无环境问题。
- 可验证的测试框架:至少一个示例测试通过,证明框架确实配好(而不只是"配了")。
- 启动就绪清单文档:明确告诉后续会话如何操作(见 2.7 示例 1)。
- 任务分解:整个项目拆成有序任务列表,每个任务有明确验收标准(见 2.7 示例 2)。
- Git 提交作为检查点:初始化完成提交一个干净的 checkpoint,后续会话从此开始。
热启动策略:不要从空目录开始,用项目模板(如 Spring Initializr、create-react-app)预置标准目录、依赖、测试框架,只把"项目特有"的初始化留出来。
2.6 初始化的完成条件(验收)
用"启动就绪清单"四条件验收,缺一不可:
- 能启动:
mvn spring-boot:run无环境问题 - 能测试:
mvn test至少一个用例通过 - 能看进度:
state/PROGRESS.md与任务分解文件可读 - 能接手:只看仓库即可回答"怎么跑 / 怎么测 / 下一步做什么"
2.7 课程给出的三个参考示例(原样保留,作为契约模板)
示例 1:启动就绪清单文档(初始化契约)
# 初始化契约
## 启动命令
- 安装依赖:`make setup`
- 启动开发服务器:`make dev`
- 运行测试:`make test`
- 完整验证:`make check`
## 当前状态
- 所有依赖已安装并锁定
- 测试框架已配置(Vitest + React Testing Library)
- 示例测试通过(1/1)
- Lint 规则已配置(ESLint + Prettier)
## 项目结构
- src/ — 源代码
- src/components/ — 组件
- src/api/ — API 客户端
- tests/ — 测试文件
示例 2:任务分解文件
# 任务分解
## Task 1: 用户认证基础
- 实现 JWT 认证中间件
- 添加登录/注册端点
- 验收标准:pytest tests/test_auth.py 全部通过
## Task 2: 用户资料页面
- 实现用户资料 CRUD
- 添加资料编辑表单
- 验收标准:pytest tests/test_profile.py 全部通过
示例 3:初始化验收清单
## 初始化验收清单
- [ ] `make setup` 从零开始能成功
- [ ] `make test` 至少有一个测试通过
- [ ] 新的 Agent 会话能只看仓库回答"怎么跑"和"怎么测"
- [ ] 任务分解文件存在且有至少 3 个任务
- [ ] 所有内容已提交到 git
2.8 实际案例对比(React 前端项目)
| 维度 | 混合方式 | 独立初始化 |
|---|---|---|
| 第一会话行为 | 脚手架 + 首个功能同时做 | 只建结构、配框架、写示例测试、建清单、提交 checkpoint |
| 第二会话重建时间 | ~20 分钟(推断结构/框架/构建) | < 3 分钟(直接从任务列表干活) |
| 跨所有会话总重建成本 | 基准 | 多约 60% |
| 功能完成率 | 基准 | 高 31% |
独立初始化多花的那 20 分钟,会在后续成倍收回。
2.9 核心要点总结
- 初始化与实现的优化目标不同,混在一起只会互相拖后腿。
- 初始化的产出是基础设施:可运行环境、可验证测试、启动就绪清单、任务分解。
- 用"启动就绪清单"四条件验收初始化:能启动、能测试、能看进度、能接手。
- 热启动优于冷启动,用项目模板预置标准化基础设施。
- 初始化投入的时间会在后续 3–4 个会话中完全收回,是前期投资,不是额外成本。
三、示例代码搭建过程
目标:
把上面这些"软知识"固化成一个可在仓库里重放、可被新会话接手的工程结构。技术栈:Spring Boot 3.2.5 + Java 17 + Maven,包名 com.badao.ai,沿用 harness/{init,gates,skills,state} 的分层,以及 instructions/ 模块化指令、state/ 跨会话状态。
3.1 项目骨架与依赖
pom.xml 差异在于本工程没有真正调用大模型——初始化阶段的逻辑是纯 Java,因此即使不配置 DASHSCOPE_API_KEY 也能通过所有测试;只有在执行 mvn spring-boot:run 启动完整 Web 应用时才需要该环境变量。
<properties>
<java.version>17</java.version>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
</dependencies>
3.2 目录结构
spring-ai-alibaba-bailian-harness-initphase/
├── pom.xml
├── state/
│ ├── PROGRESS.md ← 进度(启动就绪"能看进度")
│ └── DECISIONS.md ← 初始化期决策留痕(杜绝隐式假设)
└── src/
├── main/
│ ├── java/com/badao/ai/
│ │ ├── SpringAiDemoApplication.java
│ │ ├── config/InitPhaseConfig.java ← 模块化指令(按阶段组装提示词)
│ │ ├── controller/InitController.java ← /api/init/run、/api/todos
│ │ ├── model/TodoItem.java ← 实现阶段产物(演示用)
│ │ ├── service/TodoService.java ← 实现阶段产物
│ │ ├── service/InitPhaseService.java ← 对外暴露初始化阶段执行与验收
│ │ ├── runner/InitDemoRunner.java ← 启动即跑"独立初始化阶段"
│ │ └── harness/
│ │ ├── init/InitializationPhase.java ★ 核心:初始化阶段
│ │ ├── gates/InitReadinessGate.java ★ 核心:启动就绪闸门(四条件验收)
│ │ ├── skills/TemplateBootstrapSkill.java ★ 热启动:基于模板预置结构
│ │ └── state/InitContractManager.java ★ 固化契约/任务分解/决策
│ └── resources/
│ ├── application.yml
│ └── instructions/
│ ├── AGENTS.md ← 入口概览 + 硬约束 + 文档索引
│ ├── docs/init-contract.md ← 启动就绪清单契约
│ ├── docs/task-breakdown.md ← 任务分解
│ ├── docs/anti-pattern.md ← 反面案例(混合方式代价)
│ └── legacy/GIANT_AGENTS.md ← 反面教材:巨型初始化文件
└── test/java/com/badao/ai/
├── TodoModelTest.java / TodoServiceTest.java ← 示例测试(满足"能测试")
├── InitReadinessGateTest.java ← 闸门逻辑单测
└── InitPhaseComparisonTest.java ← 独立初始化 vs 混合 对比实验
3.3 核心类逐一讲解
(1)InitializationPhase —— 初始化阶段本身
它把"初始化"固化为一个可被后续任意新会话无歧义重放的步骤。关键约束:本阶段只搭基础设施,不写业务功能代码。
@Component
public class InitializationPhase {
private final TemplateBootstrapSkill bootstrapSkill;
private final InitReadinessGate readinessGate;
private final InitContractManager contractManager;
public InitResult run() {
log.info("===== 初始化阶段开始(本阶段只搭基础设施,不写业务功能) =====");
bootstrapSkill.bootstrapFromTemplate(); // 1) 热启动:预置标准结构
contractManager.writeInitContract(); // 2) 写启动就绪清单
contractManager.writeTaskBreakdown(); // 3) 写任务分解
contractManager.recordInitDecision(...); // 4) 记录框架/目录决策,杜绝隐式假设
InitReadinessGate.ReadinessReport report = readinessGate.verify(); // 5) 四条件验收
return new InitResult(report.allGreen(), report);
}
public record InitResult(boolean success, InitReadinessGate.ReadinessReport report) {}
}
(2)InitReadinessGate —— 把"初始化完成"变成可自动验证的硬指标
四项条件缺一不可,只有全绿才算交付:
@Component
public class InitReadinessGate {
private final Path base;
public ReadinessReport verify() {
boolean canBuild = Files.exists(base.resolve("pom.xml"));
boolean canTest = hasExampleTest(); // src/test/java 下存在 *Test.java
boolean hasProgress= Files.exists(base.resolve("state/PROGRESS.md"))
&& Files.exists(base.resolve(".../task-breakdown.md"));
boolean canHandoff = Files.exists(base.resolve(".../init-contract.md"))
&& canBuild && canTest && hasProgress;
return new ReadinessReport(canBuild, canTest, hasProgress, canHandoff);
}
public record ReadinessReport(boolean canBuild, boolean canTest,
boolean hasProgress, boolean canHandoff) {
public boolean allGreen() {
return canBuild && canTest && hasProgress && canHandoff;
}
}
}
设计要点:
base可注入(默认当前工作目录),便于在测试中指向临时目录做"独立初始化 vs 混合"对比。
(3)TemplateBootstrapSkill —— 热启动优于冷启动
不要从空目录让 Agent 自行推断结构,而是基于模板预置标准目录与依赖:
@Component
public class TemplateBootstrapSkill {
public void bootstrapFromTemplate() {
log.info("[热启动] 基于模板预置标准目录与依赖结构...");
ensureDir("src/main/java/com/badao/ai");
ensureDir("src/test/java/com/badao/ai");
ensureDir("src/main/resources/instructions/docs");
ensureDir("state");
}
}
真实场景可对接 Spring Initializr / create-react-app 等脚手架;这里用本地目录约定演示"标准化结构就位,只留项目特有初始化"。
(4)InitContractManager —— 让仓库"随时可接手"
把初始化的产出固化成仓库里的可见文件,使任何新会话只看仓库即可接手:
init-contract.md:怎么跑、怎么测、当前状态(启动就绪清单)task-breakdown.md:下一步做什么、验收标准state/DECISIONS.md:初始化期的框架/目录决策留痕,杜绝隐式假设
@Component
public class InitContractManager {
public void writeInitContract() { /* 写入启动就绪清单 */ }
public void writeTaskBreakdown() { /* 写入任务分解 */ }
public void recordInitDecision(String decision, String reason, String alternatives) {
/* 追加决策日志,标注"初始化期" */
}
}
3.4 配置、服务、控制器、启动器
InitPhaseConfig:模块化指令风格——AGENTS.md常驻,init-contract.md/task-breakdown.md按需加载,并提供buildPromptForPhase("init"|"impl")体现"初始化与实现在提示词层面也分离"。同时保留legacy/GIANT_AGENTS.md作为巨型文件反面教材。InitPhaseService:编排InitializationPhase与InitReadinessGate,返回四条件 JSON。InitController:POST /api/init/run触发独立初始化阶段;GET /api/init/readiness仅验收(新会话判断能否接手);/api/todos属于实现阶段功能。InitDemoRunner:实现CommandLineRunner,应用一启动先跑初始化阶段(对应"第一个会话只做初始化"),通过四项验收后再进入正常服务功能。
3.5 指令文档与反面教材
初始化说明拆成:
AGENTS.md:概览 + 全局硬约束(其中第 1 条即"初始化阶段禁止写业务功能")+ 文档索引docs/init-contract.md:启动就绪清单契约docs/task-breakdown.md:任务分解docs/anti-pattern.md:混合方式危害与量化对比legacy/GIANT_AGENTS.md:把一切揉进一个巨型文件的反面教材(对比用)
3.6 测试即"可验证的测试框架"产出
TodoModelTest / TodoServiceTest 是"实现阶段"的示例测试,它们的存在同时满足了启动就绪清单中"能测试"这一项。
InitReadinessGateTest 验证闸门判定:四项齐全 => 全绿;缺 task-breakdown.md => 非全绿。
InitPhaseComparisonTest 即课程"课后练习 2"的落地——独立初始化 vs 混合方式的量化对比:
@Test
void separateInitPhaseProducesReadinessAllGreen() {
InitializationPhase phase = new InitializationPhase(
new TemplateBootstrapSkill(), new InitReadinessGate(), new InitContractManager());
InitializationPhase.InitResult result = phase.run();
assertTrue(result.success(), "独立初始化阶段应通过启动就绪四项验收");
assertTrue(result.report().allGreen());
}
@Test
void mixedApproachLeavesReadinessGap() throws IOException {
// 混合方式:脚手架和第一个功能一起写,基础设施不全(缺契约/任务分解/进度)
Path base = Files.createTempDirectory("mixed");
Files.write(base.resolve("pom.xml"), ".".getBytes());
Files.createDirectories(base.resolve("src/test/java"));
Files.write(base.resolve("src/test/java/DemoTest.java"), ".".getBytes());
// 故意不写 init-contract.md / task-breakdown.md / state/PROGRESS.md
InitReadinessGate.ReadinessReport report = new InitReadinessGate(base).verify();
assertFalse(report.allGreen(), "混合方式不应通过启动就绪验收");
assertFalse(report.canHandoff(), "混合方式无法让新会话直接接手");
}
四、如何运行
# 1) 编译并跑测试(无需大模型 Key,验证初始化阶段逻辑)
mvn test
# 2) 触发"独立初始化阶段",返回启动就绪四项验收 JSON
mvn spring-boot:run
curl -X POST "http://localhost:886/api/init/run"
# 3) 仅做验收(模拟新会话判断能否接手)
curl "http://localhost:886/api/init/readiness"
# 4) 进入实现阶段(基础设施就绪后的功能验证)
curl "http://localhost:886/api/todos"
运行完整 Web 应用需设置
DASHSCOPE_API_KEY环境变量;纯测试与初始化阶段逻辑不依赖它。
预期:/api/init/run 返回 allGreen=true,并打印"初始化阶段通过四项验收,后续会话可直接接手实现"。
五、课程课后练习(建议动手)
- 启动就绪清单设计:为自己的项目写一份清单,开新 Agent 会话时只给仓库内容(不给口头上下文),记录它遇到的问题,每个问题对应清单缺失的条款。
- 对比实验:选一个中等复杂度新项目。方式 A 初始化与首次实现同时做;方式 B 先独立初始化再实现。4 个会话后对比首次验证时间、重建成本、功能完成率(本工程
InitPhaseComparisonTest已给出自动化雏形)。 - 初始化验收清单:为项目设计验收清单,让新 Agent 逐项执行,记录通过/未通过项,未通过即 harness 需补强之处。
六、小结
第六讲的本质,是把"初始化"从一个被忽视的、顺手做的事,提升为工程的第一等公民:它有独立阶段、有固定产出、有可自动验证的验收(启动就绪四条件)、有决策留痕。本示例工程用 Spring Boot 把这套理念落到了代码里——InitializationPhase 负责"只搭基础设施",InitReadinessGate 负责"四项全绿才算交付",TemplateBootstrapSkill 落实"热启动",InitContractManager 保证"仓库随时可接手"。当下一个全新会话打开这个仓库时,它不需要任何口头交代,仅凭 state/ 与 instructions/ 就能立刻知道怎么跑、怎么测、下一步做什么。
更多推荐



所有评论(0)