AI编程助手已经知道该删哪些代码了

这项由上海交通大学LLM4SE实验室联合字节跳动(抖音集团)共同完成的研究,以预印本形式发布于2026年7月20日,论文编号为arXiv:2607.18213v1,有兴趣深入了解的读者可通过该编号查询完整论文。

**当AI助手开始"刷屏"时,麻烦来了**

假设你雇了一位非常聪明的编程助手,每次你问他问题,他都会翻遍整个代码仓库,然后把所有看过的页面一张不落地放在桌子上。时间一长,桌子堆满了纸,其中95%的内容你们早就不需要再看了,但它们就这么占着地方。这位助手不得不在这堆纸里找来找去,效率越来越低,回答也越来越离谱——他在"找不到重点"的困境中越陷越深。

这不是一个比喻故事,而是当今几乎所有AI编程代理(coding agent,可以理解为能自动帮你写代码、修bug的AI助手)面临的真实困境。每次AI调用"查看文件""搜索代码""运行测试"等工具时,工具返回的原始输出——有时长达数百行——就会被原封不动地追加进AI的"记忆"里。随着对话轮次增加,这些记忆越堆越厚,大量重复的、再也不会被引用的内容挤占着有限的空间,不仅让API调用成本飙升,还会导致AI在超长上下文中"迷失方向",答非所问。

研究团队在SWE-Bench Verified这个标准测试集上统计发现,一个典型的AI编程代理在解决代码问题时,光是"读文件"这一类工具调用产生的输出,就占据了整个对话70%以上的token(可以理解为AI处理文字的基本单位,越多越费钱、越难处理)。这个数字触目惊心,也正是这项新研究希望解决的核心痛点。

研究团队将他们的方案命名为**SWE-Pruner Pro**。这个名字中的"Pruner"意为"修剪者",整个系统的核心思路是:在AI助手把工具输出"记入记忆"之前,先帮它把那些没用的行删掉,只留下真正有价值的内容。但真正让这项研究与众不同的,是他们为AI"修剪"内容的方式——他们不依赖任何外部评判,而是让AI助手用自己的内心感受来决定什么该留、什么该删。

---

**一、那堆"记忆残片"里,AI其实已经知道该扔掉什么**

在解释SWE-Pruner Pro到底做了什么之前,有必要先理解它背后最关键的一个发现——而这个发现,才是整篇论文真正令人拍案叫绝的地方。

当AI助手读取一段工具输出时,它的内部会发生一件复杂的事情:它会通过数以百亿计的神经网络参数,把每一个词、每一行代码都转化成一种叫做"隐层表示"(hidden representation)的东西——可以把它理解成AI大脑对这段内容的"内心感受",一个多维度的数字向量,编码了这段内容在当前任务背景下的种种意义。

研究团队提出了一个问题:当AI读完一段工具输出后,它的"内心感受"里,有没有隐含着"这行重要"或"这行没用"的判断?

为了验证这个猜想,他们收集了大约2260次多轮工具对话记录,涉及约15.5万行代码和文本,让Claude Sonnet 4.6(一个强大的AI模型)为每一行打上"保留"或"删除"的标签。然后,他们冻结了另一个AI模型(Qwen3-Coder-Next)的参数,让这个模型读取这些工具输出,提取每行内容对应的最后一层隐层表示,再用一个极其简单的线性分类器(逻辑回归,就是大学统计课上最基础的那种分类方法)来尝试区分"该保留的行"和"该删除的行"。

结果非常清晰:两类行在AI的内心感受空间里,确实有着可以被线性区分的差异。沿着"最能区分两类"的方向投影,保留行和删除行的分布有明显的中心偏移。这个简单的线性分类器在测试集上达到了AUC 0.83、最优F1值0.63——对于一个从未专门训练过"判断重要性"任务的冻结模型来说,这是非常强的信号,远远超过了纯粹猜测的上限(如果只猜"删除",F1上限约为0.46)。

