更多博客
博客进阶

构建新版 GitHub Copilot 内联建议模型:第一部分

Building the new GitHub Copilot Inline Suggestions Model: Part One

AI 导读

这是 VS Code 官方博客系列的第一篇,介绍 GitHub Copilot 内联建议的三种形态:补全式幽灵文本、光标附近的下一处编辑建议和远距离编辑,此前各由专门的模型路径支持。文中提到过去 90 天内超过 610 亿次成功的模型请求,并说明团队已将三者统一到一个“3-in-1”模型。适合关注 Copilot 内联建议模型演进的人阅读。

分级
值得看
所属主题
编程智能体
收录时间
2026-10-29

中文精读

原文约 5,848 词 · 24 节

  1. 开篇:把三类内联建议统一到一个模型

    • 原文指出,补全式幽灵文本(ghost text)、光标附近的下一处编辑建议(NES)和更远处的编辑,原本由各自独立的模型驱动。
    • 团队把这三类能力合并成一个“三合一”模型,并总结出:训练、评估与编辑器设计需要协同演进才能得到最佳效果。
    • 内联建议的使用规模很大,过去 90 天里成功模型请求超过 610 亿次。
    • 写作过程很少是线性的,下一步有用的改动可能只在光标处几个字符,也可能是附近重写,或代码库别处的相关编辑。
  2. 为什么统一:统一带来的核心收益

    • 统一的首要收益是质量:让单个模型端到端地为开发者当前工作挑选最佳编辑,而不是用程序化逻辑在多个专用模型之间挑选。
    • 原文以企鹅喂食程序为例:用户已输入 `class Fa`,旧的独立模型先触发补全模型,它只能接续前缀,于是给出 `FastingPenguin`;统一模型则语义化地把 `Fa` 重写为 `Fish`,后者才是更合适的建议。
    • 第二个收益是速度:统一模型能在一次响应中输出多个编辑,前序建议被接受后,后续编辑可走缓存、以低延迟出现,形成更顺畅的 tab-tab-tab 体验。
    • 统一还降低了后续功能扩展的难度,例如多文件编辑;同时一次模型改进可以惠及所有内联建议类型,而不再只改善某一类。
    • 实现路径是先重构建模任务(尤其是提示与输出格式),再合并三个独立模型在数据质量、训练评估与客户端设计上的经验,并做了 200 多次离线实验和 20 次线上实验。
  3. 为什么要统一

    • 统一前,VS Code 内联建议的生产实现由三个独立模型组成:补全(completions,幽灵文本)、NES(光标上下几行内的编辑)、长距离 NES(离光标更远的同类建议),由客户端编排何时触发哪个模型。
    • 原文 Figure 1 描述的用户视角流程为:先触发补全模型预测幽灵文本;无幽灵文本则调用 NES 重写光标周围窗口;仍无编辑则调用长距离模型预测要跳转的行号;若发生跳转,再调用一次 NES 重写该行周围窗口。
    • 该图有意简化:实际 provider 调用行为更复杂,例如会并行调用补全与 NES,具体取决于用户光标位置以及被优先的模型响应有多快。
    • 这种按任务分工的做法对单个任务是有效的、也是引入新编辑能力的必要一步,但带来三方面不足:用户流程上有时给出次优或零碎的编辑而非一个最贴切上下文的清晰编辑;延迟上每次机会最多可能发起 4 次模型调用;对未来功能(如多文件编辑)的可扩展性受限。
    • 统一还带来复利收益:一个共享模型支撑所有内联建议后,每次模型改进或新特性都能惠及完整体验,而不只局限于某一种建议类型。
  4. 重构代码编辑任务:diff patch 输出格式

    • 团队意识到这是重构代码编辑问题的机会,并且从模型输出格式入手:预测后缀(补全)、重写窗口(NES)、预测行号(光标跳转)这三种各自独立的任务,都不足以完整表达其他任务。
    • 由此设计出统一的 diff patch 输出格式。以修改函数签名为例:在光标处补全新参数、随后更新下方调用点、再调整文件中别处的校验逻辑,本是不同交互,共享的补丁语言让模型把它们当作同一问题的连续步骤来推理,并以更流畅的方式呈现给用户。
    • 原文示例中,`<|cursor|>` 表示光标位置;补全模型单独只输出 `od`,NES 输出整段窗口重写,长距离模型只输出行号 `5`(下一处应把 fish 换成 food 的行);diff patch 则串起多个补丁:改函数签名、改第 5 行打印语句、改第 8 行调用,乃至另一个文件 antarctica.py 第 42 行。
    • 多补丁响应让后续补丁(示例中第 5、8、42 行的编辑)可被缓存,前序补丁被接受后以低延迟出现,形成快速的 tab-tab-tab 编辑体验。
    • 该格式能完整表达此前三种任务,并为跨文件编辑等未来行为保留灵活的优雅表达。原文指出,tool calls 之类更通用的格式在预训练中更常见、也支持编辑之外的动作,但在代码编辑上该格式最紧凑且仍有表达力。
    • 输入上下文也随之简化:原先包含无行号的近期浏览片段、带行号的当前文件、统一 diff 格式的近期编辑历史、光标上下 ±15 行的待编辑区域、光标上方 2 行下方 5 行的重写窗口;统一后删除后两项,替换为带行号和文件内容的 3 行光标区块。
    text
    def feed_penguin(penguin, fo<|cursor|>):
      penguin.meals += 1
      penguin.hungry = False
      penguin.last_ate_time = datetime.now()
    
      print(f"{penguin.name} ate {fish.type}")
    
    penguin = Penguin("emperor")
    feed_penguin(penguin, fish="silverfish")
  5. 重构代码编辑任务:diff patch 输出格式(续)

    • 光标位置区块仍放在提示末尾而非直接嵌入当前文件内容,因为光标位置不断变化,这样能最大化与历史提示的公共前缀,优化键值缓存(KV cache)。
    • 统一分两步:先用统一 diff patch 格式把 NES 与长距离能力合并为 2-in-1 模型,补全模型保持不变,客户端编排简化为先补全、后 2-in-1;再把 2-in-1 与补全模型合并得到 3-in-1 模型。
  6. 第一阶段:2-in-1 模型

    • 团队刻意先处理 NES 与长距离编辑,最后再动补全模型:经验上补全模型占内联建议流量的大部分,超过 70% 的编辑机会。
    • 团队此前的实验还显示,NES 建议比幽灵文本更容易被用户 dismiss(撤回/关闭),因此先合并另外两个模型,把旗舰的补全模型放到最后。
    • 由于 diff patch 输出格式同样适用于 2-in-1 模型,后续 3-in-1 的训练可以直接利用 2-in-1 的数据与其他经验,加快实验迭代速度。
  7. 评估

    • 没有单一分数能概括内联建议模型的质量,因此采用四个互补的评估阶段,与此前 next edit suggestion 发布所用的流程类似。
    • 离线评估:有针对性的离线测试,用于检查补全行为、困难编辑场景、补丁有效性、建议长度以及不同编辑类型之间的平衡。
    • 内部试用(dogfooding):日常使用能暴露聚合分数看不到的问题,如合理但时机不对、过于急切、不完整或呈现别扭的建议。
    • 大规模人工评估:让不同背景的程序员在数千个编码任务上按结构化反馈模板作答,从真实开发者处获得量化的质量评估;受控线上实验:对有希望的候选模型用 A/B 测试上线,评估其对真实用户的最终影响。
    • 各阶段回答不同问题:离线判断候选是否值得测;试用给出真实工作流中的第一手感受;人工评估把试用体验量化并提供结构化反馈;线上实验判断整个系统相比现有生产方案是否创造了使用价值。
  8. 离线基准

    • 原 NES 模型的离线评估主要由三项基准组成,各自考察不同维度。
    • 模拟测试(STests):团队手工整理的工作区录制集,展示必备、加分与必须避免的建议行为。
    • HumanEval:在 OpenAI 的 HumanEval 基础上扩充,用于评估模型在单行、多行和随机代码片段上的补全能力。
    • 输出视图类型(Output View Kind):让样例经过此前的生产模型,记录其输出的视图类型(幽灵文本、并排 NES、无编辑等),用于跟踪新候选模型相比原模型在视图类型分布上的差异。
  9. 训练 v1 模型:diff patch 版 NES

    • 先构建工具,把原 NES 模型的评估与训练数据稳健地转换成 diff patch 格式;转换后先做监督微调(SFT),再在仅含 NES 的训练数据上做强化学习(RL),这一轮称为 v1 模型。
    • 如预期,这些模型在离线基准上能与原 NES 模型持平,但它们学到的编辑分布恰恰就是 NES 编辑,几乎没有长距离编辑。
    • 即使使用完全相同的数据样本,数据格式的差异有时也会导致行为差异:编辑倾向更增量,可能因为 diff patch 格式天然表达特定位置的定向改动,而不是重写整个目标窗口(原文指出这一点在讨论 3-in-1 模型时会再次出现)。
    • 试上线这些模型时还发现了处理新输出格式的若干客户端细节,包括缓存与错误处理,这些在早期就被打磨,使后续候选模型受益于这次试运行。
    • 在 diff patch 的问题表述下,长距离 NES 情形有一个简洁性质:NES 编辑 + 长距离 NES 编辑 = 多补丁响应(multi-patch response)。多补丁响应也是关键补充,因为缓存后续补丁可实现快速跟进的编辑,带来用户体验上的规模效益。
  10. 训练 v2 模型:加入长距离 NES

    • 长距离编辑与 NES 的既有数据集不可直接拼接,因为它们来自完全不同的文件与编辑历史,需要从单编辑数据自举出多编辑数据。
    • 方法一,用 v1 模型伪标签自举:先把 diff patch 格式的 NES 数据“快进”,即把输出标签移入编辑历史;再喂给训练出的最好的 v1 模型;若模型产出了编辑响应,就把这个伪标签作为另一个补丁追加到原数据的输出标签上,重复直到 v1 模型不再产出编辑,从而得到含 n 个补丁的样本。
    • 方法二,编辑回放(edit playback):解析含用户编辑历史的 NES 数据模型输入,用这些编辑把工作区文件状态回退 1 到 n 个编辑,再把这些编辑从输入历史中移除并前置到输出中。
    • 数据足够后,用 LLM 评判(LLM-as-a-judge)加基于规则的质量过滤器筛选:前者依据从以往代码编辑模型开发中提炼的开发者偏好标准,如正确性、必要性、意图与编辑历史的一致性;后者关注补丁有效性与代码重复等不良行为。
    • 用这批数据与原有 NES 数据的新混合做 SFT,得到平均产出 1.3 到 1.4 个补丁的模型(原先接近 1)。原文认为这一数值是甜点区间:在不降低质量的前提下明显改善体验;因为要做到平均每响应 2 个补丁,就意味着每次编辑机会平均都要出现长距离编辑,而现有带专用长距离编辑模型的生产设置并非如此。
    • 随后以类似方式对 v2 SFT 模型做 RL,用同样技术自举多补丁数据,并修改评分逻辑以纳入多补丁响应的指导;该模型的离线结果明显强于 SFT 模型。
  11. 伪在线评估基准(Pseudo-Online Evaluation,POE)

    • 线上 A/B 测试中,v2 模型相对生产环境的三模型对照组整体有阶跃式提升(step-function improvement);除撤回率(dismissal rate)升高外,接受率、展示率、接受与保留字符数、用户参与度等其余指标都没有统计显著变化。
    • 一个意外发现是离线结果对线上结果的预测力不强:某个在 STests 中拿到历史最高分、离线表现最强的候选,在 A/B 飞行实验中反而不如 STests 更低的另一候选。团队由此要缩小离线与线上结果的差距,以便更高效、更有信心地迭代候选模型。
    • 为此建立第四个离线基准——伪在线评估(POE):由三个等量样本桶组成,分别取自生产模型输出中的接受、拒绝、无编辑三类,并附带建议质量信号;每个样本都喂给候选模型,再由基于上下文学习(in-context learning)的 LLM 评判器,结合该样本已知的真实结果,判断候选回复会被接受还是拒绝。
    • POE 最重要的指标是:拒绝率(reject rate,越低越好)、接受与拒绝率之比(accept-to-reject-rate ratio,越高越好),以及“由拒绝转为无编辑”与“由接受转为无编辑”之比——用来衡量模型是否学会对原本差的回复更多弃权,同时不损失原本好的回复。
    • 团队还量化了补丁有效性错误(patch validity errors)以及回复中补丁数量等其他指标;这些指标虽仍不是完美的代理,但已成为后续候选模型线上表现更强的预测因子。
  12. 伪在线评估基准(续)

    • 核心指标为:拒绝率(reject rate,越低越好)、接受与拒绝率之比(accept-to-reject-rate ratio,越高越好),以及“由拒绝转为无编辑”与“由接受转为无编辑”之比——衡量模型是否学会对原本差的回复更多弃权、同时不损失原本好的回复;同时还量化了补丁有效性错误等其他指标。
  13. 评分器与质量过滤器的校准

    • POE 的开发引出另一个问题:针对 diff 补丁的新 RL 评分器是否还有提升空间,能否借助额外数据与质量信号改进。团队采用自精炼(self-refinement)方式让评分器更好地对齐用户反馈,并开发了一个执行该循环的 agent 技能。
    • 循环步骤为:用 POE 样本跑评分器,得到它对生产模型回复的判断;统计假阳性、假阴性、真阳性、真阴性的混淆矩阵。
    • 再从各类别随机采样,理解评分器的常见失败模式与盲区规律;把明显成规律的反馈写入现有评分器指令,然后重复该循环。
    • 所用的质量信号本身常有噪声,但借助这一流程仍能自动提高 RL 评分器与这些质量信号的一致性。
    • 结果是假阴性减少 8.4%,假阳性减少 1.1%,提升了训练、评估与数据过滤流程的可靠性。
  14. 解决无效补丁

    • 后续 v2 变体受益于更新后的 RL 评分器和更好的离线选型,但在更频繁的 dogfooding 中暴露出新问题,其中之一是无效补丁:模型有时生成格式错误或引入可程序化验证的错误。
    • 补丁有效性分两级:影响用户体验的关键问题(critical issues),以及浪费 token 的次优补丁(suboptimal patches);团队用细粒度的基于规则的过滤器捕捉这些情况。
    • 关键问题的一个例子是:在光标所在行的前几行或后几行重复代码,用户可见并损害建议质量。次优问题的一个例子是空操作补丁(no-op patch),即新增与删除完全抵消、完全undo自身工作,浪费 token 与延迟但不损害用户看到的编辑。
    • 把规则式补丁有效性检查加入 RL 评分器确实降低了无效回复占比,但降幅低于预期。团队查看了 RL 数据集的分布,推测其未覆盖生产环境中导致这些错误的常见上下文。
    • 为验证这一怀疑,对触发有效性失败的最新 v2 案例做定向数据挖掘:跑有效性检查,借助这些检查用迭代式 LLM 反馈回路自动修复,再对修复样本做质量过滤。
    • 通过谨慎调整这些样本在训练中的采样比例,在不负面影响其他场景的前提下减少了补丁有效性问题。
  15. 训练 v3 模型:解决插入模式传播问题

    • 接近强发布候选时,dogfooding 又暴露一个持续问题:模式传播(pattern propagation)中的插入位置错误,具体是内容正确但位置错误。
    • 这是 v2 的特定失败模式:当合适回复应是按用户意图在文件其他位置插入内容以延续重复插入的模式时出错。
    • 例如用户在 Web 表单的 email 字段后加了 password 字段,就应把 Password 标签加在 Email 标签之后、把 validate_password 检查加在 validate_email 检查之后,但 v2 常把 Password 放在 Email 之前,甚至放在字段列表最底部。
    • 承接抑制无效补丁行为的经验,团队用两种方式处理。第一种是用 LLM 评判器从 v2 输出中找出这类失败样本;这相当困难,因为长距离插入场景比替换场景少见得多。
    • 即使经过 LLM 回复修复与质量过滤,也只得到数千正例、几百个呈现原始位置错误的负例。
    • 把这份数据并入 SFT 和 RL 后,模型行为没有明显变化。团队推测正例与修正后负例的分布或对模型学习的价值并不相同,于是在 RL 中对负例加权。
  16. 训练 v3 模型:解决插入模式传播问题(训练侧后续)

    • 把针对性数据并入 SFT 和 RL 后,模型行为没有明显变化;团队推测正例与修正后负例的分布或对模型学习的价值并不相同,于是在 RL 中对负例加权。
    • 加权后检查 RL 训练中的模型 rollout,意外发现即使数据表征充足,RL 评分器也没有可靠标出这类插入模式传播错误。
    • 由于主评分器已承担多个目标,为避免动摇其既有行为,团队引入辅助评分器(auxiliary grader)专门补上插入模式跟随的不足。
    • 该辅助评分器被门控在真值或预测中含插入的样本上。
    • 数据挖掘流程为:从模型输出收集原始数据,过滤出特定问题样本,再经修复与质量过滤后用于训练。
  17. 定性必备项基准

    • 插入模式传播问题只在 dogfooding 中出现,飞行实验与离线评估都没发现,这促使团队审视既有评估方法的缺口。
    • 由此建立第五个离线基准“定性必备项”(Qualitative Must-Haves,QMH):一个持续维护的场景集合,收录过往与当前模型觉得困难、常由 dogfooding 发现的情形。
    • 其目标与其他基准不同:其他基准规模更大、偏定量,而 QMH 刻意保持精简小巧,作为“金丝雀”集合快速标记模型问题、快速筛选或排除候选模型;它更侧重防止历史上的不良行为,而非泛泛衡量模型质量。
    • 这个名字有些名不副实:随着开发出可充作人类代理的 LLM 评判器来判断结果,它最终也变成了定量指标。
    • 2-in-1 和 3-in-1 建模都从中受益,用它作为模型回退与潜在用户不满的启发式信号,并因其规模小而实现快速迭代;QMH 中新增的场景也被纳入大规模人工评估。
    • 重要经验之一:训练数据与流程共用的模型,若监控不细,未来模型仍可能重现过去的问题,尤其当目标改变(例如新增功能)时。拥有一小组测量关键行为、能快速标记回退的样本,对模型选型是改变游戏规则的做法;且这些样本必须覆盖充分、真实代表过去与潜在回退。
  18. 经验总结

    • 统一 NES 与长距离编辑的关键在于把输出重构为单一的 diff 补丁格式。这一种表示能表达此前所有任务,并能把多次编辑串成多补丁回复,是低延迟 tab-tab-tab 流程的基础。
    • 为新场景引导数据:多补丁训练数据此前不存在,团队用伪标签与编辑回放从单编辑数据合成,再做质量过滤,构成 SFT 与 RL 的初始数据,启动多补丁模型开发的飞轮。
    • 形成了修复特定失败模式的可复现配方:定向样本、难例加权、带门控的辅助评分器,是把“好模型”打磨成发布候选的关键,解决了无效补丁与插入模式传播问题,并成为后续通用手法。
    • 努力缩小离线与线上的差距:这催生了 POE 基准,以及用模型输出与质量信号校准评分器的自精炼回路,使实验更快、更有信心。
    • 把成熟经验与直觉带入新领域:在数据过滤器和 RL 评分器中都贯彻“编辑应按优先级和与光标的邻近度向外流动”的准则,源自三模型方案中搭建内联建议体验的经验。
    • dogfooding 能捕捉指标遗漏的问题:离线和线上都看不到的问题只在日常使用中浮现,这促成了小型 QMH“金丝雀”基准,用于防止共用流程中的回退。
  19. 试用

    • 统一的内联建议体验现已向 VS Code 中付费 GitHub Copilot 用户开放:更新到最新版 VS Code,并确保已启用下一编辑建议(next edit suggestions)。
    • 无论在重构还是编写新函数时都可尝试 tab-tab-tab 流程并反馈意见。
    • 本文第二部分将深入讲解转向 3-in-1 内联建议模型的过程。
  20. 致谢

    • 感谢 Luciana Abud、Alexandru Dima、Yu Hu、Simona Liao、Shengjie Ma、Elsie Nallipogu、Nick Trogh 对本文的反馈、见解与贡献。
    • 感谢开发者社区持续反馈,以及 GitHub 与 Microsoft 的研究员、工程师、产品经理和设计师在训练数据、训练流程、评估套件与服务栈上的工作,还有 VS Code 与 GitHub Copilot 团队顺利完成的模型发布。
  21. 定性必备项基准(续)

    • 另一条经验是 dogfooding 的重要性:并非一切都能体现在定量评分卡上,以最终用户身份亲自试探模型边界提供独特视角,团队视其为开发面向开发者的产品时必需的一环和文化组成部分。
  22. 相关阅读:VS Code Wrapped(GitHub Universe 2026)

    • 概览 VS Code 从 GitHub Universe 2025 到 2026 之间的演进,包括新的编码方式、与 agent 协作,以及跨工具与环境管理工作。
    • 原站标注阅读时长约 8 分钟。
  23. 线上结果

    • 团队把 2-in-1 候选模型与生产基线(补全、NES、光标跳转三个独立模型)做飞行实验。
    • 最终发布候选在所有关键指标上均无统计显著回退,包括接受率、撤回率、展示率、重复参与度、接受与保留字符数。
    • 此外,建议展示所需时间下降 7%,输出 token 减少 59%,这主要归功于更高效的输出与输入格式以及更简化的客户端逻辑。
  24. 相关阅读:可配置的 Eagerness、第二部分预告

    • 一篇介绍如何通过可配置的 eagerness 调节 GitHub Copilot 内联建议主动程度的内容,原站标注阅读时长约 11 分钟。
    • 另有第二部分文章,讲述 GitHub Copilot 如何把补全能力并入统一的内联建议模型、改进 ghost text 与编辑器行为并提升用户满意度,原站标注阅读时长约 16 分钟。