跳到主要内容

Issue 生成、巧妙之处与边界

验证后的 bug 已经是合格练习题,但还缺一样东西:题面——一段像真人报的 GitHub issue。本章讲怎么用 LLM 反写 issue,然后收束全项目的精华、边界与横向对比。

1. Issue 生成:给 bug 配一段「报错描述」

1.1 要解决的小问题

SWE-agent 训练时,agent 看到的是「一段 issue 文字」,要据此定位并修 bug。但 SWE-smith 的 bug 是机器造的,没有自带 issue。所以要反过来:已知 bug 和它弄挂的测试,让 LLM 写出一段听起来像用户/开发者报的 bug 描述

1.2 思路与输入

issue_gen/generate.pyIssueGen.generate_issue(:307)给模型三类材料:

  1. system prompt + few-shot 示范:从 SWE-bench Verified 真实 issue 里随机抽几条当范例(get_demo_issues,:296),让生成的 issue 风格逼真。
  2. 失败测试的输出:从验证日志里截出测试报错(get_test_output,:249),并用 maybe_shorten(:145)把超长文本掐头去尾压进 token 预算。
  3. 相关测试的源码:get_test_functions(:278)随机挑 F2P 测试的源码,帮模型写出「合理的复现步骤」。

模型产出 n_instructions 段候选 issue,存进每实例的 <instance_id>.json,最后合并回数据集成为 problem_statement 字段(:502-525)。

1.3 工程细节

  • 可恢复:已生成的实例会跳过(_should_do_instance,:230),中断后能续跑。
  • 双后端:支持 litellm 直连,也支持 Portkey 网关(PortkeyModel,:81,带指数退避重试)。
  • 预 clone 防竞争:并发前先把所有需要的仓库 clone 好,因为 RepoProfile.clone 不是线程安全的(run:453-465 注释明说)。

配置模板见 configs/issue_gen/ig_v2.yaml 等;get_from_tests.py/get_static.py 提供不依赖 LLM 的替代出题方式。

2. 巧妙之处(可借鉴的技术)

① 「挖空重写」造 bug 比「故意搞破坏」更高明。 让模型重新实现一个挖空的函数(LLM-rewrite,01 章 §5),产出的偏差更接近真实开发者的错误,而不是刻意的乱改。挖空靠 CodeEntity.stub(adapters/python.py:115)。

② 用「代码属性标签」把变异器和目标精准配对。 先给每个函数打 HAS_LOOP/HAS_BINARY_OP 等标签(constants.py:47),变异器声明所需标签,can_change 做门禁(procedural/base.py:34)——避免大量「想改却无处可改」的空转。

③ 验证用「对照实验」而非「绝对判定」。 不问「测试是否通过」,而问「相对基线,哪些测试从绿变红」(get_valid_report,grading.py:40),天然排除了那些本来就失败的测试,信号干净。

④ 纯内存 difflib 生成补丁,绕开 git 子进程。 generate_patch_fast(bug_gen/utils.py:58)直接算 unified diff,procedural 这条高吞吐路径因此快很多,不必每个 bug 都 git add/diff/reset

⑤ 「一个 bug 一个分支」。 gather.py 把每个有效 bug 推成 mirror 仓库的独立分支,加载时 git checkout <instance_id> 即还原(profiles/base.py:493)——数据集的可复现性落在 git 这个最稳的基础设施上。

⑥ 单例 profile + 双键注册表。 让几百个仓库各自只是一个三行子类,却能共享缓存与统一接口(profiles/base.py:70:680)。

3. 边界与局限(诚实)

  • 平台:强依赖 Docker,README 明说不支持 Windows/macOS,在 Ubuntu 开发测试。
  • bug 的「真实性」上限:procedural bug 本质是语法层变异,可能不如真实 bug 那样涉及深层逻辑/跨模块交互;项目用 LLM-rewrite、PR-mirror、combine 来补这块,但仍是合成数据。
  • 测试覆盖决定一切:被改代码若没有测试覆盖,验证阶段永远测不出来,直接被丢弃——所以高质量 bug 数量受限于上游仓库的测试质量。
  • flaky 测试可能污染 F2P 判定(见 02 章 §6)。
  • min_testing 是启发式:靠文件命名约定猜相关测试(profiles/base.py:564),猜错会漏跑或误判。
  • issue 由 LLM 编写:可能与真实用户报告的分布有偏差,且有成本。

4. 横向对比(同 shelf 兄弟)

  • vs SWE-bench:SWE-bench 是评测基准(收集真实 PR 当测试题),SWE-smith 是数据工厂(合成无限训练题)。前者重「真实但稀缺」,后者重「合成但可规模化」,互补——SWE-smith 还直接复用了 swebench.harness 的常量与评分原语(grading.py 顶部 import)。
  • vs SWE-agent:SWE-agent 是消费方(用 SWE-smith 数据训练/运行的 agent)。SWE-smith 产出的容器和数据集就是喂给它的。
  • 设计取舍:相比「靠人工标注/挖真 issue」,SWE-smith 押注「造 bug 比找 bug 便宜,只要验证闸门够硬,合成数据的规模优势能盖过真实性损失」——5.2 万任务 + SWE-agent-LM-32B 在 SWE-bench Verified 上 +32% 的结果是其论据。

5. 全项目代码地图(总索引)

子系统入口/核心文件关键符号
全局常量与数据结构swesmith/constants.pyCodeEntityCodePropertyBugRewriteGIT_APPLY_CMDS
仓库档案 + 注册表swesmith/profiles/base.pypython.pyRepoProfileRegistryregistryPythonProfile
实体抽取swesmith/bug_gen/adapters/get_entities_from_filePythonEntity
造 bug:proceduralswesmith/bug_gen/procedural/ProceduralModifierCommonPMsMAP_EXT_TO_MODIFIERS
造 bug:LLMswesmith/bug_gen/llm/gen_bug_from_code_lm(modify)、main(rewrite)
造 bug:PR mirrorswesmith/bug_gen/mirror/generate.pyrecover_sweb_inst
造 bug:合并swesmith/bug_gen/combine/same_file.mainsame_module
汇总补丁swesmith/bug_gen/collect_patches.pymain
验证闸门swesmith/harness/valid.pygrading.pyrun_validationget_valid_report
组装数据集swesmith/harness/gather.pyprocess_instance
出题面swesmith/issue_gen/generate.pyIssueGengenerate_issue
训练swesmith/train/traj_mgr/run/ft_*
建镜像swesmith/build_repo/create_images.py

6. 一条端到端命令路径(把全书串起来)

# 示意,改编自各模块 __main__ docstring —— 从健康仓库到带 issue 的数据集
# ② 造 bug(procedural)
python -m swesmith.bug_gen.procedural.generate <repo> --max_bugs 1000 -i
# 汇总成一个 JSON
python -m swesmith.bug_gen.collect_patches logs/bug_gen/<repo>
# ③ 验证:跑测试,筛出弄挂 1+ 测试的
python -m swesmith.harness.valid logs/bug_gen/<repo>_all_patches.json -w 8
# 组装:有效 bug 推成 mirror 分支 + 任务列表
python -m swesmith.harness.gather logs/run_validation/<repo>
# ④ 出题面:给每条任务反写 issue
python swesmith/issue_gen/generate.py -d logs/task_insts/<repo>.json -c configs/issue_gen/ig_v2.yaml

读到这里,你应该能讲清 SWE-smith「是什么、怎么转、每个工位的原理」了。要深挖某一段,回对应章节的代码地图按符号名 grep 即可。