换句话说,AI在被动阅读工具输出的过程中,已经在潜移默化地形成了"哪行重要"的内部判断。这些判断没有被显式地表达出来,但它们真实地编码在了隐层表示里。就像一个心不在焉翻阅报纸的人,即便没有刻意标注,他的眼睛其实已经知道哪段新闻值得细读、哪段可以略过——只是他没有把这种直觉写下来。

然而,线性分类器存在一个明显的瓶颈:在"中间地带"(既不明显重要也不明显无用的行)上,两类行的分布高度重叠,简单的线性边界无法有效区分。此外,一段代码该保留多少行,还跟这段代码本身有多长密切相关——一个5行的小函数,删掉2行是灾难;一个300行的类定义,删掉50行可能无关痛痒。这两个局限性,共同催生了SWE-Pruner Pro的具体设计。

---

**二、轻量级"翻译头":把内心感受变成删除决定**

SWE-Pruner Pro的整体运作方式,可以用一个图书馆管理员的比喻来理解。

每次AI助手从工具那里收到一份返回结果(比如"cat命令"读取了某个文件的全部内容),管理员会悄悄地站在AI身边,观察AI在读这份材料时脸上的表情变化——哪段让AI眼睛一亮,哪段让AI面无表情地快速翻过。管理员把这些表情信息记录下来,转化成一份"删除清单",然后在AI把这份材料归档进记忆之前,悄悄地按照清单撕掉那些无用的页面,只留下真正有价值的部分。

具体来说,当AI助手在第t轮对话中发出工具调用、收到原始返回结果之后,按照正常流程,AI的主体模型(冻结的大型语言模型骨干)会对这段新内容做一次"预填充"(prefill)——可以理解为大脑快速扫描并处理新信息的过程。在这个扫描过程中,每个token都会被转化为一个隐层表示向量。SWE-Pruner Pro就在这个时刻介入:它"蹭"着这次本就要发生的扫描,顺手读取每个token的最后一层隐层表示,然后把这些表示送进一个轻量级的"判断头"(pruning head)。

这个判断头由两个核心部件组成,共同决定每行内容的命运。

第一个部件叫**长度感知嵌入**(length-aware embedding)。它的作用是让判断头知道当前这段工具输出总共有多少行。研究团队把行数按照对数尺度划分成8个区间(比如:0-2行、3-5行、6-10行……超过200行),每个区间对应一个可学习的向量。这个向量会被叠加到所有token的隐层表示上,相当于给整段内容打上一个"这是短文本/中等文本/长文本"的标签,让判断头在做决定时把内容长度纳入考量。这种设计的直觉非常自然:对于一个只有5行的函数,每一行都可能是关键的,判断头应该更加谨慎;对于一个500行的文件,缺失几行往往无关大局,判断头可以更果断地清理。

第二个部件是**逐token前馈分类器**(per-token feed-forward classifier)。它的结构并不复杂:一层LayerNorm(归一化,让数值更稳定),两个"线性变换→GELU激活→Dropout"的堆叠块(Dropout是一种防止过拟合的技巧,相当于训练时随机忽略一些连接,让模型更健壮),最后一个线性层输出一个单一的分数,再经过Sigmoid函数压缩到0到1之间,代表"这个token属于应保留内容"的概率。

这个分类器会对工具输出中的每个token都输出一个概率分数。然后,按照"每行中超过一半的token被判为保留,则这行保留;否则删除"的多数投票规则,得出每行的最终去留决定。被判为删除的行会从工具输出中移除,替换为"(已过滤N行)"的占位标记,然后这个精简后的版本才被追加进AI的对话历史,作为下一轮的输入。

关键点在于:AI助手在第t轮自己的生成过程中,仍然看到的是完整的原始输出(因为判断和修剪发生在这轮生成之后);只有在第t+1轮及以后,AI才会看到经过修剪的压缩版本。这样设计是为了确保当前轮次的AI决策不受影响,而长期累积的"记忆负担"则被有效控制。

整个判断头大约有1800万个参数,相比于动辄数百亿参数的主体AI模型,这是个微不足道的小零件。而且,因为它"蹭"的是本就要发生的预填充过程,不需要额外调用一次模型,额外的计算开销被控制在非常小的范围内。

