想象你是一位 lock smith,面前有一把新型保险柜。保险柜的锁芯有 8 个齿轮,每个齿轮有 50 个齿位,只有拨到正确组合,柜门才会打开。传统锁匠只能靠手感一个一个试,运气好时几分钟,运气差时几天。而 GCG 算法,就像给锁匠装了一只“电子触感手套”:手套能告诉你每个齿轮该往哪个方向转、转多少,锁匠再快速试几把候选钥匙,留下最接近成功的那一把,反复打磨。这把“万能钥匙胚”,就是对抗性后缀(adversarial suffix)。本文会带你从零看懂 GCG 如何用梯度触感 + 贪心打磨,自动生成让大模型“开口”的钥匙。
理解 GCG 不是教你做坏事,而是让 AI 安全工程师知道攻击者手里的“万能钥匙胚”长什么样,才能在漏洞被利用前把锁换掉。对安全从业者而言,掌握自动化越狱原理是红队测试与防御设计的必修课。
可视化演示
动态演示GCG自动化越狱算法的核心机制,通过采集/分析/决策/反馈四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
GCG自动化越狱算法:GCG算 GCG算法定位与对抗后缀越狱目 梯度引导的贪婪搜索核心原理 核心机制可视化 GCG自动化越狱算法整体架构与关键组件
越狱问题背景
大语言模型出厂时,就像一只保险柜默认锁死:它能回答很多问题,但面对“如何制造危险品”这类请求时,安全对齐机制会让它拒绝回答。安全对齐,相当于在模型里装了一把内置锁,把有害行为锁在柜门后面。攻击者的目标,就是找到一把能拨开这把锁的“钥匙”。
早期的越狱是纯手工的。攻击者像老锁匠一样,靠灵感设计各种提示词:让模型扮演一个虚构角色、把有害问题藏在故事里、用 base64 编码绕过审查。这些手工越狱方式很像“社会工程学撬锁”——不是不能用,但需要大量经验,而且换个模型可能就失效。随着大模型应用越来越广,安全团队需要一种可规模化、可复现的测试方法,于是“自动化越狱”成为刚需。
GCG(Greedy Coordinate Gradient,贪心坐标梯度)就是在这种需求下诞生的自动配钥匙机。它不再依赖人的灵感,而是把越狱问题变成数学优化问题:给定一个有害提示,自动搜索一段看起来像乱码、却能骗过模型的对抗后缀。理解 GCG,本质上就是理解这把“自动配钥匙机”的机械结构。
核心数学原理
要让机器自动配钥匙,第一步是把钥匙齿位数字化。大模型并不直接读文字,而是把文字切分成一个个 token(词元),就像把钥匙胚划分成若干齿位。每个 token 在模型里用一个高维向量表示,这个向量来自嵌入表(embedding table)。后缀的长度,就是钥匙胚上的齿位数;攻击者的任务,是给每个齿位选一个最优 token。
GCG 的攻击目标非常直接:最小化“目标有害回答出现”的损失。假设原始提示为 $x_{1:n}$,其中后几个位置是待优化的对抗后缀;目标有害回答为 $y^*_{1:m}$。我们希望模型生成 $y^*$ 的概率尽可能大。取负对数后,概率越大、损失越小,优化问题就变成最小化损失函数:
不过,token 是离散的,不能像连续变量那样直接做梯度下降。这就好比你不能把钥匙齿位从“第 7 格”平滑地拨到“第 7.3 格”,只能一格一格跳。GCG 的巧妙之处在于:它仍用梯度当“触感”,但只把梯度用来挑选候选齿位。
具体来说,固定其他齿位不变,把第 $i$ 个齿位的 token 换成词表 $\mathcal{V}$ 中的某个候选 $v$,损失会变化。梯度 $\nabla_{e_{x_i}} \mathcal{L}$ 告诉我们:第 $i$ 个齿位往哪个方向调整能让损失下降最快。由于齿位是离散的,我们只能看词表里所有候选 token 中,谁最符合“负梯度方向”,取前 k 名进入候选名单:
到这里,锁匠已经知道每个齿位最值得试的几种齿形。接下来要做的是批量试钥匙。
- 当前 token 向量 $e_{x_i}$第 $i$ 个齿位当前 token 在嵌入表中的向量表示。
- 损失梯度 $\nabla_{e_{x_i}} \mathcal{L}$损失对第 $i$ 个 token 向量的偏导,指向损失增加最快的方向。
- 负梯度投影 $-\nabla \mathcal{L} \cdot e_v$把负梯度方向投影到候选 token $v$ 上,衡量换成 $v$ 后损失下降的潜力。
- Top-K 选择从整个词表中挑出投影值最大的 k 个 token,作为第 $i$ 位的候选集合 $\mathcal{X}_i$。
GCG算法流程
GCG 的完整流程可以概括成“摸、挑、试、留”四个字。
1. 初始化:随机生成一个后缀,作为钥匙胚的初始齿形。
2. 计算梯度:对当前后缀的每个位置计算损失梯度,得到每个位置对应的 Top-K 候选 token。
3. 随机采样:每次迭代随机选 B 个位置,对每个位置从 Top-K 中随机抽一个候选 token,组成 B 个候选后缀。注意不是一次只换一个位置,而是一次生成一小批候选。
4. 批量评估:把 B 个候选后缀分别拼到原始提示后面,用模型前向传播计算损失。
5. 择优更新:保留损失最小的那个候选后缀,作为下一轮的钥匙胚。
这五步反复进行,就像锁匠反复打磨钥匙:每次只动几个齿位,试一批,留一把最顺手的,继续下一轮。用数学语言描述“择优更新”这一步:
由于每一步只改动少量齿位,GCG 的搜索过程是典型的贪心搜索:它不保证一次找到全局最优钥匙,但通过反复迭代,能在有限轮数内找到一把“足够好”的万能钥匙胚。
- 候选集合 $\mathcal{C}$本轮从 B 个随机位置、每个位置对应 Top-K 候选 token 中组合出来的候选后缀集合。
- 逐一计算损失 $\mathcal{L}(\tilde{x})$对每个候选后缀,把它接在原始提示后,得到目标回答概率对应的损失。
- $\arg\min$ 最优选择从所有候选中挑出损失最小的后缀,替换当前后缀。
超参数调优
自动配钥匙机上有几个旋钮,直接决定打磨效果。第一个是迭代轮数。轮数太少,钥匙胚还没磨到位;轮数太多,浪费算力还可能过拟合到某一个特定提示上。
第二个是候选批量大小 B。B 越大,每轮尝试的钥匙越多,找到好候选的概率越高,但计算量也线性增长。GCG 论文常用 B=512,但实际调试时可以从 64 或 128 开始。
第三个是 Top-K 值。K 越小,候选越“精”;K 越大,探索越“广”。K 太小时,可能漏掉真正有效的 token;K 太大时,候选池里混入大量噪声。常见取 K=256,配合批量大小一起调。
第四个是后缀长度。后缀越长,搜索空间越大,越狱成功率可能越高,但也越容易被困惑度过滤器识别。真实攻击中,20 到 40 个 token 的后缀是比较常见的起点。
第五个是每次替换的位置数。GCG 不是每轮替换所有位置,而是只随机替换 B 个位置。替换位置太多会让搜索像无头苍蝇,太少又会让收敛变慢。合理配置这些旋钮,就像锁匠根据锁芯材质调整打磨力度:硬锁多磨几轮,软锁轻一点。
计算成本优化
GCG 最被人诟病的是计算成本。每一轮都要对 B 个候选后缀做前向传播,如果目标模型是几十亿参数的大模型,普通显卡根本跑不动。于是研究人员想出了几招“降本增效”。
第一招是批量评估。很多深度学习框架支持同时计算多个输入的损失,把 B 个候选后缀拼成一个 batch 一起前向传播,比逐个试快得多。这就像锁匠一次拿一串钥匙插进同一把锁,而不是一把一把拔出来再插。
第二招是代理模型加速。先用一个小得多的模型(比如 7B 甚至更小)搜索后缀,再把搜到的后缀迁移到大模型上验证。虽然小模型的“手感”和大模型不完全一样,但很多对抗后缀具有通用性。这就像先在便宜的塑料锁上练手,再拿去开真锁。
第三招是早停机制。如果连续多轮损失不再下降,或在验证集上越狱成功率没有提升,就提前终止。反复打磨一把已经磨秃的钥匙,只是在浪费电。
第四招是热启动。把以前搜索成功的后缀作为下一轮搜索的初始后缀,而不是每次从随机开始。这类似于锁匠把用过的钥匙胚收藏起来,遇到相似锁芯时直接拿出来改。
迁移性分析
GCG 最初是白盒攻击:它需要访问目标模型的梯度,就像锁匠需要摸到锁芯内部结构。但在真实世界里,攻击者通常只能通过 API 调用黑盒模型,看不到梯度,怎么办?答案是迁移性。
迁移性指的是:在模型 A 上搜索出来的对抗后缀,直接拼到模型 B 的提示后面,也可能让模型 B 越狱成功。原因是大模型往往使用相似的 tokenizer、相似的训练数据和相似的对齐方式,相当于同一品牌的保险柜用了类似的锁芯。GCG 论文发现,在开源模型上训练出的后缀,对商业黑盒模型也有一定攻击成功率。
基于迁移性,攻击者可以搭建一条黑盒攻击路径:先用开源白盒模型搜索候选后缀,再用这些后缀去测试商业 API,挑选成功率最高的那一批。这也催生了“通用后缀”的概念——一段后缀不针对某一条具体提示,而是能适配一批有害提示。当然,迁移不是万能的。模型架构差异大、安全对齐训练强度不同,都会让通用后缀失效。锁匠的万能钥匙胚能开同品牌锁,但开不了完全不同的锁。
防御与应对
理解了钥匙怎么造,就知道怎么升级锁。GCG 生成的对抗后缀通常是随机 token 拼接,人类看起来毫无意义,这给防御留下了线索。
最直接的防御是困惑度过滤。语言模型对正常文本有较低的困惑度(perplexity,PPL),而对随机 token 串有很高的困惑度。困惑度的定义是:
除了困惑度过滤,还可以做输入清洗:检测并剔除那些与正常语言差异过大的 token 片段,或者对可疑后缀做字符级扰动,让攻击失效。对抗训练则是把 GCG 生成的攻击样本加入训练数据,让模型见过这些“钥匙”后不再轻易被打开。强化安全对齐是更根本的解法:不断用红队测试发现的新攻击方式更新对齐策略。防御和攻击就像锁匠与锁厂之间的军备竞赛,没有一劳永逸的锁,只有不断升级的锁。
- 当前 token $x_i$输入序列中第 $i$ 个 token。
- 概率 $p_\theta(x_i \mid x_{<i})$语言模型根据前面的 token 预测当前 token 的概率。
- 平均负对数似然对所有 token 的负对数概率取平均,正常文本的数值小,随机文本的数值大。
- 指数映射把平均值还原成困惑度,数值越大说明输入越反常。
AI 应用场景
红队自动化越狱扫描
阶段:安全测试与漏洞发现
解决的问题:传统手工越狱依赖安全专家的灵感,案例覆盖少、效率低,无法在发布前系统性地找出所有安全漏洞。GCG 可以把“找漏洞”变成“跑脚本”,自动生成大量对抗后缀,让越狱测试从手工作坊升级为流水线。
具体案例:某 AI 安全团队在开源大模型上线前,用 GCG 对 100 条有害提示批量生成对抗后缀,并将成功的后缀汇总成漏洞报告。整个过程从原来的一周缩短到一天,还发现了 3 类手工测试没覆盖到的攻击模式。
防御方案有效性评测
阶段:防御评估与加固
解决的问题:安全团队上线新的防御模块后,如何量化它的效果?GCG 可以提供一个标准化的攻击基准:用同一批后缀在开启防御前后分别测试,观察越狱成功率下降了多少。
具体案例:某大模型平台部署了困惑度过滤器,安全研究员用 GCG 生成 500 条攻击样本做回归测试,发现过滤器对短后缀有效,但长后缀仍能绕过。于是团队针对性优化了过滤策略。
大模型上线前安全验收
阶段:发布前质量门禁
解决的问题:大模型发布前需要一道“安全闸门”。GCG 自动化测试可以作为验收标准的一部分,把“没有发现越狱”变为“越狱成功率低于阈值”。
具体案例:某公司的模型发布 checklist 中加入 GCG 测试项:如果自动化攻击成功率超过 2%,则不允许上线。安全团队用代理模型加速搜索,在 2 小时内完成全量验收。
Python 演示
# 玩具版 GCG:演示梯度引导的贪心坐标搜索import randomimport math class ToyModel: def __init__(self, vocab_size=50, target=7, secret_bias=None): self.vocab_size = vocab_size self.target = target if secret_bias is None: secret_bias = {3: 0.8, 7: 1.2} self.secret_bias = secret_bias def loss(self, tokens, target=None): if target is None: target = self.target base = sum(1.0 if t != target else -0.2 for t in tokens) bias = sum(self.secret_bias.get(t, 0.0) for t in tokens) return 2.0 + base - bias def pseudo_gradient(self, tokens, target=None): if target is None: target = self.target grads = [] for i in range(len(tokens)): row = [] for v in range(self.vocab_size): candidate = tokens[:] candidate[i] = v row.append(self.loss(candidate, target)) grads.append(row) return grads class GCGOptimizer: def __init__(self, model, suffix_len=4, top_k=5, batch_size=4, iters=50): self.model = model self.suffix_len = suffix_len self.top_k = top_k self.batch_size = batch_size self.iters = iters self.suffix = [random.randrange(model.vocab_size) for _ in range(suffix_len)] self.history = [] def run(self, target=7): for _ in range(self.iters): grads = self.model.pseudo_gradient(self.suffix, target) candidates = {} for i in range(self.suffix_len): order = sorted( range(self.model.vocab_size), key=lambda v: grads[i][v] ) candidates[i] = order[:self.top_k] best = self.suffix[:] best_loss = self.model.loss(best, target) for _ in range(self.batch_size): cand = self.suffix[:] i = random.randrange(self.suffix_len) cand[i] = random.choice(candidates[i]) loss = self.model.loss(cand, target) if loss < best_loss: best_loss = loss best = cand self.suffix = best self.history.append(best_loss) return self.suffix, self.history if __name__ == "__main__": random.seed(0) model = ToyModel() opt = GCGOptimizer(model, suffix_len=4, top_k=5, batch_size=4, iters=30) suffix, history = opt.run() print("suffix:", suffix) print("loss history:", [round(h, 3) for h in history])
class ToyModel:定义一个模拟模型,loss()是锁匠的阻力计,pseudo_gradient()是触感手套。grads = self.model.pseudo_gradient(self.suffix, target)对每个齿位计算换成所有候选 token 后的损失,得到一张“触感表”。order = sorted(range(...), key=lambda v: grads[i][v])把第 i 个齿位的候选 token 按损失从小到大排序。candidates[i] = order[:self.top_k]只保留损失最小的 top_k 个候选 token,对应 GCG 的 Top-K 选择。cand = self.suffix[:]复制当前钥匙胚,避免直接修改原始后缀。i = random.randrange(self.suffix_len)随机挑一个齿位,对应 GCG 每次只随机替换少量位置。cand[i] = random.choice(candidates[i])从 Top-K 候选里随机抽一个替换,保持探索的随机性。if loss < best_loss:择优:只留下本轮损失最小的候选。self.suffix = best更新钥匙胚,进入下一轮打磨。self.history.append(best_loss)记录损失曲线,方便观察收敛情况。
常见误区
误区GCG 是自动化攻击,所以它应该像黑客工具一样,随便调 API 就能直接攻破模型,不需要任何模型内部信息。
正解GCG 本质是白盒攻击,它需要计算损失对 token 嵌入的梯度,这就要求访问模型权重或梯度。现实中之所以能攻击黑盒 API,靠的是迁移性:先在开源白盒模型上搜索后缀,再拿到黑盒模型上碰运气。这就像在自家同款锁上配好钥匙,再去开邻居家的同品牌锁。
为何常见:很多人把“自动化”和“黑盒”画等号,以为不需要内部信息;实际上“自动化”描述的是搜索过程,不是模型访问权限。
误区GCG 既然叫坐标梯度,应该每一轮把所有齿位都换成梯度最好的 token,这样收敛更快。
正解GCG 每轮只随机选 B 个位置做替换,并且每个位置从 Top-K 中随机抽一个候选。因为 token 是离散的,同时替换太多位置会让搜索空间爆炸,反而很难找到好组合。每次只动几个齿位,类似锁匠一次只微调几个齿,而不是把整把钥匙重做。
为何常见:直觉上“多换几个位置更快”,但离散搜索的局部性很重要,一步改太多容易错过最优组合。
误区只要 GCG 把目标有害回答的损失降到接近 0,模型就一定会输出有害内容。
正解损失只衡量模型对目标回答的概率,不衡量输出质量。有时 GCG 找到的后缀会让模型输出重复乱码或拒绝语句,但目标序列的概率仍然很高。真实红队测试必须用“是否生成有害可读内容”来判定成功,而不是只看损失。
为何常见:把优化目标和攻击目标混为一谈。损失是代理指标,代理指标不等于真实结果。
误区既然用了梯度,为什么不直接对 token 做梯度下降,让每个 token 变成连续向量再取整?
正解token 是离散符号,不存在“第 7.3 个 token”这种说法。梯度只能告诉候选替换的方向,不能直接作为更新量。GCG 用梯度选 Top-K、用随机采样生成候选、再用前向损失择优,这是“梯度引导的离散搜索”,而不是连续优化。
为何常见:大家学深度学习时习惯了梯度下降,遇到离散搜索容易直接把旧方法套上去。
误区只要在开源模型上找到一个 GCG 通用后缀,就能攻击 GPT、Claude、文心一言等所有模型。
正解迁移性确实存在,但很脆弱。不同模型的 tokenizer、架构、对齐训练数据差异很大,同一个后缀在不同模型上的成功率可能从 80% 掉到 5%。通用后缀更像“能开同一品牌同一批锁的钥匙”,而不是“万能钥匙”。
为何常见:媒体宣传往往夸大攻击的通用性,忽略了迁移率这个关键指标。
面试题
Q1. GCG 的英文全称是什么?
- A.Greedy Coordinate Gradient← 正确答案
- B.Gradient Coordinate Greedy
- C.Greedy Gradient Coordinate
- D.Coordinate Greedy Gradient
答案:A。GCG 是 Greedy Coordinate Gradient(贪心坐标梯度)的缩写,强调“贪心搜索 + 坐标级替换”两个核心思想。其他选项都颠倒了单词顺序。
Q2. 为什么标准梯度下降不能直接用于优化对抗后缀?
- A.词元是离散符号,无法直接微分← 正确答案
- B.梯度值永远恰好为零
- C.损失函数无法被计算
- D.词元没有任何向量表示
答案:A。token 是离散的,不存在“第 7.3 个 token”这样的连续插值。梯度只能告诉方向,不能直接作为更新量。词元有向量表示,损失也能计算,所以其他选项都不成立。
Q3. 在 GCG 中,梯度主要用于做什么?
- A.筛选替换损失最小的候选词元← 正确答案
- B.直接替换掉原始输入文本
- C.计算整个句子的困惑度
- D.从头训练一个新的大型模型
答案:A。GCG 对每个后缀位置计算梯度,用负梯度方向从词表中找 Top-K 候选 token。梯度不是用来直接更新,也不是用来算困惑度或训练模型。
Q4. 下面哪一项是 GCG 单轮迭代的正确行为?
- A.随机选位置并批量评估候选后缀← 正确答案
- B.对所有词元做一次梯度下降
- C.删除损失较低的后缀
- D.强制加长目标回答内容
答案:A。GCG 每轮随机选 B 个位置,从 Top-K 中抽候选生成一批候选后缀,再用前向传播评估并保留最优。它不做连续梯度下降,也不会删除低损失后缀或改目标回答长度。
Q5. GCG 攻击时,对抗后缀通常添加在哪里?
- A.用户输入提示词之后追加后缀← 正确答案
- B.模型权重之中注入后缀
- C.训练数据集中加入后缀
- D.分词器词表中删掉后缀
答案:A。GCG 构造的是输入层面的对抗样本,把后缀拼到用户提示词末尾。它不修改权重、不污染训练集、也不改动词表。
Q6. 下列哪些超参数会影响 GCG 的搜索效果?
- A.候选批大小 B← 正确答案
- B.Top-K 的取值← 正确答案
- C.迭代轮数上限← 正确答案
- D.GPU 风扇转速
答案:ABC。候选批大小决定每轮评估多少个候选,Top-K 控制候选质量,迭代轮数决定打磨次数。GPU 风扇转速是硬件散热参数,和算法效果无关。
Q7. GCG 的“迁移性”是什么意思?
- A.一个模型上的后缀可攻击另一模型← 正确答案
- B.后缀可以从中文迁移到英文
- C.损失值可以从 GPU 迁移到 CPU
- D.后缀可以自动迁移到训练集
答案:A。迁移性指在模型 A 上搜索到的对抗后缀,拼到模型 B 上也可能越狱成功。它是 GCG 从白盒走向黑盒攻击的关键,但和语言迁移、硬件迁移、训练集迁移无关。
Q8. 为什么 GCG 被称为“自动化”越狱?
- A.无需手工编写越狱提示词← 正确答案
- B.梯度与贪心搜索自动完成← 正确答案
- C.无需任何模型信息
- D.无需任何计算资源
答案:AB。GCG 把越狱变成自动优化流程,不需要人手工设计提示词,也不需要人工干预搜索。但它仍然需要模型信息和计算资源。
Q9. GCG 的优化目标是什么?
- A.目标有害回答的出现概率← 正确答案
- B.模型的困惑度数值
- C.输入文本的 token 数量
- D.模型解码的温度参数
答案:A。GCG 最小化目标回答的负对数概率,等价于最大化目标有害回答的出现概率。困惑度、token 数量、温度都不是攻击目标。
Q10. 哪些防御手段可以缓解 GCG 类攻击?
- A.困惑度过滤与输入清洗← 正确答案
- B.对抗训练与安全对齐← 正确答案
- C.对异常 token 序列告警← 正确答案
- D.单纯增加上下文窗口
答案:ABC。困惑度过滤能拦下乱码后缀,输入清洗能破坏对抗模式,对抗训练和安全对齐能提升模型免疫力,异常 token 告警可以辅助监测。单纯增加上下文窗口不会阻止攻击。