一份不能重跑的质量报告只是备忘录:可复现脚注的字段设计与纯标准库生成实现

举报
霍格沃兹测试学社 发表于 2026/09/28 18:11:44 2026/09/28
【摘要】 本文提出AI质量报告的可复现性设计:通过模型、prompt、数据集、随机种子、运行标识五要素脚注,确保每条结论自带“配方”。纯标准库实现自动采集、校验与标注,杜绝配置漂移导致的误判,让报告从备忘录升级为可验证证据。

一份不能重跑的质量报告只是备忘录:可复现脚注的字段设计与纯标准库生成实现

半年前报告里那句『已验证修复』,今天被人翻出来质疑。你想重跑当时那次回归,却卡在三个问题上:那会儿用的哪个模型版本?prompt 改过没有?随机种子锁没锁?报告给了结论,没给配方——它从写下那天起,就不是一份能重跑的东西。

AI 系统的结论强依赖配置:换一个模型、动一版 prompt、加一批用例,同一条『通过/失败/已修复』可能整个反过来。一份不能重跑的质量报告,只是备忘录,不是证据。要让它变成证据,每条关键结论都得自动生成一行 provenance 脚注,最少带五件东西。

一、五件最小 provenance:缺哪件,带跑哪类结论

先给这五件定个位。它们不是给报告凑格式,而是每一件对应一类会『看走了眼』的结论;采不到哪一件,就在给哪一类误判开门。

provenance 字段 漏了会带跑哪类结论 采不到的后果
模型版本 model 底层模型一换,同一条『已修复』重跑就翻车 不可复现:别人拉最新模型跑不出来,反咬是你用例的问题
prompt 版本 prompt 提示词一改通过率就动,锅却扣到模型头上 不可归因:调 prompt 还是调模型分不清,优化全凭猜
数据集版本 dataset 用例/样例集一扩容,通过率虚高或虚低 不可比:分母都换了,还在横比两个百分比
随机种子 seed 温度没锁死,同一输入每次结果都抖 不可复现:这条通过是能力还是运气,说不清
运行标识 run(时间戳+流水线号) 拿历史某一次的数字,冒充这一次 不可追溯:出事找不到当初那次到底跑的啥

五件里最容易被省的是 seed 和 run:前者被『又不做科研,锁什么种子』一句带过,后者被『流水线号谁记啊』一句带过。恰恰是这两件,决定了半年后那条结论还能不能被原样重放。

这五件还有个共同的心得:它们都是『能改变结论』的东西。你多记十个字段——操作系统版本、机型、依赖库版本号——当然更好,但那是锦上添花;而 prompt 和 seed 反过来,看似小设置,实则直接决定结论的死活。provenance 设计的价值不在堆字段,在于『承认哪一件配置能让你当初的判断翻车』。这件事,AI 系统比传统软件严重得多:传统软件换个 OS 通常不至于把『退款计算』跑反,但换个 prompt 会。

二、同 meta 才能横比:配置不一致,就拒绝出结论

光把五件写下来还不够,得让它们管事。第一条硬规矩:只有除运行标识以外的四件(模型、prompt、数据集、种子)完全一致,才允许把两次的数字放在一起比谁高谁低。run 本身就是时间戳+流水线号,每次必然不同,不参与比对——要是连 run 也拿去要求相同,那天下就没有可比的两份报告了。

这条规矩专治一种高频话术。今天回归 96%,报告兴冲冲写『较半年前的 88% 提升 8 个点,确认修复』。可一查脚注:那次 model=v1.1、prompt=v5、数据集是上一季的 gs-23q4,这次全变了。这 8 个点是修复带来的,还是换模型、换 prompt 白送的,根本没法归到『缺陷被修好』头上。配置不齐,就先把『提升』俩字咽回去,横比直接拒绝——把两个不同配方的数摆一起下结论,比不报还危险。

这条对齐规矩最好也写成一条肌肉记忆:报告里凡引用历史基线的数字,先比对四件 provenance 是否一致;不一致,那个基线只能当『背景』放进展示,不许生成任何『提升/退化』的差值话术。把『不可比』做成一个显式动作,才能挡住每一次版本迭代都顺手『涨一波』的集体自欺。

