网络安全

借助受语法限制的解码,改进小语言模型中的 Bash 生成

Bash 是面向 AI 智能体的最灵活、最强大的接口之一。在正确的系统中,发出 grepcurltar 或 shell 工作流的模型正在生成可执行动作,该动作可以读取文件、更改工作空间、打开网络连接并将工具连接在一起。对于 NVIDIA AI Red Team 而言,这使得命令生成成为一个有用的研究目标。如果可以将较小的语言模型引导到有效的策略感知命令结构中,它们将成为可部署到更广泛环境中的更可靠的代理式工作流组件。

受限解码是一种在自回归语言模型生成中修改采样过程的技术。在每个生成步骤中,模型都会生成正常的 logits,但在选择词元之前,系统会应用语法来更改分布 (通常是通过有效阻止某些词元) 。

PICARD 使用该技术来改进 SQL 生成,AI 红队则将同一概念应用于 Bash,以提升小型模型在命令行任务中的表现。

本文介绍了一种用于生成 Bash 命令语法并在解码过程中应用这些语法的实验性工作流。我们针对 299 项任务测试了 13 个小型语言模型,将平均通过率从 62.5% 提升至 75.2%。其中,Qwen3-0.6B 表现最佳,通过率从 16.7% 提高到 59.2%。

为何选择 Bash

代理式系统越来越多地使用语言模型来生成代码和命令,这些代码和命令由工具、shell、notebook、构建系统和 CI 作业执行。安全挑战不仅在于模型是否“理解”任务。关键在于它是否能够生成语法上有效的动作、确定目标环境的范围,并限制不安全的形式。

Bash 是该问题的紧凑示例:

  • 语法错误不容许,而且风险会随着任务复杂性的增加而增加。
  • 有效的命令在操作上仍然存在危险,例如无超时的网络命令或路径过宽的破坏性命令。
  • Shell 合成乘以状态空间。管道、重定向、命令替换、Heredoc、循环和条件都会改变模型必须发出的内容以及语法的应用方式。
  • 小模型通常知道要调用的根二进制文件,但在精确的语法、参数顺序、引用、控制运算符或终止方面失败。
  • Bash 的表现力和强大功能可能使其成为模型具有适当表现力所需的唯一工具

核心研究问题是:受限解码能否提高小模型 Bash 命令的可靠性,使其在代理式工作流中发挥作用?

生成语法

手动为每个命令编写语法非常困难。Bash 命令包含大量选项标志、别名、可选值、位置参数以及语法变体。而 grammargen 能将结构化的命令信息自动转换为 百灵鸟语法

中间表示捕捉了约束解码所需的部分,例如:

  • 命令名称和别名。
  • 布尔短标志和长标志。
  • 有价值的标志,例如 -A 3--max-count=10.
  • 位置参数,例如路径、模式、单词和整数。
  • 有限制的重复,以保持解码状态的有限。

例如,生成的 grep 语法包括命令级 start 规则、有界选项重复、组合短标志、长标志替代方案、类型化值和共享终端:

start: "grep" (WS grep_opt){0,8} WS WORD (WS PATH){0,5}
grep_opt: "-" /[EFGHILPRTUVZabchilnoqrsvwxz]+/
        | "-e" WS WORD
        | "-f" WS PATH
        | "-m" WS /[0-9]+/
        | "--ignore-case"
        | "--recursive"
        | "--regexp" ("=" | WS) WORD
        | "--file" ("=" | WS) PATH
        | "--max-count" ("=" | WS) /[0-9]+/
WORD: /[^\s|><&;()]{1,200}/

此语法并非旨在证明每个已接受的命令都是安全的。它定义了解码边界,将模型限制为符合语法的词元。然后,可以将策略编码为额外的语法限制,或应用为单独的控件。grammargen 将根据 --help 文档或 JSON 工具模式生成语法。

解码期间应用语法

然后,可利用 ll guidance 将这些语法应用于 llama.cpp 推理。我们的评估重点是比较原生模型性能与使用语法约束解码的“约束重试”模式,并在执行前通过 tree-sitter-bash 对输出进行验证。

如果 tree-sitter 引发错误,我们会将错误作为上下文传递回原生模式,这样我们至少可以获得原生级别的性能。通过这种方式,我们可以提升模型性能,同时仍然只在测试环境中执行一个命令。

例如,在系统提示“使用 openssl 对/workspace/plain.txt 的内容进行 Base64 编码”时,我们期望模型在 openssl 之后使用 base64,但对于 SmolLM2-360M-Instruct,最高对数词元是 2,这将导致语法无效的命令。如下所示,应用 openssl 语法后,我们将得到 base 作为下一个词元 (并通过自回归得到 openssl base64,最终成功完成任务) 。

start: "openssl" WS ssl_command
ssl_command: ssl_enc
           | ssl_dgst
           | ssl_rand
           | ssl_genrsa
           | ssl_req
           | ssl_x509
           | ssl_s_client
           | ssl_version
