Function-Aware Fill-in-the-Middle:面向 Coding Agent 基础模型的中期训练

  • 关联论文:2607.12463
  • 作者:spark
  • 更新:2026-07-20

一句话结论

本文提出 Function-Aware Fill-in-the-Middle(FIM) 作为 Coding Agent 基础模型的中期训练(mid-training) 自监督目标:通过程序依赖图分析与"复杂度可推断性"双重标准筛选可遮蔽函数,让 Qwen2.5-Coder-Instruct(7B/14B)与 Qwen3-8B 在 SWE-Bench-Verified 上提升 +2.8 ~ +3.2、在 SWE-Bench-Lite 上提升 +3.7 ~ +5.4,且缓解了 Agentic post-training 对通用编程与工具调用能力造成的侵蚀。

解决的真问题

Coding Agent 的工作流是 "动作(Action)→ 观察(Observation)→ 继续(Continuation)" 的循环:模型发出工具调用,环境返回结果,模型接着推理。这与一个经典编程结构同构:函数调用点(call site)把实参绑定给被调函数,被调函数返回计算结果,下游代码消费这个结果。

但标准的"从左到右"的代码预训练只把这种结构以正向(forward)形式暴露给模型,反向(mask 整个函数体、给定上下文推断函数实现)从未被显式训练。结果是:

  1. Coding Agent 缺乏"在已有调用点处插入正确函数实现"的归纳偏置,遇到新工具、新 API 时往往需要大量 prompt 才能对齐调用格式;
  2. Agentic post-training 改善了 Agent 能力,却常常以"非 Agent 编程能力下降"为代价(如 LiveCodeBench、HumanEval 上的退步),这种现象被称为"能力侵蚀(capability erosion)";
  3. 现有的 FIM(Fill-in-the-Middle)训练通常是随机遮蔽任意一段代码,没有针对函数调用结构设计,归纳偏置弱。

FIM mid-training 直击这三个问题,把"函数调用结构"作为一等训练信号灌进基础模型。

核心方法

1. 关键观察:Action-Observation-Continuation ≡ 函数调用点

作者提出 Coding Agent 的工具调用循环与编程语言里的函数调用点在结构上同构:

Coding Agent 工作流 函数调用点
Action:模型发出工具调用 caller:绑定实参、发起调用
Observation:环境/工具返回值 callee:在某处(定义处)实现函数
Continuation:模型消费返回值继续推理 downstream code:消费 callee 的返回值

这意味着互联网上普通代码里无处不在的函数调用结构,本身就是 Action-Observation-Continuation 模式的海量天然样本。无需专门构造 Agent 数据。

2. 训练目标:Function-Aware FIM

标准的 FIM 任务是把一段代码按位置切三段(前缀 prefix / 中间 suffix / 后缀 suffix,原版 FIM 是 prefix / middle / suffix),遮蔽 middle 让模型补全。Function-Aware FIM 的关键差异:遮蔽单元是函数(function),而非任意位置,且函数选择满足两个条件:

(a) 程序依赖图(program dependency graph, PDG)分析

用静态分析构建代码的 PDG,控制流与数据流依赖显式化。只有被其他代码通过函数调用依赖的函数(即"被调用的函数")才被选为遮蔽目标。这样模型学到的不是任意代码片段的补全,而是"在已有调用约束下实现函数"的归纳偏置。

(b) 复杂度可推断性(complexity-inferability)双重标准

仅靠 PDG 还不够 —— 一些被调函数的实现过于简单(如一行 return x),遮蔽它们学不到有用的归纳偏置;另一些函数实现过于复杂(如上千行),无法在自监督下可靠学习。作者引入"复杂度可推断性"作为第二道筛选:函数体长度在合理区间(原文未明确具体上下界),且函数逻辑可从调用上下文与被调函数签名推断。

两个标准叠加,最终筛选出的"可训练函数"集合在 968 个 GitHub 仓库、2.6B token 的去重/去污染语料上展开 mid-training。

