Claude Haiku 5.5长上下文计费陷阱:Node守卫防5倍账单

Claude Haiku 5.5 超 100K token 即按 5 倍价计费,需在请求前计数剪枝并如实记录计费档位。
文章揭示 Claude Haiku 5.5 阶梯计费的陷阱:prompt 一旦超过 100,000 token,输入与输出单价同时涨到 5 倍,且缓存命中无法绕过阈值,因为档位按请求总 token 数判定。作者给出 TypeScript 守卫方案:用 countTokens 在发请求前计数,把目标设为 97K 留出估算余量;利用 token 数随 chunk 单调增长的特性,用二分搜索保留排名最靠前的最大可容纳前缀,超预算时记录丢弃数量。日志层面强调实际计费 prompt 是 cache_read、cache_creation 与 input_tokens 三项之和,只看 input_tokens 会严重低估并误判档位。最后主张剪枝比换模型更划算,并用 Vitest fixture 在 CI 中拦截成本漂移。
为什么99K和101K的Prompt,账单差5倍
你的代码里,99,000 token和101,000 token的prompt看起来一模一样。但在Claude Haiku 5.5上,后者的成本大约是前者的五倍。
据Anthropic官方定价页面,Haiku 5.5对100,000 token以内的prompt按每百万输入token $0.10、每百万输出token $0.50计费;一旦超过这条线,输入涨到$0.50、输出涨到$2.50。注意,输出也按高价档计费。
算一笔账:一个请求输出1,000 token,99K prompt的成本是$0.0104,101K prompt的成本是$0.0530。prompt只多了2%,成本变成5.1倍。按每天10万次请求(不考虑缓存和批量折扣)估算,日账单从约$1,040跳到$5,300。

