garak run.spec 选择解析机制全解:从 `_selection.py` 看探针与 Buff 的统一选择流水线

发布时间:2026/9/16 17:36:05
garak run.spec 选择解析机制全解:从 `_selection.py` 看探针与 Buff 的统一选择流水线 garak run.spec 选择解析机制全解从_selection.py看探针与 Buff 的统一选择流水线【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garakgarakLLM vulnerability scanner通过统一的run.spec选择语法来决定一次扫描运行哪些探针probes与 buff而garak/_selection.py正是把run.spec语法树解析为具体插件名的核心模块。本文以 docs/source/_selection.rst 为骨架结合 garak/_selection.py、garak/_spec.py 源码与 CLI 集成系统讲解run.spec从命令行/配置文件语法、逐层解析、tier/tag/intent 过滤到空选择诊断与旧配置迁移的完整链路。读完你将掌握--spec的全部选择器语义、排除优先规则以及 garak 内部如何区分未知选择器与已知但未激活的插件。一、语法与解析两个模块各司其职garak 把选择语言拆成两个层次这一分工在 docs/source/_selection.rst 开篇即已点明语法层garak/_spec.py负责run.spec的解析与序列化把 CLI 字符串或配置文件中的include/exclude列表统一转换为内部Spec对象解析层garak/_selection.py把Spec中的选择器Selector对照插件注册表garak/_plugins.py 中的 active/tier/tag 状态解析为具体的插件名如probes.dan.DanInTheWild。resolve_spec是 CLI 与 harness 使用的唯一入口而同一套插件路径解析核心_resolve_plugin_paths也被探测器的parse_plugin_spec适配器复用见 garak/_config.py只是探测器仍保留旧的无前缀 spec 字符串形式尚未并入run.spec源码注释明确说明_CATEGORIES (probes, buffs)见 garak/_spec.py。两种输入形态最终都落到同一个内部数据结构输入形态解析函数说明CLI 字符串逗号分隔parse_spec_stringgarak/_spec.py-前缀表示排除或无前缀表示包含配置文件YAML/JSON 的include/exclude列表parse_spec_filegarak/_spec.py列表项为插件路径字符串或单键映射如{tag: owasp:llm01}Spec内部是一个include列表加一个exclude列表每个元素都是Selector。Selector的kind取值有五种plugin_path、none、tag、tier、intent其中plugin_path与none携带类别前缀如probes.dan、probes.none并设置category而tag/tier/intent属于非插件轴category为None见 garak/_spec.py。语法约束要点从parse_spec_string的源码可以看到两条硬性约束选择器之间不允许空白run.spec必须是单个逗号分隔的 token选择器间出现空白会直接抛ValueErrorgarak/_spec.py。这样设计的好处是纯逗号列表在 shell 中无需引号但*通配符仍是 shell 通配符含*的 spec 必须加引号或改用all别名。intent:每个代码一个选择器intent:S004,S005这种逗号分隔写法是语法错误必须写成intent:S004, intent:S005garak/_spec.py。_classifygarak/_spec.py负责把裸 token 归类识别tag:、tier:、intent:前缀把裸none规范化为probes.none把all/*规范化为probes.*校验类别前缀必须是probes或buffs否则报错。二、解析流水线resolve_spec的四层处理resolve_spec(spec, skip_unknownFalse)garak/_selection.py把整个选择过程组织为四个层次顺序与优先级是理解run.spec的关键。第 1 层探针候选集plugin-path include如果include中存在类别为probes的plugin_path选择器则调用_resolve_plugin_paths得到候选集否则如果存在显式的probes.nonenone选择器候选集为空——这是故意为之的空选择不是错误否则未指定任何探针路径默认取所有激活探针等价于probes.*。关键代码garak/_selection.pyprobe_includes [ s for s in spec.include if s.kind plugin_path and s.category probes ] probe_none any(s.kind none and s.category probes for s in spec.include) if probe_includes: candidate, rej, inact _resolve_plugin_paths(probe_includes, probes) ... elif probe_none: candidate set() else: candidate { p for p, active in _plugins.enumerate_plugins(categoryprobes) if active is True }第 2 层正向过滤tier tagAND 关系tier:选择器是**包含式inclusive**的日志级别语义tier:N放行 tier 1..N。多个 tier 选择器取max作为天花板garak/_selection.pytag:选择器按前缀过滤多个 tag 前缀之间是AND关系需同时命中至少一个匹配前缀见_has_any_taggarak/_selection.py。tier_ceilings [int(s.value) for s in spec.include if s.kind tier] if tier_ceilings: ceiling max(tier_ceilings) candidate {p for p in candidate if _tier_of(p) ceiling} tag_prefixes [s.value for s in spec.include if s.kind tag] if tag_prefixes: candidate {p for p in candidate if _has_any_tag(p, tag_prefixes)}这里有个重要细节tier与tag过滤作用于整个候选集包括显式点名的类。文档给出的反例是probes.foo.Bar,tier:1在foo.Bar为 tier 3 时解析结果为空docs/source/configurable.rst。第 3 层Buff 收集buffs的选择是buffs.*include 的并集且没有隐式默认——不写buffs:选择器就不会运行任何 buffgarak/_selection.py。第 4 层排除exclude最后应用排除优先遍历spec.exclude按选择器类型逐一从候选集中移除garak/_selection.pyplugin_path移除对应探针/ bufftier:N精确移除 tier 恰好等于 N 的探针注意与tier:Ninclude 的包含式语义不对称tag:移除带该标签前缀的探针。tier:3include 放行 1..3再-tier:2精确移除 tier 2最终得到 tiers {1,3}——这正是官方文档示例garak --spec probes.*,tier:3,-tier:2的含义。三、插件路径解析核心_resolve_plugin_paths_resolve_plugin_paths(selectors, category)garak/_selection.py是类别无关的单一解析核心镜像了旧parse_plugin_spec的三档粒度选择器形态语义category.*全部激活的插件category.module该模块下所有激活的插件familycategory.module.Class精确匹配单个类忽略 active 状态返回三元组(names, unknown, inactive)其中三者的区分是 garak 诊断质量的关键names解析出的插件名集合unknown代码中rejected指向根本不存在的选择器inactive模块存在但其下所有插件都标记为 inactive 的裸模块选择器known-but-empty区别于 unknown对应 issue #830 的语义。精确点名第三档会忽略active状态这意味着你可以用probes.fitd.FITD这种形式强制运行一个默认未激活的探针——官方示例garak --spec probes.all,probes.fitd.FITD正是全部激活探针 一个特定未激活类的组合。四、tier 语义重要性分级与包含式过滤tier 由 garak/probes/_tier.py 的Tier枚举定义Tier名称含义1OF_CONCERN需要关注低通过率或低 z-score 可能存在问题应上报安全/对齐团队并考虑写入模型卡2COMPETE_WITH_SOTA与 SOTA 竞争低 z-score 可能存在问题建议检查结果3INFORMATIONAL信息性结果与具体使用场景相关若你已知探针契合某场景可视为 Tier 29UNLISTED未列入重复、废弃、波动或非对抗性探针源码中未声明 tier 的探针默认取_DEFAULT_TIER 9garak/_selection.py。_normalize_tiergarak/_spec.py支持整数与枚举名两种写法tier:of_concern等价于tier:1非法值会给出use an int (1..3, 9) or a Tier name的明确报错。五、intent 轴独立于插件选择的选择维度intent:是run.spec中独立的一条轴它不增删任何探针而是为意图型探针IntentProbe子类与 IntentService 收集 typology 代码。处理逻辑在 garak/_selection.pyintent:*/intent:all表示选择所有意图由 IntentService 展开空泛的哨兵值其他代码必须匹配 typology 说明符格式校验正则garak/_spec.py为re.fullmatch(CTMS?)?, intent_specifier)即首字母C/T/M/S后跟可选的 3 位数字和可选小写叶子后缀如S整条 Safety 分支、S001类别、S001mis叶子。未指定intent:时默认注入SSafety 分支DEFAULT_INTENT_SCOPE Sgarak/_spec.py保证在run.spec覆盖后意图范围依然存活typology 成员的展开与无探测器过滤发生在 IntentService 阶段受run.serve_detectorless_intents等run.*意图修饰符控制选择intent:但没有选中任何IntentProbe时会给出警告并继续运行解析结果中的intents、blocked_intents、intents_explicit会被 CLI 存到_config.transient.*供 IntentService 在 harness 加载时消费garak/cli.py。注意intent代码的格式校验发生在解析时错误计入rejected而 typology 成员校验在 IntentService 加载时——两者时机不同。六、空选择诊断empty_reason的贴心报错当 spec 解析不出任何探针时garak 不会抛出晦涩的错误而是尽力给出可操作的诊断_empty_reasongarak/_selection.py若同时存在 tier 天花板与显式探针点名报probe X is tier N but the spec restricts to tiers 1..M; widen the tier filter or drop the explicit probe——直接点名冲突双方若存在 tag/tier 过滤报no active probe matches the given tier/tag filters; widen the filters否则报every included probe was removed by an exclusion; adjust includes/excludes。CLI 侧的兜底逻辑在 garak/cli.py当未设置--skip_unknown且解析结果为空时打印❌ No probes selected: empty_reason并抛出ValueError中止运行。唯一的例外是显式none选择——那是故意的空运行no-op不算错误。另外若选择器全部指向 inactive 模块报错信息会建议按名字点名例如all plugins in probes.xxx are marked inactive; select one or more by name (e.g. probes.xxx.ClassName) to continue。resolve_spec返回的Resolution数据类garak/_spec.py包含selected按类别映射到规范名category.module.Class、rejected未知选择器、inactive已知但全 inactive 的模块、empty_reason、intents/blocked_intents/intents_explicit并提供了probes与buffs便捷属性。若rejected非空且未设置skip_unknown会直接抛ValueError(funknown run.spec selectors: {rejected})。七、CLI 集成与旧配置迁移--spec命令行入口统一选择参数是--spec短选项-S定义在 garak/cli.py默认值取自_config.run.spec。CLI 解析流程garak/cli.py为--spec优先若有--spec则通过parse_spec_string解析并写入_config.run.spec旧的--probes/--probe_tags/--buffs参数会被映射到run.spec并打出弃用提示两者同时给出时--spec获胜。后续在_check_selection附近garak/cli.pyCLI 调用resolve_spec(parse_spec_file(_config.run.spec), skip_unknownTrue)把解析出的rejected/inactive交给人性化检查函数并把意图轴与激活探针列表存入 transient 状态。--list_probes还可以与--spec组合使用如--list_probes --spec probes.dan来预览某个 spec 会选中哪些探针garak/cli.py。配置文件形态在 YAML/JSON 配置中run.spec使用include/exclude列表示例见 docs/source/configurable.rstrun: spec: include: - probes.dan - tag: owasp:llm01 exclude: - probes.dan.DanInTheWildparse_spec_file要求列表项要么是插件路径字符串要么是单键映射{tag: ...}、{tier: 1}多键映射会报错。旧键迁移映射legacy_selection_specgarak/_spec.py是plugins.probe_spec/plugins.buff_spec/run.probe_tags三个废弃键到run.spec字典的单一映射点被配置加载 shim_map_legacy_selection、CLI 标志处理和run.specfixer 迁移共同复用。其语义无意义值缺失、空串、auto视为未指定返回Nonenone映射为显式空选择probes.noneall/*映射为probes.*旧键的值不允许带类别前缀如probes.dan这种写法在 legacy 键下会报错提示 legacy 键取无前缀值如encoding.CharCode。仓库中还提供了自动迁移工具garak/resources/fixer/20260612_run_spec.py内部同样通过parse_spec_fileresolve_spec(..., skip_unknownTrue)校验并重写旧配置。探测器适配器parse_plugin_spec(spec, category, probe_tag_filter)garak/_config.py是探测器解析的薄适配层它把 legacy 无前缀 spec 经_legacy_path_selectors转成Selector再调用_resolve_plugin_paths复用同一核心并额外支持probe_tag_filter标签过滤返回(sorted names, unknown clauses)其中 unknown 保留裸形式以兼容旧行为。detectors 目前仍走这条 legacy 路径对应plugins.detector_spec即 CLI 的-d尚未折叠进run.spec——从源码结构看Resolution.selected被设计成可按类别扩展的字典正是为未来把 detectors 纳入统一 spec 预留的空间。八、实战示例汇总以下示例均可在命令行直接运行源自 docs/source/configurable.rst并可与上文源码语义互相印证# 整个 dan 家族去掉其中 DanInTheWild 类exclude 优先 garak --spec probes.dan,-probes.dan.DanInTheWild # 家族 tag 过滤 garak --spec probes.grandma,tag:owasp:llm06 # 全部激活探针 全部激活 buff排除 paraphrase buff* 需引号 garak --spec probes.*,buffs.*,-buffs.paraphrase # 全部激活探针 一个特定的未激活类all 是免引号的 * garak --spec probes.all,probes.fitd.FITD # tiers {1,3}tier:3 放行 1..3再 -tier:2 精确移除 tier 2 garak --spec probes.*,tier:3,-tier:2 # 意图探针 单个意图类别intent 是独立轴 garak --spec probes.grandma.GrandmaIntent,intent:S004配置文件形态YAMLrun: spec: include: - probes.latentinjection exclude: []{ run: { spec: { include: [probes.latentinjection], exclude: [] } } }九、延伸阅读语法层详解docs/source/_spec.rstrun.spec选择语法、Spec/Selector数据结构用户视角配置指南docs/source/configurable.rst配置层级、run.spec全部选择器说明与示例意图选择轴docs/source/cas.rstintent:的 typology 展开与 IntentService核心实现garak/_selection.py、garak/_spec.py插件注册表状态来源garak/_plugins.pyenumerate_plugins、plugin_infotier 枚举定义garak/probes/_tier.pyCLI 集成点garak/cli.py 与 garak/cli.py迁移工具garak/resources/fixer/20260612_run_spec.py探测器适配器garak/_config.py理解run.spec的解析机制意味着你不仅能精确控制跑什么、不跑什么还能在解析结果为空的场景下从empty_reason的报错中一眼定位是 tier/tag 过滤过严、排除误伤还是插件模块全部 inactive——这让 garak 的大规模批量扫描配置变得可预测、可调试。【免费下载链接】garakthe LLM vulnerability scanner项目地址: https://gitcode.com/GitHub_Trending/ga/garak创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考