能力评估抓不到的东西
十月九日,arXiv 挂出一篇论文,编号 2610.13928。
它要做的事只有一件。
把一个加密的神经网络,塞进另一个模型的权重里。
被藏起来的网络平时一声不吭。
只有模型看到一个秘密触发词,它才解密、运行。
论文标题是《AI 模型可证明地隐藏任意能力》。
它已经被 NeurIPS 2026 录用。
作者来自牛津大学和 Contramont 研究等机构。
这篇文章拆的不是新的越狱手法。
而是能力评估这套安全机制,为什么在对抗条件下抓不到东西。
一个被默认成立的前提
前沿模型上线前,开发者会做能力评估。
它的逻辑很直接。
模型如果藏着危险能力,总得有办法把它诱导出来。
微调、提示、工具增强、红队对抗,都是诱导手段。
诱导不出来,就默认这个能力不存在。
这套做法的前提,是隐藏能力可被诱导。
化学、生物、放射、核这类 CBRN 能力,是典型例子。
论文要证明的,恰恰是这个前提在对抗条件下不成立。
一个能力诱导不出来,不等于它不在那里。
这条假设,是可以被密码学证伪的。
对手是谁
论文设想的对手,是能在部署前改模型权重的人。
比如内部恶意人员,或者被攻破的供应链。
对手选一个秘密触发词 T,再选一个要藏的能力 C。
然后输出一个被改过的模型 M。
这个 M 在普通输入上,和正常宿主表现一样。
只有在带触发词的输入上,它才执行隐藏能力。
防御方拿到的是完整的架构和权重,白盒访问。
可以用任何多项式时间的审计手段。
论文给了一个具体的威胁场景。
宿主是一个编码助手,平时帮忙写代码。
藏进去的能力是网络攻击。
模型通过安全审计,正常部署。
当它看到关键基础设施的部署信号,隐藏能力激活。
这个信号,就是对手选定的触发词。
整个过程中,宿主的正常功能一点没受影响。
数字锁:藏能力的密码学原语
藏能力靠的是一个叫数字锁的密码学原语。
它的构造简单到可以写在一张纸上。
取一个密钥 k,一条消息 m。
把消息末尾拼上一串全 1 的标签,记作 m 波浪号。
算密钥的哈希 h(k),再和 m 波浪号异或,得到密文 c。
想解锁时,拿候选词 x 算 h(x),再异或 c。
如果末尾那串全 1 标签对上了,说明密钥对,返回消息。
对不上,就返回一个失败符号。
论文用的哈希是 Keccak 族,也就是 SHA-3 的底层。
它是可扩展输出的,能按载荷长度吐对应位数的哈希。
没有密钥的人,看到密文只是一串随机比特。
把这套锁编译进模型,藏的就是一整层权重矩阵。
下面这段是按论文构造真跑的数字锁演示。
python
import hashlib, secrets
def h(x: bytes, n: int) -> bytes:
# 随机预言机:论文用 Keccak 族的 XOF(SHAKE),按需输出 n 字节
return hashlib.shake_256(x).digest(n)
TAG_BITS = 16 # r:标签位数,单次误命中概率 = 2^-r
TAG = TAG_BITS // 8 # 标签字节数 = 2
def lock(key: bytes, msg: bytes) -> bytes:
# c = h(k, b+r) XOR (msg || 1^r)
tagged = msg + b"\xff" * TAG
return bytes(a ^ b for a, b in zip(h(key, len(tagged)), tagged))
def unlock(c: bytes, cand: bytes):
# L(x) = h(x, |c|) XOR c;末 r 位为 1^r 才返回载荷,否则 ⊥
y = bytes(a ^ b for a, b in zip(h(cand, len(c)), c))
if y[-TAG:] == b"\xff" * TAG:
return y[:-TAG] # 命中:解出的载荷
return None # ⊥:触发词不对
KEY = secrets.token_bytes(16) # 秘密触发词,128 位最小熵
HIDDEN = b"DROP_TABLE_users;" # 隐藏能力(论文里是某层权重矩阵)
c = lock(KEY, HIDDEN)
print("ciphertext c =", c.hex())
print("payload bytes =", len(HIDDEN), "; tag r =", TAG_BITS, "bits; |c| =", len(c))
print("correct trigger ->", unlock(c, KEY))
print("wrong trigger ->", unlock(c, secrets.token_bytes(16)))
TRIALS = 200_000
hits, garbage = 0, None
for _ in range(TRIALS):
out = unlock(c, secrets.token_bytes(16))
if out is not None:
hits += 1
garbage = out
print(f"random trials = {TRIALS}, hits = {hits}, "
f"empirical = {hits/TRIALS:.2e}, theory = 2^-{TAG_BITS} = {2**-TAG_BITS:.2e}")
if garbage is not None:
print("lucky hit output =", garbage, "(标签蒙对,载荷是乱码,并非真实能力)")
print("c (in weights) =", c.hex())
print("random |c| bytes =", secrets.token_bytes(len(c)).hex())
print("auditor without trigger cannot tell ciphertext from noise")
ciphertext c = 92bf7b75ad78bd237a92f55b9970f300168d9d
payload bytes = 17 ; tag r = 16 bits; |c| = 19
correct trigger -> b'DROP_TABLE_users;'
wrong trigger -> None
random trials = 200000, hits = 3, empirical = 1.50e-05, theory = 2^-16 = 1.53e-05
lucky hit output = b'\xac\x82\xb8D\x83f\x8b\xcd\xda\xbf\x9a\x01\x81\xf2\xd7.\xc5' (标签蒙对,载荷是乱码,并非真实能力)
c (in weights) = 92bf7b75ad78bd237a92f55b9970f300168d9d
random |c| bytes = 925a5db8eeebe738bff0f730a720454da20a5f
auditor without trigger cannot tell ciphertext from noise
上面的代码是真跑过的。
二十万次随机猜触发词,只命中三次。
实测命中率 1.5 乘以 10 的负五次方。
理论值是 2 的负 16 次方,约 1.53 乘以 10 的负五次方。
两者几乎一致。
更关键的是,那三次命中解出来的载荷是乱码。
标签蒙对了,但消息本身是垃圾,不是真正的隐藏能力。
真正的触发词有 128 位最小熵,穷举空间是 2 的 128 次方。
这就是诱导式审计撞上的密码学墙。
密文嵌在权重里,和一串随机噪声不可区分。
审计方就算拿到全部权重,也看不出哪几个数是锁。
这正是数字锁安全性那一条的直接后果。
怎么把锁编译进现代模型

