“缓存命中率会改变长上下文应用的延迟与成本。”这句话有 21 个字符。在 2026-08-01 的一次本地对照中,tiktoken 0.13.0 的 cl100k_base 将它编码为 24 个 token,o200k_base 则得到 16 个。文字没有变,计量结果却少了三分之一。

这个数字不证明 o200k_base 对所有中文都更省,也不能直接推出账单会少三分之一。它只暴露了一个常被预算表掩盖的事实:Token 不是文本天然携带的单位,而是具体 tokenizer 对文本编码后的结果。

很多成本估算从“多少字约等于多少 token”开始,再乘以每百万 token 的单价。但即使先不讨论单价,这个起点也不稳定:换模型或换 tokenizer,同一份中文语料的 token 数可能变化;即使 token 数相同,未缓存输入、缓存读取、输出生成、重试与工具调用仍会形成不同的任务成本。

真正需要回答的不是“这一万字多少钱”,而是:这项任务怎样被编码?哪些前缀被复用?它实际生成了什么?经历了多少调用和重试?最后是否通过了独立验证?本文从一段中文如何变成 token 开始,一路追到经验证成功任务的总成本。

同一句中文在 cl100k_base 与 o200k_base 下的字符数和 token 数对比
同一句中文在 cl100k_base 与 o200k_base 下的字符数和 token 数对比

第一章:Token 不是字,也不是稳定的字符换算

1.1 先把三个容易混用的单位拆开

讨论成本前,先把三个容易被混用的单位拆开。

单位描述什么是否由 tokenizer 决定能否直接用于模型计费
字符文本包含多少书写符号否否
UTF-8 字节文本存储或传输所占字节否否
tokentokenizer 输出的离散 ID 序列是通常是

字符和字节属于原始文本的属性。比如一段中文可能有 30 个字符、82 个 UTF-8 字节;这既不等于 30 个 token,也不提供跨模型可复用的 token 换算公式。token 则是模型输入层的计量:文本先被编码成一串 ID,模型处理的是这串数字而不是编辑器里看到的汉字。

1.2 一个 tokenizer 具体做了什么:BPE 的两步

先看最常见的 BPE(Byte Pair Encoding)。它分两步:学习阶段统计相邻单元对的出现频率,逐轮把最高频的 pair 合并成一个新单元,直到词表达到预设大小;编码阶段对新文本贪心应用学到的合并规则,把文本逐步合并成词表内的单元,输出一串 token ID。8

一个纯示意,不代表任何真实词表:

初始单元:中 | 文 | 处 | 理
合并规则:文+处 -> 文处(学习阶段发现的高频 pair)
编码结果:中 | 文处 | 理

“初始单元”从哪来,决定了不同语言的不同待遇。GPT-2 采用的 byte-level BPE 以 UTF-8 字节而不是字符作为初始单元:任意 Unicode 文本都能被无损编码,不存在“词表外字符”的问题;代价是一个汉字在 UTF-8 下占 3 个字节,如果词表没有覆盖它,这个字就会被拆成字节级片段。9 反过来,词表里直接收录的常见汉字和常用词越多,中文被拆碎得越少——这是后文 o200k_base 相对 cl100k_base 的中文 token 变化在机制上的来源之一。

在 BPE 之前,tokenizer 通常还做两件事:规范化和预切分。英文文本一般先按空格与标点切成词,再做 BPE;中文没有空格边界,预切分路径不同。规范化(大小写折叠、全角半角归一、NFKC 等)会在切分前改写文本,所以同一个词的不同书写形式也可能得到不同的 token 数。SentencePiece 把 Normalizer、Trainer、Encoder、Decoder 拆成独立组件,正是为了说明这些环节共同决定最终切分,而不是“一句话等于固定几个 token”。2 以 OpenAI 的 tiktoken 为例,它把自己定义为供模型使用的 BPE tokenizer,其示例直接展示了“文本 -> token 序列 -> 原文本”的可逆编码过程;预切分则按其官方实现的正则规则执行。1

从原始文本到 Token ID 序列的 tokenizer 处理流程
从原始文本到 Token ID 序列的 tokenizer 处理流程

1.3 为什么经验换算可以估量,却不能当公式

因此,token 既可能对应常见词、子词或单个字符,也可能只是字节片段。把它翻译成“字”“词”或“固定几个字符”都会丢掉关键条件。一个英文 BPE 的经验平均值,既不能当作中文公式,也不能拿来比较另一套 tokenizer。

