想象你走进一家魔法餐厅,菜单是空白的,服务员只递来一张卡片:“请描述你想吃的菜。”你说“来一道暖胃的鸡汤”,后厨叮叮当当一阵,一碗热腾腾的鸡汤就端到了面前——你全程只说了“想要什么”,没说“半只鸡、三碗水、小火炖四十分钟”。这就是LLM辅助程序合成(LLM-Assisted Program Synthesis)的体验:用户用自然语言描述需求(点菜),大语言模型自动生成满足需求的代码(做菜),再通过测试验证确保这道菜真的能上桌。 过去让计算机做事,必须像给机器厨师递精准食谱一样,把每一步逻辑写到分毫不差。如今,LLM大厨读过的代码比任何人一辈子写的都多,它能从一句“帮我按金额排序订单”的模糊描述里,直接端出可运行的Python函数。当然,大厨也会失手——菜可能太咸、可能没熟,于是我们需要“试吃”(测试)、“反馈”(报错信息回传)、“回锅”(自我修正),甚至请“食品安全实验室”(形式化验证)把关。这篇文章就用“开一家魔法餐厅”的视角,把LLM程序合成的原理、方法与工程实践一一拆解,让你从“食客”变成“懂行的餐厅老板”。

岗位关联

对AI从业者而言,LLM程序合成是通往“AI自动写代码”时代的核心技能:掌握它的完整闭环,你才能在设计AI编程工具、搭建代码智能平台时做出真正可靠的产品决策。

目录
  1. 可视化演示
  2. 程序合成基础:从“点菜描述”到“自动上菜”
  3. LLM生成原理:大厨的肌肉记忆
  4. Prompt策略:点菜的话术艺术
  5. 执行反馈闭环:试菜与回锅
  6. 验证与保障:后厨质检三层关
  7. 工具增强:大厨的装备库
  8. 评测基准:厨艺大赛怎么打分
  9. 工程实践:把合成流水线搬进后厨
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

程序合成基础:从“点菜描述”到“自动上菜”

想象你走进一家新开张的“魔法餐厅”,菜单上只有一句话:“请描述你想吃的菜。”你说“想要一道暖胃的鸡汤”,后厨叮叮当当一阵,一碗热腾腾的鸡汤就端出来了。注意,你只说了“想要什么”,没写“半只鸡、三碗水、小火炖四十分钟”。程序合成(Program Synthesis)就是这样的“魔法后厨”:你给出需求规格(specification),机器自动产出满足需求的程序,而不是你手写每一行代码。

在LLM出现之前,这家餐厅的“后厨”更像一位德国机械师:你递给它一份精密到毫米的工程图纸,它按图施工,精确但死板。传统程序合成需要在一个巨大的程序空间里搜索,往往只能处理教科书级别的小问题。而LLM大厨读过海量代码,像尝遍世间百味的老饕,一句“要一盅暖胃鸡汤”,它就知道放姜片而不是辣椒——这就是LLM带来的质变:合成从“搜索”变成了“生成”。

形式化地说,程序合成要解决的问题是:给定一个逻辑规格φ,寻找一个程序P,使得对所有可能的输入x,执行P(x)的结果都满足φ。在LLM时代,规格可以放宽为自然语言描述,程序可以是Python、SQL或任何语言。

传统后厨靠“精确图纸”合成菜品,LLM让“一句话描述”就能变成美味程序。但注意,规格越模糊,翻车概率越大——顾客说“随便来点吃的”和大厨翻车的相关性,恰恰是后面所有章节要解决的问题。

∃P, ∀x, execute⁡(P,x) satisfies ϕ(x)\displaystyle \exists P,\ \forall x,\ \operatorname{execute}(P, x) \ \text{satisfies}\ \phi(x)
这个公式是“程序合成问题”的标准定义:存在一个程序,让所有输入都满足规格要求。换成人话就是——总有一道菜,任何人来吃都觉得满意。
  1. 存在量词 ∃存在一个程序P。我们不需要“所有程序”都满足条件,只要找到一个能用的就行,就像餐厅只需要有一位大厨能把鸡汤做好。
    ∃P\exists P
  2. 全称量词 ∀对所有的输入x。程序必须对任何输入都正确,而不只是你举的那一个例子;就像这碗鸡汤无论谁来喝都得觉得暖胃。
    ∀x\forall x
  3. 执行关系 execute(P,x)把程序P应用到输入x上得到的输出。这是验证的手段——把菜端出来给顾客尝一尝。
    execute⁡(P,x)\operatorname{execute}(P,x)
  4. 满足规格 φ(x)输出符合用户的需求描述。规格是验收标准,菜好不好吃最终由它说了算。
    satisfies ϕ(x)\text{satisfies}\ \phi(x)

