揭示 GitHub Copilot 在 VS Code 中的底层工作流和架构设计。展示了 AI 编程助手如何适配不同模型和工具生态,对开发者设计 AI 工具链有借鉴意义。
每次发布新模型,同样的对话就会重新上演。哪个模型最聪明?哪个最快?我们应该用哪一个?这些都是有用的问题,但对于Visual Studio Code这样的产品来说,模型只是AI智能体编码体验的一部分。开发者真正交互的是编码工具链:这一层汇聚上下文、暴露工具、运行智能体循环、解释工具调用,并将模型的输出转化为编辑器中有用的内容。在这篇文章中,我们将探讨这个工具链的作用、为什么它很重要,以及我们如何随着模型和开发者工作流的演变而对其进行评估。
语言模型自身无法编辑文件、执行命令或运行测试。它们只能产生文本。编码工具链是充当代码编辑器和语言模型之间桥梁的系统。它将文本转化为行动,并将结果反馈给模型,以便模型决定接下来做什么。
在VS Code中,编码工具链有三个主要职责:
上下文构建:在任何请求到达模型之前,工具链构建一个提示词。该提示词包括带有行为指令的系统消息、用户查询、工作区结构(语言、框架、打开的编辑器)、之前轮次的对话历史、工具结果、自定义指令以及早期会话的记忆。工具链决定模型看到什么,这些决定直接影响质量。
上下文构建:在任何请求到达模型之前,工具链构建一个提示词。该提示词包括带有行为指令的系统消息、用户查询、工作区结构(语言、框架、打开的编辑器)、之前轮次的对话历史、工具结果、自定义指令以及早期会话的记忆。工具链决定模型看到什么,这些决定直接影响质量。
工具暴露:工具链声明模型可以调用的工具:读取文件(read_file)、编辑代码(replace_string_in_file或apply_patch)、运行终端命令(run_in_terminal)、搜索代码库(semantic_search)等。每个工具都有一个模型必须遵循的JSON模式和一个模型用来决定何时调用它的描述。可用工具的集合可以按请求改变。某些工具仅对特定模型启用,某些工具在执行前需要用户确认,用户可以在工具选择器中打开和关闭工具,MCP服务器和扩展可以贡献全新的工具纳入同一循环,自定义智能体(.agent.md)可以将其工具集限制在特定的子集。
工具暴露:工具链声明模型可以调用的工具:读取文件(read_file)、编辑代码(replace_string_in_file或apply_patch)、运行终端命令(run_in_terminal)、搜索代码库(semantic_search)等。每个工具都有一个模型必须遵循的JSON模式和一个模型用来决定何时调用它的描述。可用工具的集合可以按请求改变。某些工具仅对特定模型启用,某些工具在执行前需要用户确认,用户可以在工具选择器中打开和关闭工具,MCP服务器和扩展可以贡献全新的工具纳入同一循环,自定义智能体(.agent.md)可以将其工具集限制在特定的子集。
工具执行:当模型请求运行工具(使用JSON如{"name": "run_in_terminal", "arguments": {"command": "npm test"}}),工具链负责验证参数、运行工具、处理错误、格式化结果,并在下一次迭代中反馈。例如,如果模型请求编辑文件,工具链写入diff。如果模型请求运行shell命令,工具链负责启动进程、捕获输出并转发。
工具执行:当模型请求运行工具(使用JSON如{"name": "run_in_terminal", "arguments": {"command": "npm test"}}),工具链负责验证参数、运行工具、处理错误、格式化结果,并在下一次迭代中反馈。例如,如果模型请求编辑文件,工具链写入diff。如果模型请求运行shell命令,工具链负责启动进程、捕获输出并转发。
这些任务都无法由语言模型直接完成。然而,这些输入决定了模型的行为和结果,以及你在代码编辑器中的体验。
协调这些任务的逻辑,决定何时继续或停止迭代以及如何保持对话在多个轮次中的连贯性,这就是智能体循环。
其本质上,当你在VS Code中使用智能体时,会发生一个工具调用循环:"思考→行动→观察→再思考"的循环。在每次迭代中,智能体工具链构建提示词(系统指令+上下文+历史+到目前为止的所有工具结果),将其发送给模型,并检查响应。如果响应包括工具调用,工具链执行这些工具、捕获它们的结果并循环返回。如果没有工具调用,循环可以结束,助手的文本成为最终响应。
一个轮次是用户可见的聊天交换:你发送一条消息,智能体最终生成一个响应。在该轮次中,智能体循环可能执行多个回合。一个回合是循环的一次通过:构建提示词、调用模型、接收文本和/或工具调用、执行任何工具、记录结果并决定是否继续。所有这些回合的完整执行是循环的一次运行。单个用户轮次可能触发多个回合,因为模型搜索文件、读取代码、编辑文件、运行测试、读取输出并对失败进行迭代。
工具调用循环受循环控制检查的限制。我们强制工具调用限制、检查回合之间的取消,并运行停止钩子。停止钩子是扩展点,可以检查智能体状态并允许它完成或推动它继续工作。在循环中,提示词在每次迭代时重新构建。这意味着模型总是看到工作区的最新状态:如果它在三个回合前编辑了一个文件,当前提示词反映该编辑。工具链还管理对话摘要。当累积历史变得过大时,它将早期回合压缩成摘要,以便模型可以继续工作而不会达到上下文窗口上限。
注:想看工具链的实际行动吗?你可以浏览VS Code源代码、在Chat中使用Tools UI查看可用于请求的工具,并打开Chat Debug View来检查提示词、工具调用和结果。
当新模型发布时,它需要适应现有的工具链。系统提示词、工具定义、循环逻辑、上下文构建,所有这一切都是在几个月的真实世界使用中构建和调整的。模型在填补空白方面变得更好,但工具链定义了空白是什么。
这变得更加重要,因为GitHub Copilot让你可以使用来自多个模型提供商的模型。GitHub Copilot在VS Code中支持一个不断增长的模型生态系统。开发者可以在模型之间切换、使用自动选择、带上自己的密钥或通过扩展安装额外的提供商。这意味着VS Code必须处理一个广泛且不断演变的生态系统,而不是单一的稳定API。
工具链是使VS Code能够处理这种模型灵活性的东西,而不强制开发者每次都重新学习产品。你应该能够切换模型或尝试新提供商,同时保持核心体验熟悉:聊天、会话、工具、终端输出、调试和源代码控制。
但集成新模型很少只是在模型选择器中添加一个额外选项。提供商在如何暴露工具调用、结构化输出、推理控制、提示词缓存、上下文限制和错误行为方面有所不同。有些模型更擅长长期规划。有些模型更擅长简洁编辑。每个模型都有不同的优势,我们在每次发布前与模型提供商密切合作,以相应地调整系统提示词、工具描述和循环行为。提供商经常授予我们对新模型检查点的早期访问权,这些是即将发布的模型的预发布快照,所以我们可以在模型普遍可用之前开始调整工具链。
不同的模型需要不同的护栏行为。Claude 模型使用 replace_string_in_file 进行编辑;GPT 模型使用 apply_patch。Gemini 需要提醒使用工具调用而不是叙述,在历史记录中遇到孤立工具调用时会出现问题。某些模型支持扩展思维并需要推理难度控制。某些模型最适合简明的系统提示;其他模型需要详细的结构化说明才能保持正轨。护栏为每个模型选择不同的系统提示 - Claude Sonnet 4 获得与 Claude 4.5 不同的提示,后者又获得与 Opus 不同的提示。
所有这些按模型的差异都不是微不足道的。它们转化为按模型的系统提示、按模型的工具集和按模型的对话管理。这意味着当新模型发布时,我们不能只是翻一个开关,而是需要验证其行为。我们验证工具架构、重新调整默认值,并在发布前重新运行完整的 AI 智能体会话。除了模型正常运行外,更难的问题是我们如何验证新模型实际上给出了更好的结果。
就像你需要在发布新功能前进行测试一样,模型也需要进行测试。这就是模型评估的作用所在。在模型在 VS Code 中发布前,我们从多个角度评估它。我们运行离线基准、内部测试,并将其与产品中已有的模型进行比较。模型上线后,我们持续测量:A/B 测试、聚合使用信号和每周报告帮助我们理解模型在真实开发者工作流中的表现。
有多个公开的模型基准,它们作为共享参考点很有用。我们使用这些基准来与更广泛的模型生态系统进行比较,并捕捉明显的回归。但在前沿水平,它们不再足以作为质量指标。OpenAI 停止报告 SWE-bench Verified 结果,因为发现前沿模型有时可以从记忆中重现黄金补丁,使污染变得难以忽视。
覆盖范围是另一个限制。SWE-bench 很有价值,但它仍然集中在公开的 bug 修复任务上。Terminal-Bench 对于测量命令行能力很有用,但许多任务看起来更像是孤立的终端谜题,而不是开发者实际带到编辑器中的工作流。真实世界的编码 AI 智能体需要做的不仅是修补已知的 bug 或解决 shell 挑战。它们需要搭建项目、迁移代码库、跨文件重构、遵循指令以及处理终端和浏览器。
我们仍然运行这些基准,但它们只是一个起点。要决定哪些模型已准备好在 VS Code 中发布,我们需要更接近我们实际构建的产品的东西。这就是我们建立 VSC-Bench 的原因,这是我们针对 VS Code AI 智能体行为的离线评估套件。VSC-Bench 专注于公开基准未能很好覆盖的 VS Code 特定开发者任务:自定义 AI 智能体模式、扩展工作流、MCP 和工具使用、终端和浏览器交互、多轮对话以及跨 TypeScript、Python、C++ 等的多语言编码任务。
我们使用 VSC-Bench 来测量模型在解决方案正确性、AI 智能体工作量、令牌效率和延迟方面的行为。下面的图表专注于解决率和令牌使用,但在模型成为 VS Code 体验的一部分之前,我们评估了所有维度的完整集合。在模型或推理设置成为编辑器中的默认值之前,这种权衡很重要。
此图表总结了跨八个模型工作量配置的 40 次 VSC-Bench 运行。每个点代表一个模型工作量配置,较高的点解决更多任务,较右边的点使用更多令牌。对于这组 VSC-benchmark 任务,xhigh 使用比 high 更多的令牌,但解决的任务略少,这可能表明它已经超过了有用工作量的最佳点,其中额外的思维不再转化为更好的结果。
每个 VSC-Bench 任务在可重现的容器化工作区中运行。护栏启动 VS Code,打开工作区,向 AI 智能体发送一个或多个用户提示,让 AI 智能体用文本和工具调用响应,然后评估发生了什么。这给我们提供了对完整 AI 智能体循环的更现实的视图:不仅仅是最终代码是否看起来正确,还要看 AI 智能体是否以与 VS Code 体验相匹配的方式使用了编辑器、终端、语言服务、浏览器和工具。
结合公开基准,VSC-Bench 为我们提供了更平衡的信号:公开评估告诉我们模型与该领域的比较情况,而产品特定评估告诉我们它是否已准备好应对开发者在 VS Code 内期望的体验。
基准测试不仅仅用于发布模型。它们也是我们在合并前审核护栏更改的方式。如果 PR 涉及核心工具、系统提示或任何其他可能改变 AI 智能体行为的东西,我们希望在合并前获得基准数字。
对于这些 PR,VS Code 团队使用自动评估评估流程。将 ~requires-eval-assessment 标签添加到 PR 会启动该过程:PR 被构建、作为评估 AI 智能体发布、基准测试,结果被发布回 PR:
构建 PR。webhook 将标签事件路由到 vscode-engineering 中的工作流,该工作流针对 PR 的合并引用启动 Azure DevOps 构建。失败时自动重试一次;PR 获得"排队 1 of 2"评论,以便审阅者可以跟随。
发布评估 AI 智能体。在成功构建时,发布管道将版本化 AI 智能体 (0.0.0-dev.<sha>) 发布到 dev 标签上的 vscode-evals npm 源。PR 评论翻转为"排队 2 of 2"。
提交 evald 问题。发布管道向 vscode-engineering 发回 repository_dispatch,在 github/evald 上打开固定到该确切发布的 AI 智能体的模型评估问题。
报告返回。evald 运行基准、监控它并生成分析评论。Azure Logic App 仅将评论 URL(不是分析正文 — 这保留在 evald 上是私密的)作为另一个 repository_dispatch 转发回来,在原始 VS Code PR 上发布链接。
我们从开发者每隔几个月提出的一个问题开始:哪个模型最好?但对于编码 AI 智能体,这个问题有点像问:哪个引擎最好?引擎很重要,但单靠它还不够。它是 AI 智能体看到的上下文、它能够到达的工具、保持其运行的循环以及确保一切正常工作的评估。那就是护栏,那是我们大部分工程时间花费的地方。
随着模型获得更长的上下文、更好的规划和本机工具使用等新功能,护栏也随之演进以利用它们。随着开发者将 AI 智能体模式推入新的工作流,我们将学到的东西反馈到循环、工具和评估中。每个 VS Code 版本都伴随模型更新一起发布护栏改进。
如果你很好奇护栏如何工作,你可以今天动手体验它。探索 VS Code 源代码,使用 Chat 中的 Tools UI 查看对请求可用的工具,并打开 Chat Debug View 来检查 AI 智能体运行背后的提示、工具调用和结果。尝试切换模型、添加你自己的工具,并让我们知道哪些有效 — 在我们的 GitHub 仓库中分享你的反馈。