garak 的 Paraphrase Buff 插件:基于 Pegasus 与 T5 的提示改写漏洞扫描增强指南
【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garak
导读
garak.buffs.paraphrase是 LLM 漏洞扫描器 garak 中的一组提示改写(paraphrase)Buff 插件,其核心作用是在探针(probe)与生成器(generator)之间插入一个改写层,把原始探测提示自动扩展为多个语义相近的改写变体,从而扩大攻击面覆盖、检验模型在"同一语义、不同措辞"下的健壮性。本文以 docs/source/buffs/paraphrase.rst 文档为骨架,结合 garak/buffs/paraphrase.py 源码、Buff 基类、HFCompatible 混入类及测试用例,完整讲解PegasusT5与Fast两个内置改写器的默认参数、加载与生成流程、变换语义,以及如何用命令行和 YAML 配置在真实扫描任务中启用它们。
一、Paraphrase Buff 在 garak 架构中的位置
在 garak 中,Buff 是一类"夹在探针与生成器之间"的插件,负责增强、约束或扰动两者之间的交互。官方文档 docs/source/index_buffs.rst 的定义是:
Buff plugins augment, constrain, or otherwise perturb the interaction between probes and a generator. These allow things like mapping probes into a different language, or expanding prompts to various paraphrases, and so on.
也就是说,paraphrase 类 Buff 的典型应用场景正是文档中列举的"把探针提示扩展为多种改写形式"——尤其适合那些只携带少量静态提示的探针:通过改写,一次探测可以派生出多条措辞不同、语义等价的提示,考察目标生成器在措辞变化下是否仍保持一致的安全表现。
所有 Buff 都必须继承garak.buffs.base.Buff基类(garak/buffs/base.py),该基类本身继承自Configurable,负责参数加载、插件全名注册和初始化日志输出。核心的变换接口是transform():它接收一个garak.attempt.Attempt,返回一个可迭代的派生 Attempt 集合;基类的buff()方法则负责对一批源 Attempt 逐一调用transform()并以 tqdm 进度条展示"Buffing probe"过程。
paraphrase模块中的两个类都同时混入了HFCompatible(定义于 garak/resources/api/huggingface.py),因此天然复用了一整套面向 Hugging Face transformers 的私有工具方法,包括设备选择_select_hf_device()、构造函数参数收集_gather_hf_params()、生成参数提取_gather_generation_params()等。
二、PegasusT5:基于 Pegasus 的改写 Buff
2.1 类定位与默认参数
PegasusT5的文档字符串是 "Paraphrasing buff using Pegasus model",即一个以 Pegasus 条件生成模型为核心的改写器。其完整默认参数(定义于 garak/buffs/paraphrase.py 的DEFAULT_PARAMS)如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
para_model_name | garak-llm/pegasus_paraphrase | 用于改写的 Hugging Face 模型标识(原始上游为 tuner007 的 pegasus_paraphrase,见类属性doc_uri) |
hf_args.device | cpu | 模型加载与推理设备;默认 CPU 便于无 GPU 环境使用 |
hf_args.trust_remote_code | False | 是否信任远端自定义代码,Pegasus 场景默认关闭 |
max_length | 60 | 输入 token 截断长度与生成最大长度 |
temperature | 1.5 | 采样温度,值越高改写多样性越强 |
此外,类属性lang = "en"声明该 Buff 面向英文提示,_unsafe_attributes = ["para_model", "tokenizer"]用于标记需要延迟(懒加载)初始化的大对象,避免在插件枚举阶段就加载模型。
注意DEFAULT_PARAMS的构造方式是Buff.DEFAULT_PARAMS | {...},即与基类空字典做并集——这是 garak 插件定义默认参数的一贯模式。源码注释特别指出:torch_dtype在 Pegasus 上缺乏标准支持,因此hf_args中未提供 dtype 选项。
2.2 懒加载:_load_unsafe 与模型初始化
PegasusT5在__init__中只做轻量初始化:设定num_return_sequences = 6、num_beams = 6(波束数等于返回序列数)、tokenizer = None、para_model = None,然后调用基类super().__init__()。真正的模型加载被推迟到首次生成时,由_get_response()检测到self.para_model is None后触发_load_unsafe()。
_load_unsafe()的关键步骤(garak/buffs/paraphrase.py):
- 通过
_select_hf_device()确定计算设备(hf_args["device"]优先,其次按 CUDA → MPS → CPU 自动选择,见 garak/resources/api/huggingface.py 中的_select_hf_device); - 用
PegasusForConditionalGeneration.from_pretrained(self.para_model_name).to(self.device)加载条件生成模型; - 用
PegasusTokenizer.from_pretrained(self.para_model_name, trust_remote_code=self.hf_args["trust_remote_code"])加载分词器; - 在加载期间临时设置环境变量
DISABLE_SAFETENSORS_CONVERSION=true,用于"禁用 huggingface 在公开源码中打开 PR 的尝试",加载结束后恢复原值或删除该变量。
2.3 生成与 transform 变换流程
_get_response(input_text)完成一次改写生成:
batch = self.tokenizer( [input_text], truncation=True, padding="longest", max_length=self.max_length, return_tensors="pt", ).to(self.device) translated = self.para_model.generate( **batch, max_length=self.max_length, num_beams=self.num_beams, num_return_sequences=self.num_return_sequences, temperature=self.temperature, ) tgt_text = self.tokenizer.batch_decode(translated, skip_special_tokens=True) return tgt_text即:输入做最长填充与截断,波束搜索生成 6 条候选,最后批量解码返回改写文本列表。
transform()是 Buff 的对外主入口,其行为(两个类共用同一套逻辑):
def transform(self, attempt): yield self._derive_new_attempt(attempt) # 先产出原样副本 last_message = attempt.prompt.last_message() paraphrases = self._get_response(last_message.text) for paraphrase in set(paraphrases): paraphrased_attempt = self._derive_new_attempt(attempt) delattr(paraphrased_attempt, "_prompt") # hack to allow prompt set paraphrased_attempt._prompt = garak.attempt.Message( text=paraphrase, lang=last_message.lang ) yield paraphrased_attempt流程要点:
- 首先 yield 一份未经改写的派生 Attempt(保持原始提示参与扫描);
- 取当前 Attempt 的最后一条消息文本作为改写输入;
- 对生成结果去重(
set(paraphrases)),避免重复改写导致重复尝试; - 每条改写都会通过
_derive_new_attempt()派生新的 Attempt,并在notes中记录buff_creator、buff_source_attempt_uuid、buff_source_seq等溯源信息(见 garak/buffs/base.py 的_derive_new_attempt); - 通过
delattr(paraphrased_attempt, "_prompt")这一 hack 允许重新赋值_prompt,从而把改写文本写回新 Attempt,并保留原消息的语言lang。
因此,一个探测 Attempt 经过PegasusT5后最多会变成 1(原样)+ 6(去重后的改写)个派生 Attempt,正好印证了 tests/buffs/test_buffs.py 中test_buff_load_and_transform的断言:transform 应产出"原始 Attempt + 每条去重后的改写",并去除重复项。
三、Fast:面向 CPU 的 T5 改写 Buff
3.1 类定位与默认参数
Fast的文档字符串是 "CPU-friendly paraphrase buff based on Humarin's T5 paraphraser",其doc_uri指向 humarin 的chatgpt_paraphraser_on_T5_base上游模型,而默认加载的模型标识为garak-llm/chatgpt_paraphraser_on_T5_base。它专为 CPU 环境做了优化(hf_args.device = "cpu"、torch_dtype = "float32"),是比 Pegasus 更轻量的选择。
DEFAULT_PARAMS(同样记录在 garak/resources/plugin_cache.json 的插件缓存中):
| 参数 | 默认值 | 说明 |
|---|---|---|
para_model_name | garak-llm/chatgpt_paraphraser_on_T5_base | T5 改写模型标识 |
hf_args.device | cpu | 设备 |
hf_args.torch_dtype | float32 | 以 32 位浮点加载,避免 CPU 上的精度/兼容问题 |
hf_args.custom_generate | transformers-community/group-beam-search | 自定义生成实现(分组波束搜索) |
hf_args.trust_remote_code | True | 使用自定义生成时必须开启 |
与PegasusT5不同的是,Fast把生成超参数直接写在__init__实例属性里,而非DEFAULT_PARAMS:
self.num_beams = 5 self.num_beam_groups = 5 self.num_return_sequences = 5 self.repetition_penalty = 10.0 self.diversity_penalty = 3.0 self.no_repeat_ngram_size = 2 self.max_length = 128这些取值构成了一组"多样性优先"的解码策略:5 组波束、每组 5 个波束、5 条返回序列,配合极高的repetition_penalty = 10.0、diversity_penalty = 3.0与no_repeat_ngram_size = 2,强制生成彼此差异明显、不重复 n-gram 的改写结果。源码中还保留了被注释掉的self.temperature = 0.7,说明温度参数在该实现中暂未启用。
3.2 加载与 custom_generate 约束
Fast._load_unsafe()与 Pegasus 版本有几点显著差异:
- 使用
AutoTokenizer/AutoModelForSeq2SeqLM通用接口加载; - 通过
_gather_hf_params(hf_constructor=AutoModelForSeq2SeqLM.from_pretrained)从hf_args中按构造函数签名过滤出可用参数,再用_gather_generation_params()提取生成参数,并从模型加载参数中剔除重叠项; - 强制校验:若配置了
custom_generate但未开启trust_remote_code,直接抛出ValueError("When using a 'custom_generate' option 'trust_remote_code' must be enabled.")——这是因为自定义生成代码需要远端代码执行许可; - 把生成参数逐一写入
self.para_model.generation_config。
_get_response()同样给输入加上paraphrase:前缀(这是 T5 类模型的指令式提示格式),并显式传入repetition_penalty、num_return_sequences、no_repeat_ngram_size、num_beams、num_beam_groups、max_length、diversity_penalty,以及trust_remote_code(这是 transformers >= 4.57.0 对自定义生成的要求)。生成过程中若抛出OSError(典型如模型下载失败、权重加载异常),会被包装为garak.exception.GarakException重新抛出,消息形如...paraphrase.Fast failed to generate a model response。
3.3 平台限制
Fast在 Windows 上不受支持。这一点在测试中体现得很明确:tests/buffs/test_buffs_paraphrase.py 对 Windows 平台断言buffs.paraphrase.Fast的_get_response会抛出包含 "paraphrase.Fast failed" 的GarakException;tests/buffs/test_buffs.py 也做了相同的平台分支处理。因此,在 Windows 上运行 garak 时建议改用PegasusT5,或者干脆不用改写类 Buff。
四、测试用例:改写 Buff 的契约约束
仓库对 paraphrase Buff 的单元测试集中在 tests/buffs/test_buffs_paraphrase.py,它通过_plugins.enumerate_plugins("buffs")自动发现所有buffs.paraphrase.*插件并参数化执行,测试样例输入是经典句子 "The rain in Spain falls mainly in the plains."。其断言形成了改写器的三条硬性契约:
- 必须返回至少一条改写(
len(paraphrases) > 0); - 不允许返回重复改写(
len(paraphrases) == len(set(paraphrases))); - 任何改写都不能是空串(
not any(i == "" for i in paraphrases))。
此外,tests/buffs/test_buffs.py 中的test_buff_structure还验证所有DEFAULT_PARAMS键都必须被_supported_params支持;test_buff_load_and_transform则通过 mock_get_response(返回["a paraphrase", "another paraphrase", "a paraphrase"],含重复项)验证 transform 的去重语义:最终应恰好得到 3 个 Attempt(原始 1 个 + 去重后 2 个改写)。这两条测试共同保障了改写 Buff 的插件契约与变换语义不被破坏。
五、在扫描任务中启用 Paraphrase Buff
5.1 命令行方式
garak 的--spec参数支持对 Buff 做包含/排除过滤。文档 docs/source/configurable.rst 给出的示例是:
# 所有 active buffs 除 paraphrase 之外,覆盖全部 active probes(注意引号包裹 glob) garak --spec "probes.*,buffs.*,-buffs.paraphrase"反过来,如果只想针对某个探针启用改写扩展,可以显式列出目标 Buff:
garak --model <generator> --spec "probes.grandma.Slurs,buffs.paraphrase.Fast"由于-buffs.paraphrase会匹配该命名空间下的全部子类,这种写法适合在"默认启用全部 buff"的批量扫描中精确剔除改写类 Buff,以避免额外的模型下载与推理开销。
5.2 YAML 配置方式
garak 支持用 YAML 描述完整扫描计划,典型参考是 garak/configs/bag.yaml。在该配置结构下,Buff 相关配置位于plugins段,可按插件类名逐项覆写参数。例如为buffs.paraphrase.PegasusT5指定更大的改写长度与设备:
plugins: buffs: paraphrase: PegasusT5: para_model_name: garak-llm/pegasus_paraphrase max_length: 80 temperature: 1.2 hf_args: device: cuda需要注意PegasusT5与Fast的参数体系不完全一致:温度仅对 Pegasus 生效,而num_beams、num_return_sequences等实例属性在Fast中直接定义在__init__,若需在 YAML 中调整,应确认其是否属于_supported_params允许的键(测试test_buff_structure会强制校验这一点)。
5.3 运行前提与限制
- 模型下载:两个 Buff 首次使用时都会从 Hugging Face 拉取模型权重(
garak-llm/pegasus_paraphrase与garak-llm/chatgpt_paraphraser_on_T5_base),需要网络可达;_get_response中的OSError会被包装为GarakException。 - 依赖:需要安装
transformers、torch等依赖,且Fast依赖trust_remote_code与自定义分组波束搜索实现(transformers >= 4.57.0)。 - 平台:
Fast在 Windows 上不可用(测试中明确断言会抛GarakException),Windows 用户应选用PegasusT5。 - 语言:两者
lang均为"en",改写输入按英文处理;非英文提示的改写效果不在默认契约保障范围内。 - 计算量:默认一次改写生成 5~6 条序列(PegasusT5 为 6 条、Fast 为 5 条),配合
run.generations等系统参数会放大总探测次数,实际扫描耗时需据此估算。
六、结语
garak.buffs.paraphrase用两个差异化的实现覆盖了"高质量改写"(PegasusT5,Pegasus 模型、可调温度)与"CPU 友好轻量改写"(Fast,T5 模型、强去重约束)两种需求。无论选哪一种,它们都遵循同一套 Buff 契约:通过transform()把一个 Attempt 扩展为"原样 + N 条去重改写"的派生集合,并在notes中保留完整的溯源链路,从而让漏洞扫描报告能够清晰追踪每条改写提示的来源。对于提示数量稀少、措辞敏感的探针,在扫描计划中挂载一个 paraphrase Buff,是快速扩大覆盖、检验模型改写鲁棒性的低门槛手段。
想深入了解实现细节的读者,可以直接阅读 garak/buffs/paraphrase.py 的完整源码,对照 garak/buffs/base.py 理解 Attempt 派生机制,再通过 tests/buffs/test_buffs_paraphrase.py 与 tests/buffs/test_buffs.py 验证其行为契约。
【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garak
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考