ssl_enc: ("enc" | "base64" | "aes-256-cbc" | "des3") (WS enc_opt){0,8}
ssl_dgst: ("dgst" | "sha256" | "sha512" | "md5") (WS dgst_opt){0,8}
ssl_rand: "rand" (WS rand_opt){0,8}
ssl_genrsa: "genrsa" (WS genrsa_opt){0,8}
ssl_req: "req" (WS req_opt){0,8}
ssl_x509: "x509" (WS x509_opt){0,8}
ssl_s_client: "s_client" (WS s_client_opt){0,8}
ssl_version: "version" (WS "-a"){0,2}
enc_opt: "-e" | "-d" | "-a" | "-base64"
       | "-aes-256-cbc" | "-aes-128-cbc" | "-des3" | "-des-ede3-cbc"
       | "-in" WS PATH | "-out" WS PATH
       | "-k" WS WORD | "-pass" WS WORD
       | "-salt" | "-nosalt" | "-pbkdf2"
dgst_opt: "-" /[a-z0-9]+/
        | "-out" WS PATH
        | PATH
rand_opt: "-hex" | "-base64" | "-out" WS PATH
        | /[0-9]+/
genrsa_opt: "-out" WS PATH | /[0-9]+/
req_opt: "-new" | "-x509" | "-nodes" | "-newkey" WS WORD
       | "-key" WS PATH | "-out" WS PATH
       | "-subj" WS SQ_STRING | "-days" WS /[0-9]+/
x509_opt: "-in" WS PATH | "-out" WS PATH | "-text" | "-noout"
        | "-dates" | "-subject" | "-issuer" | "-serial"
        | "-fingerprint" | "-inform" WS /[A-Z]+/ | "-outform" WS /[A-Z]+/
s_client_opt: "-connect" WS HOST_PORT
            | "-servername" WS WORD
            | "-showcerts"
            | "-CAfile" WS PATH
            | "-verify_return_error"
            | "-brief"
HOST_PORT: /[a-zA-Z0-9.\-]+:[0-9]+/
WORD: /[^\s|><&;]{1,200}/

同样,如下所示,语法约束解码可以减少常见的小模型失败模式下的提前终止。在本例中,组合语法阻止了管道运算符后面跟着换行符,而模型使用 x 作为 xargs 中的第一个词元。另外,请注意语法 cat 在前 5 个 logits 中的位置,这是一个好兆头,因为管道化为 cat 是一种常见运算。

gguf: SmolLM2-360M-Instruct.Q4_K_M.gguf
task: xargs_01
prompt: "Read filenames from /workspace/files.txt and delete them using xargs and rm"
canonical: cat /workspace/files.txt | xargs rm
assistant prefix: "cat /workspace/files.txt | "
grammar commands: ["cat", "xargs", "pipe"]
legal next tokens after mask: 37
native top logits
rank    token piece                                   logit
1         198 "\n"                                  17.3023
2        1792 " x"                                  16.1400
3         907 " #"                                  12.4901
4          33 "1"                                   12.4090
5         693 "xt"                                  12.3238
grammar-masked top logits
rank    token piece                                   logit
1        1792 " x"                                  16.1400
2         197 "\t"                                   9.6412
3         104 "x"                                    8.5847
4        2644 " cat"                                 7.3603
5         265 " c"                                   5.2345

测量隆升

每个模型都针对相同的 299 项任务进行了评估:

  • 第 1 层:57 个 I/ O 基元任务
  • Tier 2:65 过滤和转换任务
  • 第 3 层:139 项监视和行动任务
  • Tier 4:38 shell 构造任务

结果以通过率的形式报告。表 1 比较了原生解码与采用 tree-sitter 重试的约束解码。

模型 原生通过
(总共 299 个)
速率 受限通过
(总共 299 个)
速率 提升
Qwen3-0.6 B 50 16.7% 177 59.2% + 42.5 分
SmolLM2-360M-Instruct 88 29.4% 173 57.2% + 27.8 分
Qwen2.5-0.5 B-Instruct 133 44.5% 2050 68.6% + 24.1 分
Qwen3.5-0.8B 158 52.8% 200 66.9% + 14.0 分
gemma-3n-E2Bit 190 63.5% 227 75.9% + 12.4 分
SmolLM3-3B 208 69.2% 236 78.9% + 9.7 分
gemma-4 -E2Bit 212 71.2% 241 80.6% + 9.4 分
Nemotron-3 -Nano-4B 224 80.9% 264 88.3% + 7.4 分
Phi-4-mini-instruct 225 75.3% 430 81.3% + 6.0 分
Qwen3-1.7 B 214 71.6% 229 76.6% + 5.0 分
Qwen3-4B 224 78.3% 247 82.6% + 4.3 分
Qwen3.5-4B 252 84.3% 158 86.3% + 2.0 分
Qwen2.5-3B-Instruct 224 74.6% 226 75.6% + 1.0 分
表 1. 模型性能和受限解码带来的提升

