返回列表

Public 2nd Place Solution

660. Konwinski Prize | konwinski-prize

开始: 2024-12-12 结束: 2025-07-23 基础软件 AI大模型赛
公开第二名解决方案

公开第二名解决方案

作者: Camaro (Grandmaster)
发布日期: 2025-03-18

感谢举办如此有趣且实用的比赛。我也曾被 SWE-Bench[1] 所吸引,尽管由于某些限制和问题,我对它的有效性存有一些疑虑。这次比赛给了我一个解决这些担忧的机会,我花了将近 3 个月的时间来完成这个解决方案!

顺便说一下,这个解决方案的很大一部分是由 LLM 编写的。为什么不呢?

简要总结 (TL;DR)

  • 基于 Agentless 的流水线
  • 定位 -> 生成测试 -> 创建补丁 -> 验证
  • 多项检查以决定是否跳过某个问题,并防止提交错误的补丁

流水线流程图

Pipeline Flow Chart

详情

整体流程基于 Agentless[2],我从他们的仓库中 borrow 了大量代码。

1. 流水线 (Pipeline)

1-1. 问题评估 (Issue Evaluation)

第一步是单独查看问题,以确定它是否可解决。虽然这种检查对最终分数的影响 rarely 很大(因为只有尝试才知道是否可解决),但它确实有助于快速跳过那些不太可能成功的问题。实际上,这意味着为真正重要的案例节省时间。
不知何故,LLM 提供的信心水平变化很大。Qwen 倾向于谨慎,而 Gemini 则过于自信。你如何措辞提示词也有巨大的差异。例如,“告诉我是否有问题”与“你确定这在生产环境中部署是安全的吗?”截然不同。因为我们需要解决尽可能多的问题,所以我选择了更宽松的设置。

1-2. 文件定位 (File Localization)

它列出仓库中的所有 Python 文件,并挑选出 5 个可能相关的候选文件。我尝试了一些调整,比如将候选数量从 5 个增加到 10 个,进行第二轮检查以查看是否有遗漏的文件,甚至使用 embeddings 进行搜索。这些调整都没有真正帮助。即使给模型每个 ground truth 文件,分数也没有提高多少。看来,如果模型一开始就不能发现必要的文件,它就无能为力了。

1-3. 文件选择 (File Selection)

对于这 5 个候选文件,下一步是打开每个文件并检查其内容,以确定是否真的需要它。与其简单地回答是或否,不如提取可能需要更改的部分效果更好。这不仅 keeps 上下文不会变得太长,还降低了模型在不清楚应该修复什么时进行不必要修改的风险。

1-4. 测试生成 (Test Generation)

这可以说是流水线中最重要的部分。在这个模块中,我们生成了复现测试——即在应用补丁之前先复现问题,然后在应用补丁后确认问题已解决的测试。添加一些精心制作的测试示例作为 few-shot 示例显著提高了性能,但超过某一点后添加更多并没有产生额外的好处。如果测试生成失败(例如,由于语法错误),模块会反馈错误并尝试修复。我还跟踪了一个名为"good_test_rate"的指标(测试既复现问题又被 ground truth 补丁解决的频率)以提高测试质量。

1-5. 修复生成 (Repair Generation)

使用项目概述、问题描述和文件内容,此步骤以函数调用风格生成补丁(指定文件路径、旧字符串和新字符串)。如果补丁应用失败,它会反馈失败并重试。当补丁生效时,系统运行测试,如果在复现测试中确认问题已解决则停止。如果没有,它会带着新的反馈重试。对于更难的问题,允许多次尝试(最多五次),但更多的尝试有时会导致更高的失败率。

1-6. 修复验证 (Repair Verification)

生成补丁后,下一步是确保它真的安全有效。此步骤检查三件事:测试逻辑是否已验证,问题是否已解决,以及补丁是否没有引入回归问题。在这里试图过于严格会导致每个补丁都失败(例如,如果问测试用例是否充分,LLM 可能总是说“不”)。最后,只有通过这些检查的补丁才会被提交。

1-7. 时间管理 (Time Management)

enforced 总超时时间为 25 分钟(留有 5 分钟缓冲以应对意外延迟),尽管大多数任务只需 5-7 分钟。这是为了捕捉任何极端情况,例如异常长的测试执行时间或在生成过程中陷入无限循环。

2. 大语言模型 (LLM)