LLM生成原理:大厨的肌肉记忆

大厨随手就能做菜,不是因为背下了十万个菜谱,而是因为炒过的菜足够多,食材的脾气、调料的性格已经长在他手里。LLM也是一样:它在大规模代码语料上预训练,学会了代码世界的“语感”——变量怎么命名、循环怎么写、函数如何组织。你给它一个函数签名,它自然知道后面该接什么。

这种“语感”的本质是条件概率。LLM不是一个查表的程序,它按token逐个预测下一个符号。当你说“写一个函数,返回数组最大值”,模型看到前面这些词,就开始计算下一个最可能的词是什么——就像大厨听到“清蒸”就知道后面跟着“鱼”,听到“红烧”就想到“肉”。

自回归生成的核心公式如下:

理解了“下一个词预测”,就理解了为什么LLM会幻觉:它擅长生产“看起来像代码”的序列,而不是“保证正确”的程序。就像大厨光靠手感做一百次菜,偶尔也会失手——所以后面我们引入“试吃”和“质检”环节,用执行的硬反馈来弥补生成的软语感。

P(w1w2⋯wn)=∏i=1nP(wi∣w1w2⋯wi−1)\displaystyle P(w_1 w_2 \cdots w_n) = \prod_{i=1}^{n} P(w_i \mid w_1 w_2 \cdots w_{i-1})
整段代码的概率等于每个“下一个token”概率的乘积。大厨做菜时每一步都看锅里已有什么,LLM生成时每一步都看前面已写了什么。
  1. 文本序列 w₁…wₙ一段完整的程序代码可以被看作一个token序列。就像一道鸡汤从“姜片”到“葱花”是一个个步骤串起来的。
    w1w2⋯wnw_1 w_2 \cdots w_n
  2. 连乘 ∏整段代码的概率等于每个token条件概率的乘积。每一步都依赖前面的所有步骤,一步错可能步步错。
    ∏i=1n\prod_{i=1}^{n}
  3. 条件概率 P(wᵢ | w₁…wᵢ₋₁)模型生成第i个token时,会看到之前所有的token。这就是为什么上下文窗口重要——大厨下葱前得先看看锅里已经有什么。
    P(wi∣w1⋯wi−1)P(w_i \mid w_1 \cdots w_{i-1})

Prompt策略:点菜的话术艺术

同一家餐厅,有人点菜一句“看着办”,端上来的可能是惊喜也可能是惊吓;有人详细说“少油、不要香菜、微辣、用鲈鱼”,大厨几乎不会失手。在LLM程序合成中,prompt就是你点菜的“话术”——话说得好,菜就做得好。

三种经典话术:零样本(zero-shot)直接说需求,适合简单任务,就像“来碗白米饭”;少样本(few-shot)给出几个“输入-期望输出”的例子,让模型照葫芦画瓢,就像给大厨看三张参考照片说“按这个风格做”;思维链(Chain-of-Thought)让模型先拆解思路再写代码,把中间推理步骤显式展开,就像大厨一边做一边念叨“先焯水去腥,再下姜爆香,最后放鸡块”。

少样本的本质是在prompt中嵌入示例,让模型在“现场学习”新任务。形式化地看,生成是给定示例与需求后的条件概率:

点菜话术不是越长越好。写得混乱的prompt,就像啰嗦又自相矛盾的顾客,大厨也会懵。真正好的话术是:少而精的示例 + 明确的需求 + 必要时显式的思路引导。记住,你点的不是“字多”,而是“信息密度”。

