首页
/ 深入解读 VS Code Agent Host 的 Copilot Prompt 快照基线:以 claude-opus-4.6 模型请求体为例

深入解读 VS Code Agent Host 的 Copilot Prompt 快照基线:以 claude-opus-4.6 模型请求体为例

2026-09-07 14:34:15作者:伍希望

在 VS Code 仓库的 agent host(Agent Host,下称"宿主")E2E 测试体系中,存在一类以 *.prompt.md 命名的快照文件,它们把内置 Copilot CLI 真正发往模型端点的完整请求体逐字段钉死在仓库里。本文以 claude-opus-4.6 的基线 Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md 为主体,讲解这类快照的来源、结构、其中每个关键字段的含义,以及它们在免 token、免网络的确定性回放测试中如何被生成与校验。读完本文,你将掌握 agent host 提示词(system prompt)的分层组装机制、Anthropic Messages 请求体的真实线上形状、快照规范化(normalize)规则,以及整套 E2E 快照的更新与运行方法。

一、快照文件是什么:模型请求体的"银版照片"

.prompt.md 位于 providers/snapshots/,是 copilotPromptsE2E 测试套件(copilotPromptsE2E.integrationTest.ts)为某个模型族生成的组装提示词快照(assembled-prompt snapshot)。按照 E2E 目录 README.md 的定义,providers/__snapshots__/ 同时存放两类产物:语义化的 AHP 流量快照 *.traffic.ahp.yaml,以及这里讨论的 *.prompt.md——它钉住的是模型请求体的每一个字段

有几个关键前提需要先厘清:

  • 提示词是 CLI 的"产品":该 prompt 被编译进 @github/copilot 原生二进制中,只有 CLI 把请求体序列化到网络上时才可观测。因此测试无法在源码里"读"到它,只能从一次回放的 turn 中把它截取出来。
  • 提示词不是宿主的"产品":宿主(Agent Host)只负责在启动 session 时把若干 system 分段、工具指令与运行时上下文交给 CLI,最终字符串由 CLI 拼装。测试的目的之一,就是验证"宿主交给 CLI 的东西 + CLI 自身的拼装"最终在网络上的形状没有回归。
  • 快照采用回放模式生成基线,而不是录制模式:录制会真实触达线上 CAPI 拿模型目录与实验配置,二者都可能让 prompt 因为与本仓库无关的原因漂移。

与本文件并列的还有 claude-haiku-4.5claude-sonnet-4.5claude-opus-4.5/4.7/4.8gpt-5gpt-5.1-codexgemini-2.0-flash 等一批基线。测试中维护了 SNAPSHOT_MODELS 清单(见 copilotPromptsE2E.integrationTest.ts),其中每个条目覆盖一个模型族;claude-opus-4.6 同时在 copilotTestConfiguration.ts 中被用作 modelSwitchTarget(会话中途切换模型的回放目标)。

二、逐字段拆解:claude-opus-4.6 的请求体

该快照正文是一个 fenced json 代码块,对应 CLI 序列化出的完整 Anthropic Messages 请求体POST /v1/messages 方言)。整体骨架如下。

2.1 顶层采样参数

{
  "model": "claude-opus-4.6",
  "max_tokens": 32000,
  "stream": true
}
  • model 精确到 claude-opus-4.6,测试通过显式的模型选择动作注入(见下文第三节),不会留空让 CLI 按自身排序去选。
  • max_tokens 为 32000,即单轮回复的 token 上限;由于 session 中可能存在大量工具调用,宿主会给出较大的预算。
  • stream: true 表示使用 SSE 流式返回——这也正是回放代理(CapiReplayProxy)向 SDK 回放已录制 SSE 的依据。

2.2 system 块:分层组装出的系统提示词

请求的 system 是一个块数组(而非单个字符串),本基线含两个 type: text 文本块,每块都带:

"cache_control": { "type": "ephemeral" }

