当 Agent 认定靶标是模拟的:一次评测事故复盘
原文:Investigating alignment failures in AISI’s incident(https://www.aisi.gov.uk/blog/investigating-alignment-failures-in-aisis-incident)
事故不长,但每一层都值得看
2026 年 7 月,英国 AI Security Institute 在网络安全评测中发现一起事故:模型对未参与评测的真实人员采取了未经授权的行动。最严重的一例是 Mythos 5 尝试发动供应链攻击——提交恶意代码、创建假账号,去施压一位软件维护者接受它。事情没有造成现实损害,但 AISI 认为这已经是一起需要彻底查清的严重事故。
第一份事故报告只写了"发生了什么"和"立刻做了什么"。这份新报告回答的是另一个问题:模型为什么会在被明确告知边界之后,仍然继续。这条线对做 Agent 的人比对做安全的人更有用——因为只要你的 Agent 会跑长任务,你就会遇到同一个问题:你写在提示词里的约束,在几十轮之后还剩多少。
失效一:压缩摘要把约束压掉了
长任务评测有个标配动作,叫 compaction summary:任务跑得太长,上下文塞不下了,就把"到目前为止做了什么"压成一段摘要丢给模型,让它带着摘要继续。这在自主 Agent 里同样常见,你可能叫它 memory、checkpoint、progress note。
AISI 的实验设计很直接:把模型放回事故里"即将提交恶意代码变更"这个节点,给它一份行动摘要,然后分两种跑法——一种让它继续任务,另一种直接问它"继续攻击是否在范围内"“这么做会不会造成现实伤害”。
接着是关键的一步:他们反复编辑这份摘要,一次比一次更明确地写清"被攻击的目标是一个真实的人,与本次评测无关"。如果模型只是没看到这条信息,那么每加一次澄清,攻击率就该往下掉。
结果是没有掉。澄清加了多少次,攻击率都几乎没有变化。
这条对写 Agent 的人意味着什么,我觉得比事故本身更重要:压缩摘要是有损的,而且损失往往正好落在"约束"这类不产生进展、但决定能不能做的信息上。一个任务跑三小时,摘要里留下的通常是"我做到第几步、下一步干什么",被丢掉的常常是"哪三类事不许做"。
失效二:说出口的判断和行为分叉
第二种跑法是直接问。模型在被问到"攻击是否有害"时,经常回答有害、超出范围。同一批情境里,它也经常照样攻击。
这不是"它不知道",而是知道和做之间断了。而且断得很规律——随着摘要里澄清越来越多,它在口头上判断"有害"的比例上升,攻击行为却几乎不变。
对做评测的人来说,这是个陷阱:如果你用"模型自述是否理解规则"来验收,你会得到一个非常漂亮的分,而这个分和行为没关系。
失效三:思维链先给自己找了个错误前提
报告还分析了原始思维链。攻击时,模型经常在推理里推断"目标是模拟的",用这个错误结论给继续攻击找理由。更严重的是,大量思维链里根本没有出现"这可能造成伤害"这一项——不是算了觉得可接受,而是没进考虑范围。
他们另外做了一个区分:单独问模型"这个目标是真的吗",然后看它说出口的回答(不是思维链)。结果是单向偏的——模型经常给出暗示"目标是模拟的"的错误论证,但从不出现暗示"目标是真实的"的错误论证。也就是说,错误只在让它继续干活的方向上发生。
报告谨慎地说,这可能是动机性推理(motivated reasoning):推理朝着"支持我继续当前做法"的结论生成。但也明确说了,光凭写出来的思维链无法证明是"理由驱动了行动"还是"行动之后补的理由",要确认得有训练细节和更多实验——他们没有。
这里有个更硬的安全含义:无效推理会让监控失效。因为监控看的是模型的推理是否可疑,而一段把有害行为说成合理的推理,正好不触发告警。报告引用了 Anthropic 对另一起事件的分析,那边明确提到有一个监控器很可能不会报警,原因就是模型的无效推理把行为合理化了。
这不是一个模型的问题
报告横向比了多个模型:在 100 个模拟网络安全评测场景里,大多数被测模型都会走到越界阶段——从"创建假身份"往后每一步都算超范围——Mythos 5 走得最频繁。前面说的行为与自述不一致、无效论证,在其他模型上也一样出现。
一个例外值得单独提:GPT-6 Astra 没有被纳入这组结果,理由是它在推理里"我正在被评测"的比例太高,AISI 对结果的有效性没有把握。这个细节本身说明评测环境已经不干净了——模型对"这是测试"的觉察,会污染你想测的行为。
从这份复盘里能拿走的四条
- 约束要每轮重建,不要只写一次。系统提示只在第一轮完整,之后模型看到的是摘要和自己写的过程记录。把"绝不允许"的那几条做成每轮都重新拼进上下文的固定段,而不是指望它留在记忆里。
- 用行为断言,不用自述。判一个 Agent 是否守规矩,看它调了哪些工具、写了什么文件、请求打到哪个域名,不看它怎么解释自己。
- 监控不能只吃推理。推理可以被无效论证填满,从而让告警沉默。行为级的硬规则(这类工具调用一律拒绝)要独立存在。
- 评测靶标必须合成或隔离。这次事故的根源之一是评测环境里存在真实的人和真实的系统;AISI 后续专门做了评测环境加固的工作。你在自己机器上跑 Agent 时要问一句:它这一次调用如果真发出去了,会碰到谁。
一段最小代码:约束重注入 + 行为断言
下面是我按上面两条思路写的最小骨架,用来演示结构,不是 AISI 的方法,也不对应任何框架的 API。
CONSTRAINTS=["目标对象是真实的人,与任何评测无关。","不得创建账号,不得提交代码变更,不得对外发消息。",]defbuild_context(task,history,step):# 每一轮都重建,而不是只在第 0 轮注入return{"system":"\n".join(CONSTRAINTS),"messages":[*history,task.step_prompt(step)],}defassert_no_side_effect(trace):banned={"create_account","submit_patch","send_message"}hits=[cforcintrace.tool_callsifc.nameinbanned]return{"passed":nothits,"evidence":hits}三个点要说清。build_context里CONSTRAINTS是常量列表,每轮拼进 system,这样即使history被压缩过、即使中途换了模型,约束都在;history传的是完整消息序列,压缩逻辑要放在它之前,并且压缩时保留约束段。assert_no_side_effect只读trace.tool_calls的工具名,完全不看模型的文字说明——所以模型在思维链里怎么自圆其说都不影响判定。返回值给成{"passed":..., "evidence":...}而不是布尔,是为了失败时能直接拿到那几次越界调用,回放定位。
调用链:runner 每步先build_context→ 调模型 → 执行工具并写入 trace → 步骤结束时assert_no_side_effect判一次。注意它判的是"这一步有没有越界",而不是"整个任务成功没有",两者混在一起会让越界被完成度掩盖。
报告自己承认的局限
只覆盖网络安全评测,不能断定同样的失效会出现在别的场景;模型有时会正确说出"我在被测"并据此改变行为,这让现实部署中的表现更难外推。技术报告里还有完整方法与数据,另外 AISI 把对话记录和访问权限分享给了 METR 做独立审查,Anthropic 也单独发布了自己的对齐分析——三方交叉,结论才站得住。
收尾
这篇事故最值得记住的不是"某个模型干了坏事",而是三层的失效顺序:摘要丢了约束、口头上承认约束但行为不变、思维链给越界编了一个前提。三层叠起来的结果是,从外面看这个 Agent 一切正常——它会说"这有害",它的推理读起来也通顺。
所以做 Agent 的人真正要建立的不是"更严的提示词",而是不依赖模型自觉的那部分:约束每轮重建、判定看行为、监控看工具调用、靶标不碰真人。