你的 AI 编程助手在想什么?Context Window 逐项拆解

你的 AI 编程助手在想什么?Context Window 逐项拆解

缘起

用 GitHub Copilot (Agent Mode) 写了一整天代码后,我突然好奇:这个 128K 的 Context Window,到底被谁瓜分了?

VS Code 提供了一个隐藏的 Context Window 使用面板,展示了当前会话中每一类内容的 token 占比。我截取了一张,看完之后觉得这个话题值得深入拆解——因为这直接关系到你的 AI 编程助手到底在多大程度上理解你的代码。

GitHub Copilot Context Window 使用面板截图

VS Code 中的 Context Window 使用面板(Settings → "context window")

下面我们逐项拆解截图中的每一行,追溯到磁盘上的具体文件。

全景数据

我的这次会话快照:

60.8K已用 tokens
128K总窗口
48%已使用

还没聊几轮就用掉了将近一半。每一项占比如下:

Messages
13.8%
Tool Definitions
10.9%
System Instructions
9.0%
Tool Results
7.6%
Files
5.7%
Other
0.5%

下面逐项拆解,追溯到磁盘上的具体文件路径。


1. System Instructions(9.0% ≈ 11,520 tokens)

系统指令是每次 API 调用都会携带的"底色"——你还没说话,它已经在了。

它由 三层 叠加而成:

层级来源文件内容≈ tokens
① Copilot 内置~/.vscode/extensions/github.copilot-chat-0.37.8/dist/extension.js身份提示 "You are a highly sophisticated automated coding agent..."、输出格式规则、KaTeX 指令、Markdown Linkification 规则、Notebook 指令~4,000
② 项目指令.github/copilot-instructions.md → 符号链接 → CLAUDE.md项目导航地图、命令手册、架构图、Content Conventions、Communication Style~3,000
③ Skills 描述列表.claude/skills/*/SKILL.md (30 个)
~/.claude/skills/*/SKILL.md (20 个)
每个 Skill 的 name + description + file 路径,打包成列表注入 system prompt~4,000

层级 ①:Copilot 硬编码提示

藏在一个 17MB 的压缩 JS 文件里:

~/.vscode/extensions/github.copilot-chat-0.37.8/dist/extension.js

用 grep 能挖出多条系统提示模板,分别对应不同场景:

$ grep -oE '"You are [^"]{20,80}"' extension.js | head -4

"You are a helpful code reviewer who..."
"You are a highly sophisticated automated coding agent with expert-level knowledge."
"You are a strictly grounded assistant limited to the"
"You are a coding agent running in VS Code. You are expected to be precise, safe, and helpful."

不同的 Chat Participant(github.copilot.default、github.copilot.editsAgent、github.copilot.notebook...)会选用不同的系统提示模板。在 Agent Mode 下,用的是那条最长的 "highly sophisticated automated coding agent"。

层级 ②:项目指令(你自己写的)

这是最有意思的发现——我的仓库里有个符号链接:

$ ls -la .github/copilot-instructions.md
lrwxr-xr-x  1 polly  staff  12  1月 30 23:49
    .github/copilot-instructions.md -> ../CLAUDE.md

一个文件,两个入口:Claude Code 读 CLAUDE.md,VS Code Copilot 读 .github/copilot-instructions.md,实际是同一个 293 行的项目指令文件。这是我当初有意为之的设计——维护一份文档,两个 AI 编程助手都能读到。

层级 ③:Skills 描述列表

这部分跟我在 Skill Pipeline 深度拆解 中分析的完全对应。Copilot 会扫描两个层级的 Skills:

Workspace 级(30 个):    .claude/skills/*/SKILL.md
User 级(20 个):         ~/.claude/skills/*/SKILL.md

每个 Skill 只抽取 name 和 description 字段(约 100-150 tokens),打包成一个列表注入 system prompt。30 + 20 = 50 个 Skill 描述 × ~80 tokens ≈ 4,000 tokens。

Skills 是 System Instructions 中最大的单项开支。50 个 Skills 的描述列表就吃掉了系统指令的 ~35%。如果你觉得 Context Window 不够用,第一个优化点就是清理不常用的 Skills。

2. Tool Definitions(10.9% ≈ 13,952 tokens)

工具定义比系统提示还大——它是最沉默的 token 大户。

类型来源内容≈ tokens
内置工具 (Active)extension.js 硬编码read_file、grep_search、replace_string_in_file、run_in_terminal、semantic_search、create_file、list_dir、manage_todo_list 等 ~15 个工具的完整 JSON Schema~8,000
Deferred 工具列表extension.js 硬编码fetch_webpage、run_notebook_cell、get_changed_files 等 ~50 个工具的名称 + 简述~3,000
MCP 外部工具.claude/settings.json + MCP 服务器动态注册mcp_master-transl_*、mcp_tavily-remote_*、mcp_pylance_* 等~3,000

