跳转至

LLMs Lean on Priors, Not Programming Language Semantics

会议: ICML 2026
arXiv: 2510.03415
代码: https://EngineeringSoftware.github.io/PLSemanticsBench (有)
领域: 可解释性 / LLM 代码推理 / 形式语义评测
关键词: 形式语义, 程序执行, 规则条件化, 语义扰动, 代码理解

一句话总结

作者构建 PLSemanticsBench——把一个 featherweight C 语言 \(\text{C}^{\star}\) 配上 small-step 操作语义 \(\mathbb{S}\) 与 K 语义 \(\mathbb{K}\) 两套形式系统,并通过 KeywordSwap(互换 +/- 等运算符语义)与 KeywordObf(替换为 Caucasian-Albanian 罕用符号)系统性扰动语义,测了 11 个前沿 LLM 后发现:标准语义下最高 90% 的最终状态预测准确率,在语义扰动下骤降 40–60 个百分点,长程规则保持准确率最高只有 35%,说明当代 LLM 主要靠预训练词法先验、而不是真的在按显式形式规则做推理。

研究背景与动机

领域现状:评测 LLM 代码能力主流走两条路——一是输出预测 / 程序修复 / 代码生成这类端到端基准(HumanEval、MBPP、CodeContests),二是看 chain-of-thought 上的"逐步执行"模仿。两类基准默认模型遇到的语言是它在预训练里见过的,符号含义和大众约定一致。

现有痛点:这种设定无法区分两种截然不同的能力——(a) 模型真的在按提供的规则做形式推理;(b) 模型只是把熟悉的符号(+whileif)映射到预训练里学到的统计联想,然后猜一个看起来合理的答案。在 (b) 占主导时,分数再高也不代表模型理解形式语义。

核心矛盾:要剥离"语义条件化"和"语法熟悉度",必须在保留语法表象的前提下系统性改写语义——这恰好是形式语义学(结构化操作语义、K 语义)的天然能力,因为规则是符号化的、原子粒度均匀的,可以直接换掉 + 对应的 E-Add 规则而不动语法树。但已有 LLM 代码评测从未把这个工具引进来。

本文目标:(1) 设计能机械性扰动语义的基准;(2) 把"按规则推理"拆成可单独测的四个能力——全局组合 (H1)、无状态变更下挑规则 (H2)、长程保持 (H3)、新语义下服从供给规则 (H4);(3) 系统量化前沿 LLM 在这四个能力上的表现。

切入角度:选 C 而非 Python,避免缩进敏感语法把"恢复块结构"和"语义推理"耦合在一起;featherweight 化语法剔除指针、结构体等噪声;把同一份程序套到 \(\mathbb{S}\)(细粒度,每个原子计算一条规则)和 \(\mathbb{K}\)(粗粒度,重写式语义)两个体系下,控制规则粒度变量。

核心 idea:用"语义可机械替换"作为探针——同一段语法树在 std/swap/obf 下应给出三套不同执行结果,模型若真在按提供的规则推理就该跟着切换答案,反之就是在吃预训练先验。

方法详解

整体框架

PLSemanticsBench 要解决的是"如何把 LLM 的规则推理能力和预训练词法先验拆开测",办法是把一个 featherweight C 语言 \(\text{C}^{\star}\) 配上完整的形式语义规则,再机械性地改写这些规则、看模型是否跟着改答案。整条 pipeline 分四步:先用 EBNF 定义 \(\text{C}^{\star}\) 语法,并用 small-step 操作语义 \(\mathbb{S}\) 与 K 语义 \(\mathbb{K}\) 两套体系各给一份完整规则文本;再造三档结构复杂度的程序——从 LeetCode / HumanEval / MBPP / CodeContests 改写 162 段 Human-Written、用 Qwen2.5-Inst 32B 翻译 165 段 LLM-Translated、用语义感知的 grammar-based fuzzer 生成 165 段 Fuzzer-Generated(cyclomatic 复杂度从 3 涨到 100,trace 长度从 20 涨到 190);然后把标准语义机械替换为两种扰动;最后把"程序 + 规则文本"喂给模型跑三类任务,对照 ANTLR4 / K-framework 跑出的 ground-truth 打分。下图把这条四步 pipeline 画出来,其中"形式语义双轨制""语义扰动""三任务评测"三个节点对应下面三个关键设计:

%%{init: {'flowchart': {'rankSpacing': 24, 'nodeSpacing': 28, 'padding': 6, 'wrappingWidth': 420}}}%%
flowchart TD
    A["featherweight C 语言 C*(EBNF 语法)"] --> B["形式语义双轨制<br/>操作语义 S + K 语义 K,各给完整规则文本"]
    A --> P["三档复杂度程序<br/>Human-Written 162 / LLM-Translated 165 / Fuzzer-Generated 165"]
    B --> C["语义扰动:std / KeywordSwap / KeywordObf<br/>只改'符号→规则'映射,保留语法表象"]
    P --> C
    C --> D["三任务评测<br/>PredState(H1) / PredRule(H2) / PredTrace(H3)"]
    D --> E["对照 ANTLR4 / K-framework ground-truth 打分<br/>+ Δcnd、Δis 两个 delta 指标"]

关键设计

1. 形式语义双轨制:用 \(\mathbb{S}\)\(\mathbb{K}\) 两种规则粒度做对照

为了把"模型分不清细粒度规则"和"模型真的不会执行"区分开,作者让同一套程序在两种规则粒度下各跑一遍。\(\mathbb{S}\) 走 Gentzen 风格的推理规则,把每个原子计算写成一条 transition——例如 E-Add 只负责 \(\langle v_1+v_2,\sigma\rangle \to_E v_3\),左操作数的归约由 E-AddLeftStep 单独管;\(\mathbb{K}\) 则用 rewriting 风格把多个 step 合并成一条粗规则。无论哪套体系,一段程序的执行都被形式化为 \(\llbracket s\rrbracket_\Psi(\sigma_0) = (\sigma_n, \bigoplus_{i,j}[(\sigma_i, r_{i,j})])\),状态序列 \(\sigma_i\) 与规则名序列 \(r_{i,j}\) 被同时记录,构成可机检的 ground-truth。这么设计的好处在 notation 预热实验里就显出来了:模型在 \(\mathbb{S}\) 上混淆"step vs compute"这类近义规则更严重(confusion 集中在 Arithmetic Expression rules 7–23、Relational 28–51),而 \(\mathbb{K}\) 因规则更少混淆更少——于是下游失败能归因到"粒度判别"还是"全局推理",而不会被"看不懂记号"这个无关变量污染。

2. KeywordSwap / KeywordObf:双轴语义扰动制造先验冲突

这是把"语义条件化"从"语法熟悉度"里剥离出来的核心探针。KeywordSwap 保留语法表象但把同族运算符两两互换(+-*/<>&&||),于是源码里写的 x+y 在 swap 语义下应按减法规则执行,直接与预训练先验撞车;KeywordObf 则把 =+-if-elsewhile 等全部替换成 Caucasian-Albanian 字母表里的稀有符号,使 x ⷠ y 等价于标准下的 x + y,彻底抹掉语法先验、只剩下提供的规则可依。两种扰动都只动"符号到规则的映射"、保留 inference rule 的结构。之所以要两轴交叉,是因为单独 swap 测的是"先验能否被新规则覆盖",单独 obf 测的是"完全没有先验时能否按规则执行":若 swap 掉点远大于 obf,说明模型被熟悉符号"绑架";若连 obf 也大幅掉点,则规则跟踪本身就有问题。

3. PredState / PredRule / PredTrace:三任务分别锚定 H1–H3 能力

