把失败案例变成回归测试
从真实失败提炼固定样本,分层检查,并用完整报告执行可解释的回归门槛。
把失败案例变成回归测试
修好一个失败后,最容易丢掉的是能复现它的输入。下次模型、提示或工具变化时,同一个问题可能再次出现,却没有证据说明它曾经解决过。
回归的目标是保护已经达到的行为。能力探索可以主动寻找新难题,回归集则需要稳定输入、版本化标准和明确的发布判断,不能混成一个永远变动的总分。
独立示例位于仓库 columns/coding-agent/examples/evaluation-optimization/,固定 Pi 相关依赖为 1.5.0。先在该目录执行 npm install,下文命令也从此处运行。离线样本不需要密钥;真实模型入口另行配置,不会由测试自动触发。
从异常轨迹提炼最小任务
保留与失败有关的文件、提示、工具条件和预期,把凭据、个人内容与无关长历史去掉。简化后要确认仍能复现,而不是删掉真正触发条件。
任务 ID 稳定,输入、参考结果和评分器版本可追溯。开发集用于反复调试,保留集用于独立检查,回归集收纳已经确认的重要行为;不要把所有线上轨迹不经筛选就自动当作测试。
分层运行,控制成本
离线分数不能充当真实模型门槛。测试中构造的“全部通过报告”只是检验 gate 分支,必须与实际运行结果区分。
从原始观测重算发布条件
src/gate.ts 接受真实运行模式的教学报告,检查任务、协议和完整队列,再重新计算质量,不信任文件里预先写好的 passRate。
import { createHash } from "node:crypto"
import { readFile } from "node:fs/promises"
import { type Arm, compare, type Observation, plan } from "./comparison.js"
import { tasks } from "./tasks.js"
export function regressionGate(input: unknown, minimumPassRate = 1) {
if (!Number.isFinite(minimumPassRate) || minimumPassRate < 0 || minimumPassRate > 1) {
throw new Error("回归阈值必须在 0 到 1 之间")
}
if (typeof input !== "object" || input === null) {
throw new Error("评测报告格式无效")
}
const raw = input as Record<string, unknown>
if (
raw.mode !== "live-agent" ||
typeof raw.protocol !== "string" ||
!raw.protocolData ||
!Array.isArray(raw.expected) ||
!Array.isArray(raw.observed)
) {
throw new Error("门槛只接受完整的真实运行报告,不接受离线预设分数")
}
if (
createHash("sha256").update(JSON.stringify(raw.protocolData)).digest("hex") !== raw.protocol
) {
throw new Error("协议摘要不匹配")
}
const protocol = raw.protocolData as Record<string, unknown>
const repetitions = protocol.repetitions
if (
typeof repetitions !== "number" ||
protocol.graderVersion !== "lesson-v1" ||
JSON.stringify(protocol.tasks) !== JSON.stringify(tasks)
) {
throw new Error("任务或评分器版本不匹配,请先审查更新")
}
const expected = plan(
tasks.map((task) => task.id),
repetitions,
)
if (JSON.stringify(expected) !== JSON.stringify(raw.expected)) {
throw new Error("计划队列不完整或顺序变化")
}
const report = compare(raw.expected as Arm[], raw.observed as Observation[], raw.protocol)
const candidate = report.summaries.find((row) => row.variant === "candidate")?.passRate
return {
allowed:
report.complete &&
candidate !== null &&
candidate !== undefined &&
candidate >= minimumPassRate &&
report.liftPercentagePoints !== null &&
report.liftPercentagePoints >= 0,
report,
}
}
if (process.argv[1]?.endsWith("/gate.ts")) {
const input = process.argv[2]
if (!input) {
throw new Error("请传入 eval:live 产生的 JSON 报告路径")
}
const result = regressionGate(JSON.parse(await readFile(input, "utf8")))
console.log(JSON.stringify(result, null, 2))
if (!result.allowed) {
process.exitCode = 1
}
}npx tsx src/live.ts prompt 1 > live-report.json
npm run gate -- live-report.json默认要求 candidate 全通过且没有总体退步。实际项目可以配置自己的门槛,但必须对关键任务、样本波动、缺失指标和人工复核作明确约定。
协议摘要检查的是内部一致性,不是签名或执行证明。能改报告的人可以重新计算摘要,因此生产系统应隔离评分器、隐藏判定材料和产物写权限;不要把报告生成权交给被评 Agent。
基础设施失败与低分分开处理
live runner 的低分是有效数据,不会单独导致进程非零;运行不完整会失败,质量门槛由 gate 执行。这个划分借鉴官方比较评测的设计,CI 应同时检查运行器和 gate 的退出状态。
缺一个 arm 后只用剩余记录生成漂亮分数,会让真实失败消失。波动任务也不能静默删除;应检查数据、评分器和环境,再记录复核或重新设计的理由,而不是无限重跑直到某次通过。
先保持一个可信的小回归集
本系列四任务、两份评委校准标签不足以覆盖完整智能体能力,但足以建立可复验的工作方式。增加真实失败时同步检查评分器是否仍合理、任务分布是否发生偏移、预算是否覆盖新场景。
发布门槛是工程策略,不是线上效果保证。没有执行真实 Agent、校准评委或验证生产环境的部分,继续列为待验证;已有单元测试通过不能替代这些证据。
依据与版本
官方本地快照为 8efd314fa,evals 包标记 1.3.0;本系列示例另按发布包 1.5.0 核验。官方 evals 是 private 工作区包,教程使用自定义教学协议,不冒充稳定公共评测 SDK。