Skip to content

Latest commit

 

History

History
315 lines (236 loc) · 10.7 KB

File metadata and controls

315 lines (236 loc) · 10.7 KB

Project-Level Data Synthesis

这份文档专门回答一个问题:怎么合成适合 agentic code repair 的项目级 bug。

核心判断很简单:如果模型只要看一个函数就能猜出 patch,那还不够 agentic。Level 2 样本应该逼它做一点真实 agent 会做的事:看项目结构、读相关文件、改一个模块、跑测试、 根据反馈确认修复。

目标

我们要造的不是普通编程题,而是一批可以被 agentic loop 修复的 bug。也就是说,每条 样本都要让模型经历这个过程:

看到 issue
-> list_files 看项目结构
-> read_file 读入口文件和相关模块
-> write_file 修改一个已有源文件
-> run_tests 看 visible test 反馈
-> finish
-> verifier 跑 hidden tests 打分

如果一条题不需要探索项目,不需要工具反馈,只靠 prompt 里的一段函数就能修,那它更像 SFT 代码补全数据,不是这个项目想要的 agentic RL 数据。

样本形态

一条项目级任务应该长这样:

issue
files:
  package/__init__.py
  package/entrypoint.py
  package/buggy_module.py
visible_tests
hidden_tests
bug_type
level = project
repair_file = package/buggy_module.py
project_kind

repair_file 是给数据工厂和 toy oracle 用的,不应该直接塞进真实模型 prompt。真实 agent 应该通过 list_filesread_file 和 visible test 反馈找到它。

当前代码里的 schema 对应 agentic_code_grpo/data.py, 默认生成器对应 agentic_code_grpo/project_tasks.py

合成流程

推荐先用模板合成,不要一开始就挖大仓库:

1. 选一个小项目骨架
2. 设计一个入口模块和一个被调用模块
3. 在被调用模块里注入一个单点 bug
4. 写 visible tests,让 agent 能看到失败
5. 写 hidden tests,覆盖 visible tests 没覆盖的边界
6. 用 oracle patch 验证这题确实可修
7. 跑质量门禁,不合格就丢掉
8. 记录 manifest,固定 train/dev/test split

这样造出来的数据便宜、稳定、好 debug。等训练闭环稳定了,再把真实 commit-diff mining 接进来。

命令是:

python -m agentic_code_grpo.data_factory --level project --out benchmarks/mini_project_repair

它会生成:

benchmarks/mini_project_repair/train.jsonl
benchmarks/mini_project_repair/dev.jsonl
benchmarks/mini_project_repair/test.jsonl
benchmarks/mini_project_repair/manifest.json

manifest.json 是这批数据的体检报告。每次训练都应该能追溯到具体哪一版 manifest。

如果是方法探索或批量实验,不要直接把所有产物塞进 benchmarks/。先放到 data_runs/ 对应方法目录里,例如:

data_runs/01_template_project_mutation/candidates/
data_runs/01_template_project_mutation/accepted/
data_runs/01_template_project_mutation/manifests/

确认质量稳定后,再把 accepted split 导出到 benchmarks/

当前模板

仓库现在的 agentic_code_grpo/project_tasks.py 有 5 类小项目:

checkout_service      shipping fee 算错
checkout_service      coupon normalize 漏 strip
analytics_dashboard   leaderboard 排序方向错
access_control        奇偶判断反了
workflow_audit        取 first event 而不是 latest event

每类任务都有多个文件。比如 checkout 任务里,测试从 shop.cart 入口进来,但 bug 在 shop.pricingshop.coupons。这比单文件 solution.py 更接近真实 agentic 修复。

Bug 范围

当前 Level 2 先覆盖“小而清楚”的项目级 bug。范围故意不大,因为第一阶段重点是让 rollout、reward、verifier、manifest 这条链路稳定。

Bug family 例子 为什么适合起步
运算符错误 shipping fee 被减掉而不是加上 oracle patch 清楚,visible/hidden tests 容易验证。
归一化缺失 coupon code lower 了但没 strip 很常见,能测空格、换行、tab。
排序方向错 leaderboard 返回最低分 visible tests 能失败,hidden tests 能补更多边界。
奇偶判断反了 偶数团队被判 blocked 逻辑小,但要从入口模块追到规则模块。
下标取错 audit 取 first event 而不是 latest 项目入口和 bug 文件分开,适合 read_file 定位。

下一批可以扩这些类型:

config loader       默认值、类型转换、环境变量优先级
API serializer      字段名、None 处理、日期格式
permission checker  role 合并、deny 优先级
cache layer         key normalize、过期时间、namespace
parser pipeline     trim、分隔符、空行、重复项
state machine       状态转移漏条件、终态判断错

暂时不建议一开始放这些:

跨很多文件的大重构
需要外部网络或数据库的 bug
并发、时间、随机性很强的 bug
多个根因混在一起的 bug
测试偶发通过的 flaky bug

原因很朴素:这些 bug 更接近真实世界,但不适合作为第一批 RL 训练信号。先让模型在稳定、 可复现、可验证的小项目里学会 agentic 修复,再慢慢加复杂度。

为什么必须 agentic 实现

Level 2 数据的价值不只是“多文件”。它必须让模型真的使用工具。