作者把笼统的"按规则推理"拆成三个能单独打分的子能力。PredState 直接要最终变量表 \(\llbracket\mathcal{P}\rrbracket_\Psi^\sigma(\sigma_0)\),考察全局组合(H1);PredRule 给出一个执行中状态未变化的 expression-step 窗口,让模型从 5 个候选规则里挑对应那条,distractor 按 family→construct→semantic role 分层采样以堵住靠词法启发猜答案的捷径(H2);PredTrace 要求逐步输出整条 \((\sigma_i, r_{i,j})\) 序列,考察长程一致性(H3)。这种拆法是有讲究的:单测 final state 会把"真用规则"和"凭直觉猜"混在一起,而 PredRule 在状态不变的窗口里测,剔除了"用最终值反推规则"的捷径,PredTrace 则强制把 trace 写满才给满分,长程上先验和真规则的偏差会累积放大。为了让对比可量化,作者还定义了两个 delta 指标:\(\Delta_{\text{cnd}} = \mathrm{Acc}_{\text{std}}^{\square} - \mathrm{Acc}_{\text{na}}\) 衡量提供 vs 不提供形式规则的增益,\(\Delta_{\text{is}} = \mathrm{Acc}_{\square'}^{\square} - \mathrm{Acc}_{\text{std}}^{\square}\) 衡量扰动 vs 标准的掉点。

训练策略

本文不训练模型,统一走 zero-shot / one-shot prompting;non-reasoning 模型(除 GPT-4o-mini)温度设为 0,reasoning 模型与 GPT-4o-mini 跑 3 次取平均。CoT 是显式提示的 prompt 变体,作为单独配置出现而非新方法。

实验关键数据

主实验:PredState 在 Human-Written 数据集上 (\(\mathbb{S}\) 形式化)

模型 \(\text{Acc}_{\text{na}}\) \(\text{Acc}_{\text{std}}\) (\(\Delta_{\text{cnd}}\)) \(\text{Acc}_{\text{swap}}\) (\(\Delta_{\text{is}}\)) \(\text{Acc}_{\text{obf}}\) (\(\Delta_{\text{is}}\))
Qwen2.5-Inst 14B 33 28 (-5) 6 (-22) 8 (-20)
Llama-3.3 70B 32 25 (-7) 5 (-20) 12 (-13)
GPT-4o-mini-CoT 68 65 (-3) 3 (-62) 27 (-38)
DS-Qwen 32B 84 95 (+11) 3 (-92) 77 (-18)
DS-Llama 70B 80 89 (+9) 2 (-87) 59 (-30)
QwQ 32B 93 98 (+5) 7 (-91) 86 (-12)
o3-mini 94 100 (+6) 63 (-37) 95 (-5)
GPT-5-mini 100 100 (0) 79 (-21) 99 (-1)
Gemini-2.5-pro 93 99 (+6) 98 (-1) 100 (+1)

复杂度 / 长程消融(PredState 在 Fuzzer-Generated 上 \(\mathbb{S}\) 形式化关键样本)

模型 Human-Written std Fuzzer std Human swap Fuzzer swap
QwQ 32B 98 ~82 7 4
GPT-5-mini 100 95 79 65
Gemini-2.5-pro 99 (近持平) 98 (近持平)
多数 reasoning 模型 80–100 掉 >40 点 已塌 已塌

关键发现

  • CoT 救得了长程、救不了先验:non-reasoning 模型加上 CoT 后 std 下提升近 50 点,但 swap 下增益消失、obf 下只剩 ~40 点;这说明 CoT 帮的是"展开多步执行",没法让模型真的去读新规则。
  • swap >> obf 的不对称性:所有模型几乎都是 swap 掉点远大于 obf(如 DS-Qwen 32B 在 \(\mathbb{S}\) swap 掉 92 点而 obf 只掉 18 点),证实"熟悉符号反而是陷阱"——一旦给陌生符号,模型反倒老老实实查规则。
  • Gemini-2.5-pro 是唯一例外:在 swap 下仍保持 ≥98%,是 11 个模型里唯一展示出"能用规则覆盖先验"的;其它前沿模型(含 GPT-5-mini、o3-mini)都不同程度被先验绑架。
  • 长程一致性几乎为零:PredTrace 任务上只有少数模型拿到非零分,最好的也只有 35%,意味着即便单步规则选对,长 trace 上累积偏差很快崩盘。
  • 结构性瓶颈分两类:多元回归显示,Human-Written 上控制流深度是主要 stressor,LLM-Translated / Fuzzer-Generated 上数据流和程序规模(Halstead Volume、trace 长度)才主导失败——长 trace 上的全局状态追踪是当前模型最薄弱的环节。

亮点与洞察

  • 用形式语义当探针的设计非常巧:传统 perturbation(改变量名、加注释)只能动表象,KeywordSwap 通过保留语法、互换语义直接撞向预训练先验,这是只有形式化语义才能做到的"controlled stress test";该方法论可迁移到任何需要测"规则遵循 vs 模式匹配"的领域,如数学公理系统、自定义 DSL、新合规规则。
  • \(\Delta_{\text{cnd}}\)\(\Delta_{\text{is}}\) 两个 delta 指标的拆分让结论更扎实:单看绝对准确率会被模型基线水平掩盖,两个 delta 同时为正/负才能定性"模型是否真的在条件化",这套度量框架值得借鉴到其它 "rule-following" 类基准。
  • swap >> obf 的不对称发现颠覆直觉:通常以为"陌生符号更难",但本文证明"熟悉符号 + 反常含义"才是最致命的;这暗示在 LLM 评测里,加噪并不总等于加难度,反常语义比陌生符号更能暴露能力上限。
  • K vs S 形式化的对照给了 prompt 工程一个具体启示:粗粒度规则 (\(\mathbb{K}\)) 在 notation 理解上更稳,但在 PredRule 这类细粒度任务上反而提供的信息密度更低;做工具教学时,规则粒度需要和任务粒度匹配。

局限与展望

  • 作者承认:限定在 featherweight C,没有指针、结构体、并发;只在 \(\mathbb{S}\)\(\mathbb{K}\) 两套形式化下测,未覆盖 denotational / axiomatic 等其他语义体系;CoT prompt 模板未做大规模搜索。
  • 结论可能 underestimate 反向:所有模型都用 zero/one-shot,没做规则上的 few-shot 或 fine-tuning;若允许模型在 KeywordSwap 上做哪怕几条示例 ICL,结果可能完全不同,这意味着"LLM 不懂规则"应该读作"LLM 不在 prompt-only 设置下自动按规则推理"。
  • 改进思路:(1) 把规则 fine-tune 进模型权重,看是否能稳定覆盖 swap;(2) 加入"规则一致性奖励"的 RL 训练,专攻长程 PredTrace;(3) 把基准扩展到自定义 DSL(如 SQL 方言、Solidity 升级前后),测真实合规场景下模型能否切换规则。
  • 基准本身的潜在偏差:Fuzzer-Generated 程序虽然控制了结构复杂度,但可能落在自然代码分布之外,导致模型在 Fuzzer 上的 swap 失败更多反映 OOD 鲁棒性而非规则跟踪能力本身。

相关工作与启发

  • vs CRUXEval / LiveCodeBench 等输出预测基准:他们测"标准 Python/C++ 上能否预测输出",本文测"修改语义后能否跟新规则",把"代码理解"分解成正交的两个维度——前者的高分会被本文证明很大程度上是先验复用,二者结合才能完整刻画 LLM 代码能力。
  • vs Counterfactual reasoning benchmarks (Wu et al. 2023 等):counterfactual 主要在自然语言反事实上做,扰动语义粗、不可机械化;本文用形式语义把扰动做成了精确符号替换,ground-truth 可由 K-framework 完全自动验证,更适合作为长期跟踪指标。
  • vs Operator overloading 类研究:本文把 operator overloading 从语言特性升格为评测工具,并配以系统性多复杂度数据集,方法论上比单点案例更可推广。

评分

  • 新颖性: ⭐⭐⭐⭐⭐ 把形式语义作为 LLM 推理探针的设计独特,KeywordSwap/Obf 的对照非常巧。
  • 实验充分度: ⭐⭐⭐⭐⭐ 11 个模型 × 2 形式化 × 3 扰动 × 3 数据集 × 3 任务,配多元回归归因,覆盖罕见地全面。
  • 写作质量: ⭐⭐⭐⭐ 形式化记号密集但 notation primer 与 hypothesis 编号清晰,附录补充充分。
  • 价值: ⭐⭐⭐⭐⭐ 给"LLM 真的会推理吗"提供了可机械验证的回答模板,PLSemanticsBench 有望成为长期跟踪 rule-following 能力的标尺。