三、跑一遍:脚注生成器 + 对齐校验

下面这段只用标准库,开箱即跑。真实场景把五件从流水线注入的环境变量里读,导出报告时给每条结论自动追加一行脚注,并在横比前过一道对齐校验。

# -*- coding: utf-8 -*-
"""provenance.py —— 给每条质量结论挂可复现脚注(纯标准库,开箱即跑)

用法:python provenance.py
五件 provenance:模型/prompt/数据集版本 + 随机种子 + 运行标识。
结果落库时带 meta,导出报告时每条结论自动追加一行脚注;
并校验:meta 不一致就拒绝把两次数字横向比较。样例与运行标识均为演示构造。
"""
import os

ENV_KEYS = {"model": "MODEL_VERSION", "prompt": "PROMPT_VERSION",
            "dataset": "DATASET_VERSION", "seed": "SEED", "run": "RUN_ID"}
DEFAULTS = {"model": "v1.3", "prompt": "v7", "dataset": "gs-24q3",
            "seed": "42", "run": "20260924-0112"}


def collect_run_meta():
    """真实场景五件由流水线注入环境变量;这里给默认值保证开箱即跑。"""
    return {k: os.environ.get(ENV_KEYS[k], v) for k, v in DEFAULTS.items()}


def meta_tag(meta):
    """把五件压成一行紧凑脚注:[m=..·p=..·d=..·seed=..·run=..]"""
    return (f"[m={meta['model']}·p={meta['prompt']}·d={meta['dataset']}"
            f"·seed={meta['seed']}·run={meta['run']}]")


def same_config(a, b):
    """除 run 外,其余四件必须一致才允许横比——run 是时间戳+流水线号,本就该不同。"""
    return all(a[k] == b[k] for k in ("model", "prompt", "dataset", "seed"))


def record(case, status, meta):
    """每条用例结果落库时,带上当次采到的 run_meta。"""
    return {"case": case, "status": status, "meta": dict(meta)}


def compare_runs(this_run, baseline, this_rate, base_rate):
    """带对齐校验的横比:配置不一致直接拒绝出『提升/退化』结论。"""
    if not same_config(this_run, baseline):
        diff = "、".join(k for k in ("model", "prompt", "dataset", "seed") if this_run[k] != baseline[k])
        return (f"拒绝比较:与基线配置不一致({diff} 变了)。"
                f"先对齐配方,再谈 {this_rate}% vs {base_rate}% 谁高谁低。")
    delta = this_rate - base_rate
    verb = "提升" if delta > 0 else "退化"
    return f"配置一致,本次 {this_rate}% 较基线 {base_rate}% {verb} {abs(delta)} 个百分点。"


def render_report(records):
    lines = ["# 质量报告 · 结论溯源(演示构造)", ""]
    for r in records:
        lines.append(f"- 用例 `{r['case']}` 结论:**{r['status']}**  {meta_tag(r['meta'])}")
    return "\n".join(lines)


def main():
    meta = collect_run_meta()
    records = [
        record("退款金额边界·VIP7天内", "通过", meta),
        record("并发下券返还·幂等", "失败", meta),
        record("物流文案·错别字(半年前那版)", "已修复", meta),
    ]
    print(render_report(records))

    print("\n== 对齐校验:拿今天和半年前比『是否真修复』 ==")
    old_meta = {"model": "v1.1", "prompt": "v5", "dataset": "gs-23q4", "seed": "42", "run": "20260310-0755"}
    print(compare_runs(meta, old_meta, 96, 88))

    print("\n== 把配方对齐后才允许下结论 ==")
    aligned_old = dict(old_meta, model="v1.3", prompt="v7", dataset="gs-24q3")
    print(compare_runs(meta, aligned_old, 96, 88))


if __name__ == "__main__":
    main()

