我构建了一个将Python重写为面向模型表示的编译器

Reddit r/LocalLLaMA 工具

摘要

Vulpine是一个编译器,它将人类可读的Python代码转换为针对LLM优化的压缩宏表示,平均减少13.8%的token数,同时支持精确的结构重建。

在过去的几个周末里,我一直在开发一个用于编码LLM的编译器。思路很简单:原始源代码是为人类优化的,但编码代理将其作为模型输入来消费。我想测试一种更紧凑、面向模型的表示是否可行。我将Python编译成这种表示,并在约13k个保留文件上进行了测试:约14%的token减少,99.8%的AST等价往返成功。https://www.mehulg.com/blog/vulpine 代码:https://github.com/MehulG/vulpine 非常欢迎来自从事编码代理、编译器、分词或推理工作的人的反馈。
查看原文
查看缓存全文

缓存时间: 2026/06/03 17:45

# Vulpine - 面向编程大语言模型的编译器 来源:https://www.mehulg.com/blog/vulpine 2026年6月 ## 太长不看 Vulpine 是一项实验,旨在将面向人类编写的代码编译成一种更便宜、更清晰的表示形式,供模型进行推理,然后再编译回来。我们从真实的 GitHub 代码中挖掘重复的 AST 模式,从**约 7.6 万**个 FastAPI 训练文件中提取了**2000**个宏,并将匹配的代码片段替换为宏形式,其他部分保持普通 Python 不变。在**1.3 万**个保留的评估文件上,这转化为**13.8%** 更少的 token(`cl100k_base`)以及**99.8%** 的结构性往返成功率(在我们将宏展开回去之后)。 GitHub 仓库 (https://github.com/MehulG/vulpine) 跳转到基准测试 (https://www.mehulg.com/blog/vulpine#benchmark-run) --- ## 为什么要为 LLM 编译代码? 编程智能体主要操作原始源代码。源代码是为人类优化的:我们更容易阅读、编写、推理、审查和维护。我们可以将当今的编程语言视为机器代码之上的抽象层,是人与机器之间的接口。 这些语言是为人类优化的:语法、宏和内置函数都是为人类读者设计的。但是,LLM 是代码的另一种消费者。它们不需要人类需要的完全相同的表面形式。它们需要一种表示形式,既能保留程序含义,又能使结构更便宜、更清晰地进行处理。 带着这个使命,我们着手构建 Vulpine 编译器。编译器通常是单向的;广义上讲,很少需要从低级语言回到高级语言。 传统编译器大多朝一个方向移动: `` 人类代码 → 低级表示 → 机器 `` 而 Vulpine 需要双向移动: `` 人类代码 → 面向模型的表示 → LLM → 人类代码 `` 这改变了设计约束。编译后的形式不能仅仅是压缩的。它必须能够扩展回有用的源代码。它必须保留标识符、控制流、类型、字面量、框架语义,以及人类和智能体都依赖的程序部分。 目标是测试一种面向模型的表示形式是否能够在保留足够结构(可用于编程智能体工作流)的同时,降低 token 成本。 这就是实验的初衷。 ## 一个手动调整的示例(Python) 我们首先选择了 Python,这是最熟悉的语言,易于实验且应用广泛。编译器的第一个版本是手动完成的。我们使用的分词器是 `cl100k_base`。 我们研究了 Python 中不断出现的结构,并思考如何让它们在 token 上更便宜: `` for x in range(len(y)): # 7 个 token `` 这里只有两个变量,`x` 和 `y`。我们可以用一个新宏来表示这个循环: `` for_macro(x, y) # 6 个 token `` 这已经节省了一个 token,但我们还可以通过改变宏的写法进一步推进: `` macro x y # 3 个 token | 减少 50% `` 这种缩减是显著的。我们知道了方向;难点在于将其推广到整个语言。 ## 挖掘重复的 AST 模式 第一个问题是找到哪些模式重复出现最多。听起来很简单:扫描大型代码库,找到常见形状,完成。但实际上并非如此。我们必须处理边界情况和多个意思相同的模式。在 AST 上进行匹配而不是原始字符串有所帮助。 我们运行了一个早期的模式挖掘器。坦率地说,结果令人失望。许多提取结果过于模糊或过于具体,例如 `var1 var2 var3`,几乎没有结构信号。我们必须调整关键字与变量的比率(我们将变量称为**孔**),以及关键字和孔的最小和最大计数,直到挖掘器产生有用的模板。这花费了大量的实验。 一个常见模式是经典的赋值链: `` # python a = b = c = 0 # 8 个 token # vulpine macro a b c 0 # 5 个 token | token 减少 37% `` ## 命名和应用宏 接下来我们必须为这些模式命名。在实践中,这意味着为宏命名。最 token 高效的形式是 `macro_name var1 var2 ...`,其中宏名称最好是一个单独的 token。任何宏的成本大约是 `len(vars) + 1` 个 token。我们只保留了那些优于原始跨度的模式。 命名本身就是一个难题。现有的 Python 内置函数会与真实代码冲突,而新的英文名称可能看起来像普通标识符,从而改变表示形式的读取方式。编译后的形式并不是为了让人类舒服地编写。它是源代码和模型之间的内部表示。 因此,我们采用了多语言方案。宏名称可以是来自其他字符集的字符或单词,只要它在分词器下保持便宜即可。这给了我们紧凑、唯一的符号,而不会与普通 Python 冲突。LLM 对 token 进行操作,所以这种语法对它们来说比人类打字更容易学习。 `` क var1 var2 var3 `` 至此,我们有了一个模式挖掘器和一个由挖掘器生成的宏映射。 ## 往返与回退 编译器步骤变得几乎是机械的。对于每段代码,我们将 Python 解析为 AST,遍历树,匹配挖掘出的模板,将命中的地方替换为宏形式,其余部分保持 Python 不变。我们不希望对不确定的代码进行转换,保留原始 Python 作为回退。当多个宏匹配时,我们选择节省 token 更多的替换。 这效果很好,我们可以可靠地将原始 Python 转换为编译后的格式。 反向路径原则上更直接:反向运行展开逻辑,由一个相同的模板库驱动的反编译器。但在实践中,它暴露了许多我们在编译器设计中反复迭代的边界情况。 ## 基准测试运行 重现这些基准测试 (https://github.com/MehulG/vulpine/blob/main/docs/reproduce_blog_benchmarks.md) ### 准备数据集 我们从公共 GitHub 构建了一个数据集:排名前 800 的 FastAPI 相关仓库(按星标数)用于模式挖掘,接下来的 200 个用于评估。经过过滤和每个仓库的上限处理后,挖掘数据库中得到了约 7.6 万个 Python 文件(约 760 个仓库),评估数据集中另有约 1.3 万个文件(约 187 个评估仓库)。 ### 挖掘模式 然后我们开始模式挖掘。为此,我们扫描超参数,以最大化有用覆盖率,同时限制噪音和琐碎匹配: | 参数 | 测试值 | 使用的值(最终运行) | |------|--------|----------------------| | 最小关键字/孔比率 | 0.3–1.0 | 0.75 | | 最大关键字/孔比率 | 2.0 | 2.0 | | 最小结构长度 | 3–7 个 AST 节点 | 4 个 AST 节点 | | 支持度阈值 | 100–1000 | 500 | | 每个模式最大孔数 | 3–12 | 6 | 这次运行产生了 2000 个挖掘出的模式。 模式挖掘结果 ### 在评估集上编译和往返 每个模式都获得了一个宏名称。我们使用了单 token 的 Unicode 符号(阿拉伯语、数学字母数字符号以及其他很少歧义的字符集),这样名称在 `cl100k_base` 下保持唯一且便宜。 挖掘出的宏将评估集 Python 转换为编译格式;未匹配的区域保持原生 Python。在 13,286 个评估文件中,32 个因无效 Python 被丢弃,157 个在压缩无帮助时原样保留。 然后我们将所有 13,254 个宏输出反编译回 Python。 ## 结果 ### Token 压缩 我们测量了 13,254 个成功编译的文件(丢弃 32 个无效输入后)。从语料库总数(18,984,865 个输入 token)来看,编译格式使用了 16,373,332 个 token(−2,611,533;相比输入减少 13.76%)。回退保护机制在宏输出使用更多 token 时写入原始版本,因此没有评估文件比其源代码更大。 Token 压缩概览 大约 94% 的文件看到了一定程度的压缩;约 17% 的文件节省了超过 20% 的 token。所有输入 token 的加权净减少量(去除顶部异常值子集后的 bootstrap 95% 置信区间):约 13.8%(置信区间 13.6%–14.0%)。 压缩率与文件大小关系 对角线以下的点是赢家;拟合线显示了压缩率如何随文件大小变化。长度区间:0–5000,5000–10000,10000+ token。 压缩率分布 `(输入 − 编译) / 输入 × 100` 的分布,带有加权净减少量、中位数、均值、75% 和 90% 百分位数的垂直标记。 ### 往返保真度 结构性检查比较去除注释后的输入与往返后的 Python。这是 AST 等价性,不是字节对字节的相等;即使状态为“正常”,格式和注释也可能不同。 | 状态 | 文件数 | 占比 | |------|--------|------| | 正常 | 13,230 | 99.82% | | 不匹配 | 13 | 0.10% | | 解析错误 | 11 | 0.08% | | **总计比较** | **13,254** | **100%** | 失败集中在 f-string、展开过程中损坏的三引号字符串以及字面量内部的 Unicode 标点符号。这不是随机漂移。 ## 这给我们带来了什么 Vulpine 押注于:现实世界中的大量代码是重复的结构,而不是独特的语法;并且一个面向 LLM 的层可以利用这一点,而不破坏面向人类的层。在 **1.3 万**个 FastAPI 评估文件上,我们看到了约 **14%** 的 token 胜利和约 **99.8%** 的 AST 往返成功率,失败集中在少数几个我们认为可以逐步解决的困难案例上。 这对于智能体的上下文窗口和成本来说是令人鼓舞的,但这并非魔法:覆盖率是部分的,宏是挖掘出来的而不是手动设计的,我们在这个语料库之外还有工作要做。接下来,我们想要进行测试,询问模型是否*真正**更好地使用这种格式,而不仅仅是我们能否将其往返。 如果你正在试验编程智能体,我们很乐意听取你的看法。

相似文章

一个可定制的编译器,用于为AI模型生成高效的融合GPU内核 [P]

Reddit r/MachineLearning

作者介绍了一款用 Python 编写、高度可定制且易于修改的 ML 编译器。该编译器通过多级 IR 流水线将 LLMs 转换为优化的 CUDA 内核,在特定操作上实现了与 PyTorch 相当甚至更优的性能。文章详细阐述了该编译器的优化过程、降级规则以及用于生成高效融合 GPU 内核的 CLI 用法。

无语义的语法:教会大语言模型用未见过的语言编程

arXiv cs.CL

本文介绍了PyLang,一种在所有预训练语料库中都不存在的编程语言,并表明在其上微调的大语言模型可以学习语法但无法迁移算法推理,导致出现“实现忠实度差距”——模型理解算法但无法用不熟悉的语言表达它们。