Token Counting

代码提示的 Token 计数:文件、Diff、日志与堆栈跟踪

代码提示的 Token 计数一旦请求中包含不止一个干净的源文件,就会变得不可靠。一个现实的调试提示可能会组合系统指令、仓库上下文、Git diff、终端日志、堆栈跟踪、工具 schema 以及响应格式。因此,只统计可见代码块可能会低估实际请求。

安全的方法很简单:先构建精确请求;如果目标模型的 tokenizer 可用,就用它来统计;然后再根据端点报告的使用情况验证实际请求。不要基于单词、行数、字节数或通用的“每个 token 对应多少字符”的比例来估算。

本指南为文件、diff、日志和堆栈跟踪提供了一套可重复的工作流程,并附带四个紧凑的 fixture,你可以使用 TokenTest 在自己的端点上进行测试。

为什么代码提示的 token 计数容易被误判

Token 并不等同于单词或字符。Tokenizer 可能会以不同方式拆分标点、缩进、标识符、空白、Unicode 文本和特殊标记。即使是相同的可见文本,在提供商应用聊天模板或添加模型特定的控制 token 后,最终的输入计数也可能不同。

这就是为什么代码提示的 token 计数需要三个独立数字:

  1. Artifact tokens: 你希望模型检查的文件、diff、日志或跟踪。
  2. Request tokens: artifact 加上系统指令、用户指令、消息角色、工具定义、schema 以及其他请求内容。
  3. Reported input tokens: 已完成请求的 live endpoint 返回的 usage。

第三个数字是该次调用的运行结果。前两个数字有助于你解释并减少它。

OpenAI 目前的 token 计数指南也作出了相同的核心区分:本地 tokenization 可以统计字符串,而精确的 API 计数可以考虑消息、图片、文件、工具和 schema 等结构化输入。Hugging Face 的 chat-template 文档也说明了为什么消息格式很重要:聊天消息在生成前会被转换为带有控制 token 的特定模型序列。

统计请求,而不只是粘贴的代码

在比较提示之前,先序列化所有可能传递给模型的输入层:

系统或开发者指令
+ 用户任务
+ 仓库/文件上下文
+ Git diff
+ 日志
+ 堆栈跟踪
+ 工具定义和 JSON schemas
+ 响应格式 schema
+ 提供商 chat-template 开销
= 实际模型输入

如果你只统计 artifact,请将结果标记为 artifact_tokens。不要把它表述为完整的提示总数。

为了实现可重复的代码提示 token 计数,请为每次测试保存一个小型 manifest:

{
  "test_id": "checkout-null-pointer-01",
  "requested_model": "your-model-id",
  "artifact_type": "stack_trace",
  "artifact_bytes": 982,
  "artifact_tokens_local": null,
  "request_tokens_local": null,
  "input_tokens_reported": null,
  "cached_input_tokens": null,
  "output_tokens_reported": null,
  "finish_reason": null,
  "task_valid": null
}

当某个字段未报告时使用 null。保留真实的零值为 0;它表示不同的含义。

1. 源文件的 Token 计数

源文件包含的不只是可执行逻辑。导入、注释、较长的标识符、生成代码、嵌入数据、测试夹具以及重复的样板代码都可能主导提示词。

按以下顺序处理:

  1. 从失败构建或审查中使用的精确文件版本开始。
  2. 保留为解析名称和数据流所需的导入、声明以及附近的调用方。
  3. 在移除相关逻辑之前,先移除不相关的生成内容、第三方 vendored 内容、压缩内容、锁文件或快照内容。
  4. 在计数之前添加任务说明和文件路径。
  5. 在添加工具或结构化响应 schema 之后,再次计算最终请求。

可直接粘贴的源文件夹具

任务:查找 bug 并提出最小的安全修复方案。在展示代码之前先解释失败路径。

文件:src/cart/applyCoupon.ts
```ts
type Coupon = { code: string; percentOff?: number };

export function applyCoupon(total: number, coupon: Coupon | null) {
  if (coupon.percentOff) {
    return total - total * (coupon.percentOff / 100);
  }
  return total;
}
```

