VS Code 中 GitHub Copilot 背后的编码框架
2026 年 5 月 15 日,作者:Julia Kasper、Megan Rogge 和 Aaron Munger
随着每一款新模型的发布,同样的讨论总会再次燃起:哪个模型最聪明?哪个速度最快?我们该用哪一个?这些问题固然有价值,但对于像 Visual Studio Code 这样的产品而言,模型只是智能编码体验的一部分。开发者真正交互的对象是“编码框架”(coding harness):这一层负责汇总上下文、暴露工具、运行代理循环、解读工具调用,并将模型的输出转化为编辑器内有用的操作。在本文中,我们将探讨该框架的功能、重要性,以及随着模型和开发者工作流的演进,我们如何对其进行评估。

什么是编码框架?
语言模型本身无法直接编辑文件、执行命令或运行测试,它们只能生成文本。编码框架是一个连接代码编辑器与语言模型的系统。它将文本转化为操作,并将结果反馈回模型,以便模型能够决定下一步该做什么。
在 VS Code 中,编码框架主要承担三项职责:
-
上下文汇总(Context assembly):在请求到达模型之前,框架会构建一个提示词(prompt)。该提示词包含带有行为指令的系统消息、用户的查询、工作区结构(语言、框架、已打开的编辑器)、先前对话的记录、工具执行结果、自定义指令以及来自早期会话的记忆。框架决定了模型能看到什么,而这些决定直接影响了输出质量。
-
工具暴露(Tool exposure):框架声明模型可以调用的工具:读取文件(
read_file)、编辑代码(replace_string_in_file或apply_patch)、运行终端命令(run_in_terminal)、搜索代码库(semantic_search)等。每个工具都有模型必须遵循的 JSON 架构,以及模型用于决定何时调用它的描述。可用工具集会随每次请求而变化。有些工具仅针对特定模型启用;有些在执行前需要用户确认;用户可以在工具选择器中开启或关闭工具;MCP 服务器和扩展程序可以贡献全新的工具并嵌入到同一个循环中;自定义代理(.agent.md)还可以将其工具集限制为特定的子集。 -
工具执行(Tool execution):当模型请求运行某个工具(使用类似
{"name": "run_in_terminal", "arguments": {"command": "npm test"}}的 JSON 时),框架负责验证参数、运行工具、处理错误、格式化结果,并在下一次迭代中将其反馈给模型。例如,如果模型要求编辑文件,框架会执行差异(diff)写入;如果模型要求运行 shell 命令,框架会启动进程、捕获输出并进行中继。
这些任务都无法由语言模型直接完成。然而,这些输入决定了模型的行为、结果以及你在代码编辑器中的体验。
编排这些任务、决定何时继续或停止迭代、以及如何使对话在多轮交互中保持连贯的逻辑,就是代理循环(agent loop)。
代理循环
本质上,当你使用 VS Code 中的代理时,会发生一个工具调用循环:这是一个“思考 → 行动 → 观察 → 再思考”的周期。在每次迭代中,代理框架会构建提示词(系统指令 + 上下文 + 历史记录 + 到目前为止的所有工具结果),将其发送给模型并检查响应。如果响应包含工具调用,框架会执行这些工具、捕获结果并回环。如果没有工具调用,循环结束,助手的文本即成为最终响应。