为什么这么写,三个坑值得点名。其一,same_config 刻意把 run 排除在比对之外——run 天生每次都不一样,要拿它当比对门槛,等于永远判『不可比』,校验直接形同虚设。其二,collect_run_meta 这里为了开箱即跑给了 DEFAULTS 兜底,但接进真实流水线时要反过来:五件读不到就该 fail 挂旗,绝不能填个假默认值放行。一份把『取不到配置』悄悄用默认值盖掉的报告,本身就在造假配方。其三,脚注不是人写的,是 render_report 在导出循环里调 meta_tag 自动贴上去的——手贴脚注迟早和真实配置漂移,机器贴才能保证『结果是什么,脚注就是什么』。

四、导出报告:每条结论自动挂一行脚注

跑完上面那段,报告里的每条结论会长成这样(全部演示构造):用例 退款金额边界·VIP7天内 结论:通过 [m=v1.3·p=v7·d=gs-24q3·seed=42·run=20260924-0112]。别小看这行小字,它把『无脚注』和『带 provenance』两类报告的差距,摊在了四个维度上。

维度 无脚注的报告 带 provenance 的报告
复现成本 翻聊天记录问三个人,还不一定凑得齐配方 一行脚注,直接 checkout 对应版本重跑
归因能力 通过率掉了先怀疑模型又怀疑用例,来回甩锅 哪件配置变了当场可见,归因指向明确字段
跨团队信任 别的团队不认你的结论,因为他重跑不出来 对方能用自己的流水线复算出同一个数
审计 半年后的『已修复』只是一句备忘录 每条结论带着可验证配方,才谈得上证据

对齐校验也在报告里落地:脚本最后两段拿今天和半年前比,因为 model、prompt、dataset 都变了,直接吐『拒绝比较』;把配方对齐到 v1.3/v7/gs-24q3 之后,才允许写出『本次 96% 较基线 88% 提升 8 个百分点』。同样是 8 个点,前一句是拍脑袋,后一句是有配方背书的结论。

还有一个没写进表的收益:脚注一旦常态化,报告本身会开始变得可查询。同一个 meta 的结论能被脚本自动归成一节,改了某一件的结论能被自动打上『配置变更,请复核』的标记。你得到的不只是复现,还有一套自检——报告开始会主动告诉你,哪些结论这次是『孤零零』的、找不到可比的基线。

五、挂进流水线:五个字段从环境变量注入

脚注要自动生成,前提是五件能自动采到。CI 里最省事:回归跑完后,把模型/prompt/数据集版本、锁死的种子、流水线自带的 run number,一起注入环境变量,provenance.py 直接读。

env:
  MODEL_VERSION: ${{ steps.model.outputs.version }}
  PROMPT_VERSION: ${{ steps.prompt.outputs.version }}
  DATASET_VERSION: gs-24q3
  SEED: "42"
  RUN_ID: ${{ github.run_number }}

流水线天生记着这些值,人偏偏记不住——让机器顺手把它们写进每条结论,才是可持续的做法。种子这一栏别省:AI 应用的随机性全靠它锁,今天不写 seed,明天所有『复现』都退化成『再赌一次』。

这里可能有人反驳:每条结论加五件,报告不就更臃肿了?恰恰相反——脚注是给『不展示』设计的。它可以折叠、可以用缩写、可以排在行尾,读者看到的还是一行结论,只是他要溯源时那行字就在手边。这就是脚注和一大段说明文字的区别:可复现的信息应该『挖得出来』,而不是『糊在脸上』。臃肿的是散文,紧凑的是一枚 tag。

六、回到那场复盘

下次那条『已验证修复』再被翻出来质疑,你不必靠回忆还原现场:点开结论后面那行脚注,模型 v1.3、prompt v7、数据集 gs-24q3、seed=42、哪条流水线哪次跑,一目了然,照着它就能原样重放。一份报告能不能被审,往往不取决于它写了多少结论,而取决于每条结论后面,还留没留下一张能重跑的配方。

不能重跑的结论只是观点,能带着配方重跑的结论才是证据——给报告每条判断挂上 provenance 脚注,就是让它从『当时觉得』升级成『现在还能验』。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。