将这条精确消息通过你计划使用的端点运行。重点不在于这个夹具的绝对 token 数量,而在于你的本地计数、请求计数以及端点报告的用量是否一致。

2. Git diff 的 Token 计数

Diff 通常比完整文件更小,但它包含简单行数统计会遗漏的语法:文件头、index 行、hunk 头、+- 前缀,以及未更改的上下文。

Git 官方的 git diff 文档允许你通过 -U<n>--unified=<n> 控制上下文行数。这使得上下文宽度成为一个可衡量的提示预算选择,而不是一个随意的清理步骤。

对于由 diff 构建的代码提示的 token 计数:

可直接粘贴的 Git diff 夹具

任务:检查此补丁的正确性、回归风险以及缺失的测试。

diff --git a/src/retry.ts b/src/retry.ts
index 2a1c020..4bc4410 100644
--- a/src/retry.ts
+++ b/src/retry.ts
@@ -8,7 +8,9 @@ export async function retry<T>(fn: () => Promise<T>) {
   for (let attempt = 0; attempt < 3; attempt++) {
     try {
       return await fn();
-    } catch {}
+    } catch (error) {
+      if (attempt === 2) throw error;
+    }
   }
 }

将 diff 命令和生成的 diff 都保存到你的测试记录中。否则后续运行可能会静默地使用不同的上下文。

3. 日志的 Token 计数

日志会通过重复不断增长。时间戳、请求 ID、主机名、JSON 键、健康检查和重试消息可能会重复数千次,却只增加很少的诊断价值。

危险的捷径是删除所有重复行。重复频率和时间点可以作为重试循环、速率限制、竞态条件或级联故障的证据。

应改用一种感知损失的缩减方式:

  1. 保留第一次出现、最后一次出现,以及故障边界附近的行。
  2. 仅将完全相同、连续重复的内容替换为标记,例如 [same line repeated 184 times]
  3. 保留重复次数和时间范围。
  4. 当相关 ID 连接不同服务时,保留这些 correlation ID。
  5. 同时统计原始摘录和缩减后的摘录,以便能直观看到节省效果。

可直接粘贴的日志样例

任务:识别第一个可执行的故障,并将根因与重试区分开来。

2026-07-24T08:41:02.013Z INFO  request_id=7f2 checkout started
2026-07-24T08:41:02.087Z WARN  request_id=7f2 inventory timeout attempt=1
2026-07-24T08:41:02.291Z WARN  request_id=7f2 inventory timeout attempt=2
2026-07-24T08:41:02.697Z ERROR request_id=7f2 inventory timeout attempt=3
2026-07-24T08:41:02.699Z ERROR request_id=7f2 checkout failed code=INVENTORY_UNAVAILABLE

对于生产日志,至少测试三种 prompt 形态:完整的事件窗口、以故障为中心的缩减窗口,以及摘要加原始尾部。除了 token 用量外,还要比较任务有效性。

4. 堆栈跟踪的 token 计数

堆栈跟踪会结合异常消息、路径、行号、框架内部调用、嵌套原因以及重复的异步边界。最有用的帧不一定是第一帧,而根因可能位于 Caused by 或其他嵌套异常标记之下。

在对包含堆栈跟踪的代码 prompt 进行 token 计数时:

可直接粘贴的堆栈跟踪样例

任务:追踪最可能的根因,并指出首先要检查的源文件。

TypeError: Cannot read properties of null (reading 'percentOff')
    at applyCoupon (/app/src/cart/applyCoupon.ts:4:14)
    at calculateTotal (/app/src/cart/calculateTotal.ts:27:10)
    at checkout (/app/src/checkout/checkout.ts:61:18)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

当你附加所引用的源文件时,请重新统计合并后的请求。跟踪信息和文件是独立的工件,但模型接收到的是一个总输入。

使用 TokenTest 的实用测量工作流

