5 min read
Day 09:Context Explorer:调试 Prompt 与 Token 使用

上一篇:Context Builder:AI Coding Agent 上下文构建器
下一篇:Git 与 Diff Tools:状态、补丁解析与变更摘要
English version: Context Explorer: Debug AI Agent Prompts and Token Usage

文章介绍

Day 08 我们实现了 Context Builder:把 system prompt、user request、history summary、tool definitions 和 project snippets 组装成结构化 ContextPart

但只会构建上下文还不够。真实 coding agent 的问题经常出在“模型看到的东西”本身:

  • 为什么 agent 没有使用关键文件?
  • 为什么工具定义占了这么多 token?
  • 为什么 conversation 压过了 project snippets?
  • 当前上下文离 token budget 还有多远?
  • 哪一类上下文最占空间?

Day 09 要实现一个命令行版 Context Explorer,用来调试 AI coding agent 的 prompt 和 token usage。

Day 09 context explorer dashboard

今天要解决什么

今天完成四个交付:

  1. 将 context report 从简单列表升级成分组报告。
  2. 增加 token budget 支持。
  3. 输出每个 context kind 的 token 占比。
  4. 输出最大的 context parts,帮助定位上下文膨胀来源。

Demo 命令:

npm run dev -- --context-report --context-budget 8000 "inspect context usage"

输出会包含:

Context Explorer

Total estimated tokens: ~6,018 tokens
Token budget:           ~8,000 tokens (75.2% used, ~1,982 tokens remaining)