两个容易被忽略的计费细节
Anthropic的长上下文定价文档里藏着两个关键点:
- 「请求的prompt长度计算所有输入token,包括缓存读取和缓存写入。」
- 「超过阈值的请求即使部分prompt命中缓存,也按高价计费。」
这意味着一个90,000 token的缓存系统前缀加上15,000 token的新内容,算作105,000 token的prompt,缓存读取单价也从$0.01/百万涨到$0.05/百万。Prompt缓存不能帮你绕过这条线,只有更短的prompt可以。
阈值按单次请求计算,「up to 100,000」意味着恰好100,000 token仍算基础档。守卫逻辑把超过100_000视为长档。
Prompt caching(提示缓存) 是 Anthropic API 的一项机制:对重复出现的长前缀(如系统提示、文档库),服务端可缓存其计算结果,后续请求命中时按更低的"缓存读取"费率计费,而非完整重算。正常情况下这能显著省钱,但缓存计价有独立费率表:基础档的缓存读取约为 $0.01/百万 token,长档则涨到 $0.05/百万,缓存写入费率同样分档且通常高于普通输入。关键在于计费分档看的是 prompt 的总 token 数,而非"未命中缓存的增量",因此一个大缓存前缀加少量新内容,仍会被整体判入长档。这也解释了为什么本文强调缓存只能降单价、不能改变档位归属,唯一有效的规避手段仍是缩短 prompt 本身。
发请求前先数token:TypeScript守卫的核心设计
Anthropic的TypeScript SDK把token计数端点暴露为client.messages.countTokens。它接收与Messages API相同的model、system、messages和tools,返回{ input_tokens }。三个事实决定了守卫的写法:
- 免费但有独立限流:Start档每分钟5,000次请求,不计入消息创建配额。
- 计数是估算值:官方文档承认「创建消息时实际使用的输入token数可能略有差异」。所以守卫的目标应设为97,000而非100,000,留出余量。
- Haiku 5.5换了新分词器:同样的文本比Haiku 4.5多出约30%的token。你在旧模型上测得的计数已经作废,必须重新测量。
守卫把请求构建集中在一个buildParams函数里,确保「你数的」就是「你发的」:
// src/guard.ts
export const MODEL = "claude-haiku-5-5";
export const TIER_LINE = 100_000; // over this = long tier
export const BUDGET = 97_000; // headroom: counts are estimates
export type Chunk = { id: string; score: number; text: string };
export type Params = {
model: string;
system: string;
messages: Anthropic.MessageParam[];
};
export type Counter = (p: Params) => Promise<number>;
Counter被设计成普通函数类型是刻意为之。生产环境传入apiCounter(client),单元测试传入假实现,这样剪枝逻辑无需网络和密钥即可运行。Params故意不含max_tokens,它属于创建调用而非计数调用。
分词器(tokenizer) 是把原始文本切成 token 的组件,不同模型可能使用不同版本。token 并非固定等于若干字符:英文约每 token 3-4 个字符,中文、代码和特殊符号往往更"耗" token。当厂商升级分词器时,同一段文本的 token 数会变化,这正是文中"Haiku 5.5 比 4.5 多约 30%"的根源。token 数直接决定计费档位与上下文窗口占用,所以任何在旧模型上得到的计数都不能跨模型复用。工程上,把文本长度换算成 token 只能靠近似比例(如 1M token ≈ 2.5M Unicode 字符),且这是平均值而非上界,只适合用来跳过明显低于阈值的请求;要精确判断是否跨档,必须调用官方的 countTokens 端点实测。
按排名剪枝:二分搜索找到最大可容纳前缀
检索器返回的chunk已经按相关性排好序。当全部chunk超出预算时,守卫保留排名最靠前的、能装下的最长前缀。由于token数随chunk增加而单调增长,二分搜索只需约log2(n)次计数,而不是每丢一个chunk数一次。
// ranked: best chunk first
export async function fitToBudget(
count: Counter,
q: string,
ranked: Chunk[],
budget = BUDGET,
): Promise<Fit> {
const all = await count(buildParams(q, ranked));
if (all <= budget) {
return { chunks: ranked, tokens: all, dropped: 0 };
}
// invariant: lo chunks fit, hi chunks do not
let lo = 0;
let loTokens = await count(buildParams(q, []));
if (loTokens > budget) {
throw new OverBudget(`${loTokens} tokens with no docs`);
}
let hi = ranked.length;
while (hi - lo > 1) {
const mid = Math.floor((lo + hi) / 2);
const t = await count(buildParams(q, ranked.slice(0, mid)));
if (t <= budget) {
lo = mid;
loTokens = t;
} else {
hi = mid;
}
}
return { chunks: ranked.slice(0, lo), tokens: loTokens, dropped: ranked.length - lo };
}
顺利路径下每个请求只多一次计数。超预算且有40个chunk时,是两次计数加约六次搜索计数。每次计数都是一次往返,会带来延迟。如果延迟敏感,可以对明显很小的prompt跳过计数——按官方模型概览,在该分词器下1M token约等于2.5M Unicode字符。但这个比例是平均值而非上界,只能用来跳过远低于阈值的prompt。
有两个边界情况需要在调用方处理。OverBudget意味着系统提示加问题本身就超了,这是上游bug,剪chunk救不了。如果排名第一的chunk本身巨大,lo可能归零、上下文为空,此时要检查fit.chunks.length再决定是否回答。
二分搜索(binary search) 适用于在单调序列上定位分界点:若前 k 个 chunk 能装进预算,则前 k-1 个必然也能装下,token 数随 chunk 数量单调不减,因此"能容纳的最大前缀长度"构成一个清晰的二分边界。算法维护 lo(确定可装下)与 hi(确定装不下)两个边界,每次取中点计数并收缩区间,直到 hi − lo = 1,此时 lo 即为答案,复杂度为 O(log n)。相比从末尾逐个丢弃再计数的线性做法,40 个 chunk 时二分只需约 6 次计数,把每次计数都要往返网络的延迟降到可接受水平。前提是单调性成立——即追加 chunk 不会让 token 数减少,这在纯拼接 prompt 的场景下天然满足。
记录API实际计费的档位,而不只是自己数的
计数是估算,响应里的usage才是你真正付的钱,所以两者都要记。这里有个陷阱:input_tokens只包含「最后一个缓存断点之后」的token,总数是cache_read_input_tokens + cache_creation_input_tokens + input_tokens。只看input_tokens的仪表盘,可能对一个按长档计费的请求显示15,000,让你误判。
billedPrompt函数把三项加总,档位判断用它而非input_tokens。ask函数运行守卫、发送剪枝后的请求,并按返回数据计价,日志输出counted(本地计数)、billed_prompt(实际计费prompt)、tier(档位)、dropped(丢弃chunk数)和est_usd(估算美元)。
est_usd把所有prompt token按未缓存费率计价,所以读缓存时会高估、写缓存时会轻微低估(缓存写入费率高于基础输入)。它的用途是在图表上捕捉档位跳变,账单核对交给计费系统。告警盯住两件事:任何tier: "long"的日志行,以及持续上升的dropped数——后者说明检索取回的内容超过了预算能装下的量。
usage 字段与缓存断点 需要区分清楚:响应中的 usage 会分别返回 input_tokens、cache_creation_input_tokens 和 cache_read_input_tokens。当请求带有 cache_control 标记时,其之前的 prompt 前缀被缓存,之后的 token 才计入 input_tokens。因此单看 input_tokens 只反映了断点后的新增部分,完整 prompt 长度必须把三项相加。这也意味着缓存命中并不减少 prompt 的"总长度",只改变各段的单价。在做成本监控和档位判断时,务必用三项之和(本文的 billedPrompt)而非 input_tokens,否则仪表盘会系统性低估真实用量,对恰好跨过 100K 线的请求尤其容易误判为低价档。
为什么剪枝比换模型更划算
显而易见的替代方案是把长prompt发给别的模型。但仅从价格看,这不解决问题。Haiku 5.5长档的$0.50/$2.50仍低于Haiku 4.5的$1/$5和Sonnet 5.5的$2/$10。即便算上Haiku 4.5旧分词器让同样文本短约23%,一个101,000 token的Haiku 5.5 prompt在那边约78,000 token,输入成本约$0.078,仍高于$0.0505。需要更强模型时才换模型,只是长度问题时继续用便宜的那个。
当剪枝会损害回答质量时(比如必须读完整篇文档的摘要任务),有三条路:把输入拆成低于阈值的块分别摘要再合并(请求更多,但每次都按基础档);用Batch API,长档价格减半到$0.25/$1.25(前提是任务可以等);或者有意接受长档并让日志如实记录。
用Vitest把预算检查写进CI
运行时剪枝按请求捕捉漂移,CI检查则在引发问题的那个PR里就拦住它。保留一份「配置能产生的最坏prompt」fixture:你接受的最长问题,加上topK个最大尺寸的chunk,从真实语料中抽取。然后计数并断言不超过BUDGET。
测试里用test.runIf(hasKey)让没有密钥的本地环境跳过网络测试。同时补一个前置测试:在CI环境下断言ANTHROPIC_API_KEY存在,因为GitHub Actions总会设置CI=true,这样缺失secret的流水线会直接失败,而不是静默跳过检查。计数调用免费,每个PR跑一次只消耗限流余量。
剪枝逻辑本身用假计数器做纯单元测试,约四字符一token,不碰网络。当预算测试变红时,那个提高topK或chunk尺寸的PR必须解释:为什么多出来的上下文值得五倍的价钱。有时候确实值得,那就由reviewer在发布前签字确认成本。
相关推荐

一句提示词引发的全球PHP安全风暴:AI是救星还是隐患?
一句AI提示词竟引发全球PHP安全危机,维基百科、互联网档案馆受波及。本文梳理事件脉络,解析OpenAI与亚马逊的争议,探讨AI时代漏洞发现门槛降低带来的安全悖论与开发者应对策略。

当AI被拿走:依赖捷径如何削弱你的解题韧性
一项涉及1,222人的研究发现:使用AI答题后,一旦AI被移除,人们放弃解题的比例高于从未使用AI的对照组。文章分析AI依赖对学习韧性和元认知的影响,并探讨AI学习工具的设计启示。

Carta联手NewView:让创业公司员工提前套现股权
Carta 与 NewView Capital 合作推出新计划,简化创业公司 tender offer 流程,帮助员工提前出售股权套现。本文分析该合作的核心机制、员工股权流动性痛点及对创业公司股权激励的影响。