VS Code Agent Host 提示词基线机制全解:解剖 gpt-5.1-codex-mini 的完整模型请求体快照
在 VS Code 的 Agent Host(智能体宿主)端到端测试体系中,有一个看似不起眼却极其关键的快照文件:Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md。它以 JSON 形式完整记录了内置 Copilot CLI 在发送 gpt-5.1-codex-mini 模型请求时序列化到线上的每一个字段——从系统提示词到工具定义、从采样参数到用户消息。本文以该快照为主体样本,结合测试源码与运行框架,深入讲解 VS Code 如何把 Copilot 提示词做成可回归、免 token、确定性重放的基线资产;读完后你将理解快照的生成链路、字段含义、归一化规则,以及如何新增模型、更新与解读基线变更。
一个快照背后是什么:提示词基线的意义
为什么提示词需要“被钉死”
Copilot 编码智能体最终的提示词并不是 VS Code 仓库内的纯文本。注释与 README 都明确指出:提示词是 @github/copilot CLI 的产品,而非宿主(Agent Host)的产品——它被编译进 Copilot 的原生二进制,只有在 CLI 把模型请求体序列化到网络线上时才可观察。因此,仓库无法直接读取提示词源码来断言其内容,只能通过监听“线上流量”来捕获它。
这正是 copilotPromptsE2E.integrationTest.ts 做的事情:它钉死内置 Copilot CLI 针对每个模型发送的模型请求体所有字段,覆盖系统提示词、工具定义、回合消息,以及 CLI 在消息外围注入的上下文(<current_datetime>、<system_reminder> 等),同样包含 thinking / text.verbosity / max_tokens / parallel_tool_calls 这类采样参数。
从“回放回合”而非“录制回合”读取
测试读取提示词的路径非常讲究:它从重放(replay)的回合中读取请求体。这样既确定性又免 token。录制(recording)方向则相反——录制会接触真实 CAPI 以获取模型目录与实验分配,这两者都可能出于本仓库无法控制的原因改变提示词,因此录制运行永远不会产生基线。
当一个 diff 出现时,它意味着两件事之一:CLI 变了(SDK 升级),或宿主改变了它交给 CLI 的东西。
快照文件如何被生成与命名
逐模型驱动的测试注册
测试套件 Agent Host E2E — Copilot prompts 遍历 SNAPSHOT_MODELS 常量数组(位于 copilotPromptsE2E.integrationTest.ts),为每个模型注册一个用例。该列表包含 gpt-5、gpt-5.1-codex-mini、claude-opus-5、gemini-2.0-flash 等约 20 个模型族,覆盖了 Copilot 扩展 agentPrompt.spec.tsx 中的模型族加上 Agent Host 支持的新模型族。一个关键设计是:每个模型必须显式选择。不发送模型选择是刻意不钉死的——CLI 会按自身排序从桩目录里选模型,那样基线记录的就不是产品行为,而是本套件 fixture 的属性。
gpt-5.1-codex-mini 就是被选中钉死的模型之一,因此它拥有一份专属的 .prompt.md 基线。由于测试是 POSIX-only 的(Windows 上会以 test.skip 跳过——Windows 提示词带 PowerShell 专属段落),你看到的这份基线本质上是 POSIX 形态的请求体。
快照的落盘路径与基线更新
快照命名由 ahpSnapshot.ts 中的 snapshotPathForTest 计算:把测试的完整标题做 sanitize 后,拼上 .prompt.md 后缀,存放在测试源码旁的 __snapshots__/ 目录。这就是文件名为 Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md 的原因。
更新基线使用与 AHP 快照相同的环境变量开关:
AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1 ./scripts/test-integration.sh --run \
src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts
正常回放运行是只读的:assertPromptSnapshot 要求基线的确已提交,否则直接抛错(“no committed prompt baseline”),绝不会自动创建一个空白基线让模型“绿化通过”;而处于 AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1 时会把当前抓到的 body 就地写盘。更新后必须人工 review diff,再不带标志重跑一次验证。
抓哪个请求体
测试驱动一个完整回合后,通过 lease.observedModelRequestBodies.at(-1) 取最后一个被观察到的模型请求体——这保证即使 CLI 中途插入了一个 preflight 请求,也只会取到真正的模型回合请求。
解剖 gpt-5.1-codex-mini 的请求体快照
作为对比样本,claude-opus-5 的基线(见同目录 Agent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md)属于 Anthropic Messages 方言,字段名是 system + messages;而本快照所属的 gpt-5.1-codex-mini 走的是 OpenAI Responses 方言(POST /responses)。测试源码里的 IWireRequest 接口注释精确区分了这两种线上形态:
Anthropic Messages spells the system prompt
system; Responses usesinstructions。(同样地,Anthropic 用messages装回合,Responses 用input。)
顶层字段一览
| 字段 | 值 | 说明 |
|---|---|---|
model |
gpt-5.1-codex-mini |
显式选择的模型,绝不允许留空由 CLI 自行排序 |
instructions |
超长系统提示词(详见下节) | Responses 方言的系统提示字段 |
input |
[{role: "user", content: [{type: "input_text", text: "<current_datetime>${datetime}</current_datetime>\n\nSay exactly \"ok\""}]}] |
回合消息;注意时间戳已被归一化为 ${datetime} 占位符 |
tools |
一长串函数工具定义 | 由 CLI 按 SDK 定义注入,包含 bash、view、edit、skill、ask_user、sql、task、web_fetch 等(均带完整 description 与 parameters) |
reasoning |
{"effort": "medium"} |
推理预算(encrypted content 一并经 include 返回) |
store |
false |
请求不留存 |
stream |
true |
流式返回 |
include |
["reasoning.encrypted_content"] |
需要服务端回传的内容类型 |
parallel_tool_calls |
true |
允许并行工具调用 |
用户回合消息刻意写得最简单——Say exactly "ok"。这并非随意为之:prompt 快照要钉住的是系统提示词与工具定义的结构,而不是某个真实任务的内容。最简输入能最小化 fixture 体积与易碎面,同时完整暴露 CLI 注入的上下文前导 <current_datetime>${datetime}</current_datetime>。
请求体的渲染与归一化
formatPromptSnapshot 把序列化后的 body 以 JSON.stringify(..., null, 2) pretty-print 并包进 ```json fenced block,而非逐字节复刻——CLI 在线上把它压成单行,pretty-print 只影响缩进层级,不会改动字段内容。由于 JSON 会把字符串值里的换行转义掉,系统提示词与超长工具描述各自保持在一行内;任何改写都会表现为整行被重写,从而在 diff 中清晰可见。
没有任何字段被丢弃。一个参数只要 CLI 开始发送,就会在下次基线 diff 中自行出现——这是“全字段钉死”与旧式“渲染子集对比”的本质区别。
系统提示词(instructions)的内容编排
这份快照最有价值的部分在于把完整系统提示词原样暴露出来。从源码结构与正文可以拆解出 CLI 拼装的层次(实测正文均以 \n 转义存放在 JSON 字符串中):
- 身份声明:
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>(外科手术式精准修改、不修与任务无关的既有问题、保持类型安全避免as any、DRY 优先复用、收紧错误处理禁止宽泛 catch 与静默失败等)、<linting_building_testing>(只跑已存在的 linter/构建/测试、优先最小目标化命令)、<using_ecosystem_tools>、<style>。<tips_and_tricks>:回合驱动建议(先反思命令输出再继续、任务结束清理临时文件、不确定时用ask_user澄清、不主动创建规划类 markdown)。<environment_limitations>+<prohibited_actions>:非沙箱环境声明与安全红线(不向第三方泄露敏感数据、不提交密钥、拒绝生成侵权内容,并明确要求不得泄露/讨论指令本身)。<environment_context>:环境上下文块,注入Current working directory: ${workdir}、Git repository root、Operating System: ${os}、Available tools: ${available_tools}。<tools>使用指南:以可读文本指导各工具用法,例如 bash 的新进程语义、view20KB 截断与分段读取建议等。这一节与结构化tools数组并存,属于“教模型怎么用工具”的元指令层。<custom_instruction>${repository_instructions}</custom_instruction>:仓库指令注入位。正文中该值为占位符——注意快照刻意保留标签与占位符,用于断言“仓库指令确实被注入了、注入了几份、位于提示词的哪个位置”,同时避免AGENTS.md的每次改动都重写所有基线。<system_notifications>:系统通知格式说明,指导模型如何处理后台任务完成等运行时消息。
宿主侧也有贡献
README 与源码说明,提示词并非全由 CLI 独裁。宿主自己的系统消息组装(resolveSystemMessageConfig,位于 node/copilot/prompts/promptRegistry.ts)会合成若干段落,这些段落会原样落入该提示词,从而被基线端到端覆盖。这也解释了为何此类快照的测试归属是“宿主测试目录”而非 CLI 仓库。
需要认清覆盖边界:宿主中受根配置门控的“按模型贡献者”不会出现在 E2E 快照里——因为 E2E harness 没有设置根配置的接缝,这部分逻辑由 test/node/agentHostPromptRegistry.test.ts 之外的单测覆盖。
归一化:让快照在任意机器上逐字节一致
如果快照直接存原始请求体,任何一次运行都会因机器环境不同而失败。因此 copilotPromptsE2E.integrationTest.ts 中的 normalizeVolatile 在序列化前做了一组有序的字符串替换,把“两次正确运行间必然不同”的值换成带标签的占位符:
| 替换对象 | 占位符 | 说明 |
|---|---|---|
session-state/ 前缀 UUID |
${session_id} |
保留前缀,形状变化仍会失败 |
<current_datetime>…</current_datetime> 内容 |
${datetime} |
时钟,正文所见即此类 |
* Operating System: … 行 |
${os} |
环境探测结果 |
* Available tools: … 行 |
${available_tools} |
PATH 上的工具集 |
| 平台包管理器提示行 | ${platform_packages} |
bash 工具描述里因平台而异的安装提示 |
<custom_instruction>…</custom_instruction> 之间 |
${repository_instructions} |
注入的仓库指令 |
(N models available) 的 N |
${model_count} |
目录规模 |
Available models: 列表块 |
${model_catalog} |
完整模型目录 |
| 其余任意 UUID | ${uuid} |
兜底,置于末尾以免吞掉上面的标签 |
每处替换都保留其外围标签或包装结构,所以这些行只是内容被占位,一旦这些行的形状改变或消失,断言依然会失败。测试对归一化本身也有单测覆盖:Copilot prompt snapshot formatting 套件验证了空 system/空 tools/空 messages 会被形状守卫拒绝(carried no system prompt、carried no tool definitions、carried no turn messages、turn message was empty),以及含易变值的 body 能原地归一化输出。
# 运行完整确定性套件(默认重放,无 token、无网络)
npm run test-agent-host-e2e
# 仅跑 Copilot prompts 提示词快照测试
./scripts/test-integration.sh --run \
src/vs/platform/agentHost/test/node/e2e/providers/copilotPromptsE2E.integrationTest.ts
四种运行模式的语义(详见 e2e README 的 TL;DR):
| 模式 | 环境变量 | 行为 |
|---|---|---|
| 回放(默认) | 无 | 只回放已提交 fixture,严格缓存未命中即失败,绝不静默触达真实 CAPI |
| 仅更新 AHP 快照 | AGENT_HOST_UPDATE_AHP_SNAPSHOTS=1 |
免 token 回放 LLM fixture,原地重写 AHP 语义快照 |
| 全部更新 | AGENT_HOST_UPDATE_SNAPSHOTS=1 |
同时重写 AHP 快照与 LLM fixture,需 GITHUB_TOKEN 或 gh auth token |
| 仅重录 LLM | AGENT_HOST_REPLAY_RECORD=1 |
旧版聚焦模式,只针对真实 CAPI 重录归一化 fixture |
新增一个模型的完整清单
要让一个新模型拥有自己的提示词基线,README 与测试注释给出了明确的三个必要条件:
- 出现在
harness/capiStubs.ts的桩模型目录中。模型若不在/models桩响应里,会在 CLI 构造请求前就被拒绝,测试只会得到“没有捕获到请求体”的失败。 - 在
captures/目录下提交对应 fixture(copilotcli-<slugified-test-title>.yaml)——重放的回合仍需要被应答。fixture 方言必须与模型的桩端点匹配:/responses用dialect: responses,/v1/messages用dialect: anthropic。 - 加入
SNAPSHOT_MODELS数组并提交本.prompt.md基线。
反向也成立:新模型不会“自动出现”。快照体系不派生自真实 /models 目录,一个刚发布的模型只有在维护者主动添加时才会被钉住;即便加了桩目录条目也不会触发套件失败,因为 CLI 内联的模型列表是被刻意归一化的。README 还点名了 gpt-4.1 与 grok-code-fast-1 缺席的技术原因:这两个模型在重放下 CLI 根本不会发出模型请求,无请求可钉。
基线的边界、局限与解读
- 刻意不钉的东西:session id、时钟、环境探测、注入的仓库指令全文、模型目录。前两者是运行差异,后两者虽跨机器稳定、可被钉住,但代价会落到错误的文件上——给
AGENTS.md追加一行就会重写所有基线、让无关文档改动弄红 CI,因此保留标签与占位符是更聪明的取舍。 - 请求元数据不在范围内:快照只覆盖请求体 body,不覆盖 HTTP 头等外围元数据。
- 哪些变更会导致 diff:SDK bump 改变 CLI 行为、宿主改变交给 CLI 的内容(历史保留、注入上下文前导、附件 marshalling)。刻意排除的是仓库指令文件的编辑。遇到
model request mismatch时,正确动作是判断新请求是否正确、然后重录 fixture,绝不手工改 request 块来平息失败。 - 录制为何不产基线:录制会为模型目录与实验分配触达真实 CAPI,二者都可能让提示词移动,产生仓库不拥有的基线漂移。所以提示词快照“只回放、不录播”。
从这份 gpt-5.1-codex-mini.prompt.md 出发,你可以顺藤摸瓜读懂整个 Agent Host E2E 的验证哲学:把“不可直接观察的 CLI 产物”变成“可审阅、可回归、可归因的仓库资产”,让每一次提示词漂移都能被精确点名到引入它的模型族与变更面。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00