P(program∣E1,E2,…,Ek, request)\displaystyle P(\text{program} \mid E_1, E_2, \ldots, E_k,\ \text{request})
模型生成程序时,既参考示例的风格,又满足需求的内容。示例是风格模板,需求是口味指南。
  1. 示例集合 E₁…Eₖ提示词里给出的k个“输入→输出→代码”对。就像给大厨看三张参考照片,告诉他“按这个风格来做”。
    E1,E2,…,EkE_1, E_2, \ldots, E_k
  2. request 需求描述用户对目标程序的功能描述。它决定了菜品的核心口味——是酸甜还是麻辣。
    request\text{request}
  3. 目标 program模型要生成的程序。它是给定示例和需求后,条件概率最大的那个token序列。
    program\text{program}
  4. 条件概率 P(·|·)整个生成在“模仿示例”与“满足需求”之间做平衡。示例太少,模型自由发挥;示例太多,反而可能过度模仿示例的细节,忽略真实需求。
    P(program∣⋯ )P(\text{program} \mid \cdots)

执行反馈闭环:试菜与回锅

菜上了桌,顾客动筷子一尝:“太咸了。”大厨二话不说端回去,加点糖提鲜,重新调味再端出来。LLM程序合成最强大的能力,正是这种“试菜-回锅”循环:把生成的代码真正跑起来,拿测试结果和报错信息喂回给模型,让它自己改。

这个循环有个专业名字:自我修正(Self-Debug / Self-Refinement)。第一轮生成只是“盲炒”,完全凭肌肉记忆;执行反馈让模型多了一双眼睛——它能看到程序在哪个输入上栽了跟头,报错信息是什么。就像大厨终于知道“太咸”是因为盐罐手滑,而不是整道菜重来。

形式化地,每一轮迭代可以表示为:

关键点在于:执行反馈是“闭环”而非“单向管道”。如果不把错误信息喂回去,模型的修正就是盲改——就像大厨只听一句“不好吃”却不知道是咸了还是糊了,第二次大概率还会翻车。所以工程里,测试用例的质量直接决定了合成成功率的上下限。

Pt+1=Refine⁡(Pt, Exec⁡(Pt,T))\displaystyle P_{t+1} = \operatorname{Refine}\bigl(P_t,\ \operatorname{Exec}(P_t, \mathcal{T})\bigr)
下一轮的候选程序,是LLM阅读了“本轮程序 + 测试执行结果”后重新生成的改进版本。没有执行反馈的生成是盲猜,有了执行反馈的生成才是“回锅”。
  1. 当前程序 P_t第t轮生成的候选程序,是这轮“端上桌的菜”。
    PtP_t
  2. 测试集 T一组精心设计的测试用例(输入 + 期望输出),相当于试吃员的评分标准——咸淡、火候、摆盘都要打分。
    T\mathcal{T}
  3. 执行结果 Exec(P_t, T)把程序交给真实解释器跑一遍,得到通过率、具体失败用例和错误信息。这是“顾客吃了一口后的真实反应”。
    Exec⁡(Pt,T)\operatorname{Exec}(P_t, \mathcal{T})
  4. 修正器 RefineLLM把“原始需求 + 失败的代码 + 错误信息”一起读进去,输出改进后的程序。这是大厨听完反馈后重新开火。
    Refine⁡(Pt, ⋅)\operatorname{Refine}\bigl(P_t,\ \cdot\bigr)

验证与保障:后厨质检三层关

一家正经餐厅不能只靠大厨拍胸脯说“放心,吃了没事”,后厨需要食材抽检、温度记录、留样化验。LLM生成的代码也是这样:它写得再流畅,也得过质检关。从轻到重有三道:静态检查、单元测试、形式化验证。

静态分析和类型检查是“看外观”——变量有没有定义、类型合不合理,像检查食材新不新鲜;单元测试是“试吃”——实际运行看结果对不对,像顾客动筷子;形式化验证则是“进实验室化验”——用数学方法证明程序在所有可能输入下都满足规格,像用仪器证明每一克成分都达标。

形式化验证的核心工具是Hoare三元组,它描述“程序执行前后的状态关系”:

现实中,完整的形式化验证成本高、周期长,就像把每道菜都送去实验室化验不现实。工程里通常的组合是:测试+静态检查优先(便宜高效,覆盖大部分问题),对核心模块才上形式化验证(昂贵保险,提供最强保证)。这就像后厨每天做抽检,但只有招牌菜才送实验室全项检测。

{ Pre⁡ } P { Post⁡ }\displaystyle \{\ \operatorname{Pre}\ \}\ P\ \{\ \operatorname{Post}\ \}
只要程序开始前前置条件成立,执行完程序后后置条件必然成立。这是用数学语言写下的“如果食材合格,那么出锅的菜一定达标”的保证书。
  1. 前置条件 Pre程序开始前必须成立的状态。就像“厨房必须有新鲜的鸡和干净的水”。
    Pre⁡\operatorname{Pre}
  2. 程序 P(这里的P是验证对象)要验证的代码段,相当于“炖鸡汤的完整流程”。
    PP
  3. 后置条件 Post程序结束后必须成立的状态。比如“出锅时汤温不低于60度,且无任何异物”。
    Post⁡\operatorname{Post}
  4. 花括号 { }Hoare逻辑中用花括号包裹前后条件,语义是“只要开始时Pre成立,执行完P后Post必然成立”。
    { ⋅ } P { ⋅ }\{\ \cdot\ \}\ P\ \{\ \cdot\ \}

工具增强:大厨的装备库

再厉害的大厨,也不可能把全世界所有食材和菜谱都装在脑子里。遇到没见过的食材,他会翻菜谱、查笔记、问师傅。LLM也一样——它的知识来自训练数据,一旦遇到知识边界外的问题(比如某个私有API、最新版本库的用法),就得靠外部工具帮忙。

两大核心工具:检索增强生成(RAG)和代码解释器。RAG是说在让模型生成代码前,先从代码库或文档中检索相关片段,把它塞进prompt作为参考——就像大厨快速翻到“相似菜谱”那一页;代码解释器则是给模型一个能真跑的沙盒,让模型能自己验证中间结果——就像大厨有自己的小灶台,可以偷偷试味道。

形式化地,检索增强的生成过程是:

工具增强的核心思想是“不硬背,会查”:把模型从“记忆型选手”变成“检索型选手”。就像大厨不必背完所有菜谱,但一定要知道怎么查菜谱、查完怎么活用。这正是RAG、MCP(模型上下文协议)等工程方向的理论根基。

P(program∣R(D,q), q)\displaystyle P(\text{program} \mid \mathcal{R}(D, q),\ q)
模型生成时,既看到原始需求,也看到从代码库检索出的参考片段。参考不是照抄,而是启发——就像大厨翻菜谱后,结合自己的风格加以改良。
  1. 查询 q用户的需求描述,比如“怎么把Pandas的DataFrame按列去重”。
    qq
  2. 检索函数 R(D,q)从代码库/文档D中检索出与q最相关的一组代码片段。相当于大厨快速翻到“相似菜谱”那一页。
    R(D,q)\mathcal{R}(D, q)
  3. 检索结果放入上下文模型生成时同时看到原始需求和参考片段,特别适合新库、私有API等训练数据覆盖不到的场景。
    R(D,q), q\mathcal{R}(D,q),\ q
  4. 输出 program带着参考生成的程序。注意参考不一定照抄——模型会参考风格和思路,再结合具体需求改写。
    program\text{program}

评测基准:厨艺大赛怎么打分

怎么科学地评价一位大厨的水平?不能只靠“我觉得好吃”。行业大赛定一套标准化题目、盲测打分,才能让不同大厨同台竞技。LLM程序合成领域也有自己的“厨艺大赛”——HumanEval和MBPP。

HumanEval包含164道手写编程题(函数签名 + docstring + 单元测试),要求模型补充函数体;MBPP是另一个偏初级的基准,是“编程入门题”集合。这些题库固定、不参与模型训练,只用来给模型能力打分。但注意,就像大赛评委也可能会被“选手提前背题”骗过,如果题库泄露到训练语料里(数据污染),分数就会虚高。

