← 返回 2026-02-10

CodeCircuit:通过归因图推断大语言模型生成代码的正确性 CodeCircuit: Toward Inferring LLM-Generated Code Correctness via Attribution Graphs

Yicheng He, Zheng Zhao, Zhou Kaiyu, Bryan Dai, Jie Fu, Yonghui Yang 📅 2026-02-06 👍 6 2026-07-13 08:35
代码生成 代码验证 归因图 机制可解释性 稀疏自编码器

利用机制可解释性,从LLM内部归因图的拓扑结构推断生成代码的正确性,无需外部执行验证。

前置知识

Attribution Graph(归因图)

归因图是一种对 Transformer 模型计算过程进行因果线性分解的工具。它将模型的一次前向传播表示为一个有向无环图 G=(V,E),其中节点 V 包括激活的稀疏自编码器特征、token 嵌入、误差项和输出 logits;边 $w_{ij}$ 表示节点 $i$ 的激活对节点 $j$ 预激活值的线性贡献,定义为 $w_{ij} = a_i v_{\text{in},j}^\top J_{ij} v_{\text{out},i}$,其中 $J_{ij}$ 是冻结残差流变换的 Jacobian 矩阵。归因图的关键在于它基于局部替换模型(Local Replacement Model),将标准 MLP 层替换为逐层转码器(Per-Layer Transcoders, PLTs),从而将密集激活空间中的叠加特征解缠为单义性(monosemantic)特征,使得信息流可以通过可解释的特征进行追踪。

归因图是本文方法的技术基石。只有理解了归因图如何将模型的黑箱计算转化为可解释的因果图,才能理解后续的拓扑特征提取和正确性诊断是如何工作的。

Per-Layer Transcoder(逐层转码器, PLT)

逐层转码器是一种特殊形式的稀疏自编码器,它不作用于整个模型的激活,而是逐层替换标准 MLP。对于第 $l$ 层,PLT 通过编码器 $W_{\text{enc}}$ 和非线性激活函数 $\sigma$(如 TopK 或 ReLU)将残差流 $x^{(l)}$ 映射为稀疏特征向量 $f^{(l)} = \sigma(W_{\text{enc}} x^{(l)} + b_{\text{enc}}^{(l)})$,再通过解码器 $W_{\text{dec}}$ 重建 MLP 输出 $\hat{m}^{(l)} = W_{\text{dec}} f^{(l)} + b_{\text{dec}}^{(l)}$。与标准自编码器不同,PLT 的目标是重建局部 MLP 输出而非整个残差流,这使得每个特征维度对应离散的算法原语。本文使用的转码器来自 GemmaScope 项目,专为 Gemma-2 系列模型训练。

PLT 是将模型内部的叠加(superposition)特征解缠为可解释单义特征的关键工具。没有 PLT,归因图中的节点就无法对应有意义的算法概念,整个方法的可解释性基础就不成立。

Graph Topological Features(图拓扑特征)

图拓扑特征是从归因图的结构中提取的统计量,用于刻画模型内部计算轨迹的形态。主要分为三类:(1)高层统计特征,如活跃特征数量、误差节点数、top-1 logit 概率和熵;(2)节点与激活统计,如误差影响比 $\eta_i$(误差节点总影响与特征节点总影响的比值)、平均激活值、最大激活值等;(3)图结构特征,如图密度 $\rho_i = |E_i|/(|V_i|(|V_i|-1))$、连通分量数、平均聚类系数 $C_i$、介数中心性 $C_B(v)$、度中心性以及最短路径距离等。这些特征共同构成一个固定维度的诊断向量 $x_i$,用于下游分类。

这些拓扑特征是连接归因图与正确性预测的桥梁。论文的核心发现就是:正确和错误的代码在这些拓扑特征上存在系统性差异,这使得我们可以用一个简单的分类器(如 GBDT)从图结构推断代码正确性。

Causal Intervention(因果干预)

因果干预是一种机制可解释性技术,通过主动修改模型内部特定节点的激活值来验证特征与输出之间的因果关系。在本文中,作者对归因图中特定的转码器特征节点执行激活钳制(activation clamping):将导致错误的特征激活置零(抑制),或增强与正确行为相关但激活不足的特征。如果干预后模型生成了正确的代码,则证明被干预的特征与错误之间存在因果关系,而非仅仅是相关关系。这借鉴了 Meng et al. (2022, 2023) 提出的知识编辑方法。