By kind
conversation    ~2,410 tokens    40.0%  3 parts  [######----------]
tool-results    ~1,702 tokens    28.3%  1 part  [#####-----------]
system          ~1,023 tokens    17.0%  1 part  [###-------------]
files             ~492 tokens     8.2%  1 part  [#---------------]
tools             ~391 tokens     6.5%  1 part  [#---------------]

它在 Cursor/Codex 里对应哪一层

今天实现的是 context debugging layer

在 Cursor、Codex、Claude Code 这类 coding agent 中,Context Explorer 的价值不是展示“漂亮的 prompt”,而是回答工程问题:

What context was selected?
Where did it come from?
How much budget does each category consume?
Which part is crowding out the rest?
Is the model request close to the budget limit?

Day 08 的 Context Builder 负责组装上下文。Day 09 的 Context Explorer 负责解释上下文。

这两层最好分开:

  • builder 是生产路径,影响模型输入。
  • explorer 是调试路径,解释 builder 的输出。

这样后续换 tokenizer、换模型、换文件选择策略时,Explorer 可以继续作为回归检查工具。

设计思路

1. 报告先按 context part 估算 token

当前 token 估算仍然使用简单函数:

export function estimateTokens(text: string): number {
  return Math.max(1, Math.ceil(text.length / 4));
}

这不是模型真实 tokenizer,但足够用于 Day 09 的调试目标:

  • 比较不同 context part 的相对大小。
  • 看出 conversation 是否膨胀。
  • 看出 files/tools/system 谁占主要预算。
  • 给后续接入真实 tokenizer 留接口。

2. 把 part 汇总成 kind

ContextPart 现在有:

label
kind
source
content

Context Explorer 会先生成 row:

type ContextReportRow = {
  label: string;
  kind: ContextKind;
  source: string;
  tokens: number;
  percent: number;
};

然后按 kind 汇总:

conversation
tool-results
system
files
tools

这比只看逐项列表更有用。因为 agent 的上下文预算通常是类别竞争:对话历史、工具结果、文件片段、工具定义都在抢同一个窗口。

3. 显示最大上下文来源

Day 09 新增 Largest parts 段:

Largest parts (top 3)
Conversation            conversation    ~2,393 tokens    39.8%  source: agent-loop
Agent steps             tool-results    ~1,702 tokens    28.3%  source: agent-loop
System prompt           system          ~1,023 tokens    17.0%  source: context-builder

这能快速回答:

谁最大?
它属于哪一类?
它来自哪里?
它占总上下文多少?

如果后续某天 patch diff、transcript history 或 selected files 突然变大,这个段落会直接暴露出来。

4. CLI 支持 context budget

CLI 新增可选参数:

--context-budget 8000

报告会显示:

Token budget: ~8,000 tokens (75.2% used, ~1,982 tokens remaining)

如果超过预算,会显示 over by:

Token budget: ~4,000 tokens (150.4% used, over by ~2,018 tokens)

这只是调试提示,不会阻止运行。真正的硬预算裁剪会放到后续 context selection 策略里。

实现步骤

1. 升级 context report

文件:apps/mini-harness/src/context/context-report.ts

buildContextReport 现在接收 options:

type ContextReportOptions = {
  tokenBudget?: number;
  largestPartsLimit?: number;
};

内部先分析 context parts:

const rows = parts.map((part) => ({
  label: part.label,
  kind: part.kind,
  source: part.source ?? "unknown",
  tokens: estimateTokens(part.content),
}));

再计算 total、percent、by kind 和 largest rows。

2. 输出四段报告

新的报告分为四段:

Summary
By kind
Largest parts
All parts

这样读报告时不需要从一堆明细里手动推断问题。

3. 给 CLI 增加 --context-budget

文件:apps/mini-harness/src/cli.ts

新增参数解析:

if (arg === "--context-budget") {
  const rawValue = readOptionValue(argv, index, "--context-budget");
  contextBudget = parseNumberOption(rawValue, "--context-budget");
  index += 1;
  continue;
}

打印报告时传入:

buildContextReport(summary.contextParts, {
  tokenBudget: options.contextBudget,
});

4. 更新报告格式文档

文件:docs/context-report-format.md

文档同步成 Day 09 的报告结构,避免 docs 还停留在 Day 08 的简单列表。

Demo

See Day 09 demo.

核心命令:

npm run dev -- --context-report --context-budget 8000 "inspect context usage"

输出摘要:

Context Explorer

Total estimated tokens: ~6,018 tokens
Token budget:           ~8,000 tokens (75.2% used, ~1,982 tokens remaining)

By kind
conversation    ~2,410 tokens    40.0%  3 parts  [######----------]
tool-results    ~1,702 tokens    28.3%  1 part  [#####-----------]
system          ~1,023 tokens    17.0%  1 part  [###-------------]
files             ~492 tokens     8.2%  1 part  [#---------------]
tools             ~391 tokens     6.5%  1 part  [#---------------]

Largest parts (top 3)
Conversation            conversation    ~2,393 tokens    39.8%  source: agent-loop
Agent steps             tool-results    ~1,702 tokens    28.3%  source: agent-loop
System prompt           system          ~1,023 tokens    17.0%  source: context-builder

具体数字会随文章、文件和工具定义变化。

当前系统能力变化

到 Day 09,mini harness 已经能解释当前模型上下文:

  • 总 token 估算。
  • token budget 使用率。
  • 按 context kind 分组。
  • 每组占比和条形图。
  • 最大 context parts。
  • 每个 part 的来源。

这让后续开发不再只能猜“上下文为什么不对”。当 agent 行为异常时,第一步可以先看 Context Explorer。

遇到的问题

1. 估算 token 不等于真实 tokenizer

text.length / 4 是粗略估算。英文、中文、代码、JSON 的真实 token 密度不同。

当前实现适合比较相对大小,不适合做严格预算。后续可以按模型接入真实 tokenizer。

2. 报告发生在 run 之后

现在 contextParts 里包含运行后的 ConversationAgent steps,所以报告既展示初始输入,也展示运行后的扩展状态。

这对 debug 很有用,但如果只想看“模型第一轮请求前的上下文”,后续可以增加:

--context-report-phase initial

3. budget 只是提示

--context-budget 不会裁剪上下文,也不会阻止模型请求。

真正的裁剪策略需要 Context Builder 参与,例如按优先级丢弃低价值 part、截断文件片段、压缩历史摘要。

明天做什么

Day 10 会加入 Git 和 Diff 工具,让 agent 能观察当前工作区改动。

到那时 Context Explorer 会更有用:git diff 往往很大,必须知道它占了多少 token,以及是否挤掉了更关键的文件上下文。

Comments

  • Loading comments…

Comments are posted immediately and emailed to the site owner. No account needed.