工程上最实用的结论很简单:进入成本预算的 token 数,必须附带模型或 tokenizer 的版本。 当模型迁移、工具 schema 改动、Markdown 模板增加,或者中英文比例变化时,旧的“字符数换 token”估算应当视为待验证假设,而不是历史常数。

这回答了 token 为什么会变,但还没有回答另一个更容易引发误判的问题:中文是否天生更贵?

第二章:中文成本不是语言常数,而是 tokenizer 与任务共同作用的结果

2.1 跨语言差异确实存在,但结论必须绑定 tokenizer

跨语言差异确实存在,但它不是语言本身的固定税率。Petrov 等人在 FLORES-200 平行语料上比较了多种 tokenizer,指出等义内容在不同语言中可能得到非常不同的编码长度,从而影响计费、延迟和可放入窗口的内容量。3 论文在特定 cl100k_base 对照中报告简体中文相对英文的 token premium 为 1.91;这个结果有价值,前提是连同它的语料、时间和 tokenizer 一起阅读。

它不能被缩写成“中文固定比英文贵 1.91 倍”。论文比较的是 2023 年特定 tokenizer 与平行语料,今天的候选模型、业务语料和词表都可能不同。更重要的是,模型费率、输出长度、任务质量与重试率仍可能改变最终账本。

2.2 从编码机制看,中文的 token 差异从哪来

从编码机制看,这个差异并不神秘。中文没有空格边界,一个汉字是一个独立书写单元;在 byte-level 编码下,未被词表覆盖的字会退化为 UTF-8 字节级片段(每个汉字 3 字节),而词表与训练语料中中文占比越高,能直接以字或词成 token 的中文单元就越多。o200k_base 的词表容量(约 20 万)约为 cl100k_base(约 10 万)的两倍——词表更大意味着更多中文单元有机会直接成 token,这是本地样本中中文 token 数变化在机制上的解释,而不是“新 encoding 对所有中文一定更优”的证明。

同一句中文在两种 tokenizer 下的 token 切分与字节级回退对比
同一句中文在两种 tokenizer 下的 token 切分与字节级回退对比

本地小实验正好说明了版本变化本身值得被测量。下面三条输入在完全相同的文本条件下,用两个 encoding 编码:

输入类型字符数cl100k_baseo200k_base本次实验可支持的判断
中文叙述样本 A303121同文在不同 encoding 下 token 数不同
中文叙述样本 B212416第二条中文样本再次出现变化
英文技术句721313本次单条英文样本没有变化

这组结果只是一场机制演示。它支持“迁移 tokenizer 时需要重新计数”,不支持“新 encoding 对所有中文都少三分之一”,更不支持“token 更少的模型一定更好”。没有跨厂商 tokenizer、真实 API 账单或端到端质量数据时,这些结论都不应提前写进模型选型报告。

三条样本的变化方向与第一章的机制一致——词表更大、更多中文单元直接成 token 时中文更省;但机制解释不等于统计证明,样本量也不足以推出总体分布。

2.3 从“语言结论”转向“语料回归集”

比起争论中文的平均 token 数,更稳妥的是建立一个小型语料回归集。至少保留四类固定样本:纯中文业务叙述、中英混合技术文档、包含表格与 URL 的 Markdown,以及代码、JSON 与工具 schema。每次换模型或 tokenizer,都记录版本和各类样本的 token 分布。它回答的是“计量怎样变”;任务评测回答的是“效果怎样变”。两者不能彼此替代。

token 数只是输入被如何计量。下一步要看的是:这些 token 进入模型后,实际做了哪些计算。

第三章:上下文窗口只是容量,成本发生在 prefill 与 decode

3.1 把“长上下文”拆成四个对象

“支持 1M 上下文”描述的是一次交互可以容纳的上限,不是一张成本模型。要理解长上下文的资源压力,至少要把四个对象拆开。

对象回答的问题容易出现的误解
Context window一次交互最多能容纳多少 token窗口越大,每次请求一定越贵
Prefill已有输入怎样被处理成模型状态输入只需要计数,不需要计算
KV cache已处理 token 的 key/value 状态怎样保存KV cache 等于缓存了最终答案
Decode新 token 怎样逐步生成命中缓存后输出也免费