因果干预将本文方法从被动的相关性分析提升为因果验证。它证明了归因图中的拓扑异常不是噪声或巧合,而是驱动代码生成错误的真实原因,从而为'机制性调试'(mechanistic debugging)提供了概念验证。

GBDT(梯度提升决策树)

梯度提升决策树是一种集成学习方法,通过顺序训练多棵决策树来逐步修正前一棵树的预测误差。每棵新树拟合当前模型的残差(负梯度),最终预测为所有树的加权和。本文使用 GBDT 作为诊断分类器 $h_\phi$,将归因图提取的拓扑特征向量 $x_i \in \mathbb{R}^d$ 映射为正确性概率 $\hat{y}_i = P(y_i=1|G_i; \phi)$。选择 GBDT 而非深度学习分类器的原因是提取的特征具有异构性和表格化特性,GBDT 在此类数据上通常表现优异,且能提供特征重要性排名,便于诊断导致错误的结构性原因。

GBDT 不仅是分类工具,更是诊断工具。通过分析哪些拓扑特征对错误预测的贡献最大,可以定位逻辑错误的机械性原因——例如,高误差影响比配合低介数中心性意味着模型缺乏可解释的状态维护能力。

研究动机

当前大语言模型生成代码的验证范式严重依赖外部机制,面临三大瓶颈。第一,基于执行的单元测试验证虽然直观,但需要大量人工编写测试用例,且难以覆盖所有边界条件和稀有输入场景。例如,在实际的软件工程流程中,测试覆盖率往往无法达到 100%,这意味着逻辑错误可能在部署后才被发现。第二,新兴的 LLM-as-a-Judge 框架虽然提供了可扩展的自动化验证方案(如 Jiang et al., 2025; He et al., 2025a),但引入了额外的推理开销,且面临递归依赖问题——判断模型自身的可靠性受限于评估模型自身的推理先验,形成了'谁来监督监督者'的困境。第三,所有这些外部方法都将模型视为黑箱,无法揭示代码错误的根本原因,只能告诉我们'代码错了'而无法解释'为什么错了'。在 LLM 被越来越多地部署到关键任务工作流中的背景下,这种被动的、仅关注输出结果的验证方式已无法满足对代码可靠性的严苛要求。

本文的目标是本文的核心目标是回答一个尚未被探索的根本性问题:大语言模型生成代码的功能正确性,能否仅从模型的内部计算结构中推断出来,而不需要执行代码、运行测试或依赖外部评判模型?具体而言,作者希望通过分析模型内部的归因图拓扑,建立一个白盒验证框架 CodeCircuit,使得:(1)能够准确区分正确和错误的代码生成步骤,AUROC 显著优于现有的黑盒和灰盒方法;(2)识别出的正确性信号在多种编程语言(Python、C++、Java)之间具有泛化性;(3)通过因果干预证明这些信号不仅是相关的,而且是因果性的——即可以通过修改内部电路来修复错误代码。

与已有工作不同的是,本文的独特切入点在于将代码验证从'观察输出'重新定义为'审计过程'。此前,虽然有研究探索了 LLM 生成过程中的神经元激活和表示动态(Bui et al., 2025; Huang et al., 2025; Patel et al., 2025),但这些分析都没有直接与生成代码的正确性建立联系。在机制可解释性领域,归因图已在推理任务中展示了追踪推理路径的能力(Zhao et al., 2025; Ameisen et al., 2025),但尚未扩展到代码领域。本文抓住了一个关键观察:与自然语言或抽象数学不同,代码具有刚性的、客观的拓扑结构(如控制流、变量绑定),而 LLM 可能在内部形成有限状态自动机(Zhang et al., 2025)。因此,作者假设正确的代码生成要求模型实例化特定的神经电路来镜像这种拓扑,而错误则表现为这些电路中可检测的结构性崩溃。这一假设将代码验证从黑箱输出观察转变为内部过程审计。

核心方法

