news 2026/9/16 13:52:48

garak 测试生成器 generators.test 全解析:从 Blank 到 ReasoningLipsum 的测试基座

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
garak 测试生成器 generators.test 全解析:从 Blank 到 ReasoningLipsum 的测试基座

garak 测试生成器 generators.test 全解析:从 Blank 到 ReasoningLipsum 的测试基座

【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garak

garak 是一套开源的 LLM 漏洞扫描框架,其generators.test模块为整条扫描流水线提供了一组不依赖任何真实模型、可离线运行、行为完全确定的测试生成器。本文基于 docs/source/generators/test.rst 对应的模块文档,结合 garak/generators/test.py 源码与 tests/generators/test_test.py 测试用例,系统讲解该模块中 7 个生成器的行为语义、可配置参数、默认值与典型使用场景。读完本文,你将掌握如何用test.*生成器快速验证 probe、detector 的逻辑,如何在无 GPU、无 API Key 的情况下完整跑通一次 garak 扫描,以及如何模拟“带思考痕迹(reasoning trace)”的长结构化输出。

一、为什么需要测试生成器:generators 在 garak 中的角色

garak 的扫描链路大致为:probe 构造攻击性 prompt → generator 调用目标 LLM 获取回复 → detector 判定回复是否“越狱”。其中 generator 是对“文本到文本服务”的抽象封装,所有生成器都必须继承 garak/generators/base.py 中的Generator基类。

真实生成器(如 OpenAI、Anthropic、HuggingFace)需要网络、API Key 和模型部署,这会成为开发与回归测试的瓶颈:写一个新的 probe 或 detector 时,你只关心自己的逻辑是否正确,而不是网络是否通畅。generators.test模块正是为此而生——它提供“简单、确定的系统级响应”,专门用于测试,不产生任何真实模型调用

从模块的类结构看,所有测试生成器都只覆写了一个方法_call_model(prompt, generations_this_call),而把“迭代生成、结果校验、后处理、去前缀”等通用逻辑全部交给基类Generator.generate()完成(garak/generators/base.py)。这既是理解本文各生成器的前提,也是后续学习如何编写自定义生成器(docs/source/extending.generator.rst)的入口。

二、模块总览:7 个生成器与默认类

模块garak/generators/test.py共定义 7 个生成器,并在文件末尾通过DEFAULT_CLASS = "Lipsum"指定了默认类:

类名类属性名name输出行为支持多路生成
BlankBlank恒定返回空字符串
RepeatRepeat原样返回输入的最后一轮消息
SingleSingle恒定返回固定字符串"ELIM"
NonesNones每次生成都返回None
LipsumLorem Ipsum按 unit/count 生成随机的 Lorem Ipsum 文本
ReasoningLipsumReasoning Lipsum生成“思考痕迹 + 输出”结构化文本
BlankVisionBlankVision接受文本+图像输入,恒定返回空字符串

DEFAULT_CLASS的含义是:当用户在命令行只指定到模块级(如-m test)而不指定具体类时,插件加载器会回落到该类。这一机制在 garak/_plugins.py 的加载逻辑中生效。因此-m test等价于-m test.Lipsum

在 garak/resources/plugin_cache.json 中可以查到全部 7 个类的元数据(DEFAULT_PARAMSmodalitysupports_multiple_generations等),这些元数据与源码定义完全一致,是 garak 插件自省(--list_config-L)的信息来源。

三、六个基础测试生成器逐个详解

3.1 Blank:恒定空输出

class Blank(Generator): """This generator always returns the empty string.""" supports_multiple_generations = True def _call_model(self, prompt, generations_this_call=1): return [Message("")] * generations_this_call

Blank对任何 prompt 都返回""。它是 garak 中最常用的测试生成器之一,常见用途包括:

  • 单独测试 probepython3 -m garak -t test.Blank -p mymodule -d always.Pass,把生成器固定为空输出,从而隔离 probe 构造 prompt 的逻辑(docs/source/extending.rst)。
  • 单独测试 detectorpython3 -m garak -t test.Blank -p test.Blank -d mymodule,让 probe 与生成器都“哑化”,只验证 detector 对空回复的判定。

supports_multiple_generations = True表示一次_call_model调用即可按请求批量返回多个结果。测试 tests/generators/test_test.py 验证了generations_this_call=5时会返回 5 个空Message

