本地 fixture 契约
冻结测量仪器
把 state 投影、问题文本、criteria、provider 路径、解析后的模型标识和决策规则放在同一份记录里。即使业务代码没动,别名解析结果或问题文本变了,也说明测量仪器变了。
测试 · fixture · CI 门禁 · 本地工程模式
一份可落地的 Jev 回归测试指南:冻结可重放 fixture,断言 schema、决策和副作用,用有依据的容差比较概率,在 CI 中拦截有意义的 diff,诊断漂移,并保留人工审核。
有用的 Jev 回归测试不是保存一段 prompt,而是保存一台有版本的“测量仪器”。重放同一份脱敏 state、问题、criteria、provider 路径和解析后的模型;精确断言类型化决策与必需副作用;概率用区间或差值比较;CI 只按你在看到候选结果前就写好的策略阻断。红色构建是诊断请求,不是让你反复调用直到出现想要答案的许可。
本文描述的是应用侧的测试模式,不是 provider 的保证。冻结完整的评测工件,保留原始响应与失败记录,根据有标签数据和重复运行结果选择容差,并为每次 baseline 变化安排审核。本文不使用月搜索量估计。
每个承诺都有明确负责人,回归测试才有可信度。
本地 fixture 契约
把 state 投影、问题文本、criteria、provider 路径、解析后的模型标识和决策规则放在同一份记录里。即使业务代码没动,别名解析结果或问题文本变了,也说明测量仪器变了。
本地发布策略
对有标签的决策和必需副作用做精确断言。概率用事先声明的容差或区间比较;不要因为浮点数从 0.873 变成 0.871 就阻断发布。
人工决策
红色 diff 是证据,不是“安全/不安全”的自动结论。保留本次运行,检查受影响切片;接受新 baseline 或放宽容差前,必须有明确的人工审核者。
fixture 是一份最小但完整的记录:另一位工程师可以用它重现决策,也能解释 diff 为什么出现。
fixture_id稳定 ID;不要用行号当身份。
state脱敏后的证据,移除不稳定时间戳和密钥。
questions运行时使用的精确 type、instructions 和 criteria。
expected人工标签与本地通过/复核策略,不是 provider 承诺。
instrument投影、问题、provider 和解析后的模型版本。
下面的 JSON 是说明性的 manifest,不是 Jev 的配置格式。把 provider 原始响应、请求元数据和 adapter hash 放在旁边。如果模型别名解析到了不同版本,应记录为测量仪器变化,而不是静默重写 baseline。
{
"suite": "refund_completion_v3",
"fixture_id": "completed-017",
"input": {
"state": "Order ORD-1042 was delivered. The refund was issued in full.",
"questions": {
"completed": {
"type": "noul",
"instructions": "Decide whether the refund process is complete.",
"criteria": {
"true": "The evidence says the refund was issued in full.",
"false": "The refund is pending, partial, or not evidenced."
}
}
}
},
"expected": {
"decision": "true",
"pass_min": 0.82,
"review_min": 0.55
},
"instrument": {
"provider": "your-provider",
"model": "pinned-model-id",
"projection_version": "refund-trace-v2",
"question_version": "completion-v3"
}
}类型化输出可以缩小断言面,但不能代替普通测试代码。
| 信号 | 要断言什么 | 建议的门禁 |
|---|---|---|
Noul / boolean | 有标签的结果必须是 true 或 false,并且概率位于声明的通过/复核区间内。 | 决策精确匹配;概率使用区间或容差。 |
Choice | 选中的选项必须是预期标签。如果接近并列会带来风险,再检查前两项的差距。 | 标签精确匹配;只有任务确实需要时才设置 margin 阈值。 |
Score / rubric | 分数必须落在接受区间内,同时保留原始分数供趋势分析。 | 使用区间或聚合指标差异;不要对单个样例过拟合。 |
应用状态 | 工作流确实创建了要求的记录、工具调用、队列项或状态。说得像做过,不代表副作用真的发生。 | 由普通测试代码精确断言数据库、API 或 UI 状态。 |
无效响应必须 fail closed。超时、分布格式错误或字段缺失,不等于负面决策。报告中保留这种区别,避免把基础设施失败污染语义指标。
const PROBABILITY_TOLERANCE = 0.05;
function checkCase(baseline, current, expected) {
const decisionChanged = baseline.decision !== current.decision;
const probabilityDelta = Math.abs(
baseline.probability - current.probability,
);
if (current.decision !== expected.decision) {
return { status: 'fail', reason: 'decision changed against the label' };
}
if (decisionChanged && probabilityDelta > PROBABILITY_TOLERANCE) {
return { status: 'fail', reason: 'decision flip exceeded tolerance' };
}
if (probabilityDelta > PROBABILITY_TOLERANCE) {
return { status: 'review', reason: 'probability moved beyond tolerance' };
}
return { status: 'pass', probabilityDelta };
}使用与风险匹配的最小策略。下面的数字只是示例,不是通用默认值。
对于有标签的 fixture,选中的 choice 或 boolean 通常是硬断言。概率从 0.873 变成 0.871 本身不是回归;决策翻转才是另一种信号。
对阈值自动化,保存通过、复核和失败区间。用留出标签或重复运行拟合边界,并把公式和分母放在门禁旁边。不要把一次好看的运行结果包装成保证。
接近阈值的案例应进入复核或重复运行,而不是自动放宽容差。高风险决策要记录翻转率、前两项差距和切片影响。
比较均值、错误率或校准指标时,同时写清样本量和切片定义。如果多个事件来自同一会话或客户,应按独立单位重采样。
每次变更都跑小型确定性契约套件,再把有限的 live provider 预算留给受控 smoke 或 release 套件。
name: Jev regression suite
on:
pull_request:
push:
branches: [main]
jobs:
jev-regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: pnpm install --frozen-lockfile
- run: pnpm eval:jev -- --fixtures evals/fixtures.jsonl --baseline evals/baseline.json --tolerance 0.05 --write-current artifacts/jev-current.json --markdown artifacts/jev-regression.md
- uses: actions/upload-artifact@v4
if: always()
with:
name: jev-regression-report
path: artifacts/
工作流里的 `eval:jev` 是你仓库自有 wrapper 的占位名。社区 harness 可能提供相似的 baseline 和 tolerance 参数,但它们都不是 provider 的官方接口。让 wrapper 只在预先声明的关键案例或聚合门禁失败时返回非零,并在成功和失败时都上传完整报告。
好的失败报告会直接指向下一步,也能阻止“重试到绿色”。
| 失败类别 | 保留的证据 | 下一步 |
|---|---|---|
| 请求格式错误或解析失败 | 载荷 hash、schema 错误、响应体和 adapter 版本。 | 修复契约;在打分前拒绝不兼容的 provider 响应。 |
| 超时、限流或认证失败 | HTTP 状态码、request ID、尝试次数、退避记录和 provider/模型身份。 | 先归类为基础设施问题;不要把“没有结果”转换成错误决策。 |
| 测量仪器相同,但有标签决策变了 | 旧/新成对输出、概率差、fixture 标签和受影响切片。 | 在审核者解释变化前,按语义回归处理。 |
| fixture 或标签本身有问题 | 脱敏 diff、来源引用、标签溯源和审核历史。 | 带着溯源信息修正,并保留旧记录;不要为了让 CI 变绿而删除历史。 |
打印旧/新分布、模型和 provider 身份、fixture 标签、request ID、延迟和重试元数据。把无结果案例与错误决策分开,并把 baseline 更新保留为有理由的审核变更。
自动化采集和比较;把风险判断交给真正负责的人。
确认 fixture 仍然具有代表性且脱敏正确。如果产品行为是有意改变的,更新需求和溯源,而不是只改 expected label。
检查成对证据,而不是只看聚合分数。对关键失败读取 state 投影、问题、criteria 和原始响应。
判断变化是孤立的,还是集中在某个切片:语言、来源、严重程度、证据完整度、选项数量或距离阈值的远近。
只选择一个明确结果:修 adapter、重试合资格的基础设施失败、带溯源修 fixture、阻断发布,或由指定审核者接受新 baseline 并设置复查日期。
保留旧工件和新工件。baseline 是决策记录,不是可以随手丢弃的缓存。
不要把概率阈值说成安全证书。本地门禁只说明:在这组 fixture、这台测量仪器和这套策略下,这个仓库如何处理结果。它不能证明 provider 的准确率、未来稳定性或测量范围之外的业务安全。
至少包括稳定的 fixture ID、脱敏 state、精确的 typed questions 和 criteria、人工标签或接受条件、本地决策策略,以及 provider/模型/测量仪器版本。把原始响应和请求元数据放在旁边,这样结果变化可以被诊断,而不是只显示为红色。
通常不应该。对 typed decision 和必需副作用做精确断言,再用声明的容差或区间比较概率变化。精确浮点快照很脆弱。容差应来自留出标签、重复运行波动和错误成本;本文示例中的 0.05 不是通用默认值。
每个 pull request 跑不调用 provider 的契约套件,再用固定 fixture 跑受控的 live provider smoke 或 release 套件。保存当前运行,与经过审核的 baseline 比较;只在预先声明的关键案例或聚合门禁失败时阻断,并在失败时也上传报告。把 wrapper 放在仓库里,让策略可审查。
可能是语义回归、需求有意变化、问题或 state 投影变化、provider/模型变化、fixture 缺陷,或被误归类为决策的基础设施失败。先比较完整测量仪器和原始响应。审核者说清原因和动作之前,不要更新 baseline。
不能。它只能为实际测量过的 fixture、标签、测量仪器和门禁提供可重复证据,不能建立 provider 保证,也不能覆盖未代表的流量。加入关键案例硬门禁、切片分析、延迟人工标签和生产监控,并明确每个结论的范围。
关键案例失败、接近阈值的样例、新增或删除 fixture、集中在某个切片的回归、baseline 变化,以及 provider 或模型版本变化导致的结果,都应该审核。尤其要注意:聚合指标稳定,不代表高后果案例没有翻转。