评测标准通常用pass@k——允许模型对每道题生成k个候选,只要有一个通过全部测试,这道题就算通过。这就好比赛级厨艺考核:允许大厨试做k次,只要有一次让评委满意就算过关。

评测基准不是万能的。HumanEval只有164题,覆盖不了真实业务的复杂性;数据污染让分数越来越“虚胖”。所以工程实践里,除了围观榜单,更重要的是搭一套自己的“内部厨艺大赛”——用真实业务需求构建评测集,衡量模型在自家厨房里的真实水平。

pass@k=1−(n−ck)(nk)\displaystyle \text{pass@}k = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}
从n个候选程序中随机选k个,至少有一个合格候选的概率。k越大,容错率越高,分数越好看——允许试做的次数越多,成功门槛越低。
  1. n 个候选模型对某道题生成的n个候选程序,n≥k。相当于大厨端出n盘菜,我们只随机端走k盘去评审。
    nn
  2. 通过测试的数量 cn个候选中只有c个能通过全部单元测试——这是“做对了的菜”。
    cc
  3. 组合数 C(n,k)从n个候选中选k个的所有可能选法数。
    (nk)\binom{n}{k}
  4. C(n-c,k)/C(n,k)选出的k个候选里“全部是错菜”的概率——运气差到极点的概率。
    (n−ck)(nk)\frac{\binom{n-c}{k}}{\binom{n}{k}}
  5. 1 − …至少有一个合格候选的概率。k越大,这个概率越高——相当于允许多做几次,只求一次成功。
    1−⋯1 - \cdots

工程实践:把合成流水线搬进后厨

从米其林到社区食堂,每个后厨的菜品定位不同。LLM程序合成落地时也要考虑“菜系”:哪些场景适合机器炒,哪些还得老师傅上。成熟的落地场景包括代码补全(Copilot类,边写边补)、测试生成(根据函数签名自动生成单元测试)、代码迁移(把Python翻译成Java)、NL2SQL(自然语言查数据库)。

落地最大的约束是“成功率”和“成本”。如果一个辅助工具的采纳率太低,开发者会宁可手写——就像顾客觉得大厨做的菜还不如自己泡面,就不会再来了。成本优化策略包括:用小模型过滤简单任务、只对复杂任务调用大模型;用缓存复用相似结果;用检索增强减小上下文长度。这些都是“后厨降本增效”的手段。

衡量合成工具生产力的核心指标是采纳率:

LLM辅助程序合成正在从实验室走向生产环境。它的完整工作流——需求描述→生成→测试/验证→反馈→迭代——本质上是一条“有质检的做菜流水线”。对工程师而言,掌握这条流水线的每个环节,就掌握了把“会说话的需求”变成“可信赖的代码”的能力。

Raccept=NpassNgenerated\displaystyle R_{\text{accept}} = \frac{N_{\text{pass}}}{N_{\text{generated}}}
采纳率 = 通过验证并被开发者直接使用的代码数 ÷ 生成的代码总数。它是合成工具能否在团队里活下来的生死线。
  1. 生成的候选总数 N_generatedLLM在一段时间内生成的所有程序片段。就像后厨一天出品的所有实验菜。
    NgeneratedN_{\text{generated}}
  2. 通过验证的数量 N_pass通过了单元测试、静态检查,并被开发者直接采纳的候选数。相当于“顾客愿意买单的菜”。
    NpassN_{\text{pass}}
  3. 采纳率 R两者之比。如果一个工具采纳率长期低于60%,团队很快就会弃用——因为人工检查的成本已经超过了手写。
    RacceptR_{\text{accept}}

AI 应用场景

场景一:IDE 中的 AI 代码补全与生成

阶段:代码生成(Generation)

解决的问题:开发者大量时间花在写样板代码和查文档上。传统方式是手写每个函数、每个循环;AI辅助合成让开发者只需写下函数签名或一行注释,LLM即可生成候选实现,再由开发者在IDE中运行测试验证。

具体案例:一位后端工程师在写订单管理接口,输入注释“按金额降序排序订单”,Copilot类工具即时生成排序代码。工程师点击运行单元测试,测试失败时模型根据报错自动生成修复补丁,最终通过并合入代码库。一个版本迭代周期从半天缩短到半小时。