3.2 生成时为什么要保留历史状态:attention 的最小机制

一条请求先要处理已经给出的输入。这一段通常被称为 prefill:模型读取输入 token,构造后续生成所需的历史状态。随后才进入 decode,每生成一个新的 token,都要在已有状态上继续推进。因此,首 token 出现前的等待、完整响应等待和输出 token 数不应混成一个延迟指标。

KV cache 为什么存在,只需要 attention 的最小机制就能说清。生成下一个 token 时,模型要计算这个新 token 的 Query,与历史全部 token 的 Key 做相关性比较,再按相关性加权历史 token 的 Value。关键是:Key 与 Value 只由产生它们的 token 自己决定,Query 来自当前正在生成的 token。因此历史 token 的 K/V 一旦算出就可以保存复用,不必每次从头重算——这就是 KV cache。它复用的是计算状态,不是可以跳过生成、直接返回的“现成答案”。

生成下一个 token 时 Query 查询历史 Key 和 Value 的 KV cache 机制
生成下一个 token 时 Query 查询历史 Key 和 Value 的 KV cache 机制

3.3 prefill 是一次并行的整体,decode 是逐 token 的串行

prefill 与 decode 不仅是两段工作,计算方式也不同。prefill 一次读入全部输入 token,在模型内部并行处理,一次性产出所有输入位置的 K/V;decode 每生成一个新 token,都要读入这个 token,用它的 Query 与全部历史 K/V 计算 attention,再向前推进一个位置。所以首 token 延迟主要由 prefill 决定,完整延迟则是 prefill 加上逐 token 的 decode;把两者混成一个“响应延迟”,会掩盖输入成本与生成成本的分界。

3.4 KV cache 有多大:一张可以自己算的账

KV cache 的内存占用可以近似为:

KV 字节数 ≈ 2 × 层数 × KV 头数 × 头维度 × 序列长度 × 每元素字节数

系数 2 来自 Key 与 Value 两份;“KV 头数”单独列出,是因为它由模型架构决定,且不一定等于注意力头数(见 3.5)。

一个演示量级的算例(假设值,不代表任何具体模型):假设某模型 32 层、KV 头数 8、头维度 128、FP16(每元素 2 字节)。序列 4096 token 时,单请求的 KV cache 约为 2 × 32 × 8 × 128 × 4096 × 2 ≈ 0.5 GiB;序列扩到 32,768 token 时约为 4 GiB。这还没有计入模型权重、激活值与推理中间量。长序列不仅增加输入 token 的计量,还成比例放大每个请求的 KV 内存,从而压缩单机可同时服务的请求数。

3.5 KV 头压缩:MHA、MQA 与 GQA

KV cache 的大小还取决于模型采用哪种注意力变体。早期 Transformer 是 MHA(Multi-Head Attention):每个注意力头都持有自己的 K/V,KV cache 随头数线性增长;MQA(Multi-Query Attention)让所有头共享同一组 K/V,KV cache 压到约 1/头数,但会牺牲部分质量与训练稳定性;GQA(Grouped-Query Attention)把头分组、组内共享 K/V,是当前主流模型的折中。10 11 这也解释了“同样宣称 128k 上下文”的两个模型,KV 内存压力可能差出数倍:KV 头数是架构参数,不是上下文窗口参数。

3.6 PagedAttention:KV cache 的内存管理问题

KV cache 的内存压力不只是总量问题,还有分配方式。PagedAttention 的研究从服务端视角指出:KV cache 随请求动态增长,以连续内存分配会出现碎片与重复副本,限制可用批量与吞吐;把 KV cache 切成固定大小的 block、用 block table 管理(类似操作系统虚拟内存的页表),可以显著改善内存利用并支持跨请求共享。4 这是一项自托管服务系统的证据,不能反推商业 API 的具体账单,但它足以说明长上下文并不只是一个 token 计数问题。

上下文容量边界、推理链路、KV 内存公式与 MHA MQA GQA 对比
上下文容量边界、推理链路、KV 内存公式与 MHA MQA GQA 对比

3.7 上下文容量不是使用目标

大窗口提供的是容量,不是应当主动填满的目标。此前关于 Context、Memory 和 RAG 的问题是“什么信息值得进入窗口”;本文只补上一层边界:一旦内容进入窗口,它就会进入分词、prefill、缓存和生成的计算链。