3.2 Repeat:回显最后一条输入消息

class Repeat(Generator): """This generator returns the last message from input that was posed to it.""" supports_multiple_generations = True def _call_model(self, prompt, generations_this_call=1): return [prompt.last_message()] * generations_this_call

Repeat通过prompt.last_message()取回对话(Conversation)中用户提交的最后一条消息并原样返回。测试用例 tests/generators/test_test.py 断言:输入"especially the lies",输出即为Message("especially the lies")

这种“输入即输出”的行为非常适合测试基于反射/回显语义的 detector(例如检查回复是否复述了恶意指令),或验证 probe 对多轮对话的处理。

3.3 Single:固定字符串且拒绝多路生成

class Single(Generator): supports_multiple_generations = False test_generation_string = "ELIM" def _call_model(self, prompt, generations_this_call=1): if generations_this_call == 1: return [Message(self.test_generation_string)] else: raise ValueError( "Test generator refuses to generate > 1 at a time. Check generation logic" )

Single恒定返回字符串"ELIM",并且supports_multiple_generations = False——当请求多路生成时会主动抛出ValueError而不是静默出错。

这个类的真正价值在于验证框架自身的多路生成逻辑。在基类 garak/generators/base.py 中,当生成器不支持多路生成且请求generations_this_call > 1时,框架会退回逐次调用_call_model(prompt, 1)并聚合结果;若配置了parallel_requests,还会走多进程并行分支。测试 tests/generators/test_test.py 覆盖了“随机 2~12 路生成均正确聚合”以及“直接调用_call_model(..., generations_this_call=2)抛出 ValueError”两种路径。

3.4 Nones:恒定无响应

class Nones(Generator): """This generator always returns a None for every generation.""" supports_multiple_generations = True def _call_model(self, prompt, generations_this_call=1): return [None] * generations_this_call

Nones每次生成都返回None。注意基类的_call_model签名允许返回Message | None(garak/generators/base.py),None语义上代表“模型未给出任何响应”。它用于测试无响应/超时/拒答场景下 detector 的行为——例如 tests/detectors/test_detectors.py 中要求detect()在输入为None时仍返回合法的浮点数或None列表。

3.5 Lipsum:可变的非零输出(模块默认类)

class Lipsum(Generator): """Lorem Ipsum generator, so we can get non-zero outputs that vary""" DEFAULT_PARAMS = Generator.DEFAULT_PARAMS | { "unit": "sentence", "count": 1, } supports_multiple_generations = False _unit_separators = {"sentence": " ", "paragraph": "\n", "text": "\n"}

Lipsum是模块的默认生成器(DEFAULT_CLASS = "Lipsum"),作用是产生非空且每次不同的输出,用于测试那些需要“有内容回复”才能触发的逻辑,同时保证可重复性不受外部服务影响。它依赖第三方库lorem,按unit指定的粒度生成内容,再用count控制拼接数量。

参数说明(均有默认值,均可通过DEFAULT_PARAMS覆盖):

参数类型默认值取值范围/说明
unitstr"sentence""sentence""paragraph""text"三者之一;"text"为多段落文本块
countint1每个输出中拼接多少个unit单元

单元之间的连接符由_unit_separators决定:句子之间用空格,段落/文本块之间用换行。若传入非法unit_call_model会抛出ValueError: Invalid unit '...'

测试 tests/generators/test_test.py 验证了完整行为矩阵:

  • 默认输出非空;
  • unit="sentence"时输出以句号结尾且不含换行;
  • unit="paragraph"时包含多个句子;
  • unit="text"时包含换行(多段落);
  • count=5时句子数不少于 5;
  • unit="chapter"会触发ValueError
  • 连续 200 次调用输出各不相同(len(results) > 1)。

注意Lipsumsupports_multiple_generations = False,与Single一样会走基类的逐次/并行聚合路径。

3.6 BlankVision:多模态(文本+图像)输入的空输出

class BlankVision(Generator): """This text+image input generator always returns the empty string.""" supports_multiple_generations = True modality = {"in": {"text", "image"}, "out": {"text"}} def _call_model(self, prompt, generations_this_call=1): return [Message("")] * generations_this_call

