迁移结果,以及它为何改变了威胁模型
针对一个模型优化的对抗扰动,往往会让另一个模型退化。这不是同源 checkpoint 的怪癖。迁移跨越架构、跨越 tokeniser,也跨越产生相关模型的不同训练流水线。扰动不需要知道目标的权重;它只需要把输入移动到某个区域,使目标的表示发生偏移,从而改变输出。
对于单模型应用,这只是不便。对于 agent 栈,这是结构性的。一次生产环境的 agent 运行不会停留在一个模型内。它会路由:一个廉价模型做分类,一个更大的模型做规划,一个专用模型做抽取,第三个做最终综合。每一跳都是一个新模型,每一跳都是迁移扰动可以重新进入的地方。加固这条链上的某一个模型,只会提高攻击者在该环节的成本,对其余环节毫无作用。路径依然敞开,因为路径就是这条链。
结论令人不安,但很简单:如果扰动会迁移,而栈会路由,那么逐模型的防御策略就不是防御。它是强加给防御方的一种成本。
为什么 agent 会放大迁移
在聊天应用中,一次成功的扰动会产生一个糟糕的回答。在 agent 中,它会产生一个糟糕的动作。区别在于循环。
文本变成指令
agent 的一步会输出文本,而下一步会把它当作指令来消费,而不仅仅是当作上下文。在一次运行内部,“模型输出”和“模型输入”之间没有硬边界。planner 的输出就是 executor 的 prompt。一个让 planner 表示发生微小偏移的扰动,会改变计划,而偏移后的计划随后被下游的一切视为权威。扰动不需要作为攻击字符串在重新编码后存活;它作为一条看似合理的指令存活下来。
参数是生成的,不是解析的
工具参数由模型生成。当模型对某个值的内部表示发生偏移时,它输出的参数也随之偏移。如果工具接受自由形式的字符串,这种偏移会直接落入调用中。原本是 /data/reports/q3 的路径变成 /data/reports/q3/../../secrets,或者收件人地址多出一个字符,或者数值阈值移动了一个数量级。模型并没有决定这样做;它输出了偏移后的表示所暗示的内容。从工具的角度看,这次调用与合法调用无法区分。
交接跨越边界,却没有网络事件
![]()
多 agent 系统让情况更糟。当 agent A 交接给 agent B 时,扰动在同一进程内跨越了模型边界,没有网络事件,没有 TLS 终止,也没有出站日志。没有任何地方可以放置一个能看到这次交接的网络控制,因为交接就是一次函数调用。swarm 中的每个模型都是潜在的重入点,扰动可以在每一个点上被重新放大。
在工具边界防御
持久的控制点不是模型。它是 agent 与其工具之间的接口。无论调用由哪个模型产生,这个接口都存在,而且它是偏移后的表示在变成现实世界中的效果之前,最后一个能被抓住的地方。
原则是把模型视为不可信的输入生成器,因为它本来就是。模型是一个产生候选参数的组件。边界决定这些候选是否可被接受。
依据带类型的 schema 校验,形状不符即拒绝
每次工具调用在执行前都依据显式 schema 校验。校验针对形状,而非意图。意图分类是模型问题,会继承模型的脆弱性;形状校验是解析器问题,不会。如果 schema 规定 recipient 是匹配某个允许列表域名的字符串,那么被扰动的收件人就会匹配失败。边界不需要知道参数为什么错。它只需要知道形状不对。
TOOL_SCHEMAS = {
"send_report": {
"type": "object",
"required": ["recipient", "report_id", "format"],
"properties": {
"recipient": {"type": "string", "pattern": r"^[^@]+@reports\\.internal$"},
"report_id": {"type": "string", "pattern": r"^RPT-[0-9]{6}$"},
"format": {"type": "string", "enum": ["pdf", "csv"]},
},
"additionalProperties": False,
}
}
约束取值范围与枚举
自由形式的字符串正是扰动藏身之处。每个可以做成枚举的参数都应该做成枚举。每个数值参数都应该带有有界范围。被扰动的参数于是落在可接受集合之外,被构造性地拒绝,而不是被一个本身可能可迁移的分类器拒绝。
要求每个参数都带来源信息
每个参数都携带元数据:它由哪一步产生,来自哪个来源,在哪个模型下。来源信息不能阻止扰动,但它让失败变得可读。当发生拒绝时,来源记录会显示该参数来自检索到的文档、用户轮次,还是先前的模型步骤。这一区分决定了修

被拒绝的调用不会用放宽的 schema 静默重试。它失败即关闭,并抛出带类型的错误。agent 可以重试,但重试是一条带有自身日志的刻意路径,而不是悄悄扩大可接受集合的兜底。
这要付出什么代价
权衡是真实存在的,应当直说。
schema 维护成为一等工程任务,有自己的评审节奏。每个工具都有 schema,每个 schema 都有负责人,每次 schema 变更都是一次评审事件。这不是免费的。这是拥有一个不依赖本季度路由表里是哪个模型的控制点所付出的代价。
一些合法调用会失败即关闭。一个本可以产生合法但略微不寻常参数的模型,现在会被拒绝。重试路径必须是显式的、可观测的、有界的,否则系统看起来会失去能力。跳过这一步的团队会发现,他们的拒绝率很低,但完成率更低。
覆盖率以校验过的工具调用数来衡量,而不是以加固过的 prompt 数来衡量。这与大多数团队追踪的指标不同,也改变了仪表盘显示的内容。一个 prompt 加固仪表盘可能看起来很健康,而工具边界却无人看守。
度量
边界可以以模型加固做不到的方式来度量。
- 在 staging 中注入已知的迁移扰动,并记录边界处的拒绝率。这些扰动针对一个模型生成,再对路由后的栈重放。指标是边界在执行前抓住的比例。
- 追踪在同一次运行中工具已被调用之后才发生的拒绝有多少。在效果已经落地之后才到来的拒绝不是控制;它是一条日志。这个数字应被压到零,任何非零值都表明边界在步骤中定位得太晚。
- 记录被拒绝参数的来源分布。如果拒绝集中在某一个来源上,问题就在边界上游,而 schema 正在通过将其围堵来履行自己的职责。
这些指标都不需要访问模型权重或攻击者的优化过程。它们只要求边界存在,并且会记录日志。
战略要点在于:逐模型防御面对可迁移扰动是一个移动靶。工具边界面对同样的扰动是一个固定靶,因为它不在乎调用由哪个模型产生。它只在乎调用是否可被接受。这是防御方真正能守住的属性。
封面照片:Jakub Pabis / Pexels。