单次生成里的 KV cache 解释了模型为何能持续生成。跨请求的 Prompt Caching 则进一步问:不同请求之间,哪些已完成的计算可以复用?

第四章:Prompt Caching 缓存的不是答案

4.1 先区分三种经常被叫作“缓存”的对象

“缓存”这个词太容易掩盖机制差异。至少有三种对象经常被放在一起说:

对象复用什么作用范围需要保留的边界
单次生成中的 KV cache当前序列已计算的 key/value 状态一次生成主要服务后续 decode
API Prompt Caching服务商识别到的共享提示前缀跨 API 请求规则、计费与可观测字段由产品定义
自托管 Prefix Caching运行时中可复用的共享前缀 KV 状态跨请求受版本、模型、显存与调度影响

它们的共同点是“复用已经完成的计算”,但生命周期、控制者和可观察指标并不相同。把三者都称为“把 Prompt 缓存起来”,很容易让人误以为系统存住了下一次可直接返回的答案。实际被复用的是前缀的计算状态;新的问题、动态上下文和新输出仍要继续处理。

单次 KV cache、API Prompt Caching 与自托管 Prefix Caching 的比较
单次 KV cache、API Prompt Caching 与自托管 Prefix Caching 的比较

4.2 复用的具体对象:前缀的 K/V 状态

把“复用什么”再具体一层,就能和第三章的机制接上:跨请求缓存复用的是共享前缀在 prefill 阶段产出的 K/V 状态。命中后,前缀部分的 prefill 计算被跳过;未命中的动态尾部仍需计算自己的 K/V;新输出的 decode 完全不能省。

请求 1:稳定前缀(计算并写入 K/V)| 问题 A -> 输出 A
请求 2:稳定前缀(命中缓存,跳过其 prefill)| 问题 B -> 输出 B
         <-- 命中区:K/V 已复用 -->  <-- 仍需 prefill -->  <- 仍需 decode ->

这个机制正好解释 vLLM 文档里“APC 只减少 prefill、不减少 decode”的限制来源:缓存收益落在输入侧,输出生成是另一段工作。6

4.3 匹配是 token 级、从开头连续的

命中判断发生在 token 序列层,从开头连续进行:两次请求只要从某个位置开始不同,共享前缀就在那里终止,即使两者“包含同一批内容”。这意味着序列化的稳定性同样影响缓存:同一份工具 schema,JSON 的 key 排列或字段顺序不同,token 序列就不同,可能无谓地截断共享前缀。不同产品实际的匹配粒度和可观测字段不同,最终以供应商文档为准。

4.4 API 与自托管的规则差异

在 API 场景中,缓存规则以供应商产品说明为准。OpenAI 在 2024 年的 Prompt Caching 说明中,以“最长已计算前缀”描述匹配,并在用量结构中暴露 cached_tokens。5 Anthropic 2025 年的说明也围绕最长已缓存前缀与 cache-aware 限流讨论重复长上下文的复用。7 阈值、TTL、限流和折扣都属于时效产品参数,发布时必须按当前官方文档复核,而不是从旧文章中继承。

自托管时,vLLM 的 Automatic Prefix Caching(APC)把重复查询同一长文档、多轮对话等工作负载列为典型场景,并给出一个很清晰的性能边界:共享前缀的 prefill 可以减少,新输出的 decode 不会因此消失。6 所以当输出很长、共享前缀很短,或者总时延主要不在 prefill,命中缓存的收益仍可能有限。

4.5 更细的实现:前缀树缓存

自托管运行时还有比“单条最长前缀”更细的实现。SGLang 的 RadixAttention 用前缀树组织跨请求共享的前缀,让同一份资料在多个请求、多个分支间复用,而不只依赖“整段开头连续相同”。12 它属于运行时实现层,商业 API 是否等价取决于产品内部实现,本文不推断具体供应商行为。

4.6 命中之后仍然要支付什么

命中之后,至少还有五类工作不会自动消失:未命中的动态尾部、输出生成、reasoning token、工具与检索调用,以及验证与失败重试。自托管系统还要承担缓存逐出、显存与并发的资源压力。更准确的一句话是:Prompt Caching 复用已经计算过的前缀,不是把下一次任务的答案提前存好。

既然复用依赖共享前缀,Prompt 里的信息排列就不再只是可读性问题,它会直接改变可复用计算的边界。

第五章:为什么 Prompt 的顺序会改变账单

