Harness Engineering实战:给 Agent 划清每次任务的边界示例

课程来源:Learn Harness Engineering · 第七讲《为什么 Agent 会越界且做不完》


一、背景与一句话结论

你让 Agent “给项目加上用户认证”,它很可能同时改了数据库 schema、写路由、改前端组件,还顺手重构了错误处理中间件。两小时后 12 个文件被改、800 行新代码,却没有一个功能端到端跑通。

一句话结论:Agent 天生有"多做一点"的冲动,必须给它划清每次任务的边界——WIP=1(做完一个再做下一个),并用"完成证据"和"验证完成率(VCR)"强制约束。


注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、核心知识点讲解

2.1 注意力是有限的资源(数学本质)

Agent 的上下文容量为 C,同时激活 k 个任务,每个任务平均获得 C/k 的推理资源。当 C/k 低于完成单个任务所需的最小阈值时,所有任务都做不完。这正是"贪多嚼不烂"的数学表述。Anthropic 实验数据直接支持:使用"小下一步"(等价于 WIP=1)策略的 Agent,任务完成率比宽泛提示高 37%;且代码行数与功能完成率呈弱负相关——写得越多,完成得越少。

2.2 两个共生问题

  • 过度延伸(Overreach):一次会话中激活的任务数超过最优值。可量化:同时做 5 个功能但 0 个跑通,就是 overreach。
  • 不足完成(Under-finish):已启动任务中,通过端到端验证的比例低于阈值。写了代码但没跑通测试,就是 under-finish。

二者互相加剧:overreach → 注意力分散 → under-finish → 半成品增加复杂度 → 下一任务更易 overreach。恶性循环。用 Kanban 的 Little 法则(L = λ·W)解释:在制品 L 过大,每个任务的前置时间 W 必然拉长,失败概率被放大。

2.3 核心概念术语表

  1. WIP 限制(Work-in-Progress Limit):来自 Kanban,限制同时进行的任务数。对 Agent,WIP=1 是最安全的默认值
  2. 完成证据(Completion Evidence):任务从"进行中"变"已通过"必须满足的可验证条件。"代码看起来没问题"不算,"curl 返回 201"才算。
  3. 范围表面(Scope Surface):DAG,每个节点是一个工作单元,边是依赖。状态只有四种:未开始、进行中、阻塞、已通过。
  4. 验证完成率(VCR):已通过验证的任务数 / 已启动的任务数。VCR < 1.0 时,阻止新任务启动。
  5. 完成压力(Completion Pressure):harness 通过 WIP 限制 + 完成证据要求共同产生的约束力,迫使 Agent 先完成再开始。

2.4 实施方法(四条)

  1. 强制 WIP=1:在 AGENTS.md / CLAUDE.md 写明——每次只做一个功能点,端到端验证通过后才能开始下一个,不要在实现 A 时"顺便"重构 B。
  2. 给每个任务定义显式完成证据:功能列表里每个条目都要有验证命令(如 curl ... | jq .status == 201)。
  3. 把范围表面外部化:用机器可读文件(JSON/Markdown)记录所有任务状态,任何新会话直接读,知道"谁在做、什么算完成、已通过什么验证"。
  4. 监控验证完成率:持续跟踪 VCR;VCR < 1.0 时阻止新任务启动。

2.5 实际案例(8 个功能点的 REST API)

维度 无约束模式 WIP=1 模式
首会话同时启动功能数 5 1
首会话代码量 ~800 行 / 12 文件 ~200 行 / 4 文件
端到端测试通过率 20%(仅注册跑通) 100%
第 3~4 会话后完成功能数 3 / 8 7 / 8
完成率 37.5% 87.5%

结果:WIP=1 总代码量更少但有效代码更多。“少做但做完"永远优于"多做但做半”。

2.6 核心要点总结

  • WIP=1 是 agent harness 的默认安全设置:做完一个再做下一个。
  • 完成证据必须可执行:行为验证通过才算完成。
  • 范围表面必须外部化为文件:不能只在对话里说。
  • overreach 与 under-finish 是共生问题:解决一个就解决了另一个。
  • “少做但做完"永远优于"多做但做半”:质量永远比数量重要。

三、示例代码搭建