一个“轮次”(turn)是用户可见的对话交互:你发送一条消息,代理最终产生一个响应。在此轮次中,代理循环可能会执行多次“循环圈”(rounds)。一个循环圈即通过一次循环:构建提示词、调用模型、接收文本和/或工具调用、执行工具、记录结果,并决定是否继续。所有这些循环圈的完整执行构成循环的“运行”(run)。单个用户轮次可能会触发多次循环圈,因为模型需要搜索文件、读取代码、编辑文件、运行测试、读取输出并针对错误进行迭代。
工具调用循环受到循环控制检查的约束。我们强制执行工具调用限制,检查循环圈之间的取消操作,并运行停止钩子(stop hooks)。停止钩子是扩展点,可以检查代理状态,并允许其完成工作或推动其继续工作。在循环内部,提示词会在每次迭代时重新构建。这意味着模型始终看到工作区的最新状态:如果它在三个循环圈前编辑过文件,当前的提示词会反映出该修改。框架还负责管理对话摘要。当积累的历史记录变得过大时,它会将早期的循环圈压缩为摘要,以便模型在不触及上下文窗口上限的情况下继续工作。
注:想看看框架是如何运作的吗?你可以探索 VS Code 源代码,在聊天界面使用“工具 UI”查看某次请求可用的工具,并打开聊天调试视图来检查提示词、工具调用和结果。
框架即产品
当新模型发布时,它需要适配现有的框架。系统提示词、工具定义、循环逻辑、上下文汇总——所有这些都是经过数月真实场景使用构建和调优的。模型擅长填补空白,但框架定义了什么是“空白”。
这一点之所以尤为重要,是因为 GitHub Copilot 允许你使用来自多个模型提供商的模型。VS Code 中的 GitHub Copilot 支持一个不断增长的模型生态系统。开发者可以在模型之间切换、使用自动选择、自带 API Key,或者通过扩展安装额外的提供商。这意味着 VS Code 必须应对一个广泛且不断演进的生态系统,而不是一个单一的稳定 API。
正是框架使 VS Code 能够处理这种模型灵活性,而无需开发者每次都重新学习产品。你应该能够切换模型或尝试新的提供商,同时保持核心体验的熟悉感:聊天、会话、工具、终端输出、调试和源代码管理。
但集成新模型很少仅仅是在模型选择器中添加一个选项。各提供商在如何暴露工具调用、结构化输出、推理控制、提示词缓存、上下文限制和错误行为方面各不相同。有些模型更擅长长期规划,有些则更擅长简洁的编辑。每个模型都有不同的优势,我们在每次发布前都会与模型提供商紧密合作,以相应地调整系统提示词、工具描述和循环行为。提供商通常会让我们提前访问新模型的检查点(即即将发布模型的预发布快照),以便我们在模型正式上线前就开始调优框架。

不同的模型需要不同的框架行为。Claude 模型使用 replace_string_in_file 进行编辑;GPT 模型使用 apply_patch。Gemini 需要提示它使用工具调用而非对其进行叙述,并且会在历史记录中出现孤立的工具调用时出错。一些模型支持扩展思考(extended thinking)并需要推理工作量(reasoning-effort)控制。一些在简洁的系统提示词下效果最好;另一些则需要详细、结构化的指令来保持轨道。框架会为每个模型选择不同的系统提示词——Claude Sonnet 4 的提示词不同于 Claude 4.5,后者又不同于 Opus。
所有这些针对模型的差异并非小事。它们转化为针对模型的系统提示词、针对模型的工具集和针对模型的对话管理。这意味着当新模型发布时,我们不能仅仅按下一个开关,而是需要验证其行为。我们在发布前会验证工具架构、重新调优默认值并重新运行完整的代理会话。除了确保模型运行正常,更困难的问题在于:我们如何验证一个新模型是否真的提供了更好的结果。
评估确保框架的可靠性
就像在发布新功能前需要测试一样,模型也需要测试。这就是模型评估的意义所在。在模型于 VS Code 中发布之前,我们从多个角度进行评估。我们运行离线基准测试、进行内部测试,并将其与产品中已有的模型进行比较。模型上线后,我们持续进行衡量:A/B 测试、聚合使用信号和每周报告,帮助我们了解模型在真实开发者工作流中的表现。