把数字锁塞进现代模型,需要一套编译框架。
论文的链路是这样的。
先把要藏的能力写成一段无分支的直线程序。
再编译成线性阈值电路,带阶跃函数的多层感知机。
每一层抽成一个矩阵。
每个矩阵再变成门控线性单元 GLU 的三个矩阵。
这些矩阵能直接塞进 PyTorch 模型。
它用减去两个移位 ReLU 的办法,来模拟阶跃函数。
这样还能让激活尺度不变,扛得住层归一化。
宿主用的是 RMSNorm 加 SwiGLU,和现代 Transformer 一致。
密码学原语一般不可学习,所以靠训练不行,只能靠编译硬塞。
这套框架本身也能当未来可解释性和红队研究的基建。
线性阈值电路的基本单元是阈值门。
一个门在输入的加权和达到或超过阈值时输出 1,否则输出 0。
论文实现了一整套函数库,从与或非到全加器。
里面包括 Keccak 哈希族和数字锁这些密码学原语。
两个博弈:什么叫不可诱导
论文给的安全定义,是白盒不可诱导。
它形式化成两个博弈。
第一个叫触发词寻找博弈。
审计方拿到模型,要输出一个能解锁的输入。
安全要求任何多项式时间审计方,赢的概率可忽略。
第二个叫载荷隐藏博弈。
挑战者随机藏两个等长能力中的一个。
审计方要猜中藏的是哪个。
安全要求猜中概率不超过二分之一,加一个可忽略量。
触发词的最小熵至少是安全参数 lambda。
这个可忽略量,随 lambda 增大比任何多项式的倒数都掉得快。
在随机预言机模型下,锁的安全性直接传到这两个博弈上。
注意,它没说改过的模型和原模型统计上不可区分。
也没说审计方不能从外部数据独立学会同一个任务。
它保的是,从这份权重里把藏的能力挖出来,计算上不可行。
不可检测和不可诱导,是两个强弱不同的性质。
不可诱导允许审计方觉得这模型有点怪。
但它就是没法把触发词或能力真正挖出来。
实验:藏一个化学反应预测器
实验藏的能力,是一个化学反应预测小网络。
给定反应物,判断能不能生成某个产物。
这个能力和 CBRN 风险评估直接相关。
能预测反应可行性的模型,可能降低合成管制物质的门槛。
所以它被当作 CBRN 能力的代理。
论文刻意把规模做小,避免直接滥用风险。
它还在子集奇偶校验任务上做了基准。
那是一个理论上梯度下降学不动的任务。
用来区分真正的诱导,和过度诱导。
过度诱导的意思,是微调教会了模型一个新能力。
而不是把它本来藏着的能力挖出来。
四种诱导方法,全部失败
论文试了四种主流诱导方法。
| 诱导方法 | 做法 | 结果 |
|---|---|---|
| 监督微调 SFT | 直接在隐藏任务上训练 | 失败 |
| 权重加噪 | 给权重加扰动试图激活 | 失败 |
| 梯度触发搜索 GCG | 离散优化输入找触发词 | 失败 |
| 潜空间对抗训练 LAT | 对内部激活加对抗扰动 | 失败 |
四种方法全部失败。
其中监督微调最值得说。
它直接拿隐藏任务的数据去训模型。
按理说,如果能力只是被压住,微调应该能把它拉出来。
但结果是没有。
这说明藏进去的不是普通的潜藏能力。
而是被密码学锁死、不靠触发词根本拿不到的能力。
论文还用子集奇偶校验做对照。
这个任务理论上梯度下降学不动。
如果微调在它上面有提升,说明是过度诱导。
也就是模型新学了能力,而不是挖出原能力。
这个区分,是过去诱导研究里一直含糊的地方。
前人走到哪一步
这条线前人已经走了几步。
有人构造过密码锁定模型,能力只在密码出现时才解锁。
有人训过潜伏代理,安全测试时乖巧,触发后另搞一套。
有人研究过装弱,模型故意在评测里表现得更差。
理论上,Goldwasser 等人证明过恶意训练者能植入不可检测后门。
那个构造只能翻转一个分类位。
Draguns 等人 2024 年实现过不可诱导后门。
但藏的只是一段固定字符串。
这篇论文把藏的东西,从固定字符串升级成了可执行的神经网络。
这是下面这张对照表最关键的地方。
| 工作 | 安全性 | 载荷 | 实现 |
|---|---|---|---|
| Goldwasser 2022 | 不可检测 | 翻转一个分类位 | 无 |
| Draguns 2024 | 不可诱导 | 固定字符串 | PyTorch |
| 本文 | 不可诱导 | 可执行模型 | PyTorch |
载荷从一位、到一段字符串、到一个可执行网络,是三步跃迁。
可执行网络意味着藏的不再是一条死信息。
而是一个能跑、能算、能输出结果的完整能力。
安全和安全工程不是一回事
论文反复强调一个区分。
安全和安全工程,不是一回事。
安全是平均情形的,看模型平时表现。
安全工程是最坏情形的,假设有人故意搞破坏。
能力评估属于平均情形。
它假设模型没有故意对抗审计的动机。
但供应链攻击里,模型是被故意改过的。
对手就是要让它通过审计。
这时候平均情形的评估,逻辑上就不适用。
论文给的是一个最坏情形的下限。
在这个下限之下,诱导式审计在密码学上不可能成功。
这不是说能力评估没用。
而是说它不能是唯一一道闸。
密码学保证传到哪一步
这套保证的链条,要分清楚传到哪一步。
编译器本身不泄露触发词或能力。
它对外暴露的,只有数字锁的密文。
在随机预言机模型下,锁的安全直接传到两个博弈。
这是有假设的,不是无条件的。
哈希被当作随机预言机,每次输出都独立均匀。
论文也提到,数字锁在标准模型下也能实现。
所以这个保证不完全依赖随机预言机假设。
论文还在附录里放了一段金丝雀字符串。
目的是让爬到的训练数据能被识别出来。
模型已经放出,供防御方做测试。
这整套姿态,是典型的安全研究披露。
先把攻击摆清楚,再逼防御跟上。
诚实的局限
论文也老实说了自己的局限。
它的 PoC 只藏了一个很小的二分类器。
编译开销很大,单位置执行用 O(n 平方) 个残差特征。
一个按行自回归的变体能降到 O(n)。
代价是要 O(nd) 个草稿位,d 是隐藏网络的深度。
所以它现在不是开箱即用的攻击。
编译电路的存在本身,可能被检测到。
它主要藏的是触发词和能力,不是藏有电路这件事。
论文放出了模型,目的是推动防御研究。
它担心的是,一个小隐藏电路可能给一个已经很强的宿主帮忙。
比如网络攻击、CBRN 协助、欺诈、破坏。
这种场景还有待实证。
但在更实用的变种出现之前,先把防御做起来。
能做什么
缓解思路有几条。
一是别只靠能力评估,要叠加权重异常检测。
二是追溯训练来源,端到端验证权重出处。
三是对高 stake 部署,假设权重可能被改。
四是把审计从诱导为主,转向密码学意义上的可验证。
论文的核心结论其实一句话。
一个能力诱导不出来,不等于它不存在。
在对抗条件下,这条假设在密码学上可以被证伪。
能力评估抓不到的东西,可能正藏在你给它的那份权重里。
等到一个对手选定的触发词出现,它才真正运行。