场景二:自动化单元测试生成

阶段:验证与反馈(Testing & Feedback)

解决的问题:人工写测试覆盖率低、动力不足。LLM根据函数签名和参数类型生成边界测试用例,执行后把未覆盖分支回传模型,迭代补充测试,实现“测试先行”的质量闭环。

具体案例:DevOps流水线中,每次代码合并请求触发AI测试生成器:LLM为改动的函数生成10个边界测试用例,全部通过才能合并;失败时详细报错喂给LLM生成补丁,形成“生成→执行→反馈→修复”的自动化质检流水线。

场景三:老系统代码迁移

阶段:代码转换与验证(Transformation & Validation)

解决的问题:把老旧COBOL/Java系统迁移到现代语言,人工翻译效率低、风险高。LLM逐模块翻译后做等价性测试,对输出不一致的case回传模型修正,迁移速度提升数倍。

具体案例:某银行把核心账务模块从COBOL迁移到Java:LLM生成候选Java代码,自动化测试用同一组账户数据跑新旧两套系统,比对输出结果。初版等价率78%,经三个迭代轮次后提升到99%,剩余1%由资深工程师人工处理。

Python 演示

python55 行
"""LLM辅助程序合成最小闭环演示用"需求描述-生成候选-执行测试-反馈修正"演示完整合成循环,真实系统中 rule_based_generate 由大语言模型扮演。"""SPEC = "给定一个整数列表,返回其中的最大值"TEST_CASES = [    ([3, 1, 4], 4),    ([7, 7, 2], 7),    ([-1, -5, -3], -1),    ([9], 9),]  def rule_based_generate(spec, feedback=""):    """模拟LLM根据需求与反馈生成候选代码"""    if feedback:        return "def solve(xs):\n    return max(xs)"    return "def solve(xs):\n    return xs[0]"  def verify(code, cases):    """在真实Python解释器中执行候选程序并统计通过数"""    namespace = {}    exec(code, namespace)    passed = 0    for xs, expected in cases:        try:            got = namespace["solve"](xs)        except Exception as exc:            return passed, "运行崩溃: %s -- 输入%s" % (exc, xs)        if got != expected:            return passed, "期望%s 实得%s -- 输入%s" % (expected, got, xs)        passed += 1    return passed, "全部通过"  def synthesize(spec, cases, max_rounds=3):    """主循环: 生成 -> 执行 -> 反馈 -> 重新生成"""    feedback = ""    for rnd in range(1, max_rounds + 1):        print("=== 第%d轮: LLM生成候选 ===" % rnd)        code = rule_based_generate(spec, feedback)        print(code + "\n")        passed, msg = verify(code, cases)        print("测试通过 %d/%d -> %s\n" % (passed, len(cases), msg))        if passed == len(cases):            print("合成成功!")            return code        feedback = msg    print("达到最大轮次, 合成失败")    return None  if __name__ == "__main__":    synthesize(SPEC, TEST_CASES)
目的:用最小可运行示例演示LLM程序合成的核心闭环——第一轮LLM凭经验生成“取第一个元素”的代码,测试失败;错误反馈回传后,第二轮生成“取最大值”的正确代码并全部通过。真实系统中,规则生成器被替换为LLM API调用,架构完全相同。
  1. exec(code, namespace)把LLM生成的代码字符串放进真实解释器执行,模拟“上桌试吃”。
  2. if feedback:模拟LLM观察到错误反馈后调整生成策略,这是“回锅”动作。
  3. namespace["solve"](xs)调用生成的函数并传入测试输入,得到实际输出。
  4. passed, msg = verify(code, cases)执行反馈的核心:不仅统计通过数,还返回具体错误信息。
  5. feedback = msg错误信息作为下一轮提示词的补充材料,喂回生成器。
  6. if __name__ == "__main__":可执行入口,直接运行即可观察合成闭环。

常见误区

误区“我用AI写代码,它直接给我能跑的程序,所以生成一次就够了。如果报错,说明模型不行。”