3. 模型与训练流程

  • 基础模型:Qwen2.5-Coder-Instruct(7B / 14B)、Qwen3-8B;
  • 语料:2.6B token 去污染语料(decontaminated against benchmarks),来自 968 个 GitHub 仓库;
  • 目标:self-supervised FIM 任务,遮蔽函数体、给定上下文(imports、调用点、其他函数)预测函数实现;
  • post-training:在 mid-training 后直接套用既有 Agentic post-training 流程,包括 R2E-Gym、SWE-Smith、SWE-Lego(用于非 Qwen2.5 基座验证)。

4. 评估协议

论文同时评估领域内领域外能力:

  • 领域内(Coding Agent):SWE-Bench-Verified、SWE-Bench-Lite —— 看 Agent 解决真实 GitHub issue 的能力;
  • 领域外(非 Agent 编程):LiveCodeBench —— 看通用代码生成是否被牺牲;
  • 领域外(非编程工具调用):tau-bench、BFCL —— 看通用工具调用能力是否被牺牲。

最后两类用于量化"能力侵蚀"。

关键实验与数据

关键发现 1:领域内大幅提升

  • SWE-Bench-Verified:Qwen2.5-Coder-Instruct 7B 提升 +2.8、14B 提升 +3.0;Qwen3-8B 提升 +3.2
  • SWE-Bench-Lite:三个模型分别提升 +3.7 / +4.0 / +5.4(7B / 14B / Qwen3-8B)。

SWE-Bench-Lite 的提升大于 Verified,说明 FIM mid-training 对"难度中等、需要多次工具调用"的场景帮助更大,因为这类场景正是 Action-Observation-Continuation 模式密集出现的地方。

关键发现 2:跨 post-training 流程稳定

增益在 两个不同的 post-training 流程(R2E-Gym、SWE-Smith)以及一个非 Qwen2.5 基座(Qwen3-8B 用 SWE-Lego)上都成立。这说明 FIM mid-training 带来的归纳偏置是与具体 post-training 流程解耦的,可作为通用前置训练阶段。

关键发现 3:缓解 Agentic post-training 的能力侵蚀

这是论文最有产品落地意义的发现之一:agentic post-training 通常会让非 Agent 编程(LiveCodeBench)和非编程工具调用(tau-bench、BFCL)能力下降,但前置 FIM mid-training 能部分逆转这种侵蚀 —— mid-training 学到的"函数调用归纳偏置"在 post-training 后仍然保留,且对外溢到非 Python 工具调用任务(虽然 mid-training 语料只含 Python)。

具体提升/缓解幅度(百分点)原文未在 abstract 中给出,应在正文表格中。

亮点与局限

亮点

  • 洞察精准:Action-Observation-Continuation ≡ 函数调用点的同构观察,把 Agent 训练问题转化为"已有海量数据"问题;
  • 零人工标注:完全 self-supervised,语料来自 968 个开源仓库,2.6B token;
  • 筛选严谨:PDG + 复杂度可推断性的双重标准,比随机 FIM 更聚焦;
  • 跨流程、跨基座稳定:在两个 post-training 流程与两个基座上都成立;
  • 缓解能力侵蚀:这是对工程界普遍的"Agent 训练 = 通用能力下降"问题的一个非常实际的解法。

局限

  • 依赖静态分析质量:PDG 分析对动态语言、宏、反射的支持有限,可能漏选一些本应被遮蔽的函数;
  • 2.6B token 相对较小:相比主流预训练语料是几个数量级的差距,mid-training 的提升有多大程度来自这 2.6B token、有多大程度来自"mid-training 这一阶段本身"未做消融(原文未明确);
  • 仅 Python 语料:论文观察到 Python-only 语料学到的偏置仍能外溢到非 Python 工具调用,但其他编程语言的迁移性未充分验证;
  • 复杂度可推断性的具体阈值未明确:影响复现;
  • SWE-Bench 仍是主要指标:但 SWE-Bench 与真实产品环境仍有差距(如依赖环境配置、复杂多文件工程);
  • 未给出训练成本明细:2.6B token 的 mid-training 在 7B/14B 上需要多少 GPU 小时,原文未明确

