大型语言模型 (LLM) 在软件工程领域的应用前景令人惊艳。你输入提示词,模型就能吐出可运行的代码。对于简单的任务——比如编写斐波那契数列或基本的 SQL 查询——目前的模型 (如 GPT-4) 已经相当精通。

然而,专业软件开发的现实情况往往没有那么简单。现实世界的编码涉及复杂的库 (如 TensorFlow 或 Pandas) 、复杂的逻辑以及特定的数据结构。当 LLM 面对这些“复杂代码生成”任务时,它们经常会臆造不存在的库,编写出能运行但结果错误的代码,或者无法处理边缘情况。

在这篇文章中,我们将深入探讨 CoCoST (Automatic Complex Code Generation with Online Searching and Correctness Testing,即: 基于在线搜索和正确性测试的自动复杂代码生成) ,这是一篇旨在弥合“玩具级”编码问题与现实世界开发之间差距的研究论文。CoCoST 不只是让 LLM 猜测代码;它强制模型像人类开发者一样行事: 规划、去 Google 搜索答案、编写测试,并根据实际执行结果进行调试。

问题: 为何简单的生成会失败

在理解解决方案之前,我们必须了解为什么标准的 LLM 在复杂代码上会遇到困难。研究人员指出了阻碍模型成为真正自主开发者的三个主要挑战:

  1. 静态知识局限: LLM 是在离线数据上训练的。如果你需要上周刚更新的某个库中的特定函数,或者问题需要训练集中不常见的函数利基组合,LLM 就会像无头苍蝇一样乱撞。
  2. 缺乏测试用例: 在竞技编程数据集 (如 HumanEval) 中,通常提供了测试用例。但在现实世界中,当你要求 AI “分析这个数据集”时,并没有预先写好的测试来验证输出。
  3. 隐蔽的 Bug: 改进 AI 代码的一种常用方法是“自我调试 (Self-Debugging) ”,即 AI 修复那些崩溃 (报错) 的代码。但在复杂的数据科学中,代码通常能运行且不崩溃,但会产生错误的结果 (例如,错误的张量形状或错误的统计计算) 。这些“静默 Bug”是最难捕捉的。

图 1 展示了人类开发者如何处理这些问题,并与标准模型进行了对比。人类不只是猜测;他们会搜索文档,写草稿,测试,然后优化。

图 1: CoCoST 模仿的人类开发者编写代码过程示例。收到问题后,执行在线搜索以模拟搜索结果并创建代码的初始版本。然后生成测试用例,执行代码以产生输出结果。根据这些结果的正确性对代码进行优化。

如上所示,CoCoST 旨在模仿这种确切的人类工作流。

CoCoST 框架

CoCoST 框架在两个不同的阶段运行: 检索 (Retrieval)优化 (Refinement)

其核心理念很简单。系统不完全依赖神经网络的权重,而是主动从互联网 (StackOverflow、文档) 检索信息,然后针对生成的输入对生成的代码进行严格测试。

图 2: CoCoST 的流程管道。步骤 1: 利用 LLM 对问题进行策略规划,并根据规划的步骤制定查询。这些查询使得从互联网检索各种信息成为可能。通过有效的规划和利用互联网信息,可以获得高质量的初始代码。步骤 2: LLM 生成测试用例以测试初始代码的正确性。测试结果的序列化作为后续代码优化循环的关键输入。通过迭代优化过程,初始代码的质量可以得到显著提高。

让我们详细拆解这两个步骤。

第一阶段: 基于战略规划的检索

在许多检索增强生成 (RAG) 系统中,模型只是简单地使用用户的提示词来搜索数据库。然而,对于复杂的编码问题,用户的提示词可能太模糊或涉及面太广,无法产生良好的搜索结果。

CoCoST 引入了一个规划 (Planning) 阶段。在写任何一行代码之前,LLM 会分析问题 (\(D\)) ,并生成一个分步计划 (\(P\)) 和一组搜索查询 (\(Q\)) 。

Equation 2

这意味着模型会确定它知道什么。如果计划涉及模型不确定的特定 Pandas 操作,它会生成像“pandas calculate value counts normalized (pandas 计算归一化值计数) ”这样的查询。

然后,系统使用搜索引擎 (如 Google) 执行这些搜索,以检索 URL 并提取相关信息 (\(INFO\)) 。

Equation 5 Equation 6

利用这些最新的外部信息,模型生成初始代码 (\(W_0\)) 。这通过将生成过程建立在真实文档的基础之上,有效地解决了“幻觉”问题。

Equation 3

第二阶段: 通过正确性测试进行优化

这是 CoCoST 与以前的“自我修复”机制不同的地方。大多数现有方法只有在代码崩溃 (例如 TypeError) 时才进行修复。CoCoST 关注的是正确性

但是,如果没有标准答案,你如何验证正确性呢?作者引入了测试用例生成 (Test Case Generation)

生成测试用例

由于现实世界的问题没有附带单元测试,CoCoST 要求 LLM 根据问题描述生成输入 (\(I\)) 。

Equation 8

关键在于,模型生成输入,不生成预期的输出。它使用这些输入来运行生成的代码并观察会发生什么。

复杂输出的挑战: 序列化

在简单的编程任务中,输出通常是整数或字符串。在复杂的数据科学任务中,输出可能是巨大的 NumPy 数组、Pandas DataFrames,甚至是 Matplotlib 图表。LLM 无法简单地“阅读”图表或原始二进制对象。

CoCoST 通过序列化 (Serialization) 解决了这个问题。它将复杂对象转换为 LLM 可以理解的基于文本的表示形式。

  • DataFrames/Arrays: 转换为包含类型信息、形状和截断数据预览的字符串。
  • Images (Charts): 转换为可缩放矢量图形 (SVG) 代码,这是基于文本且 LLM 可读的格式。