每个工具定义长什么样?大概是这样的 JSON Schema:

{
  "name": "read_file",
  "description": "Read the contents of a file.\n\nYou must specify the line range...",
  "parameters": {
    "properties": {
      "filePath": { "description": "The absolute path of the file to read.", "type": "string" },
      "startLine": { "description": "The line number to start reading from, 1-based.", "type": "number" },
      "endLine": { "description": "The inclusive line number to end reading at, 1-based.", "type": "number" }
    },
    "required": ["filePath", "startLine", "endLine"]
  }
}

read_file 算简单的,大约 300 tokens。而 run_in_terminal 带了一堆使用指南(Directory Management、Background Processes、Output Management...),轻松到 800 tokens。

用 grep 统计各工具在 extension.js 中的引用次数:

$ grep -oE 'replace_string_in_file|semantic_search|read_file|run_in_terminal' extension.js \
  | sort | uniq -c | sort -rn

 195 replace_string_in_file    # 引用最多——因为使用指南长
  84 semantic_search
  74 read_file
  67 run_in_terminal
  39 manage_todo_list
  36 grep_search
  12 create_file
   6 list_dir

Deferred Tools:按需加载的优化

Copilot 并不是把所有 ~60 个工具的完整 Schema 都塞进去(那要 30,000+ tokens)。它用了一个聪明的分层策略:

  • Active Tools(~15 个):完整 JSON Schema 直接注入,每次调用都带
  • Deferred Tools(~50 个):只放名称 + 一句话描述,需要时通过 tool_search_tool_regex 动态加载完整定义
Active:  read_file, grep_search, run_in_terminal, create_file...     → 完整 Schema
Deferred: fetch_webpage, run_notebook_cell, install_extension...     → 名称 + 简述

这就是为什么你有时在对话中看到 Copilot 先调 tool_search_tool_regex 再调目标工具——它在"按需展开"一个 Deferred Tool 的完整 Schema。


3. Messages(13.8% ≈ 17,664 tokens)

这是唯一没有独立磁盘文件源头的部分——它就是你和 AI 聊的内容本身。

组成来源说明
对话历史VS Code 内存 + workspaceStorage/state.vscdb你的每条消息 + AI 的每条回复,按时间顺序累积
Conversation SummaryCopilot 动态生成对话变长时,早期内容被压缩为 <conversation-summary> XML 块

持久化位置:

~/Library/Application Support/Code/User/workspaceStorage/<hash>/
  └── state.vscdb        ← SQLite 数据库,存储 chat 会话历史

Messages 是 Context Window 中增长最快的部分。每多一轮对话,你的可用空间就少一些。当接近窗口上限时,Copilot 会开始压缩早期对话——这就是长会话后期"失忆"的根本原因。


4. Files(5.7% ≈ 7,296 tokens)

这部分来自你主动提供或编辑器自动携带的文件上下文:

类型来源注入方式
用户附加文件你在编辑器中选中的内容(如 CLAUDE.md 293 行)<attachment> XML 标签包裹,完整内容注入
编辑器上下文当前打开的文件路径 + 光标位置<editorContext> 标签,路径 + 选区信息
Workspace 结构工作目录的目录树摘要<workspace_info> 标签,自动生成

这里的关键优化点:不要无脑附加大文件。把一个 500 行文件选中拖进聊天 = 白白吃掉 ~3000 tokens。只在需要时选中相关代码段。


5. Tool Results(7.6% ≈ 9,728 tokens)

每次 AI 调用工具后,工具的返回结果也要放回 Context Window——因为 LLM 需要看到结果才能继续推理。

工具返回内容Token 消耗
read_file文件内容~2000/300行
grep_search搜索结果列表~500/次
run_in_terminal命令输出变化大
get_errors编译/lint 错误~100/次
runSubagent子代理报告~2000/次

Tool Results 是"隐形消费"大户。你让 AI 读一个文件 → ~2000 tokens,再读一个 → +2000,跑个 git push → +500...不知不觉就累积了将近 10,000 tokens。


6. Other(0.5% ≈ 640 tokens)

最小的一块,但也值得知道它是什么:

<environment_info>macOS</environment_info>
<context>
  Terminals: zola, node, zsh...
  Last Command: git push
  Cwd: /Users/polly/.../www.polly.com
  Exit Code: 0
</context>

来自 VS Code 运行时环境采集:操作系统类型、已开的终端、最近执行的命令、当前工作目录。这些帮助 AI 理解你的开发环境上下文。


全景磁盘地图

把所有来源汇总到一张文件树上:

# 你的 Context Window = 60.8K tokens,来自这些文件:

~/.vscode/extensions/github.copilot-chat-0.37.8/
├── dist/extension.js         # 17MB,包含:
│   ├── System Prompt 模板     # "You are a highly sophisticated..."
│   ├── Tool JSON Schemas     # read_file, grep_search, ...
│   ├── Deferred Tools 列表   # ~50 个工具名称+简述
│   ├── Skill 加载逻辑        # .claude/skills 目录扫描
│   └── Context 组装器        # 拼接所有来源
└── package.json             # 12 个 chatParticipant 注册

<workspace>/
├── CLAUDE.md                # 项目指令 (293 行)
├── .github/
│   ├── copilot-instructions.md→../CLAUDE.md  # 符号链接!
│   └── prompts/              # 16 个 prompt 模板 (按需)
├── .claude/
│   ├── settings.json        # MCP 服务器配置
│   └── skills/               # 30 个 workspace skills
│       ├── blog-writer/SKILL.md
│       ├── paper-writer/SKILL.md
│       ├── memory-keeper/SKILL.md
│       └── ... (27 more)

~/.claude/
└── skills/                  # 20 个 user-level skills
    ├── academic-paper-writer/SKILL.md
    ├── frontend-design/SKILL.md
    └── ... (18 more)

~/Library/Application Support/Code/
└── User/workspaceStorage/<hash>/
    └── state.vscdb           # 对话历史 (SQLite)

最核心的一个文件就是那个 17MB 的 extension.js——System Prompt、Tool Definitions、Skill 加载器、Context 组装器全在里面。其余都是配置文件和内容文件,被它在运行时读取并拼装。


成本结构:固定 vs 累积

这张图是我最想表达的核心观点:

┌─────────────────────────────────────────────────┐
│         每次 API 调用的 token 构成                │
├─────────────────────────────────────────────────┤
│                                                  │
│  🔒 固定成本(每次都付)              ≈ 20%      │
│     System Instructions     9.0%                │
│     Tool Definitions       10.9%                │
│                                                  │
│  📈 累积成本(越聊越多)              ≈ 27%      │
│     Messages              13.8%                 │
│     Files                  5.7%                 │
│     Tool Results           7.6%                 │
│                                                  │
│  📦 杂项                              ≈  1%      │
│     Other                  0.5%                 │
│                                                  │
│  💨 剩余空间                          ≈ 52%      │
│     可用于继续对话                               │
│                                                  │
└─────────────────────────────────────────────────┘
128K 窗口你实际能用的只有 ~102K。System Instructions + Tool Definitions 这 20% 是"入场券"——你还没开口说话就被扣了。对话越长,Messages + Tool Results 像滚雪球一样膨胀。到 80-90% 时 Copilot 会开始压缩早期对话,这就是长会话后期"失忆"的根本原因。

优化指南

知道了每一寸 token 的出处,优化就有方向了:

优化策略针对部分效果
清理不常用的 SkillsSystem Instructions50 个减到 20 个 → 省 ~2,400 tokens
减少 MCP 工具数量Tool Definitions每少一个 MCP 工具 → 省 ~300 tokens
及时开新会话Messages归零,回收 ~15-20%
精确选中代码段,别全文拖入Files300 行→50 行 → 省 ~1,600 tokens
少让 AI 读大文件Tool Results每次 read_file 控制行数
精简 CLAUDE.mdSystem Instructions293 行→150 行 → 省 ~1,500 tokens

跟 Skill Pipeline 的关系

如果你读过我的 Skill Pipeline 深度拆解,现在可以把两篇文章串起来:

  1. Skill 描述列表常驻 System Instructions(~4,000 tokens)——这就是 Skill Pipeline 的"唯一固定代价"
  2. 触发 Skill 后,SKILL.md 全文作为 Tool Result 注入——从 Tool Results 的 7.6% 那块扣
  3. 工具调用(read_file 等)的结果也进 Tool Results——Skill 触发后续的工具调用链,每一步都在累加

整个 Skill Pipeline 的 token 足迹:

描述列表 (常驻)  →  ~80 tokens/skill  →  System Instructions
SKILL.md (按需)  →  ~500-2000 tokens  →  Tool Results
后续工具调用     →  每次 ~500-2000     →  Tool Results (累积)

结语

Context Window 就是 AI Agent 的"工作内存"。跟你用 htop 看进程内存一样,这个面板告诉你每一寸 token 被谁占了、从哪来的。

下次你觉得 AI "不够聪明"或"忘了之前说的"时,先看看 Context Window 使用率——很可能不是模型的问题,而是它的"内存"快满了。

这篇拆的是 VS Code Copilot 的 Context Window。Claude Code 呢?它的上下文组装策略完全不同——没有 Deferred Tools,有 System Prompt Caching,还有一套独特的 conversation compaction 机制。那是另一个故事了。


本文与 拆解 Skill Pipeline 为同日姊妹篇,建议对照阅读。

Comments