对工程落地的启发

  • 训练 Coding Agent 基础模型时:在通用预训练与 RLHF/Agentic post-training 之间插入一个 Function-Aware FIM mid-training 阶段,可同时提升 Agent 能力与缓解通用能力侵蚀;
  • 构造 FIM 数据时:不要随机遮蔽,应优先选择"被调用、复杂度适中"的函数,遮蔽单元比位置选择更重要;
  • 跨语言外溢:Python-only 语料的 FIM mid-training 已能外溢到非 Python 工具调用任务,说明程序语言层面的归纳偏置是相当通用的,可以用最低成本的语料获得最广的收益;
  • 能力侵蚀问题:在做 Agentic post-training 时,监控"非 Agent 编程"与"非编程工具调用"基准(LiveCodeBench、tau-bench、BFCL)的能力变化,必要时前置一个 FIM mid-training 阶段作为"防侵蚀缓冲";
  • 与其他 mid-training 任务的组合:可以探索在 FIM 之外,叠加"工具描述预测""错误信息诊断"等任务,进一步强化 Agent 偏置。

与同方向工作的关系

  • FIM / 填空训练:本文是 FIM 思想在 Coding Agent 场景的精细化升级,从"任意位置遮蔽"到"按函数调用结构遮蔽";
  • Agentic post-training 缓解能力侵蚀:与近期"如何让 Agent RL 不伤害通用能力"的工作(如 NeMo、Open RLHF、RLHF workflow studies)直接相关;
  • SWE-Bench 系列评测:与 R2E-Gym、SWE-Smith、SWE-Lego 等 Agent 训练框架互补,本文在这些框架上加了一个 mid-training 阶段;
  • 代码预训练数据工程:与 StarCoder、Code Llama、Qwen2.5-Coder 等代码基础模型的训练数据筛选工作同源,但本文聚焦"对 Agent 有用的子集";
  • 程序语言与工具使用的同构:与 Toolformer、ReAct 等"LLM 调用工具"工作共享"语言模型可以学会工具调用结构"的洞察,但本文把这一洞察下沉到 mid-training 阶段。

适合谁读

  • Coding Agent 训练工程师 —— 关心 post-training 能力侵蚀、希望前置训练阶段的人;
  • 大模型数据工程师 —— 探索 FIM 类自监督目标的设计;
  • SWE-Bench / Agent Benchmark 研究者 —— 需要新的 mid-training 信号;
  • Agent Infra 团队负责人 —— 想系统理解 Coding Agent 训练流程各阶段作用的人;
  • Agent 产品经理 —— 想理解"为什么我的 Coding Agent 通用能力变弱了"以及如何缓解;
  • 不适合:纯应用层 RAG / Prompt Engineering 读者(议题不相关)。

训练流程伪代码(简化版)

# Input: pretraining corpus D, model M, post-training pipeline P
# Output: agent-foundation model M'

# 1. 程序依赖图分析,筛选可遮蔽函数
function_pdgs = build_PDGs(D)               # 对每个仓库构建 PDG
eligible_funcs = filter_by_pdg(function_pdgs)  # 保留被调用的函数

# 2. 复杂度可推断性过滤
final_funcs = [
    f for f in eligible_funcs
    if MIN_LEN <= len(f.body) <= MAX_LEN
    and is_inferable_from_context(f, D)
]

# 3. 构造 FIM 训练样本
fim_corpus = []
for repo in D:
    for func in final_funcs_in(repo):
        # 遮蔽函数体,保留 imports + 调用点 + 其他函数
        masked = mask_function_body(repo, func)
        target = func.body
        fim_corpus.append((masked, target))

# 4. Mid-training
M' = mid_train(M, fim_corpus, objective=fim_loss)