cache_control 是 Anthropic API 的提示词缓存提示:标记为 ephemeral 的块可被服务端缓存,多轮长会话中相同的头部提示词不必重复计费与传输。它出现在每个 system 块和用户消息内容块上,说明 CLI 会主动为可复用文本做缓存标注。

第一块是身份与全局行为准则,开头为 "You are an AI assistant using Copilot SDK in VS Code. You help users with software engineering tasks. When asked about your identity, you must state that you are an AI assistant using Copilot SDK in VS Code.",随后包含:

  • <code_change_instructions> 下的 rules_for_code_changes(外科手术式最小改动、修 bug 时连同强耦合缺陷一并修复、同步更新文档等)、linting_building_testing(只运行已存在的 linter/构建/测试,优先最小定向验证)、using_ecosystem_toolsstyle
  • <tips_and_tricks>(先反思命令输出再走下一步、结束时清理临时文件、用 view/edit 而非 create 处理既有文件、拿不准时用 ask_user 澄清等);
  • <environment_limitations><prohibited_actions>(非沙箱共享环境、禁止外泄敏感数据、拒绝版权内容等安全与隐私约束)。

该块与宿主侧的提示词组装有明确对应:宿主通过 promptRegistry.tsresolveSystemMessageConfig(model, context) 解析出每个 session 的 SystemMessageConfig,而系统消息只在 session 创建/恢复时被 SDK 接受,没有中途热更新通道("launch-time freeze")。此目录的 AGENTS.md 详细记录了"基础身份 + 通用工具指令 + 按模型 contributor"的分层叠加规则。

第二块<environment_context>,即把模型所处的运行环境注入提示词,例如:

  • Current working directory: ${workdir}
  • Operating System: ${os}
  • Available tools: ${available_tools}

注意这里出现的是 ${...} 占位符而不是真实路径/系统名。这正是快照规范化的结果:正文在序列化前经过 normalizeVolatile(见 copilotPromptsE2E.integrationTest.ts),把真实机器相关的值替换成确定性的占位符,保证同一份基线在 Linux/macOS/CI 上字节级可比较。该块随后是工具使用准则(bash 的环境注意点、view 的多文件并行读取、edit 的批量顺序替换等)与系统通知说明。

此外,两块文本内部还包含 <current_datetime>${datetime}</current_datetime><system_reminder><custom_instruction>${repository_instructions}</custom_instruction> 这类包装标签,它们分别被替换为时间、复述规则与注入的仓库指令。

2.3 messages:测试驱动的首轮用户消息

"messages": [
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "<current_datetime>${datetime}</current_datetime>\n\nSay exactly \"ok\"",
        "cache_control": { "type": "ephemeral" }
      }
    ]
  }
]

消息体被包裹在 <current_datetime> 里(CLI 自行注入的时钟上下文),真正任务是极简且确定性的 "Say exactly ok"——最小化模型轮次能让 fixture 更小、更稳。这与 E2E 框架"提示词保持确定性与极简"的准则一致。

2.4 tools:宿主 + 客户端工具的全量目录

tools 数组序列化了该模型在本次 turn 中可用的全部工具定义(bash、view、edit、skill、ask_user、sql、grep、task 等,涵盖宿主本地工具与转发自工作台的客户端工具)。README 明确指出:模型目录(/models 的 stub 清单)会被 CLI 内联进 Task 工具的 schema,作为"数量 + 逐模型列表",因此快照对这部分的处理是——保留外围标签与形状、抹去具体清单内容(正则 /\(\d+ models available\)/(${model_count} models available)),否则在 capiStubs.ts 中新增任何模型都会重写全部基线。

2.5 推理与输出配置

请求体还带有 temperaturethinkingtype: adaptivedisplay: summarized)、output_configeffort: medium)与 stream 等采样/推理配置。README 明确说,这些采样参数曾因"只钉住渲染后的子集"而漏网,copilotPromptsE2E 现在把它们一并钉住——一个参数只要 CLI 开始发送,就会自动出现在下一次基线 diff 中,杜绝静默漂移。

