把 context window、token 和账单算清楚
一次 40 轮对话的输入 token 计费是自身长度的 22 倍。缓存可降 68%;时间戳放错位置会多花 20%。
本页内容
这里有一段 40 轮的客服对话,逐轮计费。里面没有任何异常:开发者询问 API,assistant 用一两段话回答。整段对话共有 5,090 个文本 token——约 8 页。
| 轮次 | prompt token | 新文本 | 输出 | 本轮成本 | 累计总额 |
|---|---|---|---|---|---|
| 1 | 213 | 18 | 183 | $0.002622 | $0.002622 |
| 5 | 892 | 14 | 123 | $0.003260 | $0.014354 |
| 10 | 1,656 | 19 | 114 | $0.004680 | $0.035530 |
| 20 | 2,868 | 18 | 103 | $0.006972 | $0.094426 |
| 30 | 3,941 | 20 | 99 | $0.009070 | $0.174170 |
| 40 | 4,947 | 17 | 142 | $0.011598 | $0.274386 |
把第二列和第三列放在一起看。到第 40 轮时,用户只输入了 17 个 token,却按 4,947 个 token 付费。这个问题并不比第一个更难;它还更短。变化在于,这次请求再次携带了整段对话——第 40 次。
这 40 次调用累计计费的输入 token 总数:112,617。这段对话本身只有 5,090 个 token。你为它付了 22 遍的钱。
本章讲清楚为什么会这样、它在各家 provider 的账单上叫什么,以及 5 类收费项中哪些是你能改变的。
查看详情
window 不是记忆
链接到此部分:window 不是记忆这个行业里最昂贵的误解,是以为模型记得一段对话。
它不记得,而第 13 章的机制准确说明了原因。transformer 在生成期间的状态是 KV cache:为序列中每个 token 计算出的 keys 和 values。这个 cache 只在一次请求的生命周期内存在。请求结束后,持有它的进程可以去服务别人,cache 也就消失了。对面没有按用户保存的存储,也没有 session。
因此下一次请求必须携带模型应当知道的一切,模型在输出第一个新 token 之前,会先对整个 prompt 跑一次 forward pass 来重建这个状态。第 15 章把 prompt 称为“完整状态”。物理原因就在这里:prompt 是完整状态,因为调用结束后没有任何别的东西会留下来。
context window 是这个 prompt 加上回答的最大长度。它限制的是你最多能重建多少状态,而不是一个会在请求之间保存东西的容器。把它叫作“模型的记忆”,把因果方向说反了——你不是在填充记忆,而是在付费重新建立一份记忆。
22 这个数字就是这么来的。第 轮携带前面全部 轮,因此一段 轮对话的总输入,是一个不断增长的序列求和,也就是二次增长:
其中 是 system prompt, 是第 轮的历史。把 40 轮中测得的累计输入拟合到 ,得到 ,它预测第 40 轮为 113,645 个 token,实测为 112,617。二次项占主导,而线性项才是用户实际输入的内容。
本章最该带走的一句话是:你的账单随对话长度的平方增长,而不是随最后一个问题增长。 同样 40 个问题,如果完全不带历史,成本是 $0.066036。保留历史的成本是 $0.274386。历史把账单乘以 4.2,而且它还会继续放大,因为乘数就是对话长度。
为什么一定会有上限
链接到此部分:为什么一定会有上限window 有限,是因为两个原因朝着同一个方向拉。第一个来自第 9 章:attention 会把每个 token 与每个其他 token 比较,所以这一层的工作量随序列长度平方增长。第二个是内存:KV cache 随序列长度线性增长,第 13 章已经算过——在长序列下,它会比权重还大。
这两个限制都被攻克过,但都没有被消除。FlashAttention1 重新组织计算,让它对高带宽内存的读写少得多,因此长序列变得可行,但没有改变渐近成本。Position Interpolation2 和 YaRN3 通过重新缩放第 9 章中的位置编码,扩展已训练模型的可用 window,而不是重新训练。它们共同解释了为什么 window 在 5 年内从 2K 到了 1M。
但它们没有让长 context 变免费。它们提高了天花板,也让斜率更平缓。斜率仍然存在,而本章后面那些价格 tier 衡量的正是它。
不是两类,而是五个桶
链接到此部分:不是两类,而是五个桶互联网上几乎所有成本计算器,都会把一次 API 调用建模为输入 token 乘以输入价格,再加输出 token 乘以输出价格。2023 年这样做是对的。现在它错了,而且会让账单在两个方向上差出 2 倍甚至更多。
现在有 5 类可计费 token:
| 桶 | 含义 | 典型价格,相对于输入 |
|---|---|---|
| 未缓存输入 | 模型必须重新处理的 prompt token | 1× |
| cache read | 从已存前缀中提供的 prompt token | 0.1× |
| cache write | 本次调用写入 cache 的 prompt token | 1.25× 到 2× |
| 输出 | 模型生成并发送给你的 token | 5× 到 6× |
| reasoning | 模型生成但没有发送给你的 token | 输出费率 |
这 5 类中有 3 类在两年前还不是单独的账单行,而两条 cache 线最容易被误解,因为 cache write 比普通输入更贵,不是更便宜。你为存储某些内容支付溢价,之后才能用折扣价读回;这笔交易是否划算,完全取决于你会读多少次。
reasoning 这个桶来自第 12 章,现在给它标上价格,而且有个细节值得说清楚:Google 文档说,定价“基于模型需要生成的完整 thought tokens,尽管 API 只输出摘要。”4 你会为从未传给你的 token 付费。这是唯一一个你无法计数、检查或验证内容的桶。
同一次调用,三种方言
链接到此部分:同一次调用,三种方言现在的问题不再是乘法,而是 normalisation。每个 provider 都用不同名称报告这些桶,而且——陷阱在这里——其中两家用同一个词表示两个不同数量。
取一次调用:从 cache 读取 4,837 个 token,110 个 fresh token,142 个可见输出 token,300 个 reasoning token。
// OpenAI-compatible
{ "usage": { "prompt_tokens": 4947,
"prompt_tokens_details": { "cached_tokens": 4837 },
"completion_tokens": 442,
"completion_tokens_details": { "reasoning_tokens": 300 } } }
// Anthropic
{ "usage": { "input_tokens": 110,
"cache_read_input_tokens": 4837,
"cache_creation_input_tokens": 0,
"output_tokens": 442 } }
// Gemini
{ "usageMetadata": { "promptTokenCount": 4947,
"cachedContentTokenCount": 4837,
"candidatesTokenCount": 142,
"thoughtsTokenCount": 300 } }看 prompt_tokens: 4947 和 input_tokens: 110。两个字段都是同一个 prompt 的输入 token 数。OpenAI 的字段包含 cached tokens;Anthropic 的字段不包含——它的文档明确写出了这个恒等式,total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens。5 Anthropic 的 input_tokens 表示“你最后一个 cache breakpoint 之后的 token”。
再看输出。OpenAI 和 Anthropic 都报告 442,里面已经包含 300 个 reasoning token。Gemini 报告 142,并把 300 放在单独字段里。第 12 章指出过,这是对同一份工作两种计数方式之间的不兼容;这里则说明它会带来什么成本。
normaliser 只有 30 行,而且不可省略:
export interface Usage {
promptTokens?: number; // input, NOT cached
cachedInputTokens?: number; // read from cache
cacheWriteTokens?: number; // written to cache on this call
completionTokens?: number; // output
reasoningTokens?: number; // billed apart from output (Gemini only)
}
const num = (v: unknown) => (typeof v === "number" && isFinite(v) ? v : 0);
export const fromOpenAI = (raw: any): Usage => {
const u = raw.usage ?? {}, d = u.prompt_tokens_details ?? {};
const cached = num(d.cached_tokens), write = num(d.cache_write_tokens);
return {
promptTokens: Math.max(0, num(u.prompt_tokens) - cached - write),
cachedInputTokens: cached,
cacheWriteTokens: write,
completionTokens: num(u.completion_tokens), // reasoning already inside
reasoningTokens: 0,
};
};
export const fromAnthropic = (raw: any): Usage => {
const u = raw.usage ?? {};
return {
promptTokens: num(u.input_tokens), // already excludes cache
cachedInputTokens: num(u.cache_read_input_tokens),
cacheWriteTokens: num(u.cache_creation_input_tokens),
completionTokens: num(u.output_tokens),
reasoningTokens: 0,
};
};
export const fromGemini = (raw: any): Usage => {
const m = raw.usageMetadata ?? {}, cached = num(m.cachedContentTokenCount);
return {
promptTokens: Math.max(0, num(m.promptTokenCount) - cached),
cachedInputTokens: cached,
cacheWriteTokens: 0,
completionTokens: num(m.candidatesTokenCount), // EXCLUDES thinking
reasoningTokens: num(m.thoughtsTokenCount), // billed at output rate
};
};把上面三个 payload 送进三个 reader,三者都会产生相同的 Usage,因此也会得到相同数字:$0.006491。写这一层的全部意义就在于得到这个一致性。
如果弄错,同一次调用会变成这样:
| 错误 | 计费 | 误差 |
|---|---|---|
把 cached_tokens 当作 prompt_tokens 的额外部分 | $0.016165 | 2.49×——prompt 被收了两遍钱 |
| 把 cache reads 当成免费,而不是 0.1× | $0.005524 | 0.85×——你吃掉 15 % |
读取 candidatesTokenCount 并忽略 thoughtsTokenCount | $0.002891 | 这次调用的 55 % 消失了 |
第三种最危险,因为它会悄悄失败,而且方向看起来像好消息。你的 dashboard 会显示 reasoning model 的成本不到真实成本的一半,同时没有任何地方报错。
计算成本
链接到此部分:计算成本bucket normalise 之后,成本函数很短。唯一不太显然的部分是 tier 查找,下一节会解释:
export interface Tier { maxPromptTokens: number | null; price: number }
export interface Pricing {
input: Tier[]; output: Tier[];
cachedInput?: Tier[]; cacheWrite?: Tier[]; reasoning?: Tier[];
}
const tierPrice = (tiers: Tier[] | undefined, contextSize: number, fallback?: Tier[]) => {
const table = tiers ?? fallback;
if (!table?.length) return 0;
const sorted = [...table].sort(
(a, b) => (a.maxPromptTokens ?? Infinity) - (b.maxPromptTokens ?? Infinity));
for (const t of sorted)
if (t.maxPromptTokens === null || contextSize <= t.maxPromptTokens) return t.price;
return sorted[sorted.length - 1].price;
};
export function computeCost(pricing: Pricing, usage: Usage): number {
const fresh = usage.promptTokens ?? 0;
const read = usage.cachedInputTokens ?? 0;
const write = usage.cacheWriteTokens ?? 0;
const out = usage.completionTokens ?? 0;
const think = usage.reasoningTokens ?? 0;
const contextSize = fresh + read + write; // the tier depends on the WHOLE prompt
return fresh * tierPrice(pricing.input, contextSize)
+ read * tierPrice(pricing.cachedInput, contextSize, pricing.input)
+ write * tierPrice(pricing.cacheWrite, contextSize, pricing.input)
+ out * tierPrice(pricing.output, contextSize)
+ think * tierPrice(pricing.reasoning, contextSize, pricing.output);
}这里有两个设计决策值得辩护。fallback——cache 价格回退到输入,reasoning 回退到输出——编码了缺失价格表的含义:Gemini 上的 reasoning tokens 按输出费率计费,所以缺失的 reasoning 价格不是零,而是输出价格。并且 contextSize 会把三类输入桶全部相加,而不只是 fresh token,因为 tier 是按 prompt 有多长来选择的,不是按其中多少按全价收费来选择的。
Prompt caching,以及写入的成本
链接到此部分:Prompt caching,以及写入的成本prompt cache 存储的是你的 prompt 某个前缀的模型已计算状态,因此后续有相同前缀的请求可以跳过重复计算。“前缀”这个词带来 4 个性质,而这 4 个都会让人意外。
它是前缀,不是集合
链接到此部分:它是前缀,不是集合cache 从渲染后的 prompt 开头向后匹配,并在第一个不同字节处停止。内容如果以不同顺序出现在后面,不会得到部分 credit。OpenAI 说得很直白:“cache 复用要求整个渲染前缀匹配。”6
它有最小长度
链接到此部分:它有最小长度低于最小长度时,不会缓存,也不会返回错误。OpenAI 上,GPT-5.6 及之后模型的最小长度是 1,024 token,较早模型是 2,048。Anthropic 则根据模型从 512 到 4,096 不等——Claude Sonnet 4.5 是 1,024,Claude Haiku 4.5 是 4,096。如果两个 cache 字段都返回零,通常就是这个原因。
写入比读取贵,也比不缓存贵
链接到此部分:写入比读取贵,也比不缓存贵在 OpenAI 和 Anthropic 上,短期 cache 的 cache write 是未缓存输入费率的 1.25×,Anthropic 的一小时 cache 是 2×。读取是 0.1×。Google 不收写入费,但收存储租金:Gemini 2.5 Pro 每百万 token 每小时 $4.50。
它会过期,而且只存在于一台机器上
链接到此部分:它会过期,而且只存在于一台机器上Anthropic 的默认条目存活 5 分钟,每次命中都会免费刷新。OpenAI 的条目在最近一次写入或复用后至少存活 30 分钟。OpenAI 还指出,cached states 存在于单台机器上,所以只有请求被路由到持有该条目的机器时才会命中——这正是 prompt_cache_key 会影响、但不能保证的事情。
break-even 小到可以记在脑子里,而 OpenAI 文档已经帮你做了算术:写入一次前缀并复用一次,成本是普通输入成本的 1.35×,而未缓存处理两次是 2×;10 次请求中,一次写入加 9 次读取是 2.15×,对比未缓存 10×。一次复用就能回本。 Anthropic 的结论也类似:5 分钟 cache 读一次回本,1 小时 cache 读两次回本。
现在再看这段 40 轮对话,开启 caching 且前缀稳定:
| 未缓存输入 | cache reads | cache writes | 总计 | |
|---|---|---|---|---|
| 无 cache | 112,617 | — | — | $0.274386 |
| caching | 2,887 | 104,783 | 4,947 | $0.088250 |
便宜了 68%,而表中有 3 个数字值得注意。
cache 到第 6 轮才开始生效。 prompt 到那时才达到 1,024 token,所以前 5 轮完全按原样计费——而第 6 轮计费还更差,因为它是填充 cache 的那一轮,要付 1.25\u00d7 写入溢价。第一次读取发生在第 7 轮。表中的 2,887 个未缓存 token 就是这笔算术:5 轮的量,不是 6 轮。Caching 是给长 prompt 的折扣,短对话拿不到。
写入溢价是 $0.002474,占 cached bill 的 2.8%。每一轮都写入新的尾部,写了 40 次,而全部写入溢价相对于读取节省的成本只是舍入误差。理解写入收费的精确含义,是为了让你停止为它过度担心。
112,617 个 token 中,只有 2,887 个按完整输入价收费。 这就是正常工作的 cache 形状:几乎一切都是读取。
prompt 的顺序决定这一切是否发生
链接到此部分:prompt 的顺序决定这一切是否发生下面这个失败会真正花钱,而且只是一个一行 bug。
把每次调用都会变化的东西放在 prompt 前面——时间戳、request id、用户姓名、“今天是”这一行、刚检索出来的文档——前缀就会从第一个字节开始不同。没有任何东西匹配。每次调用都是 miss。并且因为每次调用都呈现一个新前缀,每次调用也都会写入。
同一段对话,同样 40 轮,启用 caching,但在 system prompt 顶部放一个每次调用不同的时间戳:
| 总计 | 对比 | |
|---|---|---|
| 完全不 caching | $0.274386 | — |
| caching,稳定前缀 | $0.088250 | −67.8 % |
| caching,易变前缀 | $0.329251 | +20.0 % |
启用 prompt caching 让这段对话比不启用贵了 20%。你对 109,730 个 token 支付了 1.25× 写入溢价,却读回了零。没有错误,没有警告,功能还是开着的。
所以规则——也就是 prompt caching 的一行总结——是:稳定内容放前面,变量内容放后面。 system instructions、tool definitions 和参考材料在前;时间戳、用户身份和当前问题在后。Anthropic 明确给出了层级——cache 遵循 tools → system → messages,任何层级的变化都会使该层级及其之后的所有内容失效,因此只改一个 tool 描述,也会让整个 cache 失效。5
还有两个后果很容易踩坑。改变哪些 tools 被启用,会改变 tool definitions,所以一个为部分用户添加 tool 的 feature flag 会把 cache 一分为二。并且在 Anthropic 上,切换网页搜索或 citations 会修改 system prompt,这会在你没有改动自己任何文本的情况下,使 system 和 message caches 失效。
截断历史不是解法
链接到此部分:截断历史不是解法面对二次增长账单,最显然的反应是停止发送完整历史:只保留最近十几条消息,把其余丢掉。它确实会降低账单,但通常是错误动作,测量结果说明了原因。
| 策略 | 总计 | 相对完整历史 + cache |
|---|---|---|
| 完整历史,无 cache | $0.274386 | +211 % |
| 完整历史,caching | $0.088250 | — |
| 最近 12 条消息,无 cache | $0.118712 | +35 % |
| 最近 12 条消息,开启 caching | $0.122546 | +39 % |
截断到 12 条消息的 window,比发送全部内容且不缓存便宜 57%——这是人人都会做的比较,也是这个技术流行的原因。但它比发送全部内容且 cache 正常工作贵 39%,而截断同时开启 caching 还会稍微更差,不是更好。
机制还是前缀。滑动 window 每轮都会丢掉最旧消息,所以 prompt 不再从上次开始的地方开始,每一轮都会呈现新前缀。OpenAI 的指导明确说了这一点:“summarisation、compaction 或 context truncation 可能改变前缀并重置 cache 复用。”6 到第 40 轮时,窗口化 prompt 只有 813 token,低于 1,024-token 最小值,因此完全无法缓存。
而钱只是成本中便宜的那一半。你丢掉的,是用户在第 2 轮给出、模型在第 40 轮需要的指令。Truncation 用你看得见的账单换来你看不见的失败;要正确处理它——compaction、保存在 window 外的结构化笔记、按需检索历史——是第 24 章的主题。
跨过 tier 会给整个请求重新定价
链接到此部分:跨过 tier 会给整个请求重新定价长 context 更贵,不仅因为它更长。超过某个阈值后,它的单 token价格也更贵,而且这个阈值会追溯应用到整个 prompt。
OpenAI 对 gpt-5.6-terra 的模型页面用一句话说明:“Prompts with >272K input tokens are priced at 2x input and 1.5x output for the full request.”7 不是只针对超出的部分。是整个请求。
prompt 271,999 + 500 output -> $0.5500
prompt 272,000 + 500 output -> $0.5500
prompt 272,001 + 500 output -> $1.0970一个 token,55 美分。如果你的服务从大小不可控的检索文档构建 prompt,那么你的成本模型在一个团队没人写下来的边界上有一座悬崖。
Google 的定价也类似,阈值是 200,000 token:Gemini 2.5 Pro 对 200K 以内 prompt 的输入价是每百万 token $1.25,超过后是 $2.50,输出则从 $10.00 变为 $15.00。8 Anthropic 走了相反方向——截至 2026 年 9 月 6 日,其文档说明 Claude 4.6 及之后模型以标准价格包含完整的一百万 token window,因此“900k-token request 和 9k-token request 按相同的每 token 费率计费。”9 更早模型保留了附加费。
所以价格不是一个数字。价格是一个按 prompt 长度索引的 tier 表,这就是成本函数里 Tier[] 的用途,也解释了为什么 computeCost 用整个 prompt 来选择 tier,而不是对每个 bucket 单独选择。
Prefill、decode,以及为什么输出是输入的 6 倍
链接到此部分:Prefill、decode,以及为什么输出是输入的 6 倍5 个 bucket 映射到第 13 章的两个阶段;一旦看清这种映射,价格比例就不再显得随意。
输入 token 是 prefill。 整个 prompt 一次性通过模型,并行处理——大型矩阵乘法,受计算限制。每 token 成本低,而这个阶段决定 time to first token:4,947-token prompt 在第一个词出现前,要先完成 4,947 个 token 的 prefill。
输出 token 是 decode。 它们逐个生成,每个 token 都是一次完整的 forward pass,会读取整个 KV cache,GPU 大多数时间在等内存而不是计算。这个阶段决定 tokens per second,它无法在单个回答内部并行化,也解释了为什么本文计价模型中输出约为输入的 6 倍:每百万 token $12.00 对比 $2.00。
三个后果直接随之而来。cache read 替代 prefill 工作,所以它同时购买延迟和金钱收益——同一个折扣会表现为更低账单和更短的首 token 等待。Reasoning tokens 是你看不见的 decode,所以 reasoning model 会先几秒钟什么都不流式输出,然后很快回答:第 12 章警告过界面后果,这里是账单后果。并且中止 stream 不会停止生成——第 14 章构建了 cancellation,把价格留到本章,而价格就是完整输出计数,因为 token 已经生成并计费,无论有没有人在听。没人保留的答案也一样:第 40 轮答案重新生成 5 次,为留在屏幕上的那一次花费 $0.057990。
发送前先数 token
链接到此部分:发送前先数 token第 7 章的 tokenizer 是 Python 写的,也留在了那里。预算发生在构建请求的服务器里,所以这里必须做,而且准确度只有 3 个层级。
第一层:本地计数。 js-tiktoken 带有与 Python tiktoken 相同的 BPE merge tables,因此对 OpenAI encodings 可得到逐字节一致的计数,而且不需要网络调用:
import { getEncoding } from "js-tiktoken";
const enc = getEncoding("o200k_base");
const PER_MESSAGE = 4; // role and delimiters added by the chat template
const PER_REPLY = 3; // priming for the assistant turn
export function promptTokens(messages: { role: string; content: string }[]) {
return messages.reduce(
(sum, m) => sum + enc.encode(m.content).length + PER_MESSAGE, PER_REPLY);
}两个常量很重要,本地计数也正是在这里漂移。被 tokenized 的不是你的纯文本——第 11 章的 chat template 会先用 role markers 包裹每条消息,而这些也是你要付费的 token。每条消息 4 个、reply priming 3 个,是 OpenAI chat models 的常规近似;在上面对话的 81 条消息中,它们合计 324 个 token,占长度的 6.4%。这里的计数与第 7 章的 Python tiktoken 对全部 81 个字符串交叉检查,结果一致。
第二层:问 provider。 Anthropic 暴露 /v1/messages/count_tokens,Google 暴露 count_tokens,两者都接受与真实调用相同的请求形状,并免费返回输入 token 数。当你无法本地计数时使用它们——而 Anthropic 就无法本地计数,因为它的 tokenizer 没有公开。Anthropic 文档谨慎说明它给出的是什么:这个计数“是估计值”,并且“可能包含 Anthropic 为 system optimizations 自动添加的 token”,而这些“不会向你收费”。10
第三层:读取响应中的 usage。 那是真相,但它在钱花出去之后才到达。这正是前两层存在的原因——用于决定是否发送请求,而不是用于事后计费。
你会付费、但没人展示给你的东西
链接到此部分:你会付费、但没人展示给你的东西有 4 个不会作为 line item 出现的 line item。
system prompt,每次调用都付费。 上面那个加上 template overhead 是 192 token。40 次调用合计 7,680 token——占这段对话总账单的 5.6%,只因为一次写下的 8 行文本。它也是最佳 cache 候选,因为既稳定又在最前面。
Tool definitions。 每个 tool 的名称、描述和 JSON schema 都会随每个请求发送,provider 还会在上面加 scaffolding。Anthropic 公布了数字:只要启用 tools,就会在 Claude Sonnet 4.5 上添加一个隐藏 system prompt,tool_choice 设为 auto 时为 496 token,使用 any 或命名 tool 时为 588 token。9 这还没算你自己的 schemas。第 18 章构建目录;第 24 章会测量它吃掉了多少。
每一次 generation,包括你丢弃的那些。 重新生成 5 次就是 5 倍成本。chat 只显示一个。
你看不到的 thoughts。 计费基于完整 thought tokens,尽管只返回摘要,而你自己的任何账本都无法审计那个数字。
有 200K token 不等于会用它们
链接到此部分:有 200K token 不等于会用它们最后用一个警告收尾,因为这是自然会想到的下一步,而答案不是显而易见的那个。
一百万 token window 不代表一百万可用 token。检索准确率会随位置下降:Liu 等人发现,模型在长输入的开头和结尾能可靠定位信息,而在中间位置可靠性低得多。11 更大的 window 买到的是发送更多内容的能力,不是内容一定会被读到的确定性。
这个现象在本课程中测量过一次——同一个 853-token prompt 中 9 个位置的 retrieval rate——它属于第 24 章,因为它会改变 agent 的行为。这里引用它,是因为它会改变你应该购买什么:最便宜的 token,是你没有发送的那个。
接下来去哪
链接到此部分:接下来去哪你现在可以在发起调用前预测它会花多少钱,在调用后读懂它实际花了多少钱,并区分两者。这覆盖了请求中的一切,除了你还没碰过的部分:旋钮。
第 17 章讲 sampling——temperature、top-p、top-k、penalties,以及你并不拥有的 determinism。它从拆解领域中最普遍的错误开始:以为 temperature 是 creativity 旋钮。它不是:temperature 会在 第 4 章的 softmax 之前除以 logits;提高它不会让模型更有想象力,而是提高模型自己评分更差的 tokens 的概率。接着会讲为什么 greedy decoding 生成的文本可测量地比 sampling 更差,为什么 top-k 和 top-p 会在相反形状的分布上失败,以及本章末尾的实验:temperature 0 下 20 次相同 forward pass,如果模型单独运行,会逐 bit 完全一致;但把同一个 prompt 与别人的请求一起放进 batch,会移动它 97% 的 logits。
它们并不全都匹配。原因要从第 2 章的 floating-point 框开始。
来源和方法
链接到此部分:来源和方法本章所有价格、阈值和倍数都在 2026 年 9 月 6 日 从 provider 自己的页面读取,并注明这个日期,因为它们会改变。方法比数字更重要:bucket、前缀规则和 tier 算术这两年一直稳定,而其中每个具体数字都变过。
Stanford CS336 第 2 讲 Resource accounting 是最接近本文材料的学术处理,也是合适的下一篇阅读:它在训练侧做了与本章在 inference 侧相同的算术。这里的 token counts 使用 js-tiktoken 1.0.21 生成,采用 o200k_base 和 cl100k_base encodings,基于一段 40 轮、5,090 token 的对话;每条 message 的 template overhead 使用常规的 4 加 3 近似,并在包含时明确说明。cache、tier 和 truncation 数字,是把文档化定价规则应用到这些测得的 token counts 上,而不是实时 API 响应的观测结果——本章没有发起付费调用,这也是真实解释为什么延迟主张是定性的,而成本主张不是。
参考资料
链接到此部分:参考资料-
Dao, T., Fu, D. Y., Ermon, S., Rudra, A. and Ré, C. FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. arXiv:2205.14135 (2022)。解释为什么天花板移动了,但渐近成本没有改变。 ↩
-
Chen, S., Wong, S., Chen, L. and Tian, Y. Extending Context Window of Large Language Models via Positional Interpolation. arXiv:2306.15595 (2023)。 ↩
-
Peng, B., Quesnelle, J., Fan, H. and Shippole, E. YaRN: Efficient Context Window Extension of Large Language Models. arXiv:2309.00071 (2023)。 ↩
-
Google,Thinking,
ai.google.dev/gemini-api/docs/thinking,以及 Token counting,ai.google.dev/gemini-api/docs/tokens,访问日期均为 2026-09-06。“Pricing is based on the full thought tokens the model needs to generate, despite only the summary being output from the API.” usage object 报告total_input_tokens、total_output_tokens、total_thought_tokens、total_cached_tokens、total_tool_use_tokens和total_tokens——6 个 bucket,其中 thoughts 和 tool use 位于输出计数之外。同一数量的早期字段名仍由 generateContent surface 返回,即thoughtsTokenCount,记录在第三个页面ai.google.dev/gemini-api/docs/generate-content/thinking。 ↩ -
Anthropic,Prompt caching,
docs.anthropic.com/en/docs/build-with-claude/prompt-caching,访问日期 2026-09-06。来源包括tools→system→messages的失效层级及其表格;各模型最小可缓存长度;恒等式total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens;以及默认 5 分钟生命周期、每次命中免费刷新。 ↩ ↩2 -
OpenAI,Prompt caching,
platform.openai.com/docs/guides/prompt-caching,访问日期 2026-09-06。来源包括:完整渲染前缀规则;最小可缓存前缀(GPT-5.6 及之后为 1,024 个可见输入 token,较早为 2,048);1.25× 写入和 0.1× 读取倍数,以及 GPT-5.5 及之前没有任何写入费用;30 分钟生命周期;每请求 4 次写入和 50 个 breakpoint 限制;机器亲和性说明和prompt_cache_key;1.35×、2.15× 和 10× 的 break-even 示例;以及 summarisation、compaction 或 truncation 会重置 cache 复用的说明。 ↩ ↩2 -
OpenAI,Pricing(
platform.openai.com/docs/pricing)以及gpt-5.6-terra的模型页面,访问日期均为 2026-09-06。gpt-5.6-terra,standard service tier,每百万 token:输入 $2.00,cached input $0.20,cache writes $2.50,输出 $12.00;long context 输入 $4.00,cached $0.40,writes $5.00,输出 $18.00;“prompts with >272K input tokens are priced at 2x input and 1.5x output for the full request”;context window 1,050,000 token,最大输入 922,000 token。同一表格列出gpt-6-astra为 $10.00/$1.00/$12.50/$50.00,gpt-5.6-luna为 $0.20/$0.02/$0.25/$1.20。本章所有示例成本都使用gpt-5.6-terra的 standard short-context rates。 ↩ -
Google,Gemini Developer API pricing,
ai.google.dev/gemini-api/docs/pricing,访问日期 2026-09-06。Gemini 2.5 Pro,每百万 token:200K 以内 prompt 输入 $1.25,超过后 $2.50;输出 $10.00 和 $15.00,两者都标注“including thinking tokens”;context caching $0.125 和 $0.25,另有每百万 token 每小时 $4.50 的存储费用。Gemini 3.1 Pro Preview 使用相同 200K 阈值,输入 $2.00/$4.00,输出 $12.00/$18.00。 ↩ -
Anthropic,Pricing,
docs.anthropic.com/en/docs/about-claude/pricing,访问日期 2026-09-06。每百万 token,base input / 5-minute cache write / 1-hour cache write / cache read / output:Claude Sonnet 4.5 $3 / $3.75 / $6 / $0.30 / $15;Claude Haiku 4.5 $1 / $1.25 / $2 / $0.10 / $5;Claude Opus 5 $5 / $6.25 / $10 / $0.50 / $25。倍数:5 分钟写入 1.25×,1 小时写入 2×,读取 0.1×。也是 long-context 说明(“Claude 4.6 and later models... include the full 1M token context window at standard pricing”)、tool-use system prompt token 数(Claude Sonnet 4.5 上,tool_choice为auto或none时为 496 token,使用any或命名 tool 时为 588)以及 Claude 4.7 及之后使用新 tokenizer、对同一文本产生“approximately 30 % more tokens”的说明来源。 ↩ ↩2 ↩3 -
Anthropic,Token counting,
docs.anthropic.com/en/docs/build-with-claude/token-counting,访问日期 2026-09-06。/v1/messages/count_tokensendpoint 接收与 message 相同的输入并返回输入 token 数;文档说明该计数是估计值,可能包含 Anthropic 为 system optimisations 添加的 token,且这些不会计费。 ↩ -
Liu, N. F., Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F. and Liang, P. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172 (2023)。在这里引用,在第 24 章测量。 ↩