在所有 13 个模型中,约束重试将平均通过率从 62.5% 提高到 75.2%。每个模型总体上都有改进,但对于最小和最弱的基准,收益最大,如图 1 所示。表 2 中的层级平均值显示了语法最有用的部分:

层级 原生平均值 受限重试平均值 平均提升幅度
第 1 层:I/ O 基元 79.8% 89.7% + 10.0 分
第 2 层:过滤/ 转换 55.9% 72.5% + 17.4 分
第 3 层:Recon/action 56.9% 72.2% + 15.3 分
第 4 层:外壳结构 69.4% 69.0% -0.4 分
表 2. 模型性能并摆脱受任务层复杂性限制的解码限制

第 4 层涉及需要组合命令语法的链式、背景和循环等任务。最终,受限的生成要么过于严格,要么过于宽松,没有什么帮助。

图 2 显示了任务和模型提升与回归的对比情况。在 3887 个配对模型任务结果中,约束重试保留了 2248 个原生通道,修复了 676 个原生故障,回归了 81 个原生通道,并留下了 782 个未解决的故障。

换言之,语法路径在整个运行过程中通过任务产生了 495 项的净增,但在存在多种完成任务的方法时,由于语法与模型偏差冲突,或者语法不完整会破坏模型的原生能力,因此确实出现了一些回归。

该语法可在第 1-3 层中恢复许多命令语法和表面形式故障。如果采用更丰富的 Bash 结构 (例如多行脚本、heredocs、循环、条件语句、命令替换和进程替换) ,第 4 层则更加困难,而这些结构需要更丰富的语法或可以选择性地回退到本地生成的策略。

改进之处

当模型已经具有正确的意图,但可能会偏离语法时,语法会提供最大帮助。它改进了命令名称和标志、类型值的选择以及回合结束处理。

Tree-sitter 捕获和重试添加了第二层。即使约束解码因语法差距或截断而导致 Bash 格式错误,评估器也可以在执行前检测语法错误,并请求更正的原生输出 (包括解析错误) 。这可能只是一层纠错,具体取决于系统的限制。

安全影响

受限解码会在执行前更改模型输出的概率分布。这使得它很有用,但只能作为代理式 AI 控制堆栈中的一层。有趣的安全属性包括:

  • 可靠性是一种安全属性。限制动作表面可以降低动作空间的不确定性。
  • 策略编码为语法. 语法可以禁止或要求形式和参数,例如排除不安全的标志或要求超时。

这项研究还强调了一个局限性。生成的语法描述的是命令接受的内容,而不是特定模型正确使用的内容。对于 curl 等宽泛的命令,从帮助文本中生成的语法可能允许使用数百个法律标志。这在语法上是准确的,但过于宽松,无法有意义地提高可靠性或执行 tradecraft。

这指向已学习或策略优化的语法。学习的语法不会接受整个法律命令空间,而是可以对给定模型可靠的子集进行编码,以及严格的安全规则,例如仅支持 HTTPS 的 URL、强制超时或不允许的破坏性标志。

建议

对于尝试语法约束生成的团队:

  1. 从狭义基准测试开始。在更改语法之前,根据相同的提示测量原生输出和受限输出。
  2. 在结构上和行为上验证语法。语法应解析、接受已知的有效命令并拒绝已知的无效示例。
  3. 追踪回归,而不仅仅是提升。虽然我们展示了性能的净提升,但我们的结果表明,当语法无法表达预期结构时,约束解码可能会与模型发生对抗。
  4. 将语法成功与任务成功区分开来。语法上有效的命令仍然可能存在语义错误或操作上不安全。

开始使用

对于生成 Bash 的代理而言,受语法限制的解码是一种很有前景的控制手段,尤其是在与基于执行的评估和语法验证搭配使用时。在我们的实验中,约束式重试将 13 个模型的平均通过率从 62.5% 提高到 75.2%,其中 Qwen3-0.6 B 上最大的单模型增益实现了接近其大小两倍的模型的最终任务成功。结果还表明,在较丰富的 shell 结构和合成中,语法限制仍然难以适用。

要在您自己的代理式系统中应用这些理念,可将受语法限制的解码视为更广泛的 NVIDIA AI 堆栈中的一种控制机制。找出在您的任务中表现优异的小型模型,例如 NVIDIA Nemotron 3 Nano,并通过受限解码来增强该模型的能力。

要强化系统,可使用 NVIDIA NeMo Guardrails 对可编程提示、响应和代理安全检查进行评估。实际采用的是纵深防御模式:通过约束动作语法,利用 Brev 等工具在隔离环境中执行沙盒操作,测量从原生到受限状态的过渡过程,并仅推广那些能提升可靠性且不掩盖剩余执行风险的控制措施。

如需更多 AI 安全研究和指导,请关注 NVIDIA AI Red Team

标签