有多个公共模型基准测试可作为共享参考点,我们利用这些基准测试与更广泛的生态系统进行比较,并捕捉明显的回归。但在前沿水平上,它们已不足以作为质量指标。OpenAI 在发现前沿模型有时能从记忆中复制金标准补丁(使得污染问题难以忽略)后,停止了 SWE-bench Verified 结果的报告。
覆盖范围是另一个局限性。SWE-bench 很有价值,但它仍集中在公开的 Bug 修复任务上。Terminal-Bench 对衡量命令行能力很有用,但许多任务看起来更像是孤立的终端谜题,而非开发者带入编辑器的真实工作流。现实世界的编码代理需要做的不止是修复已知 Bug 或解决 shell 挑战,它们需要搭建项目骨架、迁移代码库、跨文件重构、遵循指令,并处理终端和浏览器。
我们依然运行这些基准测试,但它们只是起点。要决定哪些模型准备好在 VS Code 中发布,我们需要更接近我们正在构建的产品的东西。
构建 VSC-Bench
这就是我们构建 VSC-Bench 的原因,它是我们针对 VS Code 代理行为的离线评估套件。VSC-Bench 侧重于公共基准测试无法很好覆盖的 VS Code 特定开发者任务:自定义代理模式、扩展工作流、MCP 和工具使用、终端与浏览器交互、多轮对话,以及跨 TypeScript、Python、C++ 等语言的多语言编码任务。
我们使用 VSC-Bench 来衡量模型在方案正确性、代理工作量、Token 效率和延迟等方面的表现。下图重点展示了解决率(resolution rate)和 Token 使用量,但在模型成为 VS Code 体验的一部分之前,我们会评估全维度的指标。在将模型或推理设置设为编辑器默认项之前,这种权衡至关重要。
该图表总结了八种模型-工作量配置下的 40 次 VSC-Bench 运行情况。每个点代表一种配置,纵坐标越高表示解决的任务越多,横坐标越靠右表示使用的 Token 越多。对于这一组 VSC-Bench 任务,xhigh 使用的 Token 比 high 多,但解决的任务稍少,这可能表明它已超过了有效工作量的最佳平衡点,额外的思考已无法转化为更好的结果。
每个 VSC-Bench 任务都在可重现的容器化工作区中运行。框架启动 VS Code,打开工作区,发送一个或多个用户提示词给代理,让代理用文本和工具调用进行响应,然后评估发生的情况。这为我们提供了更真实的代理循环视图:不仅是最终代码看起来是否正确,还包括代理是否以符合 VS Code 体验的方式使用了编辑器、终端、语言服务、浏览器和工具。
结合公共基准测试,VSC-Bench 为我们提供了更平衡的信号:公共评估告诉我们模型在业界处于什么水平,而产品特定评估告诉我们它是否准备好提供开发者期望的 VS Code 体验。
我们如何在合并代理变更前进行基准测试
基准测试不仅用于发布模型,也是我们在变更落地前审查框架变更的方式。如果一个 PR 修改了核心工具、系统提示词或任何可能改变代理行为的内容,我们希望在合并前看到基准测试数据。
对于这些 PR,VS Code 团队使用自动化的评估评估流。为 PR 添加 ~requires-eval-assessment 标签会启动该过程:构建 PR,将其发布为评估代理,运行基准测试,并将结果发布在 PR 下。
-
构建 PR。 Webhook 将标签事件路由到
vscode-engineering中的工作流,启动针对该 PR 合并引用的 Azure DevOps 构建。如果失败则自动重试一次;PR 会获得一个“排队中 1/2”的评论,以便审查者跟进。 -
发布评估代理。 构建成功后,发布流水线会将一个版本化的代理(
0.0.0-dev.<sha>)发布到dev标签下的vscode-evalsnpm 源。PR 评论变更为“排队中 2/2”。 -
创建评估问题(Issue)。 发布流水线触发回传给
vscode-engineering的repository_dispatch,在github/evald上开启一个绑定到该发布代理的模型评估问题。 -
回传报告。 evald 运行基准测试、监控过程并生成分析评论。Azure Logic App 仅将评论链接(分析正文留在 evald 上,不公开)通过另一个
repository_dispatch回传,并在原始 VS Code PR 上发布该链接。

模型是引擎,框架是汽车。
我们始于一个开发者每隔几个月就会问的问题:哪个模型最好?但对于编码代理来说,这个问题就像在问:哪个引擎最好?引擎固然重要,但单靠它是不够的。模型所看到的上下文、它能接触的工具、维持运行的循环以及确保一切正常工作的评估,这些共同组成了“框架”。这就是我们花费绝大部分工程时间的地方。
随着模型获得更长上下文、更好的规划和原生工具使用等新能力,框架也在演进以充分利用它们。随着开发者将代理模式推向新的工作流,我们将学到的经验反馈到循环、工具和评估中。每次 VS Code 发布都会在模型更新的同时带来框架的改进。
如果你对框架的工作原理感到好奇,现在就可以动手体验。探索 VS Code 源代码,在聊天界面使用“工具 UI”查看某次请求可用的工具,并打开聊天调试视图,检查代理运行背后的提示词、工具调用和结果。尝试切换模型,添加你自己的工具,并告诉我们你的体验——请在我们的 GitHub 仓库中分享反馈。
编码愉快! 💙