5.1 缓存依赖的是连续前缀,不是内容集合相同

前缀缓存依赖的是从开头开始的连续匹配,而不是两次请求“包含同一批资料”。以下两次请求的稳定部分位于开头,因此具有较长的潜在共享前缀:

请求 A:稳定规则 | 工具 schema | 公共资料 | 用户问题 A
请求 B:稳定规则 | 工具 schema | 公共资料 | 用户问题 B
         <--------- 可复用前缀 --------->

如果将高频变化的字段放到最前面,结果就不同:

请求 A:当前时间 A | 随机 ID A | 稳定规则 | 工具 schema | 用户问题 A
请求 B:当前时间 B | 随机 ID B | 稳定规则 | 工具 schema | 用户问题 B
         X 首个差异已经出现,后续稳定内容难再构成长共享前缀

这是一条来自前缀匹配机制的工程推论,而不是对所有供应商粒度的承诺。OpenAI、Anthropic 与 vLLM 的公开资料都描述了最长或共享前缀的复用,但不同产品实际的匹配条件、缓存条件和可观测指标可能不同。5 6 7

落在实现层,匹配发生在 token 序列上:两次请求哪怕只有几个 token 不同,共享前缀就在那里终止;序列化顺序(如 JSON 的 key 排列、工具 schema 的字段顺序)变化同样会改变 token 序列,即使内容“看起来没变”。这也是 4.3 中序列化稳定性会影响命中的原因。

稳定前缀在前与动态字段在前的连续前缀匹配对比
稳定前缀在前与动态字段在前的连续前缀匹配对比

5.2 一套可执行的排列原则

由此可以得到一套可执行的排列原则:将稳定的系统规则、工具 schema 和多轮复用的公共资料放在前部;将当前查询、临时证据、时间戳、随机 ID 与不断变化的任务状态放在后部;稳定前缀变更时显式记录版本;最后只相信实际的 cached_tokens、cache read/write 或运行时命中指标,不相信“看起来差不多”。

5.3 不要为了命中率牺牲上下文正确性

但缓存命中只是局部指标。过期规则即使稳定,也不应为了命中继续发送;无关长文即使命中,也会占据逻辑上下文并可能损害结果质量。敏感信息是否能进入供应商缓存,还需要按具体产品的数据保留和隐私条款单独审查。稳定前缀在前,是一种布局建议,不是无限扩张前缀的理由。

5.4 把命中率拆成可诊断问题

当缓存没有命中时,也不要用猜测填满监控面板。至少区分五种可能:前缀内容真的变化;序列化或工具 schema 的顺序变化;模型、部署或 tokenizer 版本变化;缓存已过期或被逐出;请求没有满足当前产品的缓存条件。供应商不一定暴露全部原因,但“无法观测”应当被记录,而不是被误归因为模型变慢。

Prompt 顺序优化的只是某一段计算复用。要判断方案是否真的更便宜,还需要把分词、缓存、生成、重试和结果放进同一张账本。

第六章:给中文 AI 应用建立一张可验证的成本账本

每百万 token 单价是费率,不是成本结论。真正可比较的单位应是“通过约定验证的成功任务”:一个方案可能让单次请求更便宜,却因为输出更长、重试更多或通过率更低,反而花掉更多总成本。

6.1 先把账本分成六层

一张最低可用的账本可以分为六层:

层建议记录回答的问题
原始输入中文字符、英文字符、UTF-8 字节、文件类型、工具 schema 大小真实工作负载是什么
Tokenizer模型版本、tokenizer 版本、总输入 token、分段 token 分布同一语料怎样被计量
缓存未缓存输入、缓存写入、缓存读取、命中率、失效原因哪部分 prefill 被复用
推理首 token 延迟、完整延迟、输出 token、reasoning token输入与输出各花了什么
工作流模型调用、工具调用、重试、子 Agent、降级与人工接管单次请求之外发生了什么
结果独立验证是否通过、返工次数、最终终态成本是否换来了成功

不是每个 API 都会同时暴露 prefill、decode、缓存读写和 reasoning token。遇到这种情况,不应伪造精确分项:可以记录首 token 与完整延迟作为可观察代理,并保留供应商原始字段及其来源。跨供应商归一化的目标不是制造一张看似完美的表,而是避免把缺失信息伪装成零成本。

6.2 从请求成本走到任务成本