---

**三、教会判断头"什么是重要的":训练数据与损失函数的精心设计**

一个判断头再精巧,也需要经过训练才能发挥作用。研究团队为此构建了一个包含22609条样本的训练数据集,每条样本都是一次真实的多轮工具对话记录,包含对话历史、工具调用、工具输出,以及AI在看到这个输出后的下一步行动。

训练数据来自五个公开的HuggingFace数据集,覆盖了SWE风格的代码修改任务,以及涉及象棋、机器学习、密码学、数据库操作、Shell调度等多种命令行任务的记录。这种多样性是刻意为之的——研究团队希望判断头能处理各种类型的工具输出,而不是只认识Python代码。

每条样本的每一行,都由Claude Sonnet 4.6打上了"保留"或"删除"的标签,标注时参考了完整的对话历史和AI的下一步行动——也就是说,标注的标准是"AI在做完这步操作之后,哪些内容对它接下来的行动是有用的"。这是一个相当细致的标注过程,最终经过预处理、多样性采样和人工复核,形成了这个训练集。

从数据分布来看,一段工具输出平均76行,中位数56行,Claude标注的保留率平均约32%、中位数约23%——也就是说,典型情况下大约七成的内容是可以删掉的。这和AI实际行为中的"大量冗余"完全吻合。

训练时使用的损失函数(可以理解为衡量模型表现好坏的尺子)是这项研究的另一个创新点。

通常的做法是用"交叉熵损失"(cross-entropy loss),简单来说就是"你猜错的次数越多、惩罚越重"。但这种做法有个问题:由于绝大多数token都属于"删除"类别(保留率只有约30%),模型可能会偷懒地学会"几乎什么都删",在数量上减少错误,但实际上错误地删掉了最关键的内容。

标准的Focal Loss(聚焦损失)通过加大对"难以区分的样本"的惩罚来改进这个问题,但它对整个数据集统一处理,没有考虑单个样本的保留/删除比例差异。

研究团队设计了一种"逐样本平衡Focal损失"(per-sample balanced focal loss):对于每个工具输出样本,先分别计算"所有应保留token上的平均损失"和"所有应删除token上的平均损失",然后各取一半加权合并。这样做的效果是:无论一个样本中保留行只有3行还是90行,"保留"和"删除"两类信息对这个样本的学习贡献都是对等的。

这种设计背后有一个非常直观的洞察:当一段100行的输出里只有3行被标记为保留时,那3行一定极度重要,因为AI在浏览完整段输出后,判断只有这3行值得记住;当一段100行的输出里90行都被标记为保留时,那10行被标记为删除的内容也一定是极度冗余的,比如纯粹的版权声明或者完全无关的调试输出。两种极端情况下,少数派的信息才是最有价值的学习信号——而逐样本平衡机制正是为了保护这些少数派的信号不被淹没。

在消融实验中,研究团队对比了BCE(标准交叉熵)、Focal(标准聚焦损失)、Dice损失、Tversky损失,以及他们自己设计的逐样本平衡Focal损失,在100个样本的保留测试集上同时用F1分数和LLM评判分数(由GPT-5.4-mini在1-10分的量表上打分,评估裁剪后的内容是否足够让AI完成任务)衡量效果。结果显示,逐样本平衡Focal损失在两项指标上均表现最佳——F1为0.635,LLM评判分为7.08——相比标准BCE,F1提升了0.16,LLM评判分提升了1.13。

一个有趣的细节是:Dice损失和Tversky损失的F1值与逐样本平衡Focal损失相近(均为0.591),但LLM评判分却分别只有5.30和3.03,远低于对方。这揭示了F1分数的局限性:一个模型可能只保留了少数高置信度的行,在集合匹配上表现不错,但保留下来的内容缺乏上下文,AI根本无法据此完成工作。LLM评判分更能反映"保留内容的实际可用性",而不仅仅是"保留集合与标注集合的重叠程度"。研究团队用两个具体案例对这种差异做了详细的质性分析:一个案例中,BCE方法在pdm库的解析器代码上只保留了4行函数签名,没有函数体、没有导入,AI看了无从下手,LLM评判仅2分;而逐样本平衡Focal方法保留了30行,涵盖导入、类型检查块、函数体,LLM评判8分——尽管F1反而是前者更高。