普通函数题通常是:

看一段代码 -> 直接输出 patch

项目级 agentic 题应该是:

看 issue -> 看文件树 -> 读入口模块 -> 读被调用模块
-> 修改文件 -> 跑 visible tests -> 根据结果决定是否 finish

这能训练和评测几种单纯补全数据测不到的能力:

定位能力:bug 不一定在入口文件里
工具使用:能不能按 JSON schema 调 read/write/run_tests
反馈利用:看到测试失败后能不能改对
边界意识:visible tests 过了,不代表 hidden tests 一定过
轨迹质量:是不是少走弯路、少做 invalid action

所以这个 repo 的 Level 2 样本会要求:

files 至少包含 3 个 Python 文件
visible tests 通过项目 import 入口执行
repair_file 是已有源文件
hidden_tests 不进入 policy view
rollout 至少包含 list_files/read_file/write_file/run_tests/finish

如果以后接真实模型,repair_file 只能给数据工厂和 verifier 用,不能直接告诉模型。 否则模型是在抄定位答案,不是在做 agentic 修复。

质量门禁

每条合成样本进训练前必须过这些检查:

buggy project fails visible tests
buggy project fails hidden tests
oracle patched project passes visible tests
oracle patched project passes hidden tests
hidden tests do not appear in policy view
repair_file exists and is writable
project has multiple files

这些门禁不是形式主义。它们是在防止模型学到脏奖励,比如“测试本来就会过”“hidden test 泄漏了”“visible test 太弱,乱改也能过”。

强验证怎么做

强验证的意思是:不要只相信模型输出,也不要只相信 visible tests。最终成功必须由一个 模型看不到的 verifier 判断。

当前项目里的强验证分三层。

1. 数据生成时验证

每条任务生成后先跑这组检查:

buggy project + visible tests -> must fail
buggy project + hidden tests  -> must fail
oracle patched project + visible tests -> must pass
oracle patched project + hidden tests  -> must pass
hidden test assertions must not appear in policy view

这保证了两件事:题目真的有 bug,参考修复真的能修。

2. Rollout 过程中隔离 hidden tests

agent 可以调用:

list_files
read_file
write_file
run_tests
finish

但是 run_tests 只能跑 visible tests。如果 agent 请求 hidden tests,环境会返回 invalid action,并且 reward 会惩罚它。hidden tests 只在 trajectory 结束后由 verifier 调用。

3. 训练和评测时用外部指标兜底

每次实验至少看这些指标:

held-out hidden-test pass@1/pass@k
invalid_action_rate
avg_success_turns
test_call_efficiency
reward_rank_accuracy
manifest.num_invalid_tasks
manifest.hidden_leak_count

其中 reward_rank_accuracy 很关键:同一任务里,成功轨迹的 reward 应该排在失败轨迹前面。 如果这个指标不对,说明 reward 可能在奖励错误行为。

更强的验证可以继续加:

mutation check: 对 oracle patch 做小扰动,hidden tests 应该能抓住错误
metamorphic tests: 同一功能换输入规模、顺序、空白字符,结果仍应一致
negative controls: no_op 或错误策略不应该通过 hidden tests
split hygiene: train/test 按 project_kind 或 bug family 做 held-out
trace audit: 抽样检查轨迹里没有 hidden answer 或 repair_file 泄漏

一句话:强验证不是“多跑几个测试”这么简单,而是要证明训练信号没有泄漏、没有奖励错、 没有把测试集做成训练集的同义改写。

怎么继续扩

下一步扩数据时,优先加新的项目骨架,而不是只给旧模板换变量名。更系统的路线已经拆到 data-method-variants/

01_template_project_mutation   当前 Level 2 方法的直接扩展
02_repo_test_breaking          借鉴 SWE-smith,从真实 repo 自动造 bug
03_commit_backtranslation      借鉴 R2E-Gym,从 commit/diff 反推 issue
04_stateful_environment_tasks  借鉴 AppWorld/tau-bench,验证最终状态和 collateral damage
05_hybrid_verifier_selection   借鉴 R2E-Gym/Terminal-Bench,组合多种 verifier

短期扩模板时,可以优先加这些项目骨架:

config loader       默认值、类型转换、环境变量优先级
API serializer      字段名、None 处理、日期格式
permission checker  role 合并、deny 优先级
cache layer         key normalize、过期时间、namespace
parser pipeline     trim、分隔符、空行、重复项

每个骨架保持一个主 bug family。等这些小项目稳定后,再做真实数据:

真实 repo before/after diff
提取最小复现文件
保留或生成 visible tests
补 hidden tests
用 sandbox 跑门禁
按 repo 或 bug family 切 train/dev/test

真实数据进来后也要走同一套 verifier 和 manifest。真实不等于干净,真实 commit 里也会有 测试缺失、环境不稳定、patch 太大、多个问题混在一起这些麻烦。

最重要的边界

Policy view 只能包含 issue、files、visible tests 和工具反馈。Verifier view 才能使用 hidden tests。只要这个边界破了,训练指标就不可信。

所以项目级数据合成的目标不是“看起来像项目”,而是让 agent 必须通过项目探索和测试反馈 来修 bug,同时让 verifier 有干净的隐藏测试来打分。