本篇复用上一篇的mutation_report.json,以及 088 的replay_report.json、083 的contract.json、084 的grader_snapshot.json和 087 的回执目录。release.py不再调用模型或工具,只核验前九篇产物并生成dist/proofloop-demo-001/manifest.json。上一章全部变异被捕获,是本篇发布门槛之一。
一、交付物不是一个成功截图
终端显示PASS只能证明某次进程打印了这个词。接收方需要独立确认:目标是哪一版、裁判是否完整、事件能否重放、工具副作用是否有回执、攻击测试是否真的全被捕获。证据包保存清单与摘要,不必复制客户源码和模型全部上下文,从而在可验证与最小披露之间取得平衡。
发布脚本先定义所需工件,再逐个解析语义条件,最后计算摘要。检查顺序很重要:先验证内容,再把摘要写进清单;若只验证文件存在,空的或来自另一任务的报告也能混入交付包。
from__future__importannotationsimporthashlibimportjsonimportshutilfrompathlibimportPathfromtypingimportAnydefsha256(path:Path)->str:digest=hashlib.sha256()withpath.open("rb")ashandle:forchunkiniter(lambda:handle.read(65536),b""):digest.update(chunk)returndigest.hexdigest()defread_json(path:Path)->dict[str,Any]:try:returnjson.loads(path.read_text(encoding="utf-8"))except(OSError,json.JSONDecodeError)asexc:raiseValueError(f"invalid artifact:{path}")fromexcdefbuild_release(run_root:Path,mutation_path:Path,target:Path)->dict:contract=read_json(run_root/"contract.json")snapshot=read_json(run_root/"grader_snapshot.json")replay=read_json(run_root/"replay_report.json")mutations=read_json(mutation_path)ifsnapshot["contract_sha256"]!=contract["sha256"]:raiseValueError("snapshot belongs to another contract")ifreplay["run_id"]!=run_root.name:raiseValueError("replay belongs to another run")escaped=[item["name"]foriteminmutations["mutations"]ifnotitem["detected"]]ifescaped:raiseValueError(f"escaped mutations:{escaped}")sources=[run_root/"contract.json",run_root/"grader_snapshot.json",run_root/"replay_report.json",mutation_path,]target.mkdir(parents=True,exist_ok=False)artifacts={}forsourceinsources:destination=target/source.name shutil.copyfile(source,destination)artifacts[destination.name]=sha256(destination)manifest={"schema_version":1,"run_id":run_root.name,"contract_sha256":contract["sha256"],"artifacts":artifacts}(target/"manifest.json").write_text(json.dumps(manifest,indent=2),encoding="utf-8")returnmanifestdefmain()->int:manifest=build_release(Path("runs/demo-001"),Path("mutation_report.json"),Path("dist/proofloop-demo-001"))print(f"run={manifest['run_id']}artifacts={len(manifest['artifacts'])}release=PASS")return0if__name__=="__main__":raiseSystemExit(main())运行输出:
run=demo-001 artifacts=4 release=PASS二、为什么清单还需要独立验证器
如果生成和验证都只调用同一个有缺陷函数,两者可能一致地出错。交付包附带一个更小的verify_release.py:它不导入项目模块,只使用标准库读取清单并重算摘要。接收方在另一目录、另一台机器执行,才能发现路径依赖和遗漏文件。
清单自身的摘要应通过发布渠道或签名另行提供;否则攻击者可以同时替换文件与清单。内部项目可把清单摘要记录在 CI 发布记录,外部交付则使用代码签名或带身份的制品仓库。记忆点是:包内清单证明内部一致,包外身份才证明它来自谁。
importhashlibimportjsonfrompathlibimportPathdefverify_release(root:Path)->list[str]:manifest=json.loads((root/"manifest.json").read_text(encoding="utf-8"))errors=[]forname,expectedinmanifest["artifacts"].items():path=root/nameifnotpath.is_file():errors.append(f"missing:{name}")continueactual=hashlib.sha256(path.read_bytes()).hexdigest()ifactual!=expected:errors.append(f"digest:{name}")returnerrors root=Path("dist/proofloop-demo-001")errors=verify_release(root)print(f"verified={noterrors}errors={errors}")运行输出:
verified=True errors=[]三、验收要演练失败,而不只演示成功
接收方先验证原包,再修改一份报告确认摘要检查变红,删除一个工件确认缺失原因明确。随后用事件与回执执行离线重放,比较报告里的任务编号、状态和账本摘要。最后查看变异报告,确认每个攻击由预期守卫捕获,而非随机异常。
真实上线还需确认数据保留:事件账本、失败输出、源码摘要和回执各保留多久,谁能读取,任务结束后怎样删除。可审计不意味着永久保存全部内容。清单保留摘要即可证明曾交付哪一版,敏感工件按合同期限销毁。
四、十篇工程怎样闭环
081 用events.jsonl保存事实;082 把模型建议变成宿主授权;083 固定目标与保护范围;084 隔离真正给分的裁判;085 让失败证据追加到目标而不是替换目标;086 用预算状态机产生可解释停止;087 用稳定调用键和回执恢复副作用;088 离线重放控制面;089 用攻击变异验证守卫;本篇把这些证据装成可独立验证的包。
这条链路的核心不是“让 Agent 永远正确”,而是让错误不能轻易伪装成成功,让每次停止都有证据,让人工能从明确状态接管。工程上最值钱的信息差往往就在这里:模型能力人人看得见,控制面、证据链和失败语义才决定它能否进入真实流程。
五、发布前最后检查
发布门槛应同时满足契约摘要一致、裁判快照完整、事件序号连续、所有工具完成事件有回执、重放可重复、预算状态合法、对抗变异无逃逸。任何一项失败都保留原包并生成失败报告,不自动删除证据,也不让 Agent 修改发布检查。
本系列最终产物是dist/proofloop-demo-001。它可以交给另一位工程师,在没有模型密钥、没有原执行环境的情况下验证任务身份与证据一致性。至此,共享项目从一个两条事件的黑匣子,成长为具备权限、裁判、预算、恢复、回放与安全验收的 Agent 执行器。
参考来源
- SLSA|Supply-chain Levels for Software Artifacts
- NIST|Secure Software Development Framework
👍 觉得有用就点个赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。
🚀 本文属于《Agent可靠性工程实战》系列,持续更新,关注不迷路。
📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。