Harness Engineering实战:防止 Agent 过早宣告完成(Why Agents Declare Victory Too Early)
0. 一句话核心
Agent 系统性过早宣称任务完成 ,因为它依据"代码层面的局部信心"判断;而系统级正确性需要全局验证。
Harness 必须用基于执行的外部验证替代 Agent 的"感觉"------把"完成判定"从主观变客观。
1. 为什么 Agent 会"提前交卷"
- 过度自信(校准偏差):Guo et al. 2017(ICML)证明现代神经网络置信度系统性高于准确率;AI 编码 Agent 同样"觉得"做完了,实际差得远。
- 滑坡效应(Slippery Slope):代码看着还行、静态检查无误,但 harness 未强制全面执行验证 → Agent 跳过实际运行/部分测试 → 每次跳过加剧信息不对称(任务规范→代码实现→运行时行为持续丢失)。
- 同一模型既生成又评估会系统性过度正面评价(Anthropic 2026):即使人类认为不达标,Agent 也会给自己打高分。
2. 本课关键概念 → 代码映射
| 概念(原文) | 落地的类 / 文件 | 作用 |
|---|---|---|
| 终止标准(Termination Criteria) | model/TerminationCriterion.java + state/TERMINATION.md |
把"什么叫做完"固化为外部化、机器可读的 required 标准 |
| 三层终止校验 | harness/gates/TerminationGate.java |
STATIC→RUNTIME→SYSTEM 层层门控,低层不过不许进高层 |
| 过早完成声明(Premature Victory) | TerminationGate.evaluate 返回 PREMATURE_VICTORY |
Agent 声称完成但标准未全过即判过早 |
| 验证-确认双闸门(V&V Dual Gate) | harness/gates/VerificationValidationGate.java |
Verification(代码实现行为)+ Validation(系统端到端)两层都过才算完成 |
| 完成优先级约束(Completion Priority) | harness/state/CompletionPriorityGuard.java |
核心未验证前禁止重构/性能/风格("顺便重构"是毒药) |
| 生成与评估分离 | harness/skills/EvaluatorSkill.java |
独立、刻意挑剔的评估者,可否决生成者过度自信声明 |
| 运行时反馈信号 | harness/state/RuntimeSignalCollector.java |
应用启动/健康检查/关键路径/副作用/临时资源清理 |
| 可操作错误反馈 | harness/skills/ActionableFeedbackGenerator.java |
把 "Test failed" 转成含修复步骤的反馈 |
| 置信度校准偏差 | model/ConfidenceCalibration.java |
量化自报信心 vs 实际质量的系统性偏差 |
3. 三条你必须记住的 Harness 纪律
- 完成判定外部化 :Agent 不许自己宣布"做完了"。是否完成由
TerminationGate依据运行时信号独立判定。 - 三层缺一不可:语法、行为、系统,层层递进。单元测试通过只是 RUNTIME 的极小一部分,不构成停止信号。
- 核心验证前不许重构:核心功能没验证通过就开始重构/优化,会把隐式正确的路径改坏。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
4. 项目结构与运行
spring-ai-alibaba-bailian-harness-victory/
├── pom.xml
├── src/main/java/com/badao/ai/
│ ├── SpringAiDemoApplication.java
│ ├── model/ TerminationCriterion / VictoryClaim / ConfidenceCalibration
│ ├── harness/
│ │ ├── gates/ TerminationGate / VerificationValidationGate (三层门控 + V&V 双闸门)
│ │ ├── skills/ EvaluatorSkill / ActionableFeedbackGenerator (评估分离 + 可操作反馈)
│ │ └── state/ CompletionPriorityGuard / RuntimeSignalCollector(优先级 + 运行时信号)
│ ├── service/ VictoryService (编排演示链路)
│ ├── controller/ VictoryController (/api/victory/demo, /report)
│ ├── runner/ VictoryDemoRunner (启动即演示)
│ └── config/ VictoryConfig (模块化指令)
├── src/main/resources/instructions/
│ ├── AGENTS.md
│ ├── docs/ termination-criteria.md / actionable-feedback.md / anti-pattern.md
│ └── legacy/ SELF_APPROVAL.md (反面教材:让 Agent 自己审批自己)
├── src/test/java/com/badao/ai/ 7 个测试(见下)
└── state/ TERMINATION.md / DECISIONS.md / HANDOFF.md (跨会话单一来源)
运行:
bash
mvn test # 验证三层门控 / 校准 / 多层防御 / 双闸门 / 反馈 / 评估分离
mvn spring-boot:run
curl -X POST "http://localhost:888/api/victory/demo" # 看"提前交卷→被捕获→强制端到端→确认胜利"
curl "http://localhost:888/api/victory/report" # 查看外部化终止标准
5. 课后练习的落地(Project 05:让 Agent 自己检查自己做的对不对)
| 练习 | 测试文件 | 验证点 |
|---|---|---|
| 1. 终止校验函数设计 | TerminationGateTest |
三层门控(低层不过禁止进高层)+ 过早完成检测 + 缺失标准定位 |
| 2. 校准偏差测量 | ConfidenceCalibrationTest |
复杂任务正偏差、简单任务低偏差、越界拒绝 |
| 3. 多层防御实验 | MultiLayerDefenseTest |
(a)仅静态/(b)+单测/©完整三层:过早完成比例 5→5→1,捕获 4 个缺陷 |
| 完成优先级 | CompletionPriorityTest |
核心未验证拒绝重构,验证后允许 |
| V&V 双闸门 | VerificationValidationTest |
两层都过才算完成;单测过≠完成 |
| 可操作反馈 | ActionableFeedbackTest |
反馈含修复步骤、比原始错误更长 |
| 生成/评估分离 | EvaluatorSeparationTest |
评估者否决过度自信声明,结论不随自报信心变化 |
6. 一个完整例子(密码重置功能)
- 提前交卷路径:改 schema、写 API、加邮件模板、单元测试通过、声明完成。
- 实际遗漏:(1) 端到端流程未测;(2) DB 迁移部分失败致 schema 不一致;(3) 邮件服务配置缺失。
- Harness 介入 :
TerminationGate强制三层校验 → 仅在 C1/C2 过时判PREMATURE_VICTORY,列出缺失 C3/C4/C5;
EvaluatorSkill因缺少客观端到端证据否决生成者(confidence=0.95);缺陷在会话内发现,节省 5-10 倍后续修复成本。
关键代码:
VictoryConfig:
package com.badao.ai.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 终止校验相关的指令配置(模块化指令风格)。
* 入口概览常驻,专题文档按需加载,避免巨型任务说明(见 legacy/SELF_APPROVAL.md)。
*/
@Configuration
public class VictoryConfig {
/** 入口指令:概览 + 硬约束(完成判定外部化等) + 文档索引 */
@Bean
public String entryInstructions() throws IOException {
return load("instructions/AGENTS.md");
}
/** 终止标准:定义三层校验、门控关系、校准偏差时加载 */
@Bean
public String terminationCriteria() throws IOException {
return load("instructions/docs/termination-criteria.md");
}
/** 可操作反馈:设计/对比"无效 vs 可操作"错误反馈时加载 */
@Bean
public String actionableFeedback() throws IOException {
return load("instructions/docs/actionable-feedback.md");
}
/** 反面教材:让 Agent 自己审批自己 */
@Bean
public String selfApproval() throws IOException {
return load("instructions/legacy/SELF_APPROVAL.md");
}
/**
* 按场景组装提示词:规划阶段给"终止标准",实现阶段给"可操作反馈"。
*/
public String buildPromptForPhase(String phase) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(entryInstructions()).append("\n\n");
if ("plan".equals(phase)) {
sb.append("【当前阶段:定义外部化终止标准】先把'什么叫做完'拆成三层 required 标准。\n");
sb.append(terminationCriteria());
} else {
sb.append("【当前阶段:实现+验证】单元测试通过 ≠ 完成;错误必须转成可操作反馈。\n");
sb.append(actionableFeedback());
}
return sb.toString();
}
private String load(String path) throws IOException {
ClassPathResource r = new ClassPathResource(path);
return new String(r.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
}
}
TerminationGate:
package com.badao.ai.harness.gates;
import com.badao.ai.model.TerminationCriterion;
import com.badao.ai.model.VictoryClaim;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 终止闸门(Termination Gate)------第九讲的核心 Harness 原语:<b>三层终止校验</b>。
*
* <p>它把"完成判定"从 Agent 主观感觉外部化到 Harness,输入是运行时信号而非置信度。
* 三层递进、层层门控:
* <ol>
* <li>STATIC(语法/静态):最低成本,必须通过</li>
* <li>RUNTIME(运行时行为):核心证据</li>
* <li>SYSTEM(系统级端到端):最后防线</li>
* </ol>
* 低层未全通过,禁止进入高层;三层全部 required 标准通过,才允许声明胜利。
* 若 Agent 提前声称完成而标准未全过,判为 {@link Verdict#PREMATURE_VICTORY}(过早完成)。
*/
@Component
public class TerminationGate {
private static final Logger log = LoggerFactory.getLogger(TerminationGate.class);
public enum Verdict {
/** 三层全部 required 标准通过 → 可确认胜利 */
VICTORY_CONFIRMED,
/** Agent 声称完成,但 required 标准未全过 → 过早完成声明 */
PREMATURE_VICTORY,
/** 未声明完成、且尚未全部通过 */
INCOMPLETE
}
/**
* 层门控:判断能否进入目标层。
* STATIC 永远可进入;RUNTIME 需 STATIC 全过;SYSTEM 需 STATIC+RUNTIME 全过。
*/
public boolean canEnterLayer(TerminationCriterion.Layer target, List<TerminationCriterion> criteria) {
if (target == TerminationCriterion.Layer.STATIC) return true;
if (target == TerminationCriterion.Layer.RUNTIME) return allPassed(criteria, TerminationCriterion.Layer.STATIC);
if (target == TerminationCriterion.Layer.SYSTEM) {
return allPassed(criteria, TerminationCriterion.Layer.STATIC)
&& allPassed(criteria, TerminationCriterion.Layer.RUNTIME);
}
return false;
}
private boolean allPassed(List<TerminationCriterion> criteria, TerminationCriterion.Layer layer) {
return criteria.stream()
.filter(c -> c.getLayer() == layer && c.isRequired())
.allMatch(TerminationCriterion::isPassed);
}
/**
* 终止判定(外部化,不信任 Agent 感觉):
* 只有三层全部 required 标准通过,才允许声明胜利;
* Agent 若提前声称完成,则判为 {@link Verdict#PREMATURE_VICTORY}。
*/
public TerminationReport evaluate(VictoryClaim claim, List<TerminationCriterion> criteria) {
boolean staticOk = allPassed(criteria, TerminationCriterion.Layer.STATIC);
boolean runtimeOk = allPassed(criteria, TerminationCriterion.Layer.RUNTIME);
boolean systemOk = allPassed(criteria, TerminationCriterion.Layer.SYSTEM);
boolean allOk = staticOk && runtimeOk && systemOk;
if (claim.isDeclaredDone() && !allOk) {
log.warn("【终止判定】检测到过早完成声明:agent 声称完成(confidence={}),但 静态={} 运行时={} 系统={}",
claim.getSelfConfidence(), staticOk, runtimeOk, systemOk);
return new TerminationReport(Verdict.PREMATURE_VICTORY, staticOk, runtimeOk, systemOk, missingCriteria(criteria));
}
if (allOk) {
log.info("【终止判定】三层全部通过,确认胜利");
return new TerminationReport(Verdict.VICTORY_CONFIRMED, true, true, true, List.of());
}
return new TerminationReport(Verdict.INCOMPLETE, staticOk, runtimeOk, systemOk, missingCriteria(criteria));
}
private List<String> missingCriteria(List<TerminationCriterion> criteria) {
return criteria.stream()
.filter(c -> c.isRequired() && !c.isPassed())
.map(TerminationCriterion::getId)
.toList();
}
/** 终止判定报告 */
public record TerminationReport(Verdict verdict, boolean staticOk, boolean runtimeOk,
boolean systemOk, List<String> missing) {
/** 是否过早完成声明 */
public boolean premature() {
return verdict == Verdict.PREMATURE_VICTORY;
}
/** 是否确认胜利 */
public boolean confirmed() {
return verdict == Verdict.VICTORY_CONFIRMED;
}
}
}
VerificationValidationGate:
package com.badao.ai.harness.gates;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 验证-确认双闸门(Verification-Validation Dual Gate)------第九讲"V&V 双闸门"。
*
* <ul>
* <li><b>Verification(第一层验证)</b>:代码是否正确实现了"指定行为"(对应 RUNTIME 层)。
* 单元测试通过只覆盖这一层的极小一部分,无法暴露接口不匹配 / 状态传播错误 / 环境依赖。</li>
* <li><b>Validation(第二层确认)</b>:系统级行为是否满足"端到端需求"(对应 SYSTEM 层)。</li>
* </ul>
* 两层都过才算完成;任一不过即视为未完成。
*/
@Component
public class VerificationValidationGate {
private static final Logger log = LoggerFactory.getLogger(VerificationValidationGate.class);
/**
* 执行 V&V 双闸门检查。
*
* @param codeImplementsBehavior 代码是否实现了指定行为(Verification)
* @param endToEndSatisfied 系统级端到端需求是否满足(Validation)
*/
public VVReport check(boolean codeImplementsBehavior, boolean endToEndSatisfied) {
boolean v = codeImplementsBehavior;
boolean val = endToEndSatisfied;
if (!v) {
log.warn("【Verification 失败】代码未正确实现指定行为(接口不匹配 / 状态传播错误 / 环境依赖)");
} else if (!val) {
log.warn("【Validation 失败】系统级端到端需求未满足(单元测试通过 ≠ 任务完成)");
} else {
log.info("【V&V 双闸门】验证 + 确认均通过");
}
return new VVReport(v, val, v && val ? "OK" : "FAIL");
}
/** V&V 报告 */
public record VVReport(boolean verificationPassed, boolean validationPassed, String detail) {
/** 两层都过才算完成 */
public boolean bothPassed() {
return verificationPassed && validationPassed;
}
}
}
ActionableFeedbackGenerator:
package com.badao.ai.harness.skills;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 可操作反馈生成器(Actionable Feedback Generator)------第九讲"可操作错误反馈"。
*
* <p>把"无效反馈"(如 {@code "Test failed"})转成<b>含具体修复步骤</b>的反馈:
* 明确错在哪、怎么改。仅说"错了"不够,Agent 会在同一处反复失败。
*/
@Component
public class ActionableFeedbackGenerator {
private static final Logger log = LoggerFactory.getLogger(ActionableFeedbackGenerator.class);
/**
* 把原始错误转成可操作反馈。
*
* @param rawError 原始错误现象(如 "Test failed" / "POST /api/reset-password returned 500")
* @param fixHint 具体修复步骤(定位线索 + 可执行动作)
* @return 形如 "错误: <rawError> | 修复步骤: <fixHint>" 的可操作反馈
*/
public String makeActionable(String rawError, String fixHint) {
String actionable = String.format("错误: %s | 修复步骤: %s", rawError, fixHint);
log.info("【可操作反馈】{}", actionable);
return actionable;
}
}
EvaluatorSkill:
package com.badao.ai.harness.skills;
import com.badao.ai.model.VictoryClaim;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 评估者技能(Evaluator Skill)------第九讲"生成与评估分离"的落地。
*
* <p>基于 Anthropic 2026 研究:同一模型既生成又评估会系统性过度正面评价。
* 因此本组件刻意扮演一个<b>独立、挑剔</b>的评估角色("检查的人"),
* 不信任生成者("干活的人")的自评,而是基于独立收集的运行时客观证据重新判定。
*
* <p>当生成者自信地声称完成、但独立证据未满足端到端需求时,评估者<b>否决</b>该声明,
* 把"过早完成"挡在门外。
*/
@Component
public class EvaluatorSkill {
private static final Logger log = LoggerFactory.getLogger(EvaluatorSkill.class);
/**
* 独立评估生成者的胜利声明。
*
* @param generatorClaim 生成者(干活的人)的自评与胜利声明
* @param objectiveEvidence 独立收集的运行时客观证据是否满足端到端需求
* @return 评估结论:是否真的可以宣告胜利
*/
public EvaluationReport evaluate(VictoryClaim generatorClaim, boolean objectiveEvidence) {
if (generatorClaim.isDeclaredDone() && objectiveEvidence) {
log.info("【评估者】生成者声称完成且独立证据支持,确认胜利");
return new EvaluationReport(true, true, "生成者自评与独立证据一致");
}
if (generatorClaim.isDeclaredDone() && !objectiveEvidence) {
log.warn("【评估者否决】生成者过度自信(confidence={}),但独立证据未满足端到端,判定为过早完成",
generatorClaim.getSelfConfidence());
return new EvaluationReport(false, false, "评估者否决生成者自评:缺少客观端到端证据");
}
return new EvaluationReport(false, false, "尚未声明完成");
}
/** 评估结论 */
public record EvaluationReport(boolean objectiveMet, boolean victoryAllowed, String detail) {
/** 评估者是否允许宣告胜利 */
public boolean overridesGenerator() {
return !victoryAllowed;
}
}
}
CompletionPriorityGuard:
package com.badao.ai.harness.state;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 完成优先级守门(Completion Priority Guard)------第九讲"完成优先级约束"。
*
* <p>先验证功能正确性,再处理性能,最后管风格。
* <b>核心功能验证通过前,禁止重构 / 优化 / 风格调整</b>------
* "顺便重构"是完成判定的毒药:它会改变"已验证 / 未验证"边界,
* 可能破坏之前隐式正确的路径(Knuth:过早优化是万恶之源的新含义)。
*/
@Component
public class CompletionPriorityGuard {
private static final Logger log = LoggerFactory.getLogger(CompletionPriorityGuard.class);
private boolean coreFunctionalVerified = false;
/** 标记核心功能已验证通过,进入性能 / 风格阶段 */
public void markCoreFunctionalVerified() {
this.coreFunctionalVerified = true;
log.info("【完成优先级】核心功能已验证通过,可进入性能 / 风格阶段");
}
public boolean isCoreFunctionalVerified() {
return coreFunctionalVerified;
}
/**
* 请求重构 / 优化 / 风格调整。核心未验证则拒绝(防止过早优化破坏正确路径)。
*
* @param reason 重构理由
*/
public RefactorDecision requestRefactor(String reason) {
if (coreFunctionalVerified) {
log.info("【重构】允许:{}", reason);
return new RefactorDecision(true, "核心已验证,允许重构");
}
log.warn("【重构被拒】核心功能尚未验证通过,禁止重构(过早优化的毒药)。理由={}", reason);
return new RefactorDecision(false, "核心功能未验证,禁止重构");
}
/** 重构决策 */
public record RefactorDecision(boolean allowed, String detail) {
}
}
RuntimeSignalCollector:
package com.badao.ai.harness.state;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.EnumMap;
import java.util.Map;
/**
* 运行时信号采集器(Runtime Signal Collector)------第九讲"运行时反馈信号是客观基础"。
*
* <p>完成质量不以 Agent 感觉为准,而以程序执行的客观信号为准:
* 应用是否成功启动就绪、健康检查是否通过、关键功能路径运行时是否成功、
* 副作用(DB 写入 / 文件操作)是否正确、临时资源是否清理。
*/
@Component
public class RuntimeSignalCollector {
private static final Logger log = LoggerFactory.getLogger(RuntimeSignalCollector.class);
/** 五类运行时反馈信号 */
public enum Signal {
/** 应用是否成功启动就绪 */
APP_STARTED,
/** 健康检查通过 */
HEALTH_CHECK,
/** 关键功能路径运行时成功 */
CRITICAL_PATH_OK,
/** 副作用(DB 写入 / 文件操作)是否正确 */
SIDE_EFFECTS_CORRECT,
/** 临时资源已清理 */
TEMP_RESOURCES_CLEANED
}
private final Map<Signal, Boolean> signals = new EnumMap<>(Signal.class);
private final Map<Signal, String> evidence = new EnumMap<>(Signal.class);
/** 记录一条运行时信号及其证据 */
public void record(Signal signal, boolean ok, String evidenceMsg) {
signals.put(signal, ok);
evidence.put(signal, evidenceMsg == null ? "" : evidenceMsg);
log.info("【运行时信号】{} = {} ({})", signal, ok, evidenceMsg);
}
/** 全部信号是否全绿(且五类均已采集) */
public boolean allGreen() {
if (signals.size() != Signal.values().length) return false;
return signals.values().stream().allMatch(Boolean::booleanValue);
}
/** 当前信号快照(不可变) */
public Map<Signal, Boolean> snapshot() {
return Map.copyOf(signals);
}
}
VictoryService:
package com.badao.ai.service;
import com.badao.ai.harness.gates.TerminationGate;
import com.badao.ai.harness.gates.VerificationValidationGate;
import com.badao.ai.harness.skills.ActionableFeedbackGenerator;
import com.badao.ai.harness.skills.EvaluatorSkill;
import com.badao.ai.harness.state.CompletionPriorityGuard;
import com.badao.ai.harness.state.RuntimeSignalCollector;
import com.badao.ai.model.TerminationCriterion;
import com.badao.ai.model.VictoryClaim;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 终止校验服务(Victory Service)------把第九讲各 Harness 组件编排成"防过早完成"流程。
*
* <p>演示链路:构建外部化终止标准 → Agent 在"单元测试通过"后提前宣称完成
* → 三层终止校验捕获过早完成 → 强制端到端验证 → 最终确认胜利;
* 并演示完成优先级约束、生成/评估分离、可操作反馈、V&V 双闸门。
*/
@Service
public class VictoryService {
private static final Logger log = LoggerFactory.getLogger(VictoryService.class);
private final TerminationGate terminationGate;
private final VerificationValidationGate vvGate;
private final EvaluatorSkill evaluator;
private final CompletionPriorityGuard priorityGuard;
private final RuntimeSignalCollector signalCollector;
private final ActionableFeedbackGenerator feedbackGen;
public VictoryService(TerminationGate terminationGate, VerificationValidationGate vvGate,
EvaluatorSkill evaluator, CompletionPriorityGuard priorityGuard,
RuntimeSignalCollector signalCollector, ActionableFeedbackGenerator feedbackGen) {
this.terminationGate = terminationGate;
this.vvGate = vvGate;
this.evaluator = evaluator;
this.priorityGuard = priorityGuard;
this.signalCollector = signalCollector;
this.feedbackGen = feedbackGen;
}
/**
* 完整演示:Agent 在"单元测试通过"后提前宣告完成 → 三层终止校验捕获过早完成 →
* 强制端到端验证 → 最终确认胜利;并演示完成优先级约束与可操作反馈。
*/
public String runDemo() {
StringBuilder sb = new StringBuilder();
sb.append("=== 第九讲演示:防止 Agent 过早宣告完成 ===\n\n");
// 1. 构建外部化终止标准(单一来源)
List<TerminationCriterion> criteria = buildCriteria();
sb.append("1) 终止标准已外部化(").append(criteria.size()).append(" 条,分三层)\n");
// 2. Agent 在"单元测试通过"后提前宣告完成(STATIC 全过、RUNTIME 仅单测过、SYSTEM 未验)
criteria.get(0).markPassed("BUILD SUCCESS"); // C1 STATIC
criteria.get(1).markPassed("unit tests green"); // C2 RUNTIME: 单元测试
// C3 RUNTIME: 端到端未验;C4/C5 SYSTEM: 未验
VictoryClaim premature = new VictoryClaim(0.95, true, "单元测试都过了,功能肯定没问题");
TerminationGate.TerminationReport r1 = terminationGate.evaluate(premature, criteria);
sb.append("2) Agent 提前宣称完成(confidence=0.95) → 判定=").append(r1.verdict())
.append(",缺失标准=").append(r1.missing()).append("\n");
// 3. 层门控:未过全部 RUNTIME 时不得进入 SYSTEM
boolean canSystem = terminationGate.canEnterLayer(TerminationCriterion.Layer.SYSTEM, criteria);
sb.append("3) 门控:未过全部 RUNTIME 时能否进入 SYSTEM=").append(canSystem).append("\n");
// 4. 强制补完所有层(端到端 + 系统级),再次判定
criteria.get(2).markPassed("E2E reset-password 流程跑通"); // C3
criteria.get(3).markPassed("DB 迁移一致性校验通过"); // C4
criteria.get(4).markPassed("邮件服务配置与模板存在"); // C5
TerminationGate.TerminationReport r2 = terminationGate.evaluate(premature, criteria);
sb.append("4) 补完三层后再次判定=").append(r2.verdict()).append("\n");
// 5. 独立评估者否决过度自信(生成与评估分离):生成者声称完成,但客观证据未满足
signalCollector.record(RuntimeSignalCollector.Signal.APP_STARTED, true, "app up on :8080");
EvaluatorSkill.EvaluationReport ev = evaluator.evaluate(previousPremature(), false);
sb.append("5) 独立评估者:victoryAllowed=").append(ev.victoryAllowed())
.append("(").append(ev.detail()).append(")\n");
// 6. 完成优先级约束:核心验证前禁止重构
CompletionPriorityGuard.RefactorDecision ref = priorityGuard.requestRefactor("重命名变量提升可读性");
sb.append("6a) 核心未验证时请求重构 → allowed=").append(ref.allowed()).append("\n");
priorityGuard.markCoreFunctionalVerified();
ref = priorityGuard.requestRefactor("重命名变量提升可读性");
sb.append("6b) 核心验证后请求重构 → allowed=").append(ref.allowed()).append("\n");
// 7. 可操作反馈
String fb = feedbackGen.makeActionable("Test failed",
"POST /api/reset-password 返回 500;检查邮件服务配置是否存在于环境变量,模板应在 templates/reset-email.html");
sb.append("7) 可操作反馈:").append(fb).append("\n");
// 8. V&V 双闸门
VerificationValidationGate.VVReport vv = vvGate.check(true, false);
sb.append("8) V&V 双闸门: verification=").append(vv.verificationPassed())
.append(" validation=").append(vv.validationPassed())
.append(" both=").append(vv.bothPassed()).append("\n");
return sb.toString();
}
/** 构造一个"过度自信的提前胜利声明"(用于演示生成/评估分离) */
private VictoryClaim previousPremature() {
return new VictoryClaim(0.95, true, "单元测试都过了");
}
/** 生成外部化终止标准的机器可读报告(单一权威来源) */
public String criteriaReport() {
List<TerminationCriterion> criteria = buildCriteria();
StringBuilder sb = new StringBuilder();
sb.append("# 终止标准(Termination Criteria · 单一权威来源)\n\n");
sb.append("完成判定外部化到 Harness;三层全部 required 通过才允许胜利。\n\n");
sb.append("| id | 描述 | 层 | 运行时信号 | required |\n");
sb.append("|:---|:---|:---|:---|:---|\n");
for (TerminationCriterion c : criteria) {
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
c.getId(), c.getDescription(), c.getLayer(), c.getRuntimeSignal(), c.isRequired()));
}
return sb.toString();
}
/** 构建默认终止标准(密码重置功能示例,对应第九讲案例) */
private List<TerminationCriterion> buildCriteria() {
List<TerminationCriterion> list = new ArrayList<>();
list.add(new TerminationCriterion("C1", "编译与静态分析无错", TerminationCriterion.Layer.STATIC, "BUILD SUCCESS"));
list.add(new TerminationCriterion("C2", "单元测试通过(隔离单元、模拟依赖)", TerminationCriterion.Layer.RUNTIME, "unit tests green"));
list.add(new TerminationCriterion("C3", "端到端关键路径(重置密码)运行时通过", TerminationCriterion.Layer.RUNTIME, "E2E_OK"));
list.add(new TerminationCriterion("C4", "DB 迁移一致性校验通过(无 schema 不一致)", TerminationCriterion.Layer.SYSTEM, "db migration consistent"));
list.add(new TerminationCriterion("C5", "邮件服务配置与模板存在", TerminationCriterion.Layer.SYSTEM, "email config present"));
return list;
}
}