Equation 9

优化循环

系统使用生成的输入 (\(i\)) 执行代码 (\(W\)) 。它捕获执行结果 (\(o\)) 和任何错误 (\(e\)) 。

Equation 7

然后,LLM 充当审查者。它查看问题描述、它编写的代码,以及——至关重要的是——该代码的序列化输出

如果代码运行时没有报错,但产生了一个全为零的 DataFrame,而实际上应该有数值,LLM 就会检测到这个语义错误。然后它根据这个洞察优化代码 (\(\widehat{W}_{j+1}\)) 。这个循环持续进行,直到代码正确或达到限制次数。

实验结果

研究人员在两个具有挑战性的数据集上验证了 CoCoST: DS-1000 (数据科学代码生成) 和 ClassEval (面向对象的类生成) 。

DS-1000 性能表现

DS-1000 因其难度而臭名昭著,因为它涉及七个不同的 Python 库 (NumPy, Pandas, TensorFlow 等) 并需要复杂的逻辑。

如表 1 所示,CoCoST 实现了 71.71% 的 Pass@1 得分,显着优于基础 GPT-4 模型 (64.47%) 和先前的最先进方法 Self-Evolve (66.23%) 。

表 1: DS-1000 的主要结果和消融研究。CoCoST 的基础模型是 GPT-4。所有指标均以百分比表示。对于每个指标,粗体数字表示最高性能。

“Diff-Rewrite”一栏特别有趣。这代表了问题描述被改写或扰动的情况。CoCoST 在这里显示出极大的鲁棒性 (53.09% vs Self-Evolve 的 33.95%) ,表明检索和测试机制使得模型对用户如何提问不那么敏感。

ClassEval 性能表现

ClassEval 测试生成整个类而不仅仅是代码片段的能力。这需要维护内部状态和方法依赖关系。

表 2: ClassEval 的主要结果和消融研究。所有指标数字均以百分比表示。对于每个指标,粗体数字表示最高性能。

表 2 显示了更显著的改进。CoCoST 在类级别实现了 46.3% 的 Pass@1 率,几乎是 Reflexion (24.1%) 性能的两倍。这证明了 CoCoST 的优势不仅限于数据科学脚本,还延伸到了结构化的软件工程。

按库细分

当我们查看具体库的数据时 (表 5) ,我们发现 CoCoST 在数据结构复杂且对标准文本处理“不可见”的领域表现出色,例如 Matplotlib (绘图) 和 TensorFlow/PyTorch (张量) 。

表 5: DS-1000 中不同软件包的主要结果表。所有指标数字均以百分比表示。粗体数字表示最高性能。

图像 (Matplotlib) 和张量的序列化允许 LLM “看到”它的图表是空的或者它的张量形状是错的,从而导致成功的优化。

案例研究: CoCoST 实战

为了真正领会该框架,让我们看看论文中的两个具体例子。

示例 1: 通过在线搜索修复逻辑

在这个 TensorFlow 示例 (图 4) 中,用户想要计算多类数据集中特定类的准确率。初始的代码猜测在语法上是正确的,但在功能上是错误的——它没有使用所需的特定 tensor_scatter_nd_update 逻辑。

通过规划搜索查询 (tensorflow tensor_scatter_nd_update usage) ,CoCoST 检索到了关于如何使用该特定函数的教程。结果是一个与“标准答案”逻辑相匹配的正确实现。

图 4: 在线检索案例研究。

示例 2: 通过正确性测试捕捉静默 Bug

图 3 展示了优化阶段的威力。任务是比较 Pandas DataFrame 中的两行。初始代码使用了简单的比较: row0 != row8

在 Python 中,NaN != NaN 的计算结果为 True。这是数据科学中一个经典的“坑”。代码运行没有报错,但输出是错误的,因为它将 NaN 的匹配算作了差异。

因为 CoCoST 生成了一个包含 NaN 值的测试用例并序列化了输出,LLM 看到了意想不到的结果。它意识到了错误,并优化代码以使用 isnull() 检查,正确处理了缺失数据。

图 3: 正确性测试案例研究。

基础模型的影响

CoCoST 适用于任何模型吗?表 3 比较了使用 GPT-4 与 WizardCoder (一个开源编码模型) 运行 CoCoST 的结果。

表 3: 不同基础模型在 DS-1000 和 ClassEval 上的结果。所有指标数字均以百分比表示。对于每个部分中的每个指标,粗体数字表示最高性能。

虽然 GPT-4 获得了巨大的提升,但像 WizardCoder 这样较小的模型却很吃力,有时甚至在使用该框架后表现更差。这凸显了一个关键发现: 检索和优化需要强大的推理能力。 如果模型无法规划出好的搜索查询或无法理解序列化的反馈,额外的信息就只会变成噪音。

结论

CoCoST 代表了自动代码生成领域迈出的重要一步。通过承认 LLM 无法简单地“死记硬背”所有复杂的编码逻辑,作者构建了一个模仿胜任的人类开发者行为的系统。

对于学生和从业者来说,关键要点是:

  1. 搜索至关重要: 通过 (经由规划查询的) 实时互联网访问来补充 LLM,解决了知识截止和幻觉问题。
  2. 输出很重要: 调试不应该只在代码崩溃时发生。序列化输出使得模型能够修复那些原本不可见的语义逻辑错误。
  3. 自我测试: 我们不需要等待人类提供的测试用例。LLM 有能力生成自己的输入来验证自己的代码。

随着像 GPT-4 这样的模型变得更快、更便宜,像 CoCoST 这样的框架很可能会成为 IDE 助手的标准配置,推动我们从“代码补全”走向真正的“代码生成”。