BlankVisionBlank行为相同(恒定返回空字符串),但通过modality = {"in": {"text", "image"}, "out": {"text"}}声明其输入模态包含文本与图像。garak 的模态声明遵循“支持主流 any-to-any 大模型”的约定(garak/generators/base.py,合法输入元素包括textimageaudiovideo3d)。因此它专门用于视觉模型流水线的离线测试,例如配合visual_jailbreak类 probe 验证多模态输入的处理链路。

四、ReasoningLipsum:模拟思考痕迹的长结构化输出

ReasoningLipsumLipsum的子类,也是该模块中参数最丰富、设计意图最明确的生成器:为测试长结构化输出场景(如带<think>推理痕迹的模型回复)提供确定性模拟数据。其输出格式固定为:

{skip_seq_start}{reasoning text}{skip_seq_end}{output text}

即:开头定界符 + 推理文本 + 结尾定界符 + 正式输出。

4.1 参数与默认值

DEFAULT_PARAMS = Lipsum.DEFAULT_PARAMS | { "skip_seq_start": "<think>", "skip_seq_end": "</think>", "reasoning_length": 2000, "output_length": 500, "variance": 0.1, "respect_max_tokens": False, "unit": "sentence", }
参数类型默认值说明
skip_seq_startstr"<think>"推理痕迹的起始定界符
skip_seq_endstr"</think>"推理痕迹的结束定界符
reasoning_lengthint2000推理文本的目标字符数
output_lengthint500输出文本的目标字符数
variancefloat0.1实际长度相对目标长度的允许偏差比例(0.0–1.0),0.1即 ±10%
respect_max_tokensboolFalse是否按max_tokens * 1.4字符上限截断输出文本(不影响推理痕迹)
unitstr"sentence"生成文本的基本单元(继承自Lipsum

其中skip_seq_start/skip_seq_end在基类Generator.DEFAULT_PARAMS中默认是NoneReasoningLipsum将其覆盖为<think>/</think>——这也让它可以无缝接入基类的skip sequence 清理机制:当生成器同时配置了skip_seq_startskip_seq_end时,Generator.generate()会调用_prune_skip_sequences()(garak/generators/base.py),用正则将定界符包裹的推理段从输出中剥离,模拟真实推理模型在“后处理阶段剔除思考痕迹”的行为。

4.2 核心实现:_generate_lorem 与长度控制

_generate_lorem(min_length, variance)(garak/generators/test.py)是长度控制的内部实现:

  1. variance > 0,先用self._rng.uniform(-variance, variance) * min_length计算随机偏差,得到实际目标长度(下限为 1 个字符);
  2. 校验unit合法性;
  3. lorem库取一个生成函数,逐句拼接文本,直到长度达到目标。

_call_model中,若respect_max_tokens=True且设置了max_tokens,则output_length会被裁剪为min(output_length, int(max_tokens * 1.4))——用字符数粗略模拟 token 预算(1 token ≈ 1.4 字符),且只限制输出文本、不限制推理痕迹,这与真实推理模型“思考可长、输出受限”的特性一致。

4.3 测试如何验证其结构

tests/generators/test_test.py 对该类做了极为细致的验证,可作为理解其语义的“行为契约”:

  • 输出必须以skip_seq_start开头,且skip_seq_start必须出现在skip_seq_end之前;
  • 定界符之后必须还有非空输出文本(after_end长度 > 0);
  • 默认参数下总长度落在expected2 * expected之间(expected = reasoning_length + output_length + 定界符长度);
  • 自定义定界符(如[[REASON]]/[[/REASON]])与自定义长度(reasoning_length=5000output_length=2000)均按预期生效;
  • variance=0.0时输出长度恒 ≥ 目标长度;随机variance时长度有变化但始终非空;
  • 一次请求 200 路生成也能全部返回且都包含定界符。

五、命令行与配置实战

5.1 通过 CLI 指定测试生成器

garak 主入口是python -m garak,生成器通过-m/--model_type指定模块、-n/--model_name指定类(garak/cli.py)。使用测试生成器的典型命令:

# 用模块默认类(Lipsum)跑一个 probe python -m garak -m test -p dan.Dan_10_0 # 显式指定具体生成器类 python -m garak -m test.Lipsum -p test.Blank # 空输出 + 空 probe,验证 detector 逻辑 python3 -m garak -t test.Blank -p test.Blank -d mymodule # 开发 probe 时:固定空生成器,配 always.Pass 隔离 detector python3 -m garak -t test.Blank -p mymodule -d always.Pass

后两条来自官方扩展指南 docs/source/extending.rst 的推荐调试配方:“测试 probe 用空生成器 + always.Pass,测试 detector 用空生成器 + 空 probe,测试生成器用空 probe + always.Pass”——三种组合把链路中的无关变量全部固定,定位问题一目了然。

5.2 在配置文件中覆盖参数

test模块的类全部继承自Configurable,其DEFAULT_PARAMS可通过 garak 的插件配置机制按层覆盖(详见 docs/source/configurable.rst 中“插件包/模块/类”的命名约定,如generator.test.Lipsum)。例如要调整Lipsum的输出粒度,只需在配置中设置:

plugins: generator: test: Lipsum: unit: paragraph count: 2

同理,ReasoningLipsumreasoning_lengthoutput_lengthvariancerespect_max_tokensskip_seq_start/skip_seq_end均可这样覆盖,从而在不改任何代码的前提下,为不同测试场景定制“思考痕迹”的规模与形态。

六、如何选择:一张决策速查表

你的测试目标推荐生成器理由
只测 probe,固定生成器行为test.Blank输出恒为空,probe 结果完全可预期
只测 detector 对空回复的判定test.Blank配合test.Blankprobe 隔离所有上游变量
测 detector 对“复述输入”的判定test.Repeat输出 = 输入最后一条消息,语义清晰
验证框架多路生成/并行逻辑test.Singlesupports_multiple_generations=False会触发逐次与并行聚合路径
测 detector 对无响应的处理test.Nones恒返回None,模拟模型未应答
需要非空且变化的输出做压力/统计测试test.Lipsum(默认)输出有内容、每次不同,且零外部依赖
测长输出 / 带思考痕迹的结构化输出test.ReasoningLipsum可精确控制推理段与输出段的长度与定界符
测多模态(文本+图像)链路test.BlankVision声明modality.in = {text, image}的空输出生成器

七、总结

garak.generators.test模块用不到 210 行代码,为 garak 的“probe → generator → detector”链路提供了完整的离线测试基座:Blank/Repeat/Single/Nones覆盖确定性的空输出、回显、固定串与无响应四类极简行为;Lipsum提供可变非零输出并作为模块默认类;ReasoningLipsum通过skip_seq_start/endreasoning_lengthoutput_lengthvariancerespect_max_tokens精确模拟现代推理模型的思考痕迹结构,还能与基类的 skip-sequence 清理机制联动;BlankVision则将测试能力扩展到多模态输入。配合 tests/generators/test_test.py 中详尽的断言矩阵,你可以把这些生成器当作“可编程的假模型”,在无网络、无密钥、无 GPU 的环境下快速验证 probe 与 detector 的正确性,或参照 docs/source/extending.generator.rst 以它们为模板开发自己的生成器。

【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garak

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 13:51:10

Level 4自动驾驶系统设计62——VMC 2

本文提出一种1ms~5ms级硬实时轮胎抓地力估计模型(μ估计器),基于扩张状态观测器(ESO)与多体动力学刚性方程,全时在线融合滑移率与极限垂直载荷,实现对路面附着系数的毫秒级精准估算。该模型在49吨重卡高速行驶中应对暗冰等突发低附着工况时,通过一类硬中断触发物理熔断…

作者头像 李华
网站建设 2026/9/16 13:49:36

SiYuan Mermaid 电路图:3 个技巧,10 分钟从零画出完整电路图

SiYuan Mermaid 电路图&#xff1a;3 个技巧&#xff0c;10 分钟从零画出完整电路图 【免费下载链接】siyuan An open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间&#xff0c;让…

作者头像 李华
网站建设 2026/9/16 13:47:40

OpenClaw 发布验证中的工具链反馈包:私有脱敏上报程序设计

OpenClaw 发布验证中的工具链反馈包&#xff1a;私有脱敏上报程序设计 【免费下载链接】openclaw The AI that really does things. Any OS. Any Platform. The lobster way. &#x1f99e; 项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw 导读 本文讲解…

作者头像 李华