三、快照如何被生成:从回放 turn 中读请求体

快照不是在录制时拍的,而是从回放的 turn 上读出来的。测试流程(对应 copilotPromptsE2E.integrationTest.tsSNAPSHOT_MODELS 循环)大致是:

  1. suiteSetup 通过 AgentHostE2EServerLease(COPILOT_CONFIG) 拉起一个真实的 agent host 服务子进程,前置一个 CapiReplayProxy;代理是唯一在录制/回放间切换的组件。
  2. 每个用例先用 mkdtemp 建独立工作区,再 createRealSession(...) 创建真实 session,然后 driveTurnWithModel(client, sessionUri, model) 派发一个携带显式模型选择的 turn:
    • 动作类型为 ActionType.ChatTurnStarted,消息 text: 'Say exactly "ok"'model: { id: model }第 158-174 行);
    • 测试循环等待通知,遇到 ChatToolCallReady 时自动以 ToolCallConfirmationReason.Setting 确认工具调用,直到收到 ChatTurnCompleteChatError——错误会直接抛错,防止把坏 turn 快照成"良好 prompt"。
  3. turn 完成后,lease.observedModelRequestBodies.at(-1) 取出本次观测到的最后一个模型请求体(取最后一条以容忍 CLI 可能的 preflight 请求),交给 assertPromptSnapshot
  4. formatPromptSnapshot(body) 先做形状守卫(shape guard):解析后校验 system 非空、tools 非空、turn messages 非空且无空消息,任何一个不满足都判定"线上形状已变"并失败(第 242-261 行);随后递归执行 normalizeVolatile,把 \r\n、session id、<current_datetime>、OS 行、可用工具行、包管理器提示、<custom_instruction>、模型数量与模型目录、其余 UUID 分别替换为固定占位符(${session_id}${datetime}${os}${repository_instructions}${model_catalog}${uuid} 等)。
  5. 最终以 pretty-print 的 JSON 形态写入基线——CLI 在网络上其实把 JSON 压缩成一行,所以正文做缩进排版;但 JSON 字符串内部的反斜杠转义不会被还原,system 提示词与长工具描述各自保持单行,一句措辞改动就会体现为"整行被重写"级别的 diff。

快照的落盘路径由 ahpSnapshot.tssnapshotPathForTest 推导:与测试源文件同级的 __snapshots__/ 目录、文件名取 sanitizeName(完整测试标题) + .prompt + .md。文件名 Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md 正是由 suite 标题、用例名与后缀拼出的。

四、断言策略:什么被钉死、什么被抹去

测试对请求体采用"能钉则钉、有理由才抹"的策略。抹去项在 normalizeVolatile 与 README 中被明确记录,每一类都有动机:

被抹去/替换 原因
${workdir}${os}${available_tools}、包管理器提示 平台/机器耦合,直接对比必然永久红
<current_datetime>${datetime}</current_datetime> 每次运行都不同
${session_id} 等运行时 UUID 运行期标识
<custom_instruction>${repository_instructions}</custom_instruction>内容 保留包装标签以断言"确实注入了指令、注入了几处、位置在哪",但抹去具体文本,否则给仓库根 AGENTS.md 追加一行注释就会重写全部基线
${model_catalog} 的模型清单 同理,避免 capiStubs.ts 的一次小改动污染所有模型族的基线

其余全部钉住:消息角色与顺序、保留的历史、是否发送了 system 提示词、文本与附件内容、工具名与输入、以及采样参数。这与流量录制侧"对模型请求做断言"的机制互补——回放选择响应靠序号method, path 维度上的第 N 次请求回放第 N 个响应),而录制下的请求投影通过 harness/modelRequestProjection.ts 与实时请求比对;请求体比对失败会以 [capi-replay] N model request mismatch(es) 报错并打印两侧投影,通常意味着 capture 已过期,应重新录制而非手改请求块。

