VS Code Agent Host 端到端测试中的 Copilot 提示词快照:gpt-5.6-luna 模型请求体基线深度剖析
【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode
本技术指南聚焦 Visual Studio Code 仓库内 Agent Host(agent host)端到端测试体系中的一个特殊构件:prompt.md提示词快照。它以 Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md 为实例,讲解该快照是什么、为什么要把整个模型请求体「钉」下来、请求体每个字段的工程含义、宿主编排系统提示词(system prompt)的源码路径,以及如何确定性、免 Token 地更新和维护这份基线。读完你将掌握该 E2E 体系「提示词快照」的完整工作原理,能独立判断一次请求体 diff 是 CLI(SDK)升级、宿主改动还是无关干扰,并学会新增一个受钉扎模型的标准操作。
从一份「快照」看起:它钉住了什么
在src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/目录下,除了*.traffic.ahp.yaml语义流量快照外,还有一批*.prompt.md文件。以Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md为例,它的正文是一个被 ```json 围栏包裹的、经过易变值归一化的模型请求体(model request body)基线。文件名中的Agent Host E2E — Copilot prompts是测试套件的完整标题,gpt-5_6-luna是被钉扎的模型族(文件名将.转义为_)。
这份文件的特殊之处在于:提示词编译在@github/copilot原生 CLI 二进制内部,只有在该 CLI 把请求序列化到网络(wire)上时才可观测。因此测试无法在宿主代码里直接读到「最终提示词」,只能从一次重放(replay)回合中把它读取出来——这正是 copilotPromptsE2E.integrationTest.ts 做的事情,其文件头注释完整说明了这一设计动机。与之对比,*.traffic.ahp.yaml记录的是 Agent Host Protocol(AHP)的语义化往返流量,而本快照记录的是宿主与模型服务之间那条线上发出的完整请求体。
为什么需要「钉住整个请求体」
E2E README 的 Prompt snapshots 一节(第 314–346 行)给出了清晰的因果链:
- 此前基线只断言渲染后的提示词子集,导致采样参数(
thinking/text.verbosity/max_tokens/parallel_tool_calls)长期无人钉扎,字段悄悄变化也不会被察觉; - 请求体的每一个字段都被逐字收录——组装好的系统提示词、工具定义、携带 CLI 注入上下文(如
<current_datetime>、<system_reminder>)的回合消息,以及采样参数; - 主体按结构美化打印(CLI 在网络上将其压缩成一行),但不丢弃任何字段。因此 CLI 未来新增的任何一个请求参数,都会在下一次基线 diff 中自行现身,而不是被渲染逻辑过滤掉。
一个关键取舍是「能留多少真实提示词就留多少」:被抹除的只有每次运行必然变化的量——会话 id、系统时钟、环境探测结果(操作系统名、PATH上找到的工具)、Bash 工具里的平台专属包管理器提示、注入的仓库指令与模型目录。且每种易变值保留其外围标签或包装,因此「这些行是否还存在、是否变形」仍然构成断言。
gpt-5.6-luna 基线字段地图
对照快照文件,请求体自顶向下的字段与工程含义如下:
| 字段 | 基线中的值 | 说明 |
|---|---|---|
model | gpt-5.6-luna | 被测模型 id,与SNAPSHOT_MODELS数组中条目一一对应(见 copilotPromptsE2E.integrationTest.ts,其中gpt-5.6-luna位于 L68) |
instructions | 完整系统提示词字符串 | OpenAI Responses 方言将系统提示词命名为instructions(Anthropic Messages 方言命名为system),类型定义注释见 copilotPromptsE2E.integrationTest.ts |
input | 一个 user 回合 | 消息文本是测试的确定性提示Say exactly "ok",且被 CLI 包上<current_datetime>${datetime}</current_datetime>上下文标签(${datetime}是归一化占位符,真实运行中为时间戳)。在 Responses 方言下回合结构为type: message+content: [{ type: input_text }] |
tools | 工具定义数组 | 钉扎 CLI 实际下发的工具清单与 JSON Schema。片段中可见bash工具的完整定义(含sync/async/detach/shellId参数与结果语义说明),其描述里强调后台命令结束会收到自动通知等运行时约定 |
reasoning | { "effort": "medium" } | 思考预算配置;README 明确指出这类渲染子集字段以前未被钉扎 |
text | { "verbosity": "medium" } | 文本详细度参数,同属被补钉的采样面 |
store | false | 请求存储开关 |
stream | true | 流式开关 |
include | ["reasoning.encrypted_content"] | 请求响应中要包含的(加密)推理内容项 |
parallel_tool_calls | true | 是否允许并行工具调用 |
值得强调的是input里那行<current_datetime>${datetime}</current_datetime>:在真实请求中这是由 CLI 注入用户文本前的时间上下文,测试通过正则将其归一化为${datetime},既避免每次运行基线抖动,又保住了「该上下文确实存在且处于该位置」这一结构事实。
系统提示词的宿主侧来源
快照中的instructions是一段极其庞大的文本,但它不是黑盒:其中大量段落由 VS Code 的 Agent Host 侧代码在启动会话时编排注入,可在仓库中找到一一对应的源码。
身份与默认配置
宿主默认系统消息定义在 systemMessage.ts:
COPILOT_AGENT_HOST_IDENTITY:快照开头的身份句「You are an AI assistant using Copilot SDK in VS Code…」即出自此处(L13);COPILOT_AGENT_HOST_SYSTEM_MESSAGE:默认以mode: 'customize'携带identity段的replace覆盖(L36-L44);COPILOT_AGENT_HOST_FILE_LINK_INSTRUCTIONS:被resolveSystemMessageConfig作为尾部content追加,对应快照末尾的<file_folder_and_symbol_links>块(L16-L29);COPILOT_AGENT_HOST_WORKSPACELESS_INSTRUCTIONS:仅在工作区缺失(workspaceless)会话中追加(L53-L62)。
按模型的分段/整段编排
promptRegistry.ts 是宿主侧编排器,镜像了 Copilot 扩展的PromptRegistry:
AgentHostPromptRegistry.registerPrompt支持以自定义matchesModel谓词或模型族前缀(familyPrefixes)注册贡献者;resolveSystemMessageConfig(L156-L160)把「按模型的配置」叠加上「对所有模型生效的通用层」,最后追加文件链接指令;- 贡献者既可选择整段替换(
resolveFullSystemPrompt→mode: 'replace',会丢弃 SDK 基础提示词与护栏),也可选择分段覆盖(resolveSectionOverrides→mode: 'customize',保留 SDK 基础提示词); - 该配置在会话「创建/恢复」时解析一次(SDK 不支持中途更新系统消息),工具集变化靠重启检测触发重新计算——所以快照中钉住的工具门控内容反映的是会话启动那一刻的工具集。
通用工具指令层
toolInstructions.ts 提供模型无关的tool_instructions行:例如「大输出被落盘到临时文件时必须用view工具的窄view_range读取,禁止用cat/head/tail/sed」(COPILOT_AGENT_HOST_LARGE_OUTPUT_TOOL_INSTRUCTION),以及按设置开启的子代理模型指导、按客户端工具存在性门控的浏览器工具指导等。这正是快照instructions中「When a tool reports that its output was saved to a temporary file…」等段落的出处。resolveToolInstructionsOverride还会把通用行折进模型贡献者已有的该段覆盖,而不是粗暴覆盖。
被有意抹除的两处「非易变」内容
README 特别说明了两个抹除点不是因为运行间有差异,而是为隔离变更预算:
<custom_instruction>${repository_instructions}</custom_instruction>——CLI 会把仓库根AGENTS.md、.github/copilot-instructions.md逐字注入。若如实钉扎,给AGENTS.md加一行就会重写每个模型的全部基线。抹除后<custom_instruction>包装仍保留,从而断言「指令被注入、注入了几处、位于提示词何处」。- 模型目录——CLI 会把整个
/models清单内联进Task工具 Schema。左标签保留,因此目录从提示词消失或变形仍会使测试失败。
易变值归一化:让基线跨机器、跨平台稳定
归一化发生在测试文件的normalizeVolatile函数中(copilotPromptsE2E.integrationTest.ts),对字符串递归执行,每步替换保留标签或包装:
| 归一化项 | 替换为占位符 |
|---|---|
CRLF 换行(\r\n) | \n |
session-state/目录下的 UUID | ${session_id} |
<current_datetime>…</current_datetime> | ${datetime} |
| 系统提示词中的操作系统行 | ${os} |
Available tools:探测行 | ${available_tools} |
| Bash 工具的平台包管理器提示行 | ${platform_packages} |
<custom_instruction>…</custom_instruction>注入内容 | ${repository_instructions} |
| 模型数量提示 | ${model_count} |
Available models:目录清单 | ${model_catalog} |
| 其余任意 UUID | ${uuid} |
文件还内置了一个「格式守卫」套件Copilot prompt snapshot formatting(L372-L414),用单元测试验证:不完整请求体(无系统提示词 / 无工具定义 / 无回合消息 / 空消息)会被formatPromptSnapshot拒绝;易变值则被就地归一化。这些保护避免「一次空捕获变成一份看似合理的基线」或让坏掉的回合被当成好提示词快照。
快照如何产生:从重放回合读取,而非录制时读取
测试之所以只在重放模式下建立基线,是因为录制方向是非确定性的:录制会访问真实 CAPI 获取模型目录与实验(experiment)分配,而这两者都可能以与本仓库无关的原因改变提示词。因此 copilotPromptsE2E.integrationTest.ts 的核心流程是:
- 对
SNAPSHOT_MODELS中每个模型,在临时工作目录里通过createRealSession创建一个真实会话(provider 为copilotcli,配置见 copilotTestConfiguration.ts); - 派发
ChatTurnStarted(message.text = 'Say exactly "ok"',message.model.id = 被测模型); - 用
driveTurnWithModel驱动到turnComplete,期间对每个ChatToolCallReady自动confirmed(理由是ToolCallConfirmationReason.Setting); - 取
observedModelRequestBodies的最后一条请求体(注释说明:若 CLI 插入预检请求,取最后一条能保持断言有意义); - 经
formatPromptSnapshot格式化为 JSON 围栏文本,交给assertPromptSnapshot与已提交基线比对。
会话启动依赖AgentHostE2EServerLease(在 harness/agentHostE2ETestHarness.ts),它fork真实服务进程并前置CapiReplayProxy。重放时只有模型响应是假的,服务器、SDK 子进程、工具执行、AHP 协议全部为真。重放所用的模型夹具是提交在 captures/copilotcli-gpt-5-6-luna.yaml 的 YAML(命名规则copilotcli-<测试标题slug>.yaml);该夹具的dialect决定重放走/responses(dialect: responses)还是/v1/messages(dialect: anthropic)。
快照路径解析由 ahpSnapshot.ts 的 snapshotPathForTest(L636-L645)完成:用测试的fullTitle加名称(prompt)与扩展名(md)拼出__snapshots__下的文件。若基线文件不存在且未处于更新模式,测试会直接报错而非自动创建通过——避免「一个没人写的基线」被assertSnapshot悄悄绿化。
三种运行模式与维护命令
E2E 体系区分三个方向(详见 e2e README 的 TL;DR 与「Updating snapshots and fixtures」两节):
# 默认:重放(确定性、免 Token,CI 跑的就是这个) ./scripts/test-integration.sh --run \ src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts # 更新 AHP 语义快照 + 提示词基线(重放既有 LLM 夹具,免 Token) AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1 ./scripts/test-integration.sh --run \ src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts # 全量更新(访问真实 CAPI 重录 LLM 夹具,需要 GITHUB_TOKEN 或 gh auth token) AGENT_HOST_UPDATE_SNAPSHOTS=1 ./scripts/test-integration.sh --run \ src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts接受一份新基线后必须人工审查 Git diff(路径是否被归一化?是否混入用户名、Token、未发布模型 id?),再不带更新标志重跑一次验证已提交的快照可复现。
两个平台注意点:
- POSIX-only:该套件在 Windows 上跳过(
process.platform === 'win32' ? test.skip : test),因为 Windows 提示词携带的是 PowerShell 专属段落,而非本快照的改名版;SDK 漂移是 provider 全局性的,POSIX 运行器已能捕获。原因记录见 KNOWN_ISSUES.md。 - 失败测试会重启共享服务器:teardown 发现测试失败时先
dumpRuntimeLogsOnFailure再释放租约,避免一个卡在回合中途的会话把共享宿主卡死、殃及下一个模型。
新增一个被钉扎模型的标准流程
README 明确指出「钉扎新模型是 opt-in 的」,且不从实时/models目录派生。任何新模型必须满足:
- 进入 harness/capiStubs.ts 的桩目录——模型缺失于
/models时会在 CLI 构建请求前被拒绝,测试将因捕获不到请求体而失败; - 提交一个夹具基线——重放回合仍需要应答,夹具路径为
captures/copilotcli-<slugified-test-title>.yaml,其dialect须与模型的桩端点匹配; - 加入
SNAPSHOT_MODELS并提交夹具与基线——仅在capiStubs.ts加模型不会让套件失败,因为 CLI 内联的模型清单本就处于抹除状态;一个模型被真正钉扎,必须有人在SNAPSHOT_MODELS中也加上它。
同一方言内的多个模型族基线几乎相同——CLI 不会在方言内按模型分支提示词,宿主对所有模型贡献相同段落——但它们仍按族各自保留一份,以便未来的按模型分叉能在「引入它的那个族」上暴露。
如何解读一次 diff
README 明确给出三类 diff 的归因:
| 现象 | 根因 | 处置 |
|---|---|---|
| 快照提示词变化 | CLI(SDK)升级 | 审查新请求是否正确,正确则AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1接受新基线 |
| 宿主传给 CLI 的内容变化(历史保留、注入上下文前导、附件封送) | promptRegistry.ts 等宿主装配逻辑改动 | 同上,审查后重录 |
编辑仓库指令(AGENTS.md等) | 不会引起 diff——<custom_instruction>内容按设计被抹除 | 无需任何操作 |
同时要分清本快照的职责边界:宿主自身贡献的段落(经resolveSystemMessageConfig逐字落入提示词的段)被端到端覆盖;而被宿主配置门控的按模型贡献者不在此列——E2E 脚手架没有设置根配置的接缝,那一部分由 test/node/agentHostPromptRegistry.test.ts 等单元测试覆盖。
小结
Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md这类文件看似只是一坨 JSON,实则是 Agent Host 端到端测试的「模型请求体契约」:它以确定性、免 Token 的方式,把@github/copilotCLI 为每个模型族发出的完整请求体钉成可审查的基线,覆盖宿主编排的系统提示词层、CLI 注入的上下文、工具 Schema 与采样参数。理解它的字段地图、归一化策略与「重放而非录制」的生成哲学后,无论你是要审查一次 SDK 升级引发的提示词变化,还是为新的模型族补一份钉扎基线,都能直接对仓库内的测试、源码与 README 找到依据并完成闭环。
延伸阅读(仓库内):copilotPromptsE2E.integrationTest.ts(测试与格式化逻辑)、e2e README 的 Prompt snapshots 节(设计决策与 FAQ)、promptRegistry.ts 与 systemMessage.ts(宿主侧提示词编排)、toolInstructions.ts(通用工具指令)、ahpSnapshot.ts(快照路径解析)、captures/copilotcli-gpt-5-6-luna.yaml(对应模型夹具)。
【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考