---

**四、长度感知嵌入和额外计算开销:两个工程细节的实测效果**

长度感知嵌入带来了多大的提升?消融实验给出了清晰的答案:加入长度嵌入后,LLM评判分从6.86提升到7.08,而F1几乎没有变化(0.636 vs 0.635)。

这个结果很耐人寻味。长度嵌入并没有改变总体的行级别分类准确率(F1不变),但它改变了错误的"分布位置":它让判断头在短输出上更保守(不敢乱删),在长输出上更激进(放心地大量删除),把错误集中到了危害更小的地方。这正是设计的初衷——LLM评判分的提升意味着裁剪后的内容更加可用,即便总错误数没有减少,错误发生的位置从"致命伤"变成了"皮外伤"。

研究团队还专门测量了SWE-Pruner Pro在实际部署时引入的额外计算开销。他们用16个轨迹的MiMo-V2-Flash回放实验来量化这一点:开启判断头之后,裁剪调用的累计耗时占到AI总生成时间的15.0%,第50百分位数约14.7%,第95百分位数约34.8%。

这个15%的额外开销乍一听似乎不小,但有几个因素让它变得可以接受。第一,裁剪开销只在"每轮AI生成之后"发生一次,而它带来的token节省却会惠及"之后所有轮次"的生成——随着轮次增加,节省的总量远大于增加的代价。第二,这个15%是相对于AI的生成时间而言的,而不是总体挂钟时间;在实际部署中,AI的解码(生成)过程本身已经经历了多年的工程优化,是极度高效的,所以15%并不意味着用户会感知到明显的等待时间增加。第三,研究团队指出,如果把判断头的参数量化(压缩精度)、或者使用更高效的序列化方式,这个开销还有相当大的压缩空间。

在工程实现上,研究团队选择了SGLang(一个主流的大模型推理框架)作为服务器,开启了其内置的"返回隐层状态"功能,并为此修复了三个原本存在于隐层状态传输路径上的正确性漏洞:混合批次中的对齐问题、分块预填充下的截断问题,以及前缀缓存命中时的遗漏问题。他们还将隐层状态的序列化格式从"嵌套JSON浮点列表"(一个16000 token的隐层张量,用这种格式传输会膨胀至1-3GB的文本)改为"base64二进制封装+float16精度"(同样的张量压缩至85MB),实现了约20倍的传输体积缩减。

---

**五、四个测试集上的全面评测:省钱又不降质,偶尔还能涨分**

研究团队在四个基准测试集上评估了SWE-Pruner Pro的实际效果,并与六种现有的裁剪/压缩方法进行了对比,使用了两种不同的开源AI模型骨干。

四个测试集的性质各不相同,覆盖了从简单问答到复杂代码修改的多种场景。SWE-QA包含144个需要多轮工具调用才能回答的代码仓库理解问题;SWE-QA-Pro包含260个类似但更难的问题,且运行在真实可执行的环境中;Oolong本来是一个单轮长上下文理解基准,研究团队将其改造成了多轮工具调用场景(AI通过grep、awk等命令工具在沙箱文件里查找和汇总信息);SWE-Bench Verified包含500个真实的GitHub代码仓库修复任务,是衡量AI编程代理能力最权威的基准之一。

两种AI骨干分别是MiMo-V2-Flash(小米开发的309B参数混合专家模型,每次只激活15B参数)和Qwen3-Coder-Next(阿里开发的80B参数混合专家模型,每次只激活3B参数),两者都是专门为长上下文编程任务优化的模型。

在SWE-QA、SWE-QA-Pro和Oolong三个问答类测试集上,SWE-Pruner Pro是七种方法中唯一一个在所有测试格子(两种骨干×三个测试集=6个格子)中都能同时减少token消耗、且保持任务质量不明显下滑的方法。最高节省达39%(Qwen3-Coder-Next在SWE-QA-Pro上),长上下文场景的最高节省达30%(MiMo-V2-Flash在Oolong上)。