五、配套 fixture:回放的"答案册"

每次回放 turn 仍需要模型响应,因此每个快照模型都配套一个录制好的模型 fixture:本文件的对应物是 captures/copilotcli-claude-opus-4-6.yaml(同目录下还有 copilotcli-claude-opus-4-5.yamlcopilotcli-claude-sonnet-4-6.yaml 等,以及 codex-*claude-* 系列)。fixture 采用极简、可人工审查的格式:

version: 1
dialect: anthropic          # anthropic → POST /v1/messages,responses → POST /responses
exchanges:
  - request:
      model: claude-opus-4.6
      ...
    response:
      content: ok
      stopReason: end_turn

dialect 只在文件顶层存一次,它同时决定 turn 归属的端点与回放时使用的 SSE 再生器。凡是走 copilotcli-* fixture 的模型,其 stub 端点必须与该方言一致:/v1/messagesdialect: anthropic/responsesdialect: responses

六、如何运行与更新基线

6.1 只跑提示词快照套件

./scripts/test-integration.sh --run \
  src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts

回放是默认模式:读取已提交的 fixture 与基线,不需要 token、不触网。严格性体现在两处:没有录制过响应的请求是硬性 cache miss(CI 永远无法静默打到真实 CAPI);基线与 fixture 缺一不可——若基线不存在,assertSnapshot 的"缺失即新建并放行"行为会被显式拒绝(第 222-227 行),并提示用更新标志生成后再提交。

6.2 接受新基线

AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1 ./scripts/test-integration.sh --run \
  src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts

该标志只重写 AHP 快照(含本 prompt 基线),回放既有 LLM fixture,仍然免 token。更彻底的 AGENT_HOST_UPDATE_SNAPSHOTS=1 会同时向真实 CAPI 重录 LLM fixture(需要 GITHUB_TOKENgh auth token)。更新后应审查 Git diff 再重跑一次无标志验证。若只需更新单一场景,可加 --grep "<test title>"。整套测试并行跑可用 npm run test-agent-host-e2e

6.3 新增一个被钉住的模型

README 与测试注释共同给出两条硬约束:

  1. 模型必须先进入 harness/capiStubs.ts 的 stub 目录——不在 /models 里的模型在 CLI 构建请求前就被拒绝,测试将捕获不到任何请求体而失败;
  2. 必须提交配套 fixturecaptures/copilotcli-<slugified-title>.yaml)并提交基线,因为回放 turn 总需要被应答。

之后再把它加入 SNAPSHOT_MODELS 并用 6.2 的命令生成基线。值得注意:套件对模型选择是显式的,故意不钉"未选择"的状态——否则基线记录的将是套件自身 fixture 的属性(CLI 会按 stub 目录自排序),并随 stub 目录增删而漂移。

七、质量校验与观测价值

这份快照是 agent host 与 Copilot CLI 之间的"契约边界"。它一旦 diff,通常意味着两种事之一:CLI 升级(SDK bump)改变了拼装,或宿主交给 CLI 的内容发生了改变。宿主侧的实际贡献可溯源到 resolveSystemMessageConfig——promptRegistry.ts 组装出的各 system 分段会原样落入本 prompt,因此基线对宿主提示词组装是端到端覆盖的。被刻意排除的是两类:依赖宿主根配置的逐模型 contributor(E2E 无设置根配置的通道,交由 agentHostPromptRegistry.test.ts 单测覆盖),以及本仓库不该为它的改动买单的内容(仓库指令文本、模型目录)。

想继续深入,可依次阅读:

理解本文件的最好方式,是把它当成一面"镜子":任何一次提示词措辞调整、历史保留策略变化、附件封送改造或 SDK 升级,都会让这条基线的 diff 忠实显现——这正是把真实模型请求体钉进仓库的核心价值所在。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388