CodeCircuit 的核心思路可以用一个医学类比来理解:传统验证方法就像根据病人的外在症状(输出结果)来诊断疾病,而 CodeCircuit 则像通过 MRI 扫描(归因图)直接观察器官(模型内部电路)的结构异常。技术路线分为三个阶段:第一阶段是'成像'——利用逐层转码器(PLT)将模型的标准 MLP 层替换为稀疏可解释特征,构建每一行代码对应的归因图,将高维神经激活投影到可解释的特征基上;第二阶段是'提取影像特征'——从归因图中系统提取多层次的拓扑特征,包括电路的机械组成(误差影响比、特征数量)、全局结构(图密度、连通分量)、信息流模式(介数中心性、度中心性)以及归因动态(总 logit 归因分数);第三阶段是'诊断'——训练一个 GBDT 分类器将拓扑特征映射为正确性概率,并通过特征重要性分析定位错误的机械性原因。整个框架将代码验证从传统的后执行测试转变为对模型潜在计算完整性的前瞻性机制审计。

本文最核心的创新在于提出了一个根本性假设并通过实验验证了它:代码正确性是内嵌于模型潜在计算轨迹中的固有属性,而非仅由输出决定。具体来说,正确和错误的代码生成步骤在归因图的拓扑结构上表现出系统性的、可区分的差异——这些差异体现在图的节点组成、信息流集中度、连通性和关键路径等多个维度上。与现有方法的本质区别在于:黑盒方法(如 Temperature Scaling、MaxProb)仅依赖最终 token 的概率分布,忽略了生成过程中的计算轨迹;灰盒方法(如 Chain-of-Embedding)虽然关注中间层表示,但未建立从内部结构到正确性的因果映射;而 CodeCircuit 首次将机制可解释性中的归因图技术应用于代码验证,不仅发现了内部结构与正确性的相关性,还通过因果干预(激活钳制)证明了这种关系是因果性的——抑制特定的'贪婪匹配'特征可以直接修复二分搜索中的边界条件错误。

方法步骤详情

CodeCircuit 方法的完整流程如下。第一步,代码生成与分解:给定自然语言规范 $S$,使用 LLM 生成代码片段 $C$,然后将代码分解为 $N$ 个离散逻辑步骤(即代码行)$C = \{s_1, s_2, \ldots, s_N\}$,对每个步骤 $s_i$ 预测正确性标签 $y_i \in \{0, 1\}$。第二步,构建归因图:对每个代码步骤 $s_i$,使用 Circuit-Tracer 库构建归因图 $G_i = (V_i, E_i)$。具体操作是用逐层转码器替换标准 MLP 层,通过编码器将残差流映射为稀疏特征,然后计算从每个特征节点到输出 logits 的线性贡献。为控制图规模,使用基于影响的剪枝策略(节点阈值 0.8),保留最重要的特征节点(最多 8192 个),并显式包含误差节点 $e^{(l)} = m^{(l)} - (W_{\text{dec}} f^{(l)} + b_{\text{dec}}^{(l)})$ 来量化未解释计算的影响。第三步,特征提取:从剪枝后的归因图中提取固定维度的特征向量 $x_i = \Phi(G_i)$,包含高层统计(特征数量、熵)、聚合节点统计(误差影响比 $\eta_i$、激活统计)和图拓扑特征(图密度、介数中心性、聚类系数、最短路径距离)。第四步,诊断分类:使用 GBDT 分类器 $h_\phi$ 将拓扑特征向量映射为正确性概率 $\hat{y}_i = P(y_i = 1 | G_i; \phi)$,通过最小化交叉熵损失 $\mathcal{L}(\phi) = -\sum_i [y_i \log(\hat{y}_i) + (1-y_i) \log(1-\hat{y}_i)]$ 进行训练。第五步,特征重要性分析与因果干预:通过 GBDT 的特征重要性排名定位导致错误的结构性原因,并对归因图中的关键节点执行激活钳制来验证因果关系。

技术新颖性

CodeCircuit 的技术新颖性体现在三个层面。首先,在问题定义层面,它首次将代码验证形式化为'机制性序列标注问题'——不是验证整个代码片段的执行结果,而是逐行诊断模型内部计算轨迹的结构完整性。这种粒度的验证能够精确定位错误发生的具体代码行和对应的内部电路。其次,在方法论层面,它将归因图技术从自然语言推理领域(Zhao et al., 2025)扩展到代码生成领域,并引入了代码特有的拓扑特征(如变量绑定的上下文特征、从输入 token 节点到 logit 节点的最短路径距离),这些特征直接反映了代码的控制流和状态追踪结构。第三,在验证范式层面,它从被动的相关性分析跨越到因果验证——通过抑制归因图中的特定特征节点来实时修复代码错误,实现了'潜在调试'(latent debugging)的概念验证,即不通过修改源代码而是通过调制生成源代码的神经电路来修复软件缺陷。这为代码生成的可靠性保障开辟了一条全新的白盒路径。