对比来看,其他方法的表现都有明显的短板。LLMLingua2在Oolong上用MiMo-V2-Flash骨干时,token消耗不降反升189.8%——原因是它的压缩逻辑需要额外的处理步骤,反而带来了更多的token开销。Selective Context和RAG(基于检索的方法)在某些格子上也出现了token膨胀的情况。Self-Prune(让AI骨干自己重新回答"哪些行该保留"的方法)在质量上有明显损失,在token上的节省也有限。LongCodeZip(基于函数级别困惑度排名的代码压缩方法)效果参差不齐。SWE-Pruner(这项研究的前作,需要在每轮对话中让AI生成一个"目标提示",然后交给独立的评分模型判断哪些行重要)效果相对不错,但它有两个额外成本:每轮都要AI多生成一段文字,以及调用一次额外的评分模型,总开销并不小。

在质量指标上,Qwen3-Coder-Next骨干下,SWE-Pruner Pro是唯一一个在SWE-QA和SWE-QA-Pro两个测试集上分数高于或持平基线(不裁剪时的分数)的裁剪方法(分别为+0.02和+0.24)。在MiMo-V2-Flash骨干下,SWE-Pruner Pro在Oolong上的准确率从92.4%提升到94.6%,提升了2.2个百分点——裁剪反而让AI答得更准了。这个反直觉的结果可以用"去噪"来理解:当冗余信息被清除后,AI在有限的注意力资源下能更清晰地聚焦于真正有用的内容,避免了"在噪音中迷路"的问题。

在SWE-Bench Verified这个代码修复任务上,情况略有不同。MiMo-V2-Flash骨干下,所有裁剪方法都提升了解决率:SWE-Pruner的提升最大(+4.2%,从326/500到347/500),SWE-Pruner Pro紧随其后(+3.8%,达到345/500),但SWE-Pruner Pro的token增量只有+7.4%,而SWE-Pruner的token增量高达+14.9%——也就是说,SWE-Pruner Pro用大约一半的额外token开销,换取了相近的质量提升。

在Qwen3-Coder-Next骨干下,所有裁剪方法都造成了解决率下降,SWE-Pruner Pro的下降幅度最小(-1.2%,仅损失6道题),同时实现了所有方法中最大的token节省(-13.5%)。这表明裁剪对这个模型的伤害相对最小,是"最不坏"的选择。

研究团队还注意到一个有趣的现象:在SWE-Bench Verified上,API调用次数和token消耗量有时会朝相反方向变化。SWE-Pruner Pro在MiMo-V2-Flash上的API调用次数是所有方法中最多的(111.8次 vs. 基线的94.8次),但token消耗却是最少的之一。原因在于:裁剪改变了AI在后续轮次中看到的历史内容,这会影响AI的决策路径,有时会导致AI需要更多轮次才能完成任务,但每轮的上下文更短、更聚焦。这两个指标在不同骨干上的表现方向甚至相反,因此研究团队选择分别报告而不是合并成单一效率数字。

---

**六、对比和局限:这项研究能做什么,不能做什么**

SWE-Pruner Pro与现有方法的本质区别,在于信息来源。

通用型压缩方法(如LLMLingua2)使用固定的替代指标(如困惑度、自信息等)来判断token的重要性,这些指标与AI当前任务的关联很弱——不管AI在处理什么问题,压缩逻辑都是一样的,对AI的实际信息需求视而不见。基于检索的方法(如RAG)把工具输出拆成片段,用向量相似度检索最相关的片段,但"相似度"不等于"有用性",而且检索本身也有额外开销。代码结构感知的压缩方法(如LongCodeZip)尊重了代码的语法边界,但依然使用固定策略,不随任务变化。

SWE-Pruner(前作)是最接近SWE-Pruner Pro的方法:它明确考虑了AI的当前任务,但需要每轮让AI额外生成一段"目标提示"(goal-hint query),然后把这个提示交给一个独立的评分模型来判断哪些行重要。这个流程意味着:每轮两次额外的model call(一次生成目标提示,一次评分),以及大量的token消耗。