正解LLM是“语感”型选手,不是“证明”型选手。它擅长生成看起来合理的代码,但无法保证逻辑正确。专业工作流默认接受“第一轮大概率有错”的现实,通过执行测试拿到错误信息、再迭代修正——就像大厨默认第一锅菜需要试味。真正可靠的程序合成,生成只占三成,验证与反馈占七成。

为何常见:Demo视频通常只展示成功画面,且模型能力确实在快速变强,容易让人产生“一次到位”的错觉。实际上,复杂业务场景的失败率远高于演示用例。

误区“既然few-shot有效,那我就多给几个例子,把整个代码库相关的代码都塞进prompt,模型肯定写得更好。”

正解上下文太长会稀释模型的注意力("Lost in the Middle"现象),模型反而抓不住重点。示例要少而精:选3-5个与当前任务最相似的、风格一致的示例,远比堆砌30个互相矛盾的例子有效。就像给大厨看三张清晰的照片好过扔给他一百张模糊的菜谱复印件。

为何常见:看到few-shot有效就想“加倍”是直觉思维。但模型的注意力是有限资源,信噪比才是关键,而不是信息绝对量。

误区“单元测试全过了,代码肯定没问题,可以放心上线。”

正解测试只能证明“你测过的那些输入是对的”,无法覆盖所有边界情况——就像试吃员尝了三口说好吃,不代表每一口都没问题。对核心模块,静态分析、模糊测试、甚至形式化验证才能提供更强保证。工程上采用“测试+静态检查+人工评审”的组合拳,而不是迷信单一的“测试通过”。

为何常见:测试通过是明确的绿色信号,容易给人确定性错觉。尤其是LLM生成的测试用例往往和生成代码“同源”,两者可能共享同样的错误假设,形成“双重幻觉”。

误区“网上说'Let's think step by step'能大幅提升效果,那我所有任务都加这句话,肯定没错。”

正解思维链适合需要多步推理的复杂任务(比如算法题、多条件逻辑),但对简单任务(比如“把字符串转成小写”)反而会降低质量、增加推理成本和延迟——就像煮泡面也要先写一篇“烹饪论文”,纯属浪费时间。工程上要先判断任务复杂度,再选择提示策略:简单任务零样本直出,复杂任务才上思维链。

为何常见:社交媒体上大量CoT成功案例形成思维惯性,但那些案例集中在复杂推理场景。幸存者偏差让人忽略了“简单任务加CoT反而变差”的另一面。

面试题

Q1. 程序合成(Program Synthesis)的核心目标是:

单选题 · easy

  • A.由需求规格自动生成满足条件的程序← 正确答案
  • B.把高级语言编译成机器码
  • C.提高程序运行效率的自动优化
  • D.从代码中反向提取需求规格

答案:A。程序合成的本质是“从规格到程序”的自动转换,就像餐厅从顾客描述自动做出菜。B是编译器的职责,C是编译器优化,D是逆向工程(需求提取),都不符合“合成”的定义。

Q2. 在“执行反馈闭环”中,LLM下一轮生成时额外获得的输入是:

单选题 · easy

  • A.更多预训练数据
  • B.更长的上下文窗口
  • C.测试结果与错误信息← 正确答案
  • D.全新的损失函数

答案:C。执行反馈闭环的精髓是把运行结果(通过率、具体失败原因)作为下一轮生成的补充上下文——就像顾客告诉大厨“太咸了”,大厨才知道回锅加糖。预训练数据和损失函数在推理阶段不可变,上下文窗口长度是硬件限制,都不构成“额外输入”。

Q3. pass@k 指标的正确含义是:

单选题 · mid

  • A.生成k个候选,一个通过即算成功← 正确答案
  • B.第k次生成的代码必定正确
  • C.代码需要运行k次才能验证
  • D.模型需要k轮训练才能收敛

答案:A。pass@k允许模型生成k个候选程序,只要有一个通过全部测试,这道题就算通过。好比厨艺大赛允许大厨试做k次,一次成功即过关。B把k误解为“必然成功”,C把k误解为“验证次数”,D把k误解为“训练轮数”,都不对。

Q4. 少样本提示与零样本提示的区别在于:

单选题 · mid

  • A.少样本提示需要较小的模型
  • B.少样本提示必须修改模型权重
  • C.零样本提示完全无法理解需求
  • D.少样本带示例,零样本只给需求← 正确答案

答案:D。少样本(few-shot)在prompt中嵌入若干“输入-输出”示例,让模型现场模仿;零样本(zero-shot)只给需求描述。就像给大厨看参考照片(少样本)vs 只口头描述(零样本)。A模型大小无关,B少样本不修改权重(那是微调),C零样本只是效果差些,并非完全不懂。

Q5. 思维链提示提升复杂合成任务准确率的根本原因是:

单选题 · hard

  • A.输出代码更长,覆盖更多逻辑
  • B.显式展开推理步骤,减少中间遗漏← 正确答案
  • C.自动执行测试用例并回传结果
  • D.每轮生成前重新预训练模型

答案:B。思维链(CoT)让模型把隐式的推理过程显式写成中间步骤,相当于大厨边做边念叨“先焯水、再爆香、后下鸡”,每一步都落在文本上,就减少了跳跃式推理造成的遗漏。A只是副产品,C是执行反馈(不是CoT),D在推理阶段不可能。

Q6. 关于检索增强生成(RAG)在代码合成中的作用,正确的是:

单选题 · mid

  • A.替代了测试用例的验证功能
  • B.让模型推理时更新网络参数
  • C.从代码库检索相似片段作参考← 正确答案
  • D.使模型无需训练支持新语言

答案:C。RAG在生成前从代码库检索相关片段作为上下文参考,就像大厨遇到不熟悉的食材翻菜谱。它不替代测试(A)、不改模型参数(B)、也不能让模型“支持”从未见过的语言(D,只能说提供参考)。

Q7. Hoare三元组 {Pre} P {Post} 的正确语义是:

单选题 · extreme

  • A.只要结束状态成立,开始状态就成立
  • B.执行前条件成立,执行后条件必然成立← 正确答案
  • C.程序一定能够在有限步骤内终止
  • D.后置条件成立时程序必然成功执行

答案:B。Hoare逻辑的方向是“前→后”:如果执行前Pre成立,那么执行完P后Post一定成立。就像“食材合格→按流程炖→出锅达标”,因果方向不能倒转。A是逆命题不成立,C是终止性问题(与Hoare三元组独立),D因果顺序颠倒。

Q8. 下面哪些手段可以在LLM程序合成中有效降低幻觉概率?

多选题 · mid

  • A.执行测试并把错误信息回传模型← 正确答案
  • B.提供少量但精准的示例← 正确答案
  • C.复杂任务使用思维链提示← 正确答案
  • D.生成后不验证直接合并主分支

答案:ABC。执行反馈让模型知道哪里错了(A),精准示例缩小生成空间(B),思维链减少推理遗漏(C),这三者都能降低幻觉。D恰恰相反——不做验证直接合入,就像菜不试吃就端给顾客,是最危险的。

Q9. 关于HumanEval与MBPP基准,正确的说法有:

多选题 · hard

  • A.固定离线题库,不参与模型训练← 正确答案
  • B.通过率可能受数据污染影响虚高← 正确答案
  • C.覆盖真实业务场景绝大部分复杂度
  • D.pass@k是常用评测指标← 正确答案

答案:ABD。HumanEval和MBPP是固定的离线评测集,设计上不参与训练(A对);但如果题库意外混入训练语料(数据污染),模型“背过答案”,分数会虚高(B对);pass@k是标准打分方式(D对)。C错——只有一两百道题,覆盖不了真实业务的复杂度,这正是需要自建评测集的原因。

Q10. 下列哪些属于LLM辅助程序合成的典型工程应用?

多选题 · easy

  • A.IDE中逐行代码补全← 正确答案
  • B.依据函数签名自动生成单元测试← 正确答案
  • C.跨语言代码迁移项目← 正确答案
  • D.操作系统时钟中断处理

答案:ABC。代码补全、测试生成、跨语言迁移都是程序合成的成熟应用场景,分别对应“生成代码、生成验证代码、转换代码”。D是操作系统内核底层任务,对正确性和实时性要求极高,目前LLM合成无法胜任,且不属于常见工程落地场景。