先用一个不绑定币种和厂商的分析式约束讨论范围:

经验证成功任务成本
  = 所有请求的未缓存输入成本
  + 所有请求的缓存写入与读取成本
  + 所有输出与推理成本
  + 工具、检索与基础设施成本
  + 重试、降级与人工处理成本

为了让费率可以落地,可以把单次请求的输入侧拆成符号化形式,并与 API 返回或账单字段对应:

单次请求成本 ≈ u × p_u + w × p_w + r × p_r + o × p_o + t × p_t
符号含义常见 API 字段或计费项
u未缓存输入 tokenuncached input / input
w缓存写入 tokencache write(部分供应商单列)
r缓存读取 tokencached read / cached_tokens
o输出 tokenoutput / completion
t推理 tokenreasoning token(如适用)

字段名因供应商而异,发布时按当前官方文档核验,不能从旧文章继承。5 7

命中率也不是直接可用的省钱数字。实际省下的输入成本比例,可以近似为:

省下的输入成本比例 ≈ 前缀占比 p × 命中率 h ×(1 - 缓存折扣 d)

一个演示算例(折扣 d 为假设值,以供应商当前文档为准):设缓存输入价格为未缓存的 25%(d = 0.25)。前缀占比 80%、命中率 90% 时,输入成本约省 0.8 × 0.9 × 0.75 ≈ 54%;前缀占比降到 20% 时,即使命中率仍是 90%,也只省约 13.5%。结论有两点:命中率高不等于省得多,前缀占比低时命中再高也有限;这个比例只覆盖输入侧,输出、reasoning 与重试不在其中。

这个式子不是要求每个团队立即为每一项精确折现。它的作用是把分母放回正确位置:分母是通过验证的成功任务数,而不是请求数。一次看上去便宜但需要三次重试的调用,和一次略贵但稳定通过的调用,不能只用输入单价比较。

六层成本账本汇入经验证成功任务成本的计算框架
六层成本账本汇入经验证成功任务成本的计算框架

6.3 用同一任务做三组对照

要把这张账本用于模型迁移或 Prompt 优化,可以从三组对照开始。

对照固定什么观察什么不能直接推出什么
Tokenizer 对照真实语料各类文本的 token 分布任务质量或账单一定更低
缓存对照模型与内容稳定前缀位置、缓存指标与延迟所有请求都会按相同比例受益
任务对照任务集与验证器完整轨迹、重试、通过率与成功任务成本单次成功代表长期分布

每组都应固定或记录模型版本、tokenizer 版本、Prompt 版本、缓存设置、运行日期、样本类型、验证标准与重复次数。先用真实语料验证计量变化,再测缓存和延迟,最后才让相同验证器裁定任务结果。做完第一层 token 计数就宣布“新模型更便宜”,恰好跳过了最可能反转结论的两层。

从 tokenizer 到缓存再到完整任务的三阶段对照实验阶梯
从 tokenizer 到缓存再到完整任务的三阶段对照实验阶梯

6.4 作者实践的可用范围

目前这里能够作为事实写入的,只有两条中文与一条英文样本的 tokenizer 机制演示。没有真实缓存日志、延迟分解与账单数据前,任何关于具体 Harness、Codex 任务或知识库应用的“已降本”案例都应保留为待执行实验,而不是作者实践。这个限制并不削弱方法,反而使后续的比较有了明确证据门槛。

结语:预算真实语料,不预算抽象字符

Token、缓存和上下文不是三个彼此独立的优化点,而是一条连续链路:原始文本先被具体 tokenizer 编码,输入经历 prefill,已计算的共享前缀可能被复用,新输出仍要 decode,工作流还会继续产生工具调用、重试与追加上下文,最后由验证决定这些成本是否换来了有效结果。

因此,中文 AI 应用的最小行动不是寻找一个“每个汉字等于多少 token”的新常数,而是完成四件更可验证的事:

  1. 用真实中文和中英混合语料重新测 token,并记录 tokenizer 版本。
  2. 将稳定规则与动态任务尾部分开,观测实际缓存指标而非凭 Prompt 外观判断。
  3. 分开记录首 token、完整延迟、输出与重试,不把所有等待归结为“模型慢”。
  4. 用独立验证将所有尝试成本归到成功任务,而不是归到单次请求。

字符数描述的是你写了多少文本;token、缓存和任务轨迹,才描述模型为这项工作付出了什么。