CodeCircuit 框架概览
Figure 2: CodeCircuit 框架概览

实验结果

实验结果全面验证了 CodeCircuit 的有效性。在核心性能方面(RQ1),CodeCircuit 在 Python 上达到了 79.89 的 AUROC,相比最佳黑盒方法(Entropy,51.42)和灰盒方法(CoT-Kinetics,51.41)提升了约 28 个百分点;在 Java 上达到 68.06 AUROC(对比 MaxProb 的 56.29),在 C++ 上达到 72.21 AUROC(对比 CoE-R 的 59.07)。AUPR 指标同样显著领先,Python 上 54.77(对比 MaxProb 的 29.08),Java 上 56.83(对比 MaxProb 的 46.00),C++ 上 64.55(对比 CoE-C 的 37.72)。FPR@95(在 95% 召回率下的误报率)从基线方法的 89-97% 降至 77-80%,表明 CodeCircuit 在保持高召回的同时大幅减少了误报。在跨语言泛化方面(RQ2),在 Python 上训练的 probe 在 Java 测试集上达到 62.15 AUROC,在 C++ 上达到 57.10,均优于各自语言的 MaxProb 基线;Java 训练的 probe 在 C++ 上达到 59.33,在 Python 上达到 55.42,说明内部正确性信号包含跨语言的通用逻辑结构。在可扩展性方面(RQ3),随着代码行数从 10 增加到 20 再到 30,CodeCircuit 的 AUROC 从约 80 提升到近 92,而基线方法保持平坦,表明更长的序列为白盒方法提供了更丰富的内部激活信息。在因果干预方面(RQ5),针对二分搜索中 `high = mid`(应为 `high = mid - 1`)的经典边界错误,通过置零归因图中对应的'贪婪匹配'特征,模型成功生成了正确代码,证明了内部信号的因果性。

不同编程语言和基线方法的性能对比
Table 1: 不同编程语言和基线方法的性能对比
跨编程语言测试集上的方法性能对比
Table 2: 跨编程语言测试集上的方法性能对比
因果干预前后的代码推理对比
Table 3: 因果干预前后的代码推理对比
MBPP 增量数据集的统计信息
Table 4: MBPP 增量数据集的统计信息
代码生成错误的拓扑指纹
Figure 1: 代码生成错误的拓扑指纹
不同难度 Python 任务上的预测器性能
Figure 3: 不同难度 Python 任务上的预测器性能
不同编程语言中正确(蓝色)和错误(红色)代码生成的 PCA 特征分布
Figure 4: 不同编程语言中正确(蓝色)和错误(红色)代码生成的 PCA 特征分布
查看结构化数据
任务指标本文基线提升
Python 代码正确性检测 AUROC 79.89 51.42 (Entropy) +28.47 绝对提升
Python 代码正确性检测 AUPR 54.77 29.08 (MaxProb) +25.69 绝对提升
Java 代码正确性检测 AUROC 68.06 56.29 (MaxProb) +11.77 绝对提升
Java 代码正确性检测 AUPR 56.83 46.00 (MaxProb) +10.83 绝对提升
C++ 代码正确性检测 AUROC 72.21 59.07 (CoE-R) +13.14 绝对提升
C++ 代码正确性检测 AUPR 64.55 37.72 (CoE-C) +26.83 绝对提升
跨语言泛化(Python→Java) AUROC 62.15 56.29 (MaxProb-Java) +5.86 绝对提升
跨语言泛化(Java→C++) AUROC 59.33 54.06 (MaxProb-C++) +5.27 绝对提升
可扩展性(30行代码) AUROC ~92 ~51 (MaxProb) +41 绝对提升

局限与改进

