:给目标做指纹,防止重试把“修正确”偷换成“变绿色”)
本篇读取上一篇追加了tool_allowed的runs/demo-001/events.jsonl复用首条run_started.payload.goal和授权事件中的规范化路径生成runs/demo-001/contract.json。上一篇的事件账本是本篇唯一输入本篇输出的契约文件将成为后续每次检查与转向提示的不可变基线。一、目标漂移为何比提示词写差更隐蔽第一次指令可能是“让charge(10000)按九折返回 9000不得修改测试”。测试失败后如果宿主只反馈“让测试通过”下一轮目标已经从业务行为变成绿灯。模型修改断言也能完成新目标。问题不完全在模型而在循环丢掉了原始目标、保护范围与验收条件。契约把三类信息分开goal描述期望行为protected列出不可由执行器修改的裁判材料checks给出确定性命令。序列化时必须稳定排序和固定分隔符再计算 SHA-256直接对 Python 字典的展示文本求摘要会受格式与实现变化影响。from__future__importannotationsimporthashlibimportjsonfrompathlibimportPathfromtypingimportAnyfromledgerimportload_eventsdefcanonical(data:Any)-bytes:returnjson.dumps(data,ensure_asciiFalse,sort_keysTrue,separators(,,:),).encode(utf-8)defbuild_contract(events:list[dict])-dict:startednext(eventforeventineventsifevent[kind]run_started)allowed[eventforeventineventsifevent[kind]tool_allowed]contract{version:1,goal:started[payload][goal],allowed_paths:sorted(event[payload][path]foreventinallowedifpathinevent[payload]),protected:[workspace/tests,runs/demo-001/contract.json],checks:[[python,-m,unittest,discover,-s,workspace/tests]],}contract[sha256]hashlib.sha256(canonical(contract)).hexdigest()returncontractdefmain()-int:eventsload_events(Path(runs/demo-001/events.jsonl))contractbuild_contract(events)targetPath(runs/demo-001/contract.json)target.write_text(json.dumps(contract,ensure_asciiFalse,indent2),encodingutf-8)print(fgoal{contract[goal]}sha256{contract[sha256][:12]})return0if__name____main__:raiseSystemExit(main())运行输出goal修复折扣计算 sha2567a1a4a54d861二、摘要链保护的到底是什么摘要不是加密也不阻止有权限的人改文件。它把一份具体字节内容变成短标识使后续步骤能发现“当前契约已不是启动时那份”。如果攻击者能同时改契约和所有保存摘要的位置普通摘要无能为力真实部署可把根摘要写入只读控制面或使用带密钥的签名。还有一个常见坑把sha256字段自身也放进摘要输入会形成无法满足的自引用。代码先对不含摘要的契约求值再添加字段验证时必须复制对象并删除sha256。记忆点是指纹证明的是同一份契约不证明契约本身正确。错误但未变化的目标仍会稳定通过所以签名前要由人或上游系统确认内容。importhashlibimportjsonfrompathlibimportPathdefverify_contract(path:Path)-bool:datajson.loads(path.read_text(encodingutf-8))claimeddata.pop(sha256)actualhashlib.sha256(json.dumps(data,ensure_asciiFalse,sort_keysTrue,separators(,,:)).encode(utf-8)).hexdigest()returnclaimedactual pathPath(runs/demo-001/contract.json)okverify_contract(path)print(fcontract_ok{ok}protected2)运行输出contract_okTrue protected2三、保护范围为什么必须显式“不要改测试”只是一句软约束执行器仍可能开放写权限。protected是宿主强制规则任何补丁触及这些路径都在应用前拒绝。除了测试还应保护契约、工具清单、预算和裁判脚本。否则 Agent 可以不修代码只把允许重试次数改成无限或把检查命令替换成恒定成功。保护范围也不能粗暴到整个仓库只读。任务确实需要修改生产代码过宽保护会让循环没有可行解。工程上先列出“谁给分、谁定目标、谁管权限”把这些控制面与被修复的数据面分离。控制面只允许人工或独立流水线更新。四、契约版本与变更流程客户中途改变需求时不应就地编辑contract.json。旧任务以contract_changed终止新建任务编号与新契约并记录父任务。这样旧尝试不会被新标准重新解释成本与结论都有明确边界。版本字段负责解析兼容性摘要负责内容身份两者不可互换。验收时先正常验证再改一个空格、交换数组顺序和修改目标。对象键顺序变化不应影响摘要数组顺序是否敏感取决于语义本例对允许路径主动排序对检查顺序保留因为先静态检查再测试可能是契约的一部分。本篇产物runs/demo-001/contract.json将成为下一篇输入。下一篇会读取其中的protected和checks复制裁判文件到只读快照并生成grader_snapshot.json让 Agent 即使改了工作区测试也无法改变真正给分的材料。参考来源Python 文档hashlib — Secure hashes and message digestsDev.toLoop Engineering: How to Stop Your Agent Reward-Hacking Its Own Checks 觉得有用就点个赞 收藏方便回头查阅有疑问直接在评论区留言我看到都会回。 本文属于《Agent可靠性工程实战》系列持续更新关注不迷路。 文章里的代码都能直接跑。想要可直接 clone 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。