SWE-Pruner Pro彻底绕开了这两个额外步骤:不需要AI生成目标提示,不需要独立评分模型,直接读取AI骨干在预填充时产生的内部状态。信息本来就在那里,不过之前没有人意识到可以直接使用它。

当然,这项研究也有两个明确的局限。第一,它目前只能用于开放权重(open-weight)模型——也就是可以访问内部参数和隐层状态的模型;对于闭源模型(如GPT-4、Claude等),无法读取隐层状态,这套方案需要重新设计。第二,每换一种骨干模型,判断头就需要重新训练,因为不同模型的隐层表示维度和语义都不同。不过,训练成本相对较低(研究中提到在8块H200 GPU上训练10个epoch只需约15分钟),这个局限在实践中并不算严重的障碍。

在应用范围上,尽管训练数据以Python代码为主(约39.5%),这套方法在Oolong这个自然语言聚合任务上同样取得了良好效果,表明隐层状态中的"重要性信号"并不局限于代码场景,而是一种更通用的能力。

---

说到底,这项研究揭示了一个被长期忽视的事实:AI在被动地读完一段内容之后,它的内部世界已经悄悄地形成了"什么重要、什么不重要"的判断,只不过这些判断一直深埋在数字向量里,没有被利用起来。SWE-Pruner Pro做的,不过是在AI旁边放了一个小小的"翻译器",把这些内心判断变成了实际的删除动作。

这对普通用户意味着什么?最直接的影响是成本:AI编程助手处理一个任务所消耗的token减少了,云服务的账单就会变小。其次是质量:上下文更干净、更聚焦的AI助手,在某些情况下反而表现得更好,因为它不再被大量无关信息分散注意力。从更宏观的视角看,这项研究还指向了一个更深的问题:我们已经在大量使用AI模型的内部表示来理解它的输出,但还有多少信号被隐藏在这些表示里,等待被发掘利用?判断信息重要性只是其中一种可能。

有兴趣深入了解技术细节的读者,可以通过arXiv:2607.18213v1查阅完整论文,代码也已在论文主页开源,供感兴趣的工程师直接使用和改进。

---

**Q&A**

Q1:SWE-Pruner Pro和普通的AI上下文压缩方法有什么根本区别?

A:普通压缩方法(比如LLMLingua2)用困惑度等固定指标来判断哪些词重要,不管AI在做什么任务都用同一套标准,和AI的实际需求脱节。SWE-Pruner Pro则直接读取AI模型自己在处理工具输出时产生的内部表示(隐层状态),这些状态已经隐含了AI对"哪行内容和当前任务相关"的判断,不需要额外的评分模型或让AI重新描述自己的目标。

Q2:SWE-Pruner Pro会不会因为删掉内容导致AI产生错误答案?

A:研究实验显示,在大多数测试场景中,SWE-Pruner Pro裁剪后的任务质量与不裁剪相差极小,甚至在部分场景(如Oolong测试集和SWE-Bench Verified的MiMo-V2-Flash骨干)出现了质量提升,因为去掉冗余内容后AI反而更容易聚焦。但研究也明确指出,在特定骨干模型(Qwen3-Coder-Next)的代码修复任务上,会损失约1.2%的解决率,所以在实际部署前需要针对具体场景验证。

Q3:SWE-Pruner Pro能用在ChatGPT或Claude这类闭源AI上吗?

A:目前不能。SWE-Pruner Pro需要访问AI模型的内部隐层状态(相当于模型处理文字时的"内心感受"数值),而闭源模型的这些内部数据对外不开放。现阶段该方法只适用于开源或开放权重的大模型,比如论文中使用的MiMo-V2-Flash和Qwen3-Coder-Next。如果应用于新的开源模型,还需要重新训练一个对应的轻量级判断头。

作者声明:作品含AI生成内容
举报/反馈
分享到: 微博 QQ 空间
对本文内容有合作意向?
我们将在 1 个工作日内与您联系
留言咨询