本文存在几个显著的局限性。首先是计算开销问题,这是作者在附录中明确承认的主要瓶颈。构建归因图需要基于梯度的反向传播和特征投影,时间和内存复杂度远高于标准推理。对于大规模代码生成任务,这一要求可能限制实时部署。论文中,每个输入 prompt 的归因图在单 GPU 上以 bfloat16 精度计算,保留最多 8192 个特征节点,且跳过超过 550 字符的 prompt 以避免过长上下文,这说明当前方法对输入长度有硬性限制。其次,实验仅在相对较小的模型 Gemma-2-2B-IT 上进行验证,对于更大规模模型(如 70B、100B+)是否同样有效尚不清楚——虽然理论上归因图技术可以扩展到更大模型,但计算成本会急剧增加。第三,数据集规模有限,Python 仅 1447 个实例、Java 3126 个、C++ 3423 个,且任务来源于 MBPP 数据集的翻译版本,属于相对简单的编程问题,对于复杂工程代码(如涉及多文件依赖、并发、复杂数据结构)的验证能力未经测试。第四,代码正确性标签由 GPT-4o 生成,而非人工标注,这意味着评估标签本身可能存在噪声。第五,方法目前仅在逐行粒度上进行验证,对于需要理解多行交互(如循环不变量、递归终止条件)的复杂逻辑错误,单行分析可能不够充分。

独立分析的弱点

从独立分析的角度,CodeCircuit 存在几个值得改进的弱点。第一,归因图的计算成本是最大的工程瓶颈。当前方法需要对每一行代码都构建完整的归因图,这意味着一个 30 行的代码片段需要 30 次完整的归因图计算。改进方向可以是开发稀疏归因技术,仅针对关键逻辑节点(如条件分支、循环头)进行精确追踪,其余节点使用轻量级近似;或者利用低秩近似来降低 Jacobian 矩阵的计算成本。第二,误差影响比 $\eta_i$ 作为核心特征之一,其解释力可能受限于 PLT 的重建质量——如果 PLT 本身对某些特征的重建误差较大,$\eta_i$ 可能反映的是 PLT 的局限性而非模型逻辑的不确定性。改进方向是使用多个不同训练配置的 PLT 集成,通过交叉验证来提高特征的鲁棒性。第三,GBDT 分类器虽然提供了可解释的特征重要性,但其判别能力可能受限于手动设计的拓扑特征集。一个有前景的改进方向是开发图神经网络(GNN)直接在归因图上学习表示,可能捕获更高阶的拓扑模式。第四,当前方法仅关注代码行级别的正确性,缺乏对代码整体逻辑一致性的建模——例如,一个循环的第 5 行和第 10 行可能各自看起来正确,但组合在一起可能违反循环不变量。改进方向是引入序列级的图聚合机制,将多行归因图联合分析。

未来方向

作者在论文中指出,计算开销是当前的主要瓶颈,未来研究可以聚焦于稀疏归因技术和低秩近似来降低成本,从穷举式电路追踪转向选择性结构诊断。基于本文的成果,可以从以下方向延伸:(1)将 CodeCircuit 扩展到更大规模的代码模型(如 DeepSeek-Coder、CodeLlama 等),研究模型规模与内部正确性信号强度的关系;(2)将框架从代码生成扩展到代码理解和维护任务,如 bug 定位、代码审查和安全漏洞检测;(3)将因果干预从概念验证发展为实用的'神经调试器'——当检测到内部电路异常时,自动建议修复策略,形成一个闭环的白盒代码保障系统;(4)研究不同训练数据(如高质量代码 vs. 低质量代码)如何影响模型内部电路的组织方式,从而为模型训练提供可解释性的指导;(5)探索归因图拓扑特征作为代码复杂度和可维护性的新型度量,为软件工程提供新视角。

复现评估

本文的复现条件相对友好。代码已在 GitHub 开源(https://github.com/bruno686/CodeCircuit)。核心依赖包括:(1)基座模型 Gemma-2-2B-IT,可从 HuggingFace 免费下载;(2)GemmaScope 转码器,同样在 HuggingFace 上公开提供(mntss/gemma-scope-transcoders);(3)Circuit-Tracer 库(Hanna et al., 2025),用于归因图计算,开源可用。数据集方面,Python 使用标准 MBPP 数据集,C++ 和 Java 的语义等价任务集通过翻译 MBPP 规范并使用 Gemma-2-2B-IT 生成解决方案来构建。计算资源方面,所有实验在单 GPU 上以 bfloat16 精度运行,这意味着一块中高端 GPU(如 A100 40GB 或 RTX 4090)即可完成实验。总体复现难度中等——虽然依赖项都是公开可用的,但归因图的计算和特征提取涉及较多工程细节(如剪枝阈值设置、特征向量构造),需要对机制可解释性工具有一定的熟悉度。