技术栈:Spring Boot 3.2.5 + Java 17 + Maven,包名 com.badao.ai,沿用 harness/{state,gates,skills} 分层与 instructions/ 模块化指令、state/ 跨会话状态。

3.1 目录结构

spring-ai-alibaba-bailian-harness-scope/
├── pom.xml
├── state/
│   ├── SCOPE_SURFACE.md          ← 范围表面(机器可读,外部化)
│   └── DECISIONS.md              ← 边界事件留痕(overreach/under-finish)
└── src/
    ├── main/java/com/badao/ai/
    │   ├── SpringAiDemoApplication.java
    │   ├── config/ScopeConfig.java            ← 模块化指令(按阶段组装提示词)
    │   ├── controller/ScopeController.java    ← /api/tasks、/api/scope/*
    │   ├── model/WorkUnit.java                ← 范围表面节点(含 Status 枚举)
    │   ├── service/TaskScopeService.java      ← 编排范围控制
    │   ├── runner/ScopeDemoRunner.java        ← 启动即演示 WIP=1
    │   └── harness/
    │       ├── state/TaskScopeManager.java    ★ 核心:WIP=1 + VCR 红线 + 范围表面
    │       ├── gates/WipGate.java             ★ 核心:完成压力(wipOk/vcrOk 验收)
    │       ├── skills/CompletionEvidenceSkill.java ★ 完成证据(可执行验证)
    │       └── state/ScopeSurfaceManager.java ★ 范围表面外部化到 state/
    └── test/java/com/badao/ai/
        ├── TaskScopeManagerTest.java          ← WIP=1 / VCR 逻辑单测
        ├── WipGateTest.java                   ← 闸门判定单测
        ├── WipComparisonTest.java             ← WIP=1 vs 无约束 对比实验
        └── CompletionEvidenceTest.java        ← 完成证据单测

3.2 核心类逐一讲解

(1)WorkUnit —— 范围表面的节点
public class WorkUnit {
    public enum Status { NOT_STARTED, IN_PROGRESS, BLOCKED, PASSED }
    private final String id;
    private final String title;
    private Status status;            // 四种状态之一
    private final String verifyCommand;   // 完成证据(可执行验证命令)
    private final List<String> deps;      // DAG 边(依赖)
}
(2)TaskScopeManager —— WIP=1 + VCR 红线(范围表面核心)

它维护 DAG,并强制两条约束。startTask 在违反约束时抛出 IllegalStateException(即"完成压力"生效):

public void startTask(String id) {
    WorkUnit unit = require(id);
    if (!canStartNewTask()) {                 // WIP 超 || VCR<1.0
        String reason = currentInProgress().isEmpty()
                ? "存在未完成(under-finish),VCR<1.0,必须先收尾"
                : "已有任务进行中(overreach),WIP 限制禁止并行启动";
        throw new IllegalStateException("无法启动任务 " + id + ":" + reason);
    }
    // ...依赖检查...
    unit.setStatus(WorkUnit.Status.IN_PROGRESS);
}

public boolean canStartNewTask() {
    if (currentInProgress().size() >= wipLimit) return false; // WIP 违规
    long started = countStarted(), passed = countPassed();
    return started == 0 || passed >= started;                 // VCR>=1.0
}

public double getVcr() {                                       // 验证完成率
    long started = countStarted();
    return started == 0 ? 1.0 : (double) countPassed() / started;
}

注意 markPassed 要求任务处于 IN_PROGRESS——只有行为验证通过才允许置 passed,杜绝"代码看起来没问题"。

(3)WipGate —— 把"完成压力"变成可自动验证的硬指标
public GateReport verify() {
    boolean wipOk = !scopeManager.isOverreaching();
    boolean vcrOk = scopeManager.getVcr() >= 1.0 || scopeManager.countStarted() == 0;
    return new GateReport(wipOk, vcrOk, inProgress, started, passed);
}
// GateReport.allGreen() = wipOk && vcrOk;overreach() = !wipOk;underFinish() = !vcrOk
(4)CompletionEvidenceSkill —— 完成必须靠证据
public WorkUnit buildWorkUnit(String id, String behavior, String verifyCommand, List<String> deps) {
    return new WorkUnit(id, behavior, verifyCommand, deps);
}
public boolean verify(String expected, String actualOutput) {
    return actualOutput != null && actualOutput.contains(expected);
}
(5)ScopeSurfaceManager —— 范围表面外部化

toReport() 写入 state/SCOPE_SURFACE.md,并把 overreach / under-finish 事件留痕到 state/DECISIONS.md,使任何新会话只看仓库即可接手

3.3 服务、控制器、启动器、配置

  • TaskScopeService:编排上述组件。startTask 在越界时捕获异常、记录边界事件、返回失败结果(而非静默放行);runDemo 依次做完再做下一个,并展示并行启动被拒绝。
  • ScopeControllerPOST /api/tasks(加任务)、POST /api/tasks/{id}/start(受闸门约束)、POST /api/tasks/{id}/complete?evidencePassed=(需证据)、GET /api/scope/report|vcr|gatePOST /api/scope/demo
  • ScopeDemoRunnerCommandLineRunner,应用启动即演示 WIP=1。
  • ScopeConfig:模块化指令——规划阶段给"范围表面",实现阶段给"完成证据",并保留 legacy/GIANT_AGENTS.md 作反面教材。

3.4 测试即"可验证"的落地

WipComparisonTest 即课程"课后练习 2"的自动化:

@Test
void wipOneModeAchievesFullVcr() {
    TaskScopeManager mgr = new TaskScopeManager(1);
    // 依次 start+pass F01..F03
    assertEquals(1.0, mgr.getVcr(), 0.0001);
    assertTrue(new WipGate(mgr).verify().allGreen());
}

@Test
void unconstrainedModeLeavesLowVcr() {
    TaskScopeManager mgr = new TaskScopeManager(5); // 无 WIP 纪律
    for (int i = 1; i <= 5; i++) { mgr.addTask(...); mgr.startTask("F0"+i); }
    mgr.markPassed("F01"); // 仅 1 个真正验证通过
    assertEquals(0.2, mgr.getVcr(), 0.0001);
    assertFalse(new WipGate(mgr).verify().allGreen());
}

四、如何运行

# 1) 编译并跑测试(无需大模型 Key,验证 WIP/VCR 逻辑)
mvn test

# 2) 触发 WIP=1 演示
mvn spring-boot:run
curl -X POST "http://localhost:887/api/scope/demo"

# 3) 体验边界约束
curl -X POST "http://localhost:887/api/tasks?id=F01&title=注册&verify=status==201"
curl -X POST "http://localhost:887/api/tasks/F01/start"        # 成功
curl -X POST "http://localhost:887/api/tasks/F02/start"        # 被 WIP 闸门拒绝
curl -X POST "http://localhost:887/api/tasks/F01/complete?evidencePassed=true"
curl "http://localhost:887/api/scope/gate"                    # 查看 wipOk/vcrOk
curl "http://localhost:887/api/scope/vcr"                      # 查看 VCR

运行完整 Web 应用需设置 DASHSCOPE_API_KEY;测试与范围控制逻辑不依赖它。

预期:/api/scope/demo 返回 gateAllGreen=true、VCR=1.0;并行启动第二个任务时被拒绝并记入边界事件。


五、课程课后练习(建议动手)

  1. 任务原子化:选一个宽泛需求(如"实现用户管理系统"),拆成 ≥5 个原子单元,每个写清(a)单一行为(b)可执行验证命令©依赖,检查是否满足 WIP=1。
  2. 对比实验:同一项目跑两次,一次无约束、一次强制 WIP=1,比较 VCR、总代码行数、有效代码比例(本工程 WipComparisonTest 已给出自动化雏形)。
  3. 完成证据审计:回顾一次 Agent 运行,把每个变更分为"已完成行为 / 未完成行为 / 脚手架",给未完成行为补验证命令。

六、小结

第七讲的本质,是把"边界"从一句口头提醒提升为 harness 的一等约束:TaskScopeManager 用 WIP=1 拦住过度延伸、用 VCR 红线拦住不足完成;WipGate 把"完成压力"变成可自动验证的硬指标;CompletionEvidenceSkill 确保"完成"靠行为证据而非感觉;ScopeSurfaceManager 把范围表面外部化到仓库,让新会话无需任何口头交代即可接手。当 Agent 每次只推进一个被验证过的任务,它才真正"做完"而不是"做一半"。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