TokenTest 是一个端点测试和 token 行为工具,而不是通用的粘贴文本分词器。可用它来验证你的 OpenAI 兼容端点报告了什么,以及它在边界条件下的表现。

  1. 将一个 fixture 放入应用实际发送的精确消息结构中。
  2. 使用你正在评估的 endpoint 和模型运行它。
  3. 使用 TokenTest 的 tokenizer probe、truncation、max-output 或相关测试来检查 token 行为。
  4. 将原始响应与 TokenTest 规范化后的 usage 字段进行比较。
  5. 记录请求的和返回的模型 ID、报告的输入/输出总数、finish reason、延迟,以及答案是否真的完成了任务。
  6. 如需离线审查,将 endpoint 的响应或保存的报告 JSON 粘贴到 TokenTest 的手动 JSON 分析模式中。

这将两个团队经常混为一谈的问题分开了:

如果这些数字不一致,请在判断任一方出错之前,检查 request wrapper、chat template、special tokens、tool schemas、response schema、cache 字段和模型映射。

按 artifact 构建 prompt 预算,然后验证总量

一个有用的预算表会让每个组件都清晰可见:

Component Raw size Local tokens Included? Reduction rule
System/developer instructions Yes Keep stable across tests
User task Yes Keep success criteria explicit
Source files Yes Remove unrelated generated/vendor content
Git diff Yes Tune unified context deliberately
Logs Yes Collapse exact repeats with counts
Stack trace Yes Keep causes and application frames
Tool/schema overhead If used Count the exact definitions
Output reserve Yes Protect enough room for a valid answer

在用可选的仓库材料填充剩余上下文之前,先预留输出空间。即使一个请求满足输入上限,如果给答案留下的空间太少,仍然可能失败。

常见错误

使用固定的每 token 字符数比例

比例可以作为已知语料的大致规划提示,但代码标点、空白、标识符和混合语言会让通用比例变得不安全。应使用你自己的 artifact 和 tokenizer 进行校准。

在添加 tools 和 schemas 之前就开始计数

工具定义和结构化输出 schema 都属于输入。请统计最终载荷,而不是更早的纯文本草稿。

跨模型比较不同的 prompts

如果 wrapper、文件选择、diff 上下文或日志压缩方式发生变化,你就不再是在相同任务上比较 tokenizer 了。

把报告中的 zero 当作缺失值

将缺失数据存储为 null。请精确保留返回的 0,以免下游分析凭空生成 usage。

只优化 token 而忽略答案有效性

如果一个更短的提示会移除解决问题所需的帧、diff 上下文或日志序列,那么它并不更高效。在断言某个提示更好之前,先评估任务完成情况。

代码提示的 Token 计数:最终检查清单

在发送大型工程提示之前:

常见问题

我如何计算代码文件中的 token?

先使用目标模型的分词器对完整文件内容计数,然后在加入任务、消息角色、系统指令、工具和 schema 后,对完整请求计数。将仅文件结果与请求总量分别标注。

Git diff 总是比发送完整文件更省吗?

不一定。聚焦的 diff 往往更小,但较宽的统一上下文、多文件、冗长路径和啰嗦指令,可能会让它比预期更大。请在同一任务上测量两种提示形态。

我应该从日志中删除时间戳和 ID 吗?

只有当它们不承载诊断意义时才移除或归一化。保留能够建立顺序、延迟、重试或跨服务关联的值。

我可以从堆栈跟踪中删除框架帧吗?

你可以减少重复的框架尾部,但要保留应用代码与框架代码之间的边界、嵌套原因、异常消息,以及任何用于映射回所提供源代码所需的帧。

为什么本地 token 计数与 API 用量不同?

常见原因包括不同的分词器或模型版本、提供方添加的聊天格式化、特殊 token、隐藏请求字段、工具或 schema 序列化、附件、缓存计费,以及端点的模型重映射。

我可以直接把这些 fixture 粘贴到 TokenTest 吗?

将这些 fixture 作为发送到端点的精确提示内容使用。然后用 TokenTest 检查该端点。你也可以将保存的响应或报告 JSON 粘贴到 TokenTest 的手动 JSON 分析模式中,以便离线审查。

测量模型实际接收到的提示

当代码提示的 token 计数可复现时,它才真正有用。保存请求、记录缩减规则、捕获实时用量并验证答案。然后使用 TokenTest 比较 token 报告和边界行为,再让大型文件、diff、日志或堆栈跟踪进入生产环境。

TokenTest 博客中探索更多实用指南。

来源