论文地址:Self-Planning Code Generation with Large Language Models
摘要
尽管大型语言模型(LLMs)在代码生成方面展现出了令人瞩目的能力,但它们在处理人类提供的复杂意图时仍然困难重重。众所周知,人类通常会在实施之前通过规划来分解复杂问题并安排解决步骤。为此,我们将规划引入代码生成,以帮助模型理解复杂意图并降低解决问题的难度。本文提出了一种基于大型语言模型的自规划代码生成方法,该方法包括两个阶段,即规划阶段和实现阶段。具体来说,在规划阶段,LLM 通过少样本提示从意图中规划出简洁的解决步骤。随后,在实现阶段,模型在前述解决步骤的指导下逐步生成代码。我们在多种编程语言的不同代码生成基准上进行了广泛的实验。实验结果表明,与直接代码生成相比,自规划代码生成的 Pass@1 相对提升最高达 25.4%,与代码生成的思维链(Chain-of-Thought)相比最高达 11.9%。此外,通过人工评估发现,我们的自规划方法还提高了生成代码在正确性、可读性和鲁棒性方面的质量。
论文总结
本论文提出了一种创新的自规划代码生成方法,旨在解决大型语言模型在处理复杂代码生成任务时的局限性。该方法通过将问题分解为规划和实现两个阶段,利用少样本提示让 LLM 自主生成解决步骤,从而显著提升了代码生成的准确性和效率。实验结果表明,自规划方法在多个基准测试中均优于直接生成和思维链等基线方法,尤其在处理高复杂度问题时优势更为明显。此外,人工评估验证了生成代码在质量上的提升,证明了规划阶段在分解问题和引导模型理解意图方面的有效性。该研究为提升 LLMs 在代码生成任务中的表现提供了新的思路和方法,具有重要的理论和实际应用价值。
一、研究动机
-
LLM 代码生成的复杂意图处理瓶颈:大型语言模型(LLMs)在直接处理人类复杂编程意图时,因缺乏问题分解能力,导致代码逻辑混乱(如直接生成时硬编码错误、递归逻辑错误,见图中 Direct 组对比)。例如,生成涉及多步骤逻辑(如斐波那契序列生成 + 质数检查 + 计数筛选)的代码时,模型难以一次性整合所有子任务,功能正确性和鲁棒性差。
-
现有方法的不足:
- 直接生成(Direct):无规划阶段,复杂意图下代码逻辑碎片化(如硬编码固定值,未实现动态序列生成,见图中红色叉号案例)。
- 思维链(Code CoT):虽分步思考,但未显式分离 "逻辑规划" 与 "代码实现",对代码任务的结构化引导不足(如仅用自然语言描述步骤,未形成可执行的代码分步计划,导致实现阶段仍需处理全局复杂逻辑)。
二、设计原理
- 方法概述
- 规划阶段:利用 LLM 的少样本提示能力,将用户意图抽象分解为一系列易于解决的子问题步骤。提示由若干意图 - 计划示例组成,每个示例包含自然语言描述的意图和对应的结构化计划(如编号列表形式的步骤)。通过将测试意图与提示拼接后输入 LLM,生成对应的解决计划。
- 实现阶段:将规划阶段生成的计划附加到意图后,作为 LLM 的输入,指导模型逐步生成符合意图的代码。通过条件独立性假设简化概率模型,将代码生成概率分解为计划生成概率和基于计划的代码生成概率。
- 提示构造原则
- 计划以编号列表形式组织,每一步为简洁的动词开头的祈使句,避免过度细节,聚焦于抽象的子任务(如 "检查数字是否为质数" 而非具体实现逻辑)。
- 允许计划中包含条件(if)和循环(loop)等结构,以处理复杂逻辑,但保持步骤的顺序执行和逻辑清晰。
- 实验设计
- 基准测试:采用 MBPP、HumanEval 及其多语言版本 HumanEval-X(涵盖 Python、Java、Go、JavaScript 等)和扩展测试用例版本 MBPP-ET、HumanEval-ET。
- 基线方法:包括直接生成(Direct)、代码思维链(Code CoT)、真值规划(Ground-truth Planning)等,对比评估自规划方法的性能。
- 评估指标:执行指标 Pass@k、AvgPassRatio,匹配指标 CodeBLEU,以及人工评估代码的正确性、可读性和鲁棒性。
- 关键原理
- 问题分解:通过规划将复杂意图分解为子任务,降低 LLM 直接生成复杂代码的难度,提高问题解决的可管理性。
- 少样本提示的有效性:利用 LLM 的上下文学习能力,仅需少量示例即可引导模型生成合理计划,避免了大规模标注数据的需求。
- 阶段解耦:将规划与实现分离,使模型在规划阶段专注于逻辑抽象,在实现阶段专注于代码生成,提升了任务处理的专注度和效率。
三、数据集来源
-
MBPP(Multi-modal Benchmark for Programming Problems):包含自然语言描述的编程问题及参考代码,用于评估代码生成模型的多模态处理能力。
-
HumanEval:由 OpenAI 提出的代码生成基准,包含编程问题、参考代码和单元测试用例,专注于 Python 语言。
-
HumanEval-X:HumanEval 的多语言扩展,涵盖 Python、Java、Go、JavaScript 等主流编程语言,支持跨语言代码生成评估。
-
扩展测试用例(MBPP-ET、HumanEval-ET):对原始数据集的问题进行扩展(如增加复杂度、多步骤逻辑),用于验证模型在更复杂场景下的表现。
四、实验探究问题
*RQ1:*与基线方法相比,自规划方法在代码生成中的表现如何?
*RQ2:*基于不同 LLM 的自我规划方法表现如何?
*RQ3:*自我规划方法的最佳设计是什么?
*RQ4:*自规划方法在多语言代码生成中表现如何?
*RQ5:*问题的复杂性如何影响自我规划?
五、实验对照组设计
-
直接生成(Direct)
- 描述:LLM 直接以用户意图为输入,无规划阶段,直接生成代码。
- 作用: baseline,验证规划阶段对复杂意图分解的必要性。
-
代码思维链(Code CoT)
- 描述:LLM 通过自然语言分步提示(如 "首先...,然后...")生成代码,模拟分步思考但未显式分离规划与实现阶段。
- 作用:对比 "隐含分步" 与 "显式两阶段(规划 + 实现)" 的效果,验证规划阶段的结构优势。
-
真值规划(Ground-truth Planning)
- 描述:使用人工编写的完美计划(而非 LLM 生成的计划)作为实现阶段输入,生成代码。
- 作用:作为理想上限,验证规划阶段的理论最优性能,凸显 LLM 生成计划的近似有效性。
六、实验评价指标
-
核心执行指标:
- Pass@k(如 Pass@1):单次生成通过测试的概率,衡量代码功能正确性(关键指标,反映是否满足意图)。
- AvgPassRatio:多次生成的平均通过率,体现模型稳定性。
-
代码匹配指标:
- CodeBLEU:衡量生成代码与参考代码的句法和语义匹配度,补充 Pass@k(如代码逻辑正确但实现不同时的评估)。
-
人工质量指标:
- 正确性:是否完全满足意图(功能、边界 case 处理)。
- 可读性:代码结构清晰度(命名、注释、逻辑分层)。
- 鲁棒性:处理异常输入(空值、极值)的稳定性(是否崩溃、输出合理性)。
七、实验结论
RQ1:自规划方法 vs. 基线方法的代码生成表现
实验设计
- 基线方法 :
- Direct:LLM 直接根据意图生成代码(零样本,无提示)。
- Code CoT:使用思维链提示生成自然语言中间步骤,再生成代码(两阶段,但中间步骤为非结构化文本)。
- Ground-truth Planning:人工提供完美计划,LLM 仅执行代码生成(模拟规划阶段的理论上限)。
- 数据集:HumanEval、MBPP-Sanitized、HumanEval-ET、MBPP-ET(覆盖不同复杂度和测试用例规模)。
- 评估指标:Pass@1(单次生成正确率)、CodeBLEU(代码相似度)、AvgPassRatio(平均通过率)。
实验结果
方法 | HumanEval Pass@1 | MBPP-Sanitized Pass@1 | 相对 Direct 提升(HumanEval) |
---|---|---|---|
Direct | 48.1% | 62.7% | --- |
Code CoT | 53.9% (+12.1%) | 54.5% (+9.4%) | +12.1% |
自规划 | 60.3% (+25.4%) | 55.7% (+11.8%) | +25.4% |
Ground-truth Planning | 74.4% (+54.7%) | 65.1% (+30.7%) | +54.7% |
- 关键发现 :
- 自规划显著优于 Direct 和 Code CoT:在 HumanEval 上 Pass@1 提升 25.4%,表明规划阶段有效分解了复杂意图。
- Ground-truth Planning 的天花板效应:人工计划的 Pass@1 达 74.4%,说明自规划仍有提升空间,但已接近实际应用的高效解。
- Code CoT 的局限性:其提升幅度小于自规划,因自然语言中间步骤未结构化,对代码生成的引导不足。
RQ2:不同 LLM 对自规划方法的影响
实验设计
- 模型范围 :
- 大模型(175B):text-davinci-003(代码 + 文本训练)、code-davinci-002(代码专用)、text-davinci-002(文本训练)。
- 中小模型:code-cushman-001(13B)、text-curie-001(6.7B)、text-babbage-001(1B)、text-ada-001(350M)。
- 实验设置 :
- 规划阶段统一使用 code-davinci-002 生成计划,实现阶段换用不同模型,测试跨模型迁移性。
- 采用 4-shot 提示(受限于小模型输入长度)。
实验结果
模型名称 | Direct Pass@1 | 自规划 Pass@1 | 规划迁移(code-davinci-002 计划) |
---|---|---|---|
text-davinci-003 | 55.1% | 65.4% | 65.4%(同模型自规划) |
code-davinci-002 | 48.1% | 59.0% | --- |
text-davinci-002 | 48.1% | 50.0% | 57.1%(使用 code-davinci-002 计划) |
code-cushman-001 | 34.0% | 30.1% | 44.9%(计划迁移提升 10.9%) |
小模型(<10B) | ≤0.6% | ≤0.0% | 无显著提升 |
- 关键发现 :
- 模型规模是自规划的前提:仅当模型≥13B 时,自规划开始有效(code-cushman-001 通过计划迁移提升 10.9%);小模型因能力不足,无法利用规划。
- 代码专用模型更优:code-davinci-002 的自规划 Pass@1(59.0%)高于 text-davinci-002(50.0%),表明代码训练数据增强了规划 - 代码映射能力。
- 规划可跨模型迁移:大模型生成的计划可显著提升中小模型性能(如 text-davinci-002 使用 code-davinci-002 计划后 Pass@1 提升 9%)。
RQ3:自规划方法的最佳设计
实验设计
- 变体对比 :
- 阶段划分:两阶段(规划→实现)vs. 一阶段(同时生成计划 + 代码)vs. 多轮(迭代生成代码片段)。
- 提示规模:1-shot、2-shot、4-shot、8-shot 提示。
- 中间步骤格式:结构化计划(编号列表)vs. 叙事性 CoT(段落文本)vs. 极简计划(仅关键词)。
- 数据集:HumanEval(Python)。
关键结果
变体类型 | Pass@1 | 代码生成成本( tokens) | 人工可读性评分 |
---|---|---|---|
两阶段(8-shot) | 60.3% | 1,996 | 3.8/4.0 |
一阶段(8-shot) | 62.8% | 2,460(+23%) | 3.2/4.0 |
多轮 | 28.2% | 2,372 | 2.1/4.0 |
叙事性 CoT | 50.6% | 2,748 | 2.9/4.0 |
极简计划 | 61.5% | 1,754 | 2.5/4.0(步骤模糊) |
- 最佳设计结论 :
- 阶段划分 :两阶段方法在可读性(+0.6 分)和泛化性上优于一阶段,因解耦规划与实现降低了模型负担;多轮方法因片段拼接错误严重失败。
- 提示规模:8-shot 为最优平衡点(Pass@1=60.3%),继续增加示例因输入长度限制收益递减。
- 步骤格式:结构化编号列表(如 "1. 生成斐波那契序列")显著优于叙事文本或极简关键词,清晰的分步引导提升了 LLM 对任务的理解。
RQ4:多语言代码生成表现
实验设计
- 数据集:HumanEval-X(Python、Java、JavaScript、Go)。
- 实验设置:使用同一套英语意图 - 计划示例(未针对语言定制计划),测试跨语言泛化能力。
实验结果
语言 | Direct Pass@1 | 自规划 Pass@1 | 相对提升 |
---|---|---|---|
Python | 48.1% | 60.3% | +25.4% |
Java | 50.6% | 61.5% | +21.5% |
Go | 42.9% | 53.0% | +23.5% |
JavaScript | 53.2% | 55.8% | +4.9% |
- 关键发现 :
- 跨语言有效性:自规划在所有语言中均提升,Python 和 Go 提升显著(+23-25%),因计划中的抽象步骤(如 "检查质数")易于映射到强类型语言。
- 语言特性影响:JavaScript 提升较小(+4.9%),可能因计划示例更贴近 Python 语法(如列表推导式),说明定制语言特定计划可进一步优化。
RQ5:问题复杂度对自规划的影响
实验设计
- 难度划分 :使用 GPT-4 对 HumanEval 问题按难度评分(1-10 分),分为:
- 简单(Score≤2,67 题)、中等(Score=3,47 题)、困难(Score≥4,43 题)。
- 评估指标:Pass@1 对比。
实验结果
难度等级 | Direct Pass@1 | 自规划 Pass@1 | 相对提升 |
---|---|---|---|
简单 | 65.2% | 71.2% | +9.3% |
中等 | 36.2% | 55.3% | +52.7% |
困难 | 31.1% | 50.2% | +61.5% |
- 关键发现 :
- 复杂度正相关提升:问题越难,自规划优势越显著(困难问题提升 61.5% vs. 简单问题 9.3%),证明规划阶段对分解复杂逻辑的必要性。
- Direct 的局限性:在困难问题上 Direct Pass@1 仅 31.1%,而自规划通过分步处理将正确率提升至 50.2%,接近中等问题的基线水平。
八、相关工作分析
论文的 "Related Work" 部分系统梳理了代码生成领域的研究现状,主要分为以下四类:
1. 代码生成(Code Generation)
- 传统方法 :基于监督学习,将代码视为自然语言处理(如 Seq2Seq 模型),结合代码特性(如抽象语法树 AST、API 调用)提升生成质量。例如:
- Dong 等人提出 CODEP 模型,通过语法约束确保代码合法性。
- 早期研究如 Ling 等人利用 latent predictor networks 生成代码。
- 预训练模型:随着大模型兴起,CodeT5、UniXcoder 等模型将预训练应用于代码生成,后续模型如 Codex、AlphaCode、CodeGen 通过扩大参数规模(如 CodeGen-16B)显著提升性能。
- 本文定位:现有方法多为 "后处理"(如重排序、代码修正),而本文提出 "预处理" 的自规划框架,通过分解意图为计划,与后处理方法正交互补。
2. 提示技术(Prompting Techniques)
- 少样本提示与 CoT:Chain-of-Thought(CoT)通过生成中间推理步骤提升复杂任务性能,但原始 CoT 聚焦自然语言推理(如数学题),直接应用于代码生成效果有限(因代码需结构化实现步骤)。
- 代码相关提示 :
- PAL/PoT:生成代码作为中间步骤,利用编译器验证,提升数学推理准确性,但适用场景局限于简单计算。
- CodeGen 的多轮提示:依赖人工标注的中间步骤,而本文通过 LLM 自主生成计划,无需额外标注。
- 对比差异:本文自规划强调 "问题分解为子任务计划",而非 CoT 的 "逐步推理",更贴近软件开发的需求分析流程。
3. LLM 自我改进(Self-Improvement of LLMs)
- 自我增强方法:Self-Instruct(引导 LLM 生成指令)、Self-Refine(自我反馈优化)、Self-Evaluation(分步评估推理过程)等,通过 LLM 自身能力提升性能。
- 本文关联:自规划属于 LLM 自我改进的一种形式,利用 LLM 的上下文学习能力生成计划,无需外部数据或训练,体现 "自我规划" 的 emergent ability。
4. 规划与 LLM(Planning with LLMs)
- 任务规划应用:LLM-Planner 用于具身智能体的任务规划,HuggingGPT 整合 LLM 规划调用多模态工具,DEPS 用于 Minecraft 任务交互规划。
- 代码生成规划:本文首次将规划引入代码生成,通过分解意图为可执行的子任务步骤(如 "生成斐波那契序列→检查质数"),填补了规划在代码领域的应用空白。