深入解读 VS Code Agent Host 的 Prompt 快照机制以 Copilot gpt-5.6-sol 系统提示词为标本【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscodeVS Code本仓库内置的 Agent Host 是驱动 Claude、Copilot、Codex 等编码智能体与真实 SDK/CLI 子进程协同工作的运行时为了让LLM 每次发出的模型请求体在 CI 中可回归、可审查、免 Token它沉淀出一套Prompt Snapshot提示词快照机制。本文以仓库中一份真实基线 Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md 为解剖标本完整还原快照的生成管线、落盘格式、易变字段脱敏规则以及新增一个模型基线的完整操作路径读完你就能看懂如何复现、更新和审阅这类提示词快照。1. 快照到底是什么一条被完整焊死在磁盘上的模型请求体gpt-5.6-sol的这份基线文件本质上是 Copilot CLI 在收到用户消息Say exactly ok后、真正序列化到网络上的那一个 HTTP 请求体的逐字段快照。它以一个 JSON 代码块整体保存我们看它的顶层结构{ model: gpt-5.6-sol, instructions: You are an AI assistant using Copilot SDK in VS Code. ... 完整系统提示词, input: [ 该轮的对话消息数组含当前用户输入 current_datetime... ], tools: [ CLI 暴露给模型的全量工具定义如 bash、view、task、ask_user、sql 等 ], reasoning: { effort: medium }, text: { verbosity: medium }, store: false, stream: true, include: [ reasoning.encrypted_content ], parallel_tool_calls: true }几个值得注意的点模型走的是 OpenAIResponses 协议系统提示词字段叫instructions而非system对话历史放在input数组而非messages。采样与推理参数reasoning.effort、text.verbosity、store、stream、include同样被钉进基线这正呼应了 e2e/README.md 中连thinking/text.verbosity/max_tokens/parallel_tool_calls这类曾经被渲染子集漏掉的采样参数也不再漏掉的设计说明。快照是pretty-print而非逐字节复刻——CLI 在线上会把整个 body 压缩成一行故格式化只是为了可读但没有任何字段被丢弃CLI 一旦开始下发新参数它就会自动出现在下一次基线 diff 里。1.1 快照命名规则该文件由suite(Agent Host E2E — Copilot prompts)中的test(gpt-5.6-sol)生成。命名由snapshotPathForTest决定见 harness/ahpSnapshot.ts路径定位到测试源码同级的__snapshots__/目录虽然测试实际从out/运行快照仍解析回src文件名 {测试全名}.prompt.md其中非字母数字字符全部替换为_于是gpt-5.6-sol变成gpt-5_6-sol最终落到providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md。1.2 同一目录下的姊妹基线providers/snapshots下共有 18 个*.prompt.md基线覆盖SNAPSHOT_MODELS数组里的全部模型族见 copilotPromptsE2E.integrationTest.tsgpt-5、gpt-5-mini、gpt-5-codex、gpt-5.1系列、gpt-5.6系列sol / luna / terra、claude-haiku-4.5至claude-opus-5、gemini-2.0-flash。README 明确说明同一方言下多个模型生成的 prompt 几乎相同CLI 不会按模型分支host 对每个模型注入相同的段之所以仍按模型族各自保留一份是为了让未来的按模型分化能直接定位到引入分化的那个模型族。2. 快照从哪来为什么必须从回放而非录制中读取这是整套机制最反直觉的一点。系统提示词并不由 Agent Host 拼装——它是被编译进github/copilot原生二进制的只有在 CLI 把它序列化到网络的那一刻才可观测。因此测试不能自己构造 prompt 再比对只能真实驱动一轮对话、把 CLI 发出的 body 截获下来。相关设计直接写在测试文件头部注释与 e2e/README.md 的 Prompt snapshots 一节记录方向是非确定性的录制需要连真实 CAPI 去取模型目录/models和实验分组experiment assignment二者都可能以本仓库无法控制的原因改变 prompt所以一次录制运行永远不能作为基线来源回放方向是确定且免 Token 的因此基线一律从一次回放的 turn 里读取——模型流量由 captures/copilotcli-gpt-5-6-sol.yaml 这份 fixture 提供。配套的 fixture 极简且人工可审version: 1 dialect: responses exchanges: - request: model: gpt-5.6-sol system: ${system} messages: - role: user content: Say exactly ok response: content: ok stopReason: end_turndialect: responses对应 OpenAI/responses端点也决定了回放时使用哪套 SSE 再生成器。测试用Say exactly \ok\这种只读、极简的 prompt换取最小、最稳的 fixture。3. 生成管线逐行拆解一个测试用例如何产出基线对应的驱动代码位于 copilotPromptsE2E.integrationTest.ts。流程可概括为建临时工作区 → 起会话 → 显式选模型驱动一轮 → 取最后一个模型请求体 → 格式化 → 与基线比对。3.1 逐模型参数化用例for (const model of SNAPSHOT_MODELS) { // POSIX-only: Windows 上的 prompt 带 PowerShell 专属段见 KNOWN_ISSUES.md (process.platform win32 ? test.skip : test)(model, async function () { this.timeout(120_000); const workspaceDir await mkdtemp(${tmpdir()}/ahp-prompt-snap-); tempDirs.push(workspaceDir); const sessionUri await createRealSession(client, COPILOT_CONFIG, prompt-snap-${model}, createdSessions, URI.file(workspaceDir)); await driveTurnWithModel(client, sessionUri, model); // 取最后一个请求体若 CLI 插入 preflight 请求这仍能保持有意义 const body lease!.observedModelRequestBodies.at(-1); assert.ok(body, no model request body was captured — the turn never reached the model); await assertPromptSnapshot(this.test!, formatPromptSnapshot(body)); }); }要点用例在 Windows 上被整体跳过process.platform win32 ? test.skip : test因为 Windows 的 prompt 携带 PowerShell 专属段、且有一段受机器探测门控SDK 漂移是全 provider 共性的POSIX 运行器已足够捕获——该约束记录于 KNOWN_ISSUES.md。3.2 显式选模型并驱动到 turn 完成driveTurnWithModel通过 AHP 协议WebSocket JSON-RPC向 server 派发ChatTurnStartedaction在消息体里携带model: { id: model }c.dispatch({ channel: chatUri, clientSeq: 1, action: { type: ActionType.ChatTurnStarted, turnId: turn-${model}, startedAt: new Date().toISOString(), message: { text: Say exactly ok, origin: { kind: MessageKind.User }, model: { id: model }, }, }, });随后进入事件循环用waitForNotification一直推进到三类终态之一ChatTurnComplete正常退出循环、ChatError立刻抛错防止把坏 turn 快照成好 prompt、ChatToolCallReady自动确认该工具调用继续推进。每次模型都必须显式选择若不做选择CLI 会用自己的排序算法从 stub 目录里挑模型基线就会变成这套 fixture 的属性而非产品的属性一旦capiStubs.ts中更高排名的模型增删就会移动。3.3 形状护栏与格式化formatPromptSnapshot在序列化前先做形状护栏任何一条不满足都会直接让测试失败防止一次空捕获演变成一份看似合理的小基线请求必须携带非空系统提示词system.length 0必须携带非空工具定义数组tools.length 0必须携带非空的 turn 消息任何一条 turn 消息都不能是空文本。随后把整体请求体做 JSON pretty-print 后包裹进json代码块。normalizeVolatileValues在序列化前递归遍历整个对象对每个字符串调用normalizeVolatile——它是这套脱敏管线的主战场见下节。3.4 两条协议方言的兼容读取IWireRequest接口与readMessages/extractMessageContent显式兼容了两种线上方言Anthropic Messages 把系统提示词放在system、对话放在messagesOpenAI Responses 把系统提示词放在instructions、对话放在input。input还会遇到function_call/function_call_output这些工具调用消息类型它们会被投影为[tool_use …]/[tool_result] …文本再参与非空校验。这些读取仅用于护栏校验从不渲染进最终快照。4. 脱敏的核心哪些字段被规范化、为什么同一份 prompt 在两次正确的运行之间必然存在差异值此外还有两类稳定但属于别处变更预算的内容。normalizeVolatile见 copilotPromptsE2E.integrationTest.ts用一组有序正则把它们全部替换为占位符占位符替换内容保留的包装${session_id}session-state/后的 UUID前缀路径保留${datetime}current_datetime标签对标签保留${os}环境探测的* Operating System: …行行标签保留${available_tools}环境探测的* Available tools: …行行标签保留${platform_packages}Bash 工具里按平台的包管理器提示行行结构保留${repository_instructions}注入的.github/copilot-instructions.md与AGENTS.md全文custom_instruction包装保留${model_count}/${model_catalog}模型数量与内联进Task工具 schema 的完整/models列表计数与目录标签保留${uuid}其余任意位置的 UUID无\r\n先统一成\n再做其余行锚定匹配UUID 占位符最后替换好让上面带标签的占位符保住自己的位置。关键设计是标签保留session id、时钟、环境探测、仓库注入指令、模型目录这些内容被抹掉但包围它们的行结构/标签仍然参与比对——所以一旦这些行改变了形态或直接消失测试照样失败杜绝了删掉即通过的逃逸通道。4.1 两种非运行差异的刻意放行仓库注入指令custom_instruction与模型目录是仅有的两种并非运行间抖动、却仍被放行的脱敏。原因纯粹是成本落到错误的文件上CLI 会逐字注入.github/copilot-instructions.md和AGENTS.md内容跨机器稳定、本可被钉住但给AGENTS.md追加一行就会重写这里全部 18 份基线、让一次无关的文档编辑把 CI 打红模型目录同理——CLI 会把整个/models列表内联进Task工具的 schema计数 逐模型列出若原样保留capiStubs.ts新增一个条目就会重写所有基线包括没人快照的模型。两份标签的存留保证了指令是否注入、注入了几条、在 prompt 中处于什么位置以及目录是否存在、形状是否变化仍被断言。4.2 单元测试对管线的双重验证文件底部还内嵌了一个suite(Copilot prompt snapshot formatting)用纯函数测试钉死格式化行为拒绝残缺 body空系统提示词、空/非数组的工具、空/非数组的消息、空 turn 消息逐一断言抛错且错误信息匹配预期正则原地规范化渲染构造一个含* Operating System: Frobnitz 9、current_datetime、tool_use内容块、parallel_tool_calls、thinking、metadata.session_id的完整 body断言快照中的系统提示词变为${os}/${datetime}、metadata 中的 session UUID 变为${uuid}、其余字段原样保留。这组测试让脱敏不漏、格式化不丢字段的契约不必依赖真实 CLI 也能持续回归。5. 三处变更边界改 CLI、改 Host、改 AGENTS.md 各会命中什么综合测试头部注释与 README可以画出一张谁改了什么 → 什么会红的映射表变更是否会导致 prompt 基线 diffCopilot CLI 升级SDK bump是——CLI 是 prompt 的所有者它发的 body 变了Agent Host 改变了递给 CLI 的东西是——host 也向 prompt 注入内容AGENTS.md/.github/copilot-instructions.md内容变化否设计如此——内容被${repository_instructions}脱敏但指令的条数/位置变化仍会红capiStubs.ts的 stub 目录新增/移除模型否设计如此——目录被${model_catalog}脱敏Host 侧resolveSystemMessageConfig输出变化是——host 拼装的段会逐字落进 prompt注意 host 自身的贡献是被完整覆盖的README 指出node/copilot/prompts/promptRegistry.ts中的resolveSystemMessageConfig拼出的段会原样进入 prompt因此基线对其做了端到端覆盖唯有受 host 配置门控的按模型 contributor够不到——E2E 测试框架没有设置 root config 的接缝这类门控由test/node/agentHostPromptRegistry.test.ts这类单测覆盖。6. 实操如何运行、接受新基线与新增一个模型6.1 三种运行模式Replay无环境变量是默认模式——确定、免 Token、无网络# 只跑这份 prompt 快照测试回放不写任何文件 ./scripts/test-integration.sh --run \ src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts更新接受新基线并就地改写快照无需 TokenAGENT_HOST_UPDATE_AHP_SNAPSHOTS1 ./scripts/test-integration.sh --run \ src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts更新流程与 AHP 流量快照共用同一环境变量ahpSnapshot.ts里AgentHostUpdateAhpSnapshotsEnvVar AGENT_HOST_UPDATE_AHP_SNAPSHOTSassertPromptSnapshot检测到该变量后直接用捕获内容writeFileSync覆盖快照文件并返回测试进程内的CapiReplayProxy继续回放已提交的 fixture因此整个更新不产生任何模型消费。想要只更新单个用例时加--grep test title。更新后务必审阅 Git diff再不带 update 标志重跑一遍以验证新基线确实稳定。其余两个更新变量的边界见 e2e/README.md 的更新一节AGENT_HOST_UPDATE_SNAPSHOTS1同时更新 AHP 快照与 LLM fixture会真实转发 CAPI需GITHUB_TOKEN或gh auth tokenAGENT_HOST_REPLAY_RECORD1仅重录 LLM fixture 的旧式聚焦模式。6.2 快照更新后 Git 层面能看到什么打开 diff 你会看到整个 JSON 代码块重写。由于格式化只缩进到结构层、字符串内的换行被 JSON 转义成\n系统提示词与较长的工具描述各占一行——一句措辞改动会表现为整行被重写而非行内 diff若 CLI 新增了一个参数它会作为新增的顶层键出现在 diff 中。这正是一份让下一次 SDK bump 自动暴露的基线的价值。6.3 为新增模型补一份基线三处改动README 把约束浓缩为两条硬性前提加上测试侧共三处改动缺一不可SNAPSHOT_MODELS加条目在 copilotPromptsE2E.integrationTest.ts 的数组中加入模型 id测试循环会自动生成对应用例stub 目录必须含该模型改 harness/capiStubs.ts。模型不在/models里会在 CLI 构造请求之前就被拒绝测试失败且捕获不到任何 body。以gpt-5.6-sol为例其 stub 条目为{ id: gpt-5.6-sol, vendor: OpenAI, supportedEndpoints: [/responses, ws:/responses], maxContextWindowTokens: 1050000, maxOutputTokens: 128000, maxPromptTokens: 922000, vision: true }提交 fixture 与基线在captures/下提交copilotcli-slugified-test-title.yaml回放仍需应答 turn再以AGENT_HOST_UPDATE_AHP_SNAPSHOTS1生成并提交__snapshots__/*.prompt.md。fixture 的dialect必须匹配该模型的 stub 端点/responses用dialect: responses/v1/messages用dialect: anthropic。「pinning 新模型是 opt-in」这一点需要反复强调基线不从真实/models目录派生新模型不会自动出现往capiStubs.ts加模型本身也不会让套件失败目录被脱敏只有当它同时进入SNAPSHOT_MODELS并提交 fixture 与基线后模型才真正被钉住。7. 设计权衡小结为何值得为一行提示词维护一套流水线最后把整套机制的价值收敛成一句话这份.prompt.md不是文档而是产品契约的可执行快照。系统提示词与工具定义被编译进github/copilot二进制仓库内任何代码都看不到它唯有起真实 server → 驱动真实 CLI → 截获线上 body能观测回放保证了观测的确定性与零成本脱敏保证了基线不受机器、时间、密钥、仓库文档与远端目录的无辜波及显式选模型、形状护栏、标签保留、更新后审阅 diff 这些细节共同把误伤 CI降到最低、把真实漂移的可见度提到最高。当你下次在 VS Code 相关仓库中看到一个以Agent_Host_E2E___Copilot_prompts_*.prompt.md命名的文件、或在升级依赖后看到它整个重写时你已经知道那不是一次意外而是 Agent Host 的 prompt 契约在向你报告CLI 变了或 host 递出的东西变了该去审视这次的 diff 是否有意为之了。延伸阅读仓库内一手材料测试入口与完整管线copilotPromptsE2E.integrationTest.ts快照命名、AHP 语义投影与录制/回放harness/ahpSnapshot.ts端点 stub 与模型目录harness/capiStubs.ts体系总览、更新命令与排障e2e/README.md平台门控与已知问题KNOWN_ISSUES.md配套 fixturecaptures/copilotcli-gpt-5-6-sol.yaml【免费下载链接】vscodeVisual Studio Code项目地址: https://gitcode.com/GitHub_Trending/vscode6/vscode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
