论文链接:Narcissus: Program Synthesis Using Context-Aware LLM Approximations 代码仓库:https://github.com/Herb-AI/Narcissus/ 发表时间:2026年8月 机构:Delft University of Technology(代尔夫特理工大学)——纯高校 领域标签:cs.AI,程序综合、符号-神经混合系统
一、论文背景
要理解这篇论文,需要四个概念:归纳程序综合、固定目标语言困境、枚举搜索的顺序问题、静态先验的两种失明。
归纳程序综合是什么? 给定一个上下文无关文法(CFG)$G$ 定义的领域专用语言(DSL)和一组输入-输出示例 $E$,找一个属于该语言、且对所有示例 $p(i_j) = o_j$ 的程序。类比:给你一套乐高零件清单(文法规定了哪些积木、怎么拼接)和几张成品照片(示例),要你拼出一个对得上照片的作品。检查一个候选程序很便宜(跑一遍示例),找到能通过的那个很贵——因为程序数量随规模指数增长。
固定目标语言困境:LLM 写代码很强,但前提是目标语言在训练数据里占主导。现实中大量任务固定了语言:处理器编译器只能发射该处理器的指令、电子表格插件只认电子表格公式、抽象推理基准(ARC)用手工设计的 DSL 攻坚。这些语言在 LLM 训练数据里极稀少,把文法贴进 prompt 也替代不了对它的训练——强 LLM 也会去抓熟悉的字符串函数(substring、split、upper),而文法里根本没有。论文称 LLM 吐出的程序为「提案」(proposals)。重试无用:每次尝试都是从同一分布里重新采样的一次昂贵调用,循环大多在重犯同样的错误——在论文的基准上,最好的提案模型直接解出的任务至多三分之一。
枚举搜索的顺序问题:枚举综合器系统性地搜索语法正确的程序空间。但程序空间指数增长,任何预算内,成败取决于先试哪些候选。朴素组合(每步扩展都问 LLM)不可行——引导式综合器每任务可能扩展数百万个部分候选,LLM 的判断必须被「编译」成更便宜的东西。
静态先验的两种失明:先前最优方案(HySynth 等)在搜索前提示 LLM 若干次,把提案编译成静态、上下文无关的先验——每条文法规则按提案使用频率打一个全局分数。这丢失了两样东西:其一,提案强烈暗示构造该出现在哪(每条提案都以 concat(x, ".") 结尾?那 concat 在程序末尾是强证据、在别处不是——纯频率计数捕捉不到);其二,提案没用到过的规则得到近乎零的权重,搜索被锁在门外——恰恰在提案错误、最需要搜索自我纠正的时候。理想启发式必须:感知上下文、便宜、提案很差时仍能工作。
二、论文定位和关联工作
论文把相关工作组织为四条脉络(名字 Narcissus 取自水仙花神话:它追随的是 LLM 留下的倒影,而非 LLM 本身)。
谱系一:扩展采样-检查循环。更多采样(ARC 上每题采数千个)、自修复(把执行失败喂回模型,本文的 re-prompting baseline 即此,ARGA 上只多解一题)、约束解码(每个 token 强制符合文法——修了语法不修正确性,且扭曲模型分布)。共同点:都在从一个「不认识目标语言」的模型里反复抽取,都不搜索。
谱系二:LLM 在环的程序搜索。LLM 作为候选的生成器、变异器或打分器嵌入符号搜索。恢复了搜索,但 LLM 调用次数随搜索规模增长——这些方法花一次模型调用,正是 Narcissus 花一次树查询的地方。
谱系三:静态 LLM 近似(最接近)。HySynth 与 Li et al. 编译上下文无关启发式;Tao et al. 把文法修复的样本种子进遗传编程;PPoT 从一次生成的 token 概率重建程序分布;同期 ReaComp 把推理轨迹编译成独立求解器。共同缺陷:分布与位置无关、剪掉样本没覆盖的东西——而「上下文依赖的近似」正是 HySynth 自己指出的未来方向,本文直接落实了它。
谱系四:学习式搜索启发式(LLM 之前就有)。规则预测器、价值排序器、库学习;Euphony 甚至让规则概率以周围结构为条件,Probe 在搜索中即时重加权文法。这组缺的是对当前任务的知识:它们在其他任务上训练、或只用搜索自身的部分结果,而 Narcissus 从少量提案中获得上下文感知、任务专属的引导,零训练。
| 维度 | 扩采样循环 | LLM 在环搜索 | 静态近似 (HySynth) | Narcissus |
|---|---|---|---|---|
| LLM 调用 | 每次尝试 | 随搜索增长 | 搜索前一次性 | 搜索前一次性 |
| 感知上下文 | — | ✓(昂贵) | ✗(全局频率) | ✓(树查询) |
| 剪掉提案未用的规则 | — | 否 | 是(致命) | 否(保底项) |
| 提案错误时的下界 | 重采同错 | 依赖 LLM | 搜索被锁死 | 退化为无引导枚举 |
定位结论:Narcissus 占据「静态近似的成本」与「LLM 在环的上下文敏感性」此前不可兼得的交叉点,其前提约束(iii)——启发式可以任意重排搜索但必须保持每条规则可达——是相对全部前作的实质性设计承诺。
三、问题定义
具体问题:LLM 为固定文法任务产出的提案大多语法破碎、不满足规格;如何把提案变成枚举搜索的有效引导?
核心洞察:提案的价值不在「它是不是答案」,而在**「它对结构的位置判断」**。类比:让一个不熟悉贵公司代码规范的外包工程师照规范写代码,交来的满是违规——但他对「先解析、再校验、最后拼接」这种流程骨架的直觉仍然有信息量;与其让他反复重写,不如把他的交付物拆解成「这里通常放什么」的检查清单交给完全懂规范的编译器去系统枚举。
形式化:搜索状态是可能含「洞」(开放位置)的部分程序 $p(p_1, \dots, \square, \dots, p_m)$;用规则 $r$ 填洞是一次「扩展」。要构造的是上下文感知启发式(Definition 1):
$$H(r \mid p, \Pi)$$给定文法 $G$、一次性采样的提案集 $\Pi$,对「在部分程序 $p$ 的洞处用规则 $r$」打分,分数同时依赖 $r$ 和 $p$(静态先验是只依赖 $r$ 的退化情形)。三条要求:(i) 分数依赖被扩展的部分程序;(ii) 便宜到能在数百万次扩展中每次查询、不调 LLM;(iii) 保持 $G$ 的每条规则可达——误导性提案只能延迟解,不能藏死解。
这个抽象的精妙之处:把「LLM 的判断」从在线服务降级为离线编译对象,同时用「可达性约束」把启发式的权力限制为重排而非剪枝——这把「引导」与「约束」在语义上分开了:静态先验在弱提案下从引导退化为约束(这正是它崩溃的机制),Narcissus 在数学上不可能退化成约束。
四、问题解法
流水线五步(图 1 以「姓名缩写」SLIA 任务为例)。
4.1 采样与修复:从原始文本到可用语法树
搜索前提示 LLM $n$ 次(带文法与规格,要求只用文法构造),收集提案文本。解析成语法树后大多不合文法:用了文法没有的算子、参数个数不对、类型不对。修复策略是递归自顶向下:从根开始,把每个节点与能产生该位置期望类型的文法规则匹配;无规则匹配的子项替换为期望类型的「类型化洞」,保留周围结构(concat(true, ".") 里语法外的 true 变洞,concat(□, ".") 存活)。完全解析不出表达式树的提案才丢弃。洞被启发式当作「缺失证据」处理:对齐与复用仍匹配洞周围完好的结构,洞本身不特别支持任何规则。设计含义:错误提案不是垃圾而是部分正确的结构骨架——错误被局部化,正确被保留。
4.2 挖掘频繁子程序:扩展文法
修复后的提案树中,出现至少两次的 ≥2 节点子树被挖掘为「宏规则」加入文法,得到扩展文法 $G^+$(如 at_ind(x, I) 成为带非终结符参数的部分宏规则)。类比库学习(DreamCoder 一脉),但碎片免费——从当前任务的提案里顺手获得。它改变搜索「几次扩展内能到达哪里」的能力,与启发式正交(实验用 augmented-BFS baseline 单独隔离其贡献)。
4.3 上下文感知启发式:三个信号
前缀对齐 $H_{prefix}(r, p)$:洞的上下文是部分程序根到洞的规则链。$c(p)$ = 在这条路径每个位置都携带与 $p$ 相同规则的提案数(「同处境」提案),$s_{prefix}(r,p)$ = 其中在洞处继续用 $r$ 的数量。信号为条件份额 $s_{prefix}/c$($c=0$ 时为 0)。一条在提案总体中常见但在当前上下文不出现的规则得不到支持——这正是「问哪里」而非「问多频」的区别。
子程序复用 $H_{reuse}(r)$:包含子程序 $r$ 的提案占比 $s_{reuse}(r)/|\Pi|$。这是上下文无关的部分,但与全局频率不同:它也作用于挖掘出的宏规则,并与上下文感知项组合。适用场景:提案共享局部但整体不一致——concat(S, ".") 在不同结构里反复出现时,无论搜索走到哪都偏向含它的程序。
正则化 $H_{reg}(r, p)$:提案还指示解的规模。用提案 AST 规模的多集合构成高斯混合(每提案一个分量,带宽 $\sigma$ 为超参,归一化峰值 1),$H_{reg} = R_{size}(|p(r)|) + C$,$C > 0$ 是保底项。它把枚举集中在提案指示的复杂度带,防止搜索漂向越来越大的程序;$C$ 保证每条规则严格正分、永不被排除。提案完全无信号时所有扩展都退到 $C$、分数相同——搜索退化为无引导枚举,这是 Narcissus 的最坏情形(也是要求 (iii) 的兑现)。消融中「仅正则化」解 13–15 题,恰近似无引导枚举的水平。
合成:$H(r,p) = w_{prefix}H_{prefix} + w_{reuse}H_{reuse} + w_{reg}H_{reg}$,SLIA 上权重扫描选出等权(全 1)并固定用于所有实验——不逐域调参。
4.4 两种搜索后端
遗传搜索(自顶向下):操纵整棵程序,天然携带启发式所需上下文;变异算子(重采一个子树)本身就等于一次启发式打分的扩展,启发式原样嵌入。变异让搜索在提案区域内自由移动——保留有用片段、改变周围结构,恰好应对「提案含正确子程序但组装错误」的情形。
成本制导束搜索(自底向上,跟随 HySynth 的主导策略以便直接比较):把 HySynth 的无界优先队列换成固定宽度的 beam(1000),限制内存、让有限预算够到更深的程序。自底向上最后才建根,子程序没有根到洞的路径可条件化,故两个信号改锚定在子程序自身(前缀对齐 = 包含该子程序的提案中给它当前考虑的父规则的比例)。启发式误导时 beam 可能丢解——正则化项(静态先验没有的)防止 beam 与提案绑得过死。
4.5 输入输出总结
| 组件 | 输入 | 输出 | 成本 |
|---|---|---|---|
| 提案采样 | 文法 $G$ + 示例 $E$ | 提案文本 $\Pi$(每任务 $n$ 次调用,一次性) | LLM 调用 $n$ 次 |
| 解析修复 | $\Pi$ | 带类型化洞的 AST | 便宜 |
| 碎片挖掘 | 修复 AST | 扩展文法 $G^+$ | 便宜 |
| 启发式编译 | AST + $G^+$ | $H(r\mid p)$ | 预处理一次 |
| 引导搜索 | $G^+$, $E$, $H$ | 满足 $E$ 的程序 | 每次扩展一次树查询,零 LLM 调用 |
五、评估指标与实验证据
域与数据:五个域覆盖悬殊的「提案支持度」(至少一个提案直接解出的任务占比)——SLIA(字符串,SyGuS,支持 20–31%)、ARGA(GPT-4o 提案 11%、DeepSeek 4%)、DC(DeepCoder 列表,7%)、ARC(Hodel 通用 DSL,320 条规则,0–7%)、BV(位向量,仅 1%)。提案模型:GPT-4o(复用 HySynth 发布的每题约 100 条提案)与 DeepSeek-V4-Flash(SLIA 每题 25 条、ARGA 每题 5 条)。预算:每任务 $10^6$ 程序或 300 秒。
指标:解决任务数 vs 枚举程序数 / 墙钟时间;曲线下面积 AUC 在 $\log_{10}x$ 空间计算、按任务数归一化为百分比(100% = 瞬间全解;越陡越高)。遗传方法为 5 种子平均。AUC 把「解得多」与「解得快」分开计量——这是评估设计上的关键选择。
RQ1:每个提案支持度级别上下文感知都胜静态。
| 域/设置 | Narcissus | 静态先验 | 无引导 | 原始提案 |
|---|---|---|---|---|
| SLIA-70(GPT-4o,Genetic) | 51.4(AUC 32.2%) | 32.2(AUC 18.2%) | — | — |
| SLIA-70(DeepSeek,Genetic) | 32.6 | 13.8 | — | 20%(直接采样) |
| SLIA-100(消融语境) | 41.8 | 28.4 | — | — |
| BV-587 | 350 | 102/57 | 302 | ~0(支持 1%) |
| DC | 32.6 | — | 10 | — |
| ARC-100 | 40 | 10/12 | 4 | 13%(文法有效口径)/15%(任意) |
| ARGA-160(GPT-4o) | 45.6 | 43.0 | — | — |
| ARGA-160(DeepSeek) | 20.8 | 8.0 | — | — |
关键读数:(1) 弱提案时静态崩溃、Narcissus 不低于无引导——BV 上静态先验(102/57)反而远低于无引导 BFS(302),因为剪枝把弱提案遗漏的规则变得太贵而枚举不到,「引导退化为约束」;HySynth 靠每题约 100 条提案摸到大半个文法才避免此事;Narcissus 用 25 条 DeepSeek 提案(每条规模仅 1/3:14 vs 44 规则)仍稳。(2) ARC 上引导搜索三倍化原始提案:40% 对 13%——错误提案仍贡献碎片与结构,搜索负责组装与纠正。(3) re-prompting 全面落败:三轮自修复在 DeepSeek SLIA 上只多解 3 题、ARGA 上多 1 题。(4) 增益来自上下文而非碎片:仅碎片(augmented BFS 28)与静态先验(28.4)打平,Narcissus 41.8。
RQ2:三个信号互补,哪个承重随提案质量反转(SLIA 消融,遗传后端)。DeepSeek 提案小且结构一致 → 前缀对齐决定性(去掉它从 45.8 跌到 24.0;单独用它 43.2 已近全启发式);GPT-4o 提案大三倍 → 同一根到洞上下文很少跨提案复现,前缀对齐 faded、子程序复用接管(单独 44.2 vs 前缀单独 25.4);BV 上两者都无强信号,正则化独撑搜索不崩。没有单一信号处处占优,但每个场景恰有一个承重——全带上三个就能盲选应对。
RQ3:机制验证——更快到达提案子空间且在对的位置。首次复现任一提案前,Narcissus 枚举的程序数约为静态的 1/12(DeepSeek 提案口径)。单任务规模分布剖面(图 3):BFS 把 $10^6$ 预算全砸在规模 5 以下、静态先验摊薄到 195、Narcissus 集中在提案占据的规模带——三者中只有它落在解所在的规模,独解该题。
RQ4:搜索恢复廉价与强力提案间的差距。直接采样 GPT-4o 解 31% vs DeepSeek 20%;Narcissus+DeepSeek(25 提案)达 47%,反超 GPT-4o 直接采样、追平 GPT-4o 提案上的静态先验。AUC 领先 (+7.2) 大于最终数领先 (+1.0):同样的提案集,Narcissus 榨取得更多、且更早。
为什么实验设计能证明论点:核心主张是「上下文感知优于上下文无关近似」。(1) 静态 baseline 拟合同一批修复后提案——差异只能归因于启发式本身;(2) 两种后端 × 两种提案模型 × 五个域的网格排除后端/提案特异性;(3) augmented-BFS 隔离碎片贡献、re-prompting 隔离「更多 LLM 工作」路线;(4) RQ3 的「到提案区域距离」与规模分布把「为什么快」从结果落到机制;(5) BV 的弱支持场景检验要求 (iii):正则化保底使最坏情形=无引导而非锁死。
六、效果优势的根源解释
为什么上下文感知赢静态? 因果链:静态先验给每条规则一个全局分数 → 丢失「构造属于哪」的位置信息(concat 在程序末尾是证据、在中间是噪声,频率计数一视同仁)→ 引导变钝,AUC 差距(32.2% vs 18.2%)大于最终数差距——搜索在错误顺序下浪费预算;更致命的是未用规则近乎零权 → 弱提案(只触碰文法一角)时,解所需的规则被排到枚举序的末尾之外 → 引导实际变成了约束(BV 静态 102 vs 无引导 302)。Narcissus 的改变:前缀对齐把分数条件化在根到洞路径上——同一规则在不同位置得到不同支持;保底项 $C$ 把「零支持」从「不可达」改为「排后」→ 误导提案只付出延迟代价。这是信息结构与安全性的一揽子修复:位置信息提升效率,可达性下界保证鲁棒。
为什么胜过 re-prompting? 重提示从同一个不认识目标语言的分布重新采样 → 错误相关,三轮只多解 3 题/1 题。Narcissus 把同一预算(一次性采样)花在编译上:修复把错误局部化(错误算子变洞、周围结构存活)、挖掘把重复碎片升为一步可达的宏 → 即便整体全错,局部正确性仍以碎片与上下文份额的形式存留 → 搜索在指数空间里系统拼装纠正。类比:与其让不懂规范的人无限重写,不如把他的草稿拆成可复用的片段图集交给懂规范的枚举机。
为什么便宜提案+搜索胜过昂贵提案? DeepSeek 提案小且一致 → 前缀对齐信号最纯净(RQ2 已证此场景下它单独就近全启发式)→ 25 条小提案的条件份额统计已足够稳定 → 结构直觉被完整传递,而最终「保证语法正确+满足示例」的硬约束由搜索承担 → 47% vs GPT-4o 直接采样的 31%。反事实:若无前缀对齐只剩静态频率,DeepSeek 提案只摸到 14 条规则(GPT-4o 的 1/3),静态先验会大面积锁死规则(SLIA-DeepSeek 静态仅 13.8/32.2 分即证)。
需要诚实指出的边界:beam 后端在启发式早期误导时可能丢解(beam 外候选被剪)——正则化缓解但不根除;三信号等权是次优的(作者自陈按提案规模/一致性做逐任务加权可更好);挖掘的碎片目前逐任务丢弃,未跨任务积累成库。
七、必要知识反推
领域知识层:
- 枚举综合的两种搜索体制(自顶向下扩展部分程序 vs 自底向上组合子项)与指数复杂度——不知道「顺序即成败」就无法定位启发式的中心地位;
- CFG、AST、类型化洞的概念——修复与挖掘操作的基本对象;
- DSL 任务的现实分布(处理器指令、电子表格公式、ARC 的 Hodel DSL)——动机来自「语言固定且训练数据稀少」这一事实。
方法论知识层:
- HySynth 一脉「把 LLM 编译为搜索引导」的范式及其自指的未来方向(上下文依赖近似)——本文是该方向的直接落实;
- 概率引导的形式(条件份额 vs 全局频率、高斯混合规模先验)——信号设计的语言;
- 库学习传统(DreamCoder/ELLIS)——碎片挖掘的思想来源;
- 可达性/下界思维——要求(iii)本质是为启发式加一个「最坏情形不低于无引导」的性能下界。
工程知识层:
- Julia + Herb.jl 程序综合库——实现基座;
- beam 与遗传搜索的工程取舍(内存上限、变异算子与打分扩展的对应);
- 复用 HySynth 发布的 GPT-4o 提案缓存——保证与其自身引导信号的公平对比。
知识融合的关键节点:(1) 「提案当分布样本而非答案」的视角转换——把 LLM 输出从「对错二分」重看为「结构信念的噪声观测」,修复=降噪、挖掘=众数提取、条件份额=条件期望,一套统计视角统一了三个信号。(2) 把编译目标从「规则权重表」升级为「上下文相关的评分函数」——需要同时看到 LLM 提案里「哪里重复」和搜索里「扩展需要什么」,是两个社区(PL/SyGuS 与神经 LM)接口上的洞见。
八、论文中可以提取的通用性灵感
灵感一:把昂贵判断力「编译」为便宜查询结构,而非在线租用。 核心思想:当强判断力(LLM)的使用频次远高于获取频次时,一次性采样并将其编译成可多次廉价查询的结构(树查询/条件份额表),好过每次在线调用。 论文证据:搜索期间零 LLM 调用,每次扩展仅需一次树查询;LLM 在环方法「花一次模型调用的地方 Narcissus 花一次树查询」;SLIA 上以 25 条提案支撑数百万次扩展。 推广场景:(1) 数据库查询优化——离线学习基数估计器;(2) 推荐系统——离线蒸馏排序模型为倒排索引;(3) 实时风控——离线编译规则引擎;(4) 编译器——profile-guided optimization 把运行时观测编译为静态优化决策。
灵感二:保底项让引导永不变成约束(错误信息只延迟、不隐藏真相)。 核心思想:任何由不完美信号驱动的引导机制都应加正下界,保证被信号遗漏的选项仍可达——把「最坏情形」从「系统失效」改为「退化为无引导」。 论文证据:BV 域静态先验因剪枝崩溃到 102/57(低于无引导 302);Narcissus 的 $C>0$ 使零支持规则仅排后不消失,弱提案下解 350 超无引导。 推广场景:(1) 探索-利用算法的探索下界(UCB 的 +1 项);(2) 缓存系统——命中率信号之外保留全量可查;(3) 搜索引擎——个性化重排不屏蔽原始相关性;(4) 组织决策——专家意见加权但不否决基层上报通道。
灵感三:错误输出经「局部化修复」后仍是信息(部分正确性可提取)。
核心思想:不完美生成物不必整体接受或抛弃——把错误局部化(变洞)、保留周围正确结构,统计聚合后错误互相抵消、正确结构存活。
论文证据:语法外算子变类型化洞而周围结构存活;ARC 上错误提案仍贡献碎片使引导搜索解出三倍于原始提案的任务(40% vs 13%);全错的提案集中 concat(S,".") 仍以复用信号存活。
推广场景:(1) 众包标注——按片段聚合而非按标注者取舍;(2) 多模型集成——对齐后取结构共识;(3) 代码迁移——旧系统调用图指导新系统重构;(4) 学生错误答案分析——错解中的正确子步骤诊断。
灵感四:弱采样器 + 强验证器的组合可以超越强采样器单独工作。 核心思想:当验证便宜而生成昂贵时,「便宜的多份噪声提案 + 系统搜索验证」比「昂贵模型的一次性直出」更划算——信号纯度比信号强度更关键。 论文证据:DeepSeek-V4-Flash(25 提案)+ Narcissus 达 47%,超 GPT-4o 直接采样 31%;DeepSeek 提案小而一致使前缀对齐信号最纯净(单独 43.2 近全启发式 45.8)。 推广场景:(1) 快速草图 + 精确求解器的 CAD 流程;(2) 蒙特卡洛采样 + 约束传播;(3) 草稿翻译 + 术语库校对;(4) 低价传感器阵列 + 融合滤波。
灵感五:问「哪里」而非「多频」——条件化统计远胜边际统计。
核心思想:把全局频率(边际统计)换成同上下文下的条件份额(条件统计),信息量与鲁棒性同时提升;凡是「位置/语境影响含义」的信号都应条件化。
论文证据:同一规则 concat 在程序末尾与中间获得不同支持;AUC 领先(32.2% vs 18.2%)证明条件化让搜索更早命中——同样数据条件下化后的信息效率高出一个量级(到达提案区域快 12 倍)。
推广场景:(1) 词向量——上下文相关表示胜静态词袋;(2) 医疗诊断——条件于病史的症状权重;(3) 异常检测——条件于时段/负载的基线;(4) 销售策略——条件于客户旅程阶段的触点优先级。