# 5. Agentic post-training(外部流程)
M_final = P(M')

# 6. 评估
eval_in_domain(M_final)      # SWE-Bench-Verified, SWE-Bench-Lite
eval_out_of_domain(M_final)  # LiveCodeBench, tau-bench, BFCL

一个工程师视角的执行清单

如果你要在自己的 Coding Agent 训练流水线里加入 FIM mid-training,以下是一份最小可执行清单(基于论文可推断的部分 + 通用最佳实践):

  1. 准备去污染语料 —— 从 GitHub 拉取目标语言的仓库,用 MinHash/n-gram overlap 与 SWE-Bench、HumanEval、LiveCodeBench 等基准去重。论文用了 968 个仓库、2.6B token,规模可以作为起点。
  2. 构建 PDG —— 静态分析工具推荐使用 Tree-sitter(多语言)、PyCG(Python call graph)、Joern(多语言 PDG)。论文对 Python 用 PDG,对其他语言未明确。
  3. 筛选函数 —— 双重过滤:(a) 被其他代码调用(call graph 中出度 > 0);(b) 函数体长度落在合理区间(如 10 ~ 500 行,原文未明确),且函数逻辑可被调用上下文约束。
  4. FIM 数据格式 —— 标准 FIM 三段式:prefix(前文) / middle(被遮蔽函数体) / suffix(后文),但 middle 必须恰好是整个函数体,而不是任意切片。
  5. Mid-training 超参 —— 学习率可比主预训练低一个数量级(如 1e-5 起步),训练 1~2 个 epoch 即可观察到 SWE-Bench 增益,避免过拟合到 FIM 的局部模式。
  6. 联合 Agentic post-training —— Mid-training 后立刻接 RLHF/Agentic RL,注意监控 LiveCodeBench / tau-bench / BFCL 是否出现能力侵蚀。本文方法应能缓解这种侵蚀。
  7. 跨语言外溢验证 —— 在多语言模型上,把 mid-training 语料从 Python 扩展到 TS/Rust/Go 等,验证"函数调用归纳偏置"是否仍能外溢到非 Python 任务。

执行清单中的具体数值(如函数体长度、学习率、epoch 数)均不在原文中给出,属于工程经验补充,实施时需做小规模消融。

工程落地与核查(Jay)

事实核查

  • +2.8 ~ +3.2(SWE-Bench-Verified):⚠️ 需原文 PDF/代码库核验。文中未区分是 pass@1 还是 pass@5,亦未说明是在 R2E-Gym 还是 SWE-Smith 流程下测得。不同流程的基线不同,提升数字的可比性受限。
  • +3.7 ~ +5.4(SWE-Bench-Lite):同上,Lite 与 Verified 为不同难度子集,提升幅度大于 Verified 与论文"SWE-Bench-Lite 难度中等"的分析一致,但需原文确认。
  • "缓解能力侵蚀"具体幅度未公开:abstract 未给 LiveCodeBench / tau-bench / BFCL 的具体百分点变化;⚠️ 解读中"部分逆转"的结论有依据但无数字锚点,引用时须说明"原文未量化"。
  • 2.6B token 去污染语料:968 个 GitHub 仓库数量可核查,但具体哪些仓库、去污染方法(MinHash 参数等)原文未明确。
  • PDG 工具:论文对 Python 用 PDG 分析,但具体使用 PyCG / Joern / Tree-sitter 的哪种工具未说明。
  • 函数长度阈值:复杂度可推断性的上下界原文完全未给出,⚠️ 任何"10~500 行"的数字均为后补工程经验,不是论文数据。
  • R2E-Gym / SWE-Smith / SWE-Lego:三个 post-training 框架均来自已有工作,原文仅引用,未详细描述。

实际系统怎么用

完整流水线实现步骤:

# 1. 语料收集(GitHub 仓库)
pip install pycg tree-sitter  # PDG 构建工具

# 2. 用 PyCG 提取 Python call graph
pycg --max-calls 10000 ./repos/ -o call_graph.json

# 3. 筛选被调用的函数(出度 > 0)
python filter_called_funcs.py --cg call_graph.json \
    --min-len 10 --max-len 500 \
    --output eligible_funcs.json

# 4. 构造 FIM 样本(标准 FIM 格式:PSM)
python construct_fim.py --funcs eligible_funcs.json \
    --format PSM \
    --output fim_train.jsonl

# 5. Mid-training(以 Qwen2.5-Coder 为例,使用 Hf Trainer)
# lr=1e-5, epochs=1-2, max_seq_len=2048, per_device_batch=8
torchrun --nproc_per_node=8 \
    train_fim.py \
    --model Qwen/Qwen2.5-Coder-7B-Instruct \
    --data fim_train.jsonl \
    --lr 1e-5 \
    --epochs 2 \
    --output_dir ./fim-checkpoint

推理侧接入:FIM mid-training 产出的模型与普通代码模型使用方式完全相同,不需要修改 inference 流程。模型权重可直接替换既有 Coding Agent 的 base model。

坑在哪

坑点 具体表现 缓解方案
PDG 对动态语言覆盖不足 Python 的 eval(), exec(), 反射、__getattr__ 无法被静态 PDG 捕获;可能漏选有效候选函数 对 Python 补充基于 AST 的启发式筛选(如含 def 且在模块级可见的函数);不要完全依赖 call graph
函数长度阈值盲区 原文未给阈值,10~500 行是工程经验;过短函数 FIM 收益低,过长函数自监督学习不可靠 小规模消融:在 10/20/50 行下限 和 200/500/1000 行上限之间做 4×4 网格搜索,用 val set 上的 perplexity 选最优
mid-training 的真正贡献者不清晰 2.6B token 数据量 vs mid-training 阶段本身,二者未消融;可能是 token 量而非 FIM 任务带来的收益 至少跑一个消融:用同样 2.6B token 但做 standard forward training(非 FIM),对比 FIM 与非 FIM 的 SWE-Bench 差距
SWE-Bench 与生产环境差距 SWE-Bench 是单 repo 单 issue,生产 Agent 可能面对多 repo 协作、状态依赖更复杂的场景 以 SWE-Bench 为 proxy 但在真实场景做 A/B 测试;不要把 SWE-Bench 提升直接映射为产品指标
能力侵蚀缓解幅度未量化 论文未给出 LiveCodeBench / tau-bench 变化的具体百分点,"缓解侵蚀"是定性结论 引用时加 ⚠️ 说明;自己训练时务必同步监控这三项指标
GPU 成本不透明 2.6B token mid-training 在 7B 上约需多少 A100 GPU 小时原文没说 按 7B 模型 ~350 A100-GPU-hours / 1B token 估算;2.6B token ≈ 910 GPU-hours,约 $2,000~4,000(按 spot instance);14B 翻倍
跨语言泛化未充分验证 仅 Python 语料;TypeScript / Rust / Go 等语言的函数调用结构与 Python 有差异(如鸭子类型 vs 显式类型) 先在自己目标语言上做小规模验证(SWE-Bench 类似语料),再决定是否扩展语料
开源代码未找到 short paper,代码可能未随论文发布;复现需自行实现 PDG + 筛选 + FIM 训练全链路 可以参考 StarCoder 的 FIM 实现(bigcode/starcoder GitHub)作为 baseline,自己适配 PDG 筛选

代码/工具参考

  • PDG / Call Graph
  • PyCG(Python call graph,pip install):适合 Python 语料
  • Joern(多语言 PDG):适合 C/C++/Go 等,可扩展到 JS
  • Tree-sitter(语法树 + 语义分析):最通用,需自己写 query
  • FIM 训练框架
  • bigcode/starcoder 官方 FIM 训练脚本(GitHub)可作参考
  • HuggingFace Trainer + 自定义 collate_fn 处理 PSM 格式
  • 去污染工具
  • datasketch(MinHashLSH)用于 n-gram overlap 检测
  • data-mechanic(if available)用于 benchmark 去重
  • Benchmark
  • SWE-Bench:princeton-nlp/SWE-bench(HuggingFace)
  • LiveCodeBench:livecodebench/livecodebench_public(需申请)
  • tau-bench / BFCL:各自 GitHub 官方库