我使用了 Qwen2.5-Coder-32B[3]。在与 Qwen2.5 72B[3]、QwQ-32B[4] 和 Deepseek-R1[5] 比较后,Qwen2.5-Coder-32B 被证明是最稳定且快速的。由于流水线可以跳过真正困难的问题,深度推理并不那么关键。然而,强大的基础 LLM 仍然很重要。在 14B 和 32B 版本之间观察到了显著差异。早期,我 experimented 了 Qwen2.5-14B[3],结果证明这是一个错误,因为我花了大量时间调整提示词以修复较弱模型丢失上下文并导致更多语法错误的问题,但将 LLM 更改为 32B 几乎完全解决了这个问题。我使用 vLLM[6] 托管了一个兼容 OpenAI API 的服务器,指定 JSON schema 用于结构化输出以提高输出稳定性。没有公共 API 支持具有 130k token 上下文的结构化输出,所以我租用了 vast.ai 上的两个 RTX 3090s 来运行本地评估。

3. 开发 (Development)

3-1. 基线 (Baseline)

我首先 explored 了 SWE-Bench 上几种高分方法,Agentless 脱颖而出,成为最简单且对开发者最友好的方法。虽然整体代码变得有些复杂,但我设法提取了关键模块并将它们组装起来以适用于 SWE-Bench。

3-2. 提示词工程 (Prompt Engineering)

我采用了 LLM 联盟风格的提示词工程——通过比较多个 LLM(包括 ChatGPT、Grok、Gemini 和 DeepSeek)的输出来挑选最佳想法。我也尝试了 Anthropic 的提示词生成,但其 Claude 特定风格(如过度使用 XML)并不那么有效,因为我使用的是 JSON 结构化输出。总共,我开发了 4 个 eval_issue 提示词,3 个 localize_file 提示词,7 个 select_file 提示词,14 个 generate_test 提示词,12 个 generate_patch 提示词,和 4 个 verify_patch 提示词,并为每个任务采用了最佳提示词。我稍后会发布提示词。

4. 评估 (Evaluation)

对于评估,我使用了两个数据集:来自 SWE-Bench-Verified[7] 的 100 个实例子集和完整的 SWE-Bench-Verified(500 个实例)数据集。在排行榜上,我的模型在 10 次 late 提交中 achieved 平均分约为 0.052022(分数范围从 0.028080 到 0.070332)。对于 SWE-Bench-Verified 数据集,分数在 0.04 到 0.06 之间。然而,这些数字可以被操纵;例如,SWE-Bench-Verified 中近一半的项目是 Django,因此专门针对 Django 定制解决方案可能会人为地提高分数。
除了最终分数之外,我还跟踪了各种指标,以清楚地了解系统在每个阶段的表现。以下是指标列表及其含义:

  • score: 解决方案的整体评估分数。
  • num_solved: 被视为已解决的实例数量,包括被跳过的。
  • num_tried: 提交的样本总数。
  • num_successed: 在提交的样本中,计数有多少达到了 f2p = 1.0 和 p2p = 1.0。
  • num_failed: 失败的提交样本数量。
  • num_degraded: 在失败的样本中,指示有多少个 f2p 分数低于 1.0。
  • num_not_solved: 在失败的样本中,显示有多少个 p2p 分数低于 1.0。
  • file_recall: 在定位步骤中,在 ground truth 补丁中找到正确文件的比率。
  • selected_file_recall: 在文件选择阶段后,选中正确文件的比率。
  • selected_line_recall: 在选中的文件中识别出正确行的比率。
  • reproduced_rate: 成功复现问题的实例百分比。
  • good_test_rate: 既复现问题又被 ground truth 补丁解决的测试比例。
  • good_test_rate_in_reproduced: 类似于 good_test_rate,但仅计算在成功复现问题的实例上。
  • replacement_success_rate: 生成的补丁中匹配旧字符串的成功率。
  • syntax_success_rate: 应用后没有引入任何语法错误的补丁比率。

这些指标有助于指导进行哪些改进。然而,增强每个指标并不总是导致更好的最终分数。例如,虽然增加候选文件数量提高了文件召回率,但并不一定提高整体分数——在某些情况下,它甚至引入了意想不到的更改从而损害了性能。同样,虽然较高的 good_test_rate 是有益的,但强制过多测试有时会导致更多失败而不是更好的结果。

5. 不起作用的方法 (What didn't work)

  • 在应用补丁前后运行所有单元测试对我没有帮助。结果证明这太慢且有风险,所以我没有使用它。
  • 我还尝试在补丁生成后生成额外测试以进一步过滤掉坏补丁,但这种方法难以控制,最终弊大于利。最后,我简化了验证步骤,信任 LLM 的判断,而不是依赖过于严格的测试过程。

6. 代码 (Code)

我计划在比赛结束后公开代码。

同比赛其他方案