LLM Fundamentals 与技术底座
tokens、context window、非确定性,与那四个被点名的 model options
这是全书第二重的一章,但它考的不是「你会不会背参数」。blueprint 把 tokens、context windows、sampling、non-determinism、next-token generation 与四个 model options 压进 TS 5.1,把「支撑 AI 应用开发的基础技术概念、与 wrap REST API 的 SDK 集成、websockets」压进 TS 5.2。两条 skill 合起来回答同一个问题:这套系统在哪些地方不像你熟悉的确定性软件,你的集成层该为此做什么准备。本章从「一次生成是怎么发生的」推起,一路推到「同样的 prompt 为什么两次不一样」,再推到四个 option 各自动的是哪个旋钮。
fast mode、extended thinking、adaptive thinking、effort levels 这四个是 guide 点了名但没给行为定义的机制。本章写到的每一条行为都先查了官方文档并登记(reference/verified-behaviors.md),逐条挂 vb:。查不到行为的地方,本章只说「guide 点了这个名字」,不替它编语义——这正是 ch01 P5「机制按官方职责用」对本书自己的要求。一、一次生成是怎么发生的:从 next-token 到「两次不一样」
先把最底下那层讲清楚,因为本章后面所有判断都从它长出来。
Claude 底层是一个 autoregressive language model:预训练的目标是「给定文档中此前的上下文,预测下一个词」。官方同时点破了一件常被忽略的事——这样预训练出来的模型本身并不擅长回答问题或遵循指令,要靠 fine-tuning 与 RLHF 才变成一个可用的助手。工程含义:模型的「服从性」是被训练出来的一种倾向,不是一条被执行的规则。这句话你在 ch01 P3 那里已经见过一次,这里是它在模型层的根。
生成的单位不是字符,是 token。官方定义:token 是语言模型的最小单位,可以对应词、子词、字符甚至字节;一个 token 对应多少字符随模型与语言而变——「约 3.5 个英文字符 / token」是官方对 4.7 之前模型的说法,Claude 4.7 及之后的模型换了更新的 tokenizer,同一段文本约多 30% token。所以 3.5 只能当旧模型的量级参考,要精确数字就按目标模型调 token counting。模型每一步从一个概率分布里挑下一个 token,挑完把它接回输入,再挑下一个——「一次回应」就是这个循环跑了很多遍。
既然每一步都是从分布里挑,就有一个控制挑法的旋钮:temperature 控制生成时预测的随机性,高温更有创造性与多样性,低温更保守、更确定、倾向最可能的措辞。到这里为止都是教科书。下面这条不是。
temperature = 0 也不给你确定性
Non-determinism is a property of the system, not a bug in your code
官方原话:使用者可能在 API 上遇到 non-determinism;即使 temperature 设为 0,结果也不会完全确定,相同输入在多次 API 调用之间可能产生不同输出。这一点对 Anthropic 第一方推理服务与第三方云厂商推理同样成立。
为什么这条值得单独立一张卡 —— 它是本章撑起后面很多判断的地基。任何以「同样的 prompt 跑两次结果不同 = 我的代码有 bug / 缓存坏了 / 模型换了」为前提的排查路线,起点就错了。正确的动作是先问「这个差异是不是超出了非确定性能解释的范围」——比如它是不是系统性的(某一天起集体变化 → 走 ch01 P8 的版本轴),还是逐次抖动的(那就是这条)。
边界 — 非确定性不是「怎样都行」的挡箭牌。输出格式不稳定该修的是 schema 与校验(ch01 P7,见 ch10);这条只解释「为什么两次不会逐字相同」,不解释「为什么这次少了一个必填字段」。
口诀:逐次抖动是性质,集体漂移才是事故。
还有一层反转:在最新的模型上,采样旋钮已经不归你转了。temperature、top_p、top_k 在 Claude 4.7 及之后的模型上已 deprecated——设成非默认值返回 400;官方给的替代是「省略它们,用 prompting 引导模型行为」。SDK 层还有一个更硬的表现:Python SDK 从 v1.0 起直接把这三个参数移除了,传进去抛 TypeError。
vb-nondeterminism-o81),二是它现在会直接 400(vb-sampling-deprecated-o82)。这是本章最容易出成陷阱的一处。二、token 是计量单位:数不准,成本与容量都会算错
token 同时是三样东西的计量单位:context window 的容量、计费的基准、rate limit 的口径。所以「数得准不准」不是细节。
官方提供了一个专门的端点来数:token counting 端点接受与创建 message 相同的结构化输入(含 system、tools、images、PDFs),响应给出输入 token 总数。三条性质要记住:
- 它给的是 estimate——官方明说实际创建 message 时用掉的输入 token 数可能有小幅差异。所以拿它做「卡在上限边缘」的准入判断要留余量。
- 它免费,但受按 usage tier 的 RPM 限制;而且它与 message creation 的限额相互独立,用其中一个不会消耗另一个的额度。
- 它按你传入的
model的 tokenizer 计数——这一点在换模型时会咬人。
为什么会咬人:Claude 4.7 及之后的模型使用更新的 tokenizer,同一段输入文本产生的 token 数比更早的模型约多 30%,具体增幅依内容与负载形态而定。官方给的动作很直接:按你准备使用的那个模型重新计数,不要复用在更早模型上测得的计数。迁移场景下这条同时意味着成本估算与 context 容量估算都要重做——同一份 prompt 没改一个字,账单和占用都变了。模型选型与价格的完整讨论在 ch07。
三、context window:它是 working memory,不是仓库
官方定义:context window 指模型生成回应时能够引用的全部文本,包括回应本身;它不同于模型训练用的语料,而是模型的一块 working memory。紧跟着是一句本章最该抄进笔记的话——更大的 context window 让模型能处理更复杂更长的 prompt,但更多 context 并不自动更好:随着 token 数增长,准确率与召回会退化,这个现象叫 context rot。
这正是 ch01 P9「上下文是预算,不是仓库」的官方依据。塞进去要付两笔账:token 的钱,和注意力的稀释——第二笔在这里有了名字。
那么什么算进这个预算?官方的清单是「请求里的一切」:system prompt、messages 里的每一条消息(含 tool result、图片、文档)、以及你的 tool 定义。本轮生成的输出、包括它的 extended thinking,也算。开了缓存时输入计数被拆成 input_tokens / cache_read_input_tokens / cache_creation_input_tokens 三个字段,三者都计入窗口。
再补一条最容易想反的:被缓存的 prompt 前缀仍然占用 context window——prompt caching 改变的是这些 token 的计费方式,不是它们算不算数。「开了缓存所以窗口更宽裕了」是错的。
| 问题 | 官方答案 |
|---|---|
| 历史是怎么增长的 | 每条 user 消息与 assistant 回应都在窗口里累积,此前各轮被完整保留 |
| 每轮输入包含什么 | 全部此前历史 + 当前 user 消息;本轮输出成为下一轮输入的一部分 |
| 输入本身就超窗 | 所有模型返回 400 invalid_request_error("prompt is too long") |
输入 + max_tokens 超窗 | Claude 4.5 及更新模型上请求被接受;生成触到上限时以 stop_reason: "model_context_window_exceeded" 停止 |
最后一行是集成层的一个真实分叉:同样是「太长了」,一个是请求被拒(4xx,你的重试逻辑会看到异常),一个是请求成功但输出被截断(2xx,你的解析器会看到半截 JSON)。后者要靠读 stop_reason 才发现,这与 ch02 讲的「先读 stop_reason 再读 content」是同一条纪律。
四、model option 之一:thinking 的两种模式
guide 在同一行里点了 extended thinking 与 adaptive thinking 两个名字。它们不是同义词,也不是新旧两个说法——是两种配置形态,行为差异比语法差异大得多。
adaptive thinking:由模型决定要不要想、想多深
在 Claude Opus 5.5、Opus 5、Sonnet 5.5、Sonnet 5、Fable 5.1、Mythos 5.1、Fable 5、Mythos 5、Mythos Preview 与 Haiku 5.5 上,thinking 已经开着,无需配置;在 Claude Opus 4.8 / 4.7 / 4.6 与 Sonnet 4.6 上,thinking 关着,要设 thinking: {type: "adaptive"} 才开,之后由 Claude 自行决定何时思考、思考多深。
配置上的 display 字段控制思考内容是否被返回:"summarized" 返回摘要,"omitted"(许多型号上的默认值,包括上面 thinking 默认开着的那一批)返回 thinking 字段为空的 thinking block。两种情况下计费相同、多轮回传方式也相同——所以「我没看到 thinking 文本,说明它没思考、没花钱」是错的。thinking token 按 output token 计费,即使文本没返回给你也计费,并与回应文本一起占 max_tokens。
extended thinking(manual 模式):由你给一个预算
manual 模式用 thinking: {type: "enabled", budget_tokens: N} 给每个请求设一个思考 token 预算,Claude 在开始最终回答前对着这个预算思考。官方给它保留的适用场景很具体:当负载要求可预测的延迟、或对思考成本的精确控制时,manual 模式仍然有用。
预算有三条硬规则:至少 1,024(更小的值被 API 拒绝);必须小于 max_tokens(interleaved thinking 是唯一例外);而且——这条最容易记反——它是目标而不是硬上限:实际用量随任务变化,Claude 可能远未用完就停止推理,max_tokens 才是总输出的硬天花板。
thinking.type: "enabled" 配 budget_tokens 在 Claude 4.6 各模型上已 deprecated(请求仍然成功);Claude 4.7 及之后的模型不支持它,会以 400 拒绝。反过来,在 Claude 4.5 及更早支持 thinking 的模型上,extended thinking 是唯一可用的 thinking 模式——Claude Haiku 4.5 就属于这一类,对它传 type: "adaptive" 返回 400。两个方向的 400 都存在,方向取决于模型代次。迁移时最该防的不是语法:官方说映射很小——去掉 budget_tokens、设 thinking: {type: "adaptive"}、改用 output_config: {effort: ...} 控制推理深度。但紧接着提醒要预期的是行为差异而不只是语法差异:固定预算下 Claude 每个请求都思考;adaptive 下由 Claude 自行决定要不要思考、思考多少,低 effort 时在简单输入上可能完全跳过思考。一个按「每次都会有 thinking block」写死的下游解析器,会在迁移后的某个简单请求上直接崩掉。
五、model option 之二:effort levels
官方定义:effort 参数让你控制 Claude 回应请求时花多少 token,在「回应的周密程度」与「token 效率」之间用同一个模型做取舍;顶层 effort 参数在所有受支持模型上都可用,无需 beta header(per-message effort 另算,仍是 beta,见下文)。
| 档位 | 官方描述 |
|---|---|
max | 无 token 约束的最高能力 |
xhigh | 面向长时程工作的扩展能力 |
high | 除 Claude Opus 5.5 与 Claude Haiku 5.5 外,所有支持 effort 的模型的默认档 |
medium | 有节制的平衡档;Claude Opus 5.5 与 Claude Haiku 5.5 的默认档 |
low | 最省;显著节省 token,能力有所下降 |
默认档随模型而定:官方原话是把 effort 设成该模型的默认档(Opus 5.5 与 Haiku 5.5 上是 "medium",其他模型是 "high")与完全不设这个参数行为相同。所以「不设 effort 就等于 high」只在 Opus 5.5 与 Haiku 5.5 以外成立,迁移到这两个型号时要重新核对默认工作量。
三条性质决定了它在题目里怎么被考。
其一,它管的是响应里的所有 token。官方列的清单是:文本回应与解释、tool call 与函数参数、以及 thinking(当 thinking 在起作用时)。由此得到两个官方点名的结论——因为它作用于每一个 output token,无论是否开启 thinking 都起作用;以及它影响包括 tool call 在内的全部 token 支出,「更低的 effort 也意味着更少、更简短的 tool call」。所以在 agent 场景里,调 effort 改变的不只是话多话少,是行动的密度。
其二,它是软引导,不是预算。官方原话是 Effort is a behavioral signal, not a strict token budget:低 effort 下 Claude 遇到足够难的问题仍然会思考,只是在同一问题上比高 effort 时想得少。硬天花板不在 effort 这条轴上——官方对 max_tokens 的定性是它 remains the hard ceiling on total output。题干说「必须保证单次调用不超过 N token」时,答案永远不是调 effort。
其三,顶层 effort 是请求级设置,改它会炸缓存。顶层 output_config.effort 作用于整个请求;因为它会被渲染进 prompt,请求之间改它不保留此前轮次的缓存前缀。官方给的操作建议是:依赖长会话缓存命中、而模型又不支持 per-message effort 时,在开头选定一档并保持不变。在 Claude Fable 5.1、Mythos 5.1、Opus 5.5、Opus 5、Sonnet 5.5 与 Haiku 5.5 上(Claude API / Google Cloud;Amazon Bedrock 上的支持范围更窄,以官方列表为准)还有第二条路:per-message effort(beta,需 mid-conversation-output-config-2026-07-01 header)在对话中途换档且保留 prompt cache;其他模型只能在下一个请求改顶层值,缓存从头开始。同一条机制也适用于 thinking 配置——thinking 配置与解析后的 effort 档位都被渲染进 prompt 本身,在 adaptive / enabled / disabled 之间切换、改 budget_tokens、在请求间改顶层 effort 值,都会让此前的缓存前缀失效;per-message effort 是例外——在支持它的型号上,用 messages 里的 role: "system" 消息换档,已缓存的前缀保持不变。
thinking 管「Claude 回答前要不要在 thinking block 里思考」,effort 管「Claude 在整个回应上投入多少工作量」(adaptive 模式下包括思考的频率与深度)。官方专门写了一句警告:不要把 adaptive 当成 effort 值传——adaptive 是一种 thinking mode,不是 effort 档位。这句话本身就是一道现成的干扰项模板。六、model option 之三:fast mode
fast mode 在 premium pricing 下为受支持的 Claude Opus 模型提供最高 2.5 倍的每秒输出 token;开启方式是在请求上设 speed: "fast"。准入条件分型号:Opus 5.5 上不再需要 fast-mode-2026-02-01 beta header 或申请权限(仍带 header 的请求照常可用);Opus 5 与 Opus 4.8 上它仍是 research preview,要申请权限并带这个 beta header。
这个 option 最重要的一条是「它不改变什么」:fast mode 跑的是同一个模型、同一份权重,只是换了更快的推理配置,智能与能力没有变化;而且提速集中在 output tokens per second(OTPS),不在 time to first token(TTFT)。两句话各自砍掉一整类干扰项:想提质量的不该来这儿,想压首字延迟的也不该来这儿。
| 维度 | 官方说明 |
|---|---|
| 支持的模型 | Claude Opus 5.5、Claude Opus 5 与 Claude Opus 4.8 |
| 可用平台 | 三个型号都可在 Claude API(含 Claude Managed Agents)上用;Opus 5.5 另可用于 Azure 托管的 Claude in Microsoft Foundry 部署,Anthropic 托管的 Foundry 部署不可用 |
| 怎么确认真的用上了 | 响应 usage.speed 取值 "fast" 或 "standard" |
| 对不支持的模型请求 fast | 返回错误;超出 fast mode 限额或容量也会(429 或 529)。但官方点名了一个例外:claude-opus-4-6 带 speed: "fast" 与 beta header 的请求不报错,却按标准速度执行,usage.speed 报 "standard"——所以「没报错」不等于「用上了 fast」,要看 usage.speed |
| 与缓存 | 在 fast 与 standard 之间切换让 prompt cache 失效,不同 speed 不共享缓存前缀 |
| 不能同用的 | Batch API、Priority Tier commitment |
把三个 option 并排看,判断轴就出来了:要「想得更周到」调 effort;要「明确控制思考成本与延迟」用 manual 的 budget_tokens(在还支持它的模型上);要「同样的答案更快吐完」用 fast mode。三者动的是三个不同的变量,任何把它们当同义替换的选项都可以先按 ch01 D2 无关杠杆 处理。
七、TS 5.2 的技术底座:SDK 与它包着的那个 REST API
blueprint 在 TS 5.2 里写的是「与 wrap REST API 的 SDK 集成」——这个措辞本身就是考点:SDK 不是另一个产品,它是 HTTP 之上的一层。
官方 client SDK 是面向 Messages API 的通用客户端,覆盖 Python、TypeScript、C#、Go、Java、PHP、Ruby 七种语言;每个 SDK 提供符合该语言习惯的接口、类型安全,以及对 streaming、retries、error handling 的内建支持。文档另外把「libraries and integrations」单列出来并划清了界线:它们把 Claude 暴露在别的框架的 API 表面上,不是通用的 Messages API 客户端。
还有一条边界要记:CLI、client SDK 与 libraries 是「你自己调 Claude API」的工具——你发每一个请求、处理每一个响应;Claude Code、Claude Agent SDK 与 Claude Managed Agents 工作在更高的层级,提供 agent loop、工具执行与运行时。这条线在 ch08 讲 agent 架构时会再用一次。
用 SDK 换来的具体便利里,有两件事值得单独点名,因为它们是「不用 SDK 就得自己写对」的部分:每次 API 请求都必须带 anthropic-version 请求头(例如 anthropic-version: 2023-06-01),用官方 client SDK 时它被自动处理;以及重试——ch02 讲过长请求与流式的取舍,SDK 的内建 retry 就落在那条线上。
八、websockets:blueprint 点了名,但要点清楚它点的是什么
TS 5.2 的措辞里出现了 websockets。这里必须把两件事分开说,否则很容易学错。
第一,Claude 的流式传输机制官方写得很明确,是 SSE(server-sent events)而不是 websocket。事件骨架与集成写法由 ch02 第四节主讲,本章不重复;这里只需要这一句定性。本书没有查证到 Claude API 上的 websocket 端点,因此不写它有;正文与题目里凡出现「连一条 websocket 到 Claude API」的说法,都是本书自制的干扰项。
第二,websockets 作为技术底座概念仍然要会。它属于 SKELETON 说的「ch06 的非 Claude 部分」——工程共识级、可教科书回溯的内容,不需要 vb 登记,但也不得与 guide 冲突。两句话讲完差别:
| Server-Sent Events (SSE) | WebSocket | |
|---|---|---|
| 方向 | 单向:服务器 → 客户端 | 全双工 |
| 承载 | 普通 HTTP 响应,一条长连接上的文本事件流 | HTTP Upgrade 后切到独立的帧协议 |
| 适合 | 一次请求、增量返回一段结果(生成式回应正是这个形状) | 双方都要随时说话(协同编辑、实时游戏、双向语音) |
| 代价 | 沿用现有 HTTP 基建(代理、鉴权、重试) | 需要额外处理连接生命周期、心跳、重连与鉴权 |
把这张表接回本章的主线:一次 LLM 调用天然是「我说一句、你增量地回一段」,不是「双方随时插话」——这就是为什么生成式 API 普遍落在 SSE 那一列。你的应用自己的前后端之间要不要用 websocket,是另一层的架构决定,与调 Claude 用什么传输无关。
九、基础 prompting:zero-shot / single-shot / multi-shot
guide 在 TS 5.1 末尾点了这三个词。它们是通用 ML 术语,区别只在你在 prompt 里放了几个示范:零个(zero-shot,只给指令)、一个(single-shot / one-shot)、多个(multi-shot / few-shot)。
本章只讲这条谱系与它的代价:示例是要占 context 的(第三节),而且在按 token 计费(第二节)。所以往右走一格从来不是免费的准确率——它是拿预算换稳定性,而每加一个示例都会把每一轮请求的输入抬高一截。示例该怎么挑、怎么包、放几个(relevant/diverse/structured 与 3–5 个的建议量)由 ch10 主讲,本章不重复。
xhigh 与 max 各自在哪些模型上可用、各模型推荐哪一档,官方文档写得很细,但那是模型选型的面(TS 5.3),归 ch07;本章只讲档位语义。其二,compaction、context editing、tool result pruning 这些「窗口不够了怎么办」的手段属 TS 6.1 的 context engineering,归 ch11;本章只讲窗口本身的性质。fine-tuning 与 RAG 在官方 glossary 里有词条,但 blueprint 未覆盖(RAG/embedding 是 CCAR-P 的 blueprint 项),本书按不考处理。
budget_tokens 是目标不是上限、effort 五档与「设成该型号默认档等同不设」(Opus 5.5 与 Haiku 5.5 默认 medium)、effort 影响全部 token 且是软引导、顶层 effort 与 thinking 配置的请求间改动会炸缓存(per-message effort 例外)、fast mode 的 speed: "fast"/同权重/OTPS 而非 TTFT、zero-shot 到 multi-shot 的谱系与它的 token 代价——示例的挑选与包裹方式让给 ch10)、TS 5.2(七种语言的 client SDK 与它们包着的 Messages API、SDK 内建 streaming/retries/error handling、anthropic-version 头、libraries and integrations 的边界、Claude 流式用 SSE、websockets 作为技术底座概念与 SSE 的取舍)。相邻考点:一次调用的解剖、SSE 事件序列与 caching 结构见 ch02;model version pinning 与 prompt versioning 见 ch04;REST/JSON/异步等工程基础见 ch05;模型三轴取舍、breaking behavior changes 与缓存经济学见 ch07;agent loop 与 Agent SDK 见 ch08;prompt engineering 与输出校验见 ch10;context 预算与 compaction 见 ch11。本章题组
claude-sonnet-5 and asserts byte-equality against stored golden outputs for 40 classification prompts. It passes locally and fails in CI roughly 3 runs out of 10, always on a different subset of 2–4 prompts. The diffs are paraphrases: same label, different wording. Model ID, prompt files, and SDK version are identical in both environments. What is the right conclusion?temperature 在 Claude 4.7 及之后的模型上已 deprecated,设成非默认值返回 400。claude-sonnet-5 这样的无日期 ID 本身就是 pinned snapshot,不是 evergreen 指针。而且版本漂移会产生系统性变化,不是三成运行里随机两三条。claude-sonnet-4-5-20250929 to claude-opus-5. The request builder is unchanged and still sends "temperature": 0.2. Every request now fails immediately with a 400 before any tokens are generated; the team's Python service also started raising TypeError after the SDK was bumped to v1.0. What should the team do?temperature / top_p / top_k 在 Claude 4.7 及之后的模型上 deprecated,设成非默认值返回 400,推荐替代是「省略它们,用 prompting 引导模型行为」;Python SDK v1.0 起移除这三个参数,传入抛 TypeError。两个症状同源,一个动作同时解决。top_p 与 temperature 在同一张 deprecation 表的同一行上,换过去是同一个 400。output_config.effort to "low" on every request and removing the current max_tokens, arguing that low effort already keeps responses short. Finance needs a guarantee, not a tendency. What should ship?max_tokens——它才是 the hard ceiling on total output。题干要的正是 guarantee 而不是 tendency。max_tokens 才是 the hard ceiling on total output。claude-sonnet-5 keeps a 40k-token cached prefix (tools plus a policy system prompt) across a long session. To save money the team added logic that drops effort to "low" whenever the user's message is under 20 words, and raises it back to "high" otherwise. Spend went up 30%. cache_read_input_tokens is now near zero and cache_creation_input_tokens is large on most turns. What happened?cache_creation_input_tokens 很大——写入一直在发生,长度门槛早就过了。claude-opus-5 shows a spinner for 2.1 s before the first character appears, then streams the remaining 3,000 tokens over 9 s. Users complain about "the wait before it starts." A teammate proposes enabling fast mode. What should the reviewer say?effort levels 是 blueprint 点名的 model option,而把它往上调只会让 Claude 投入更多 token、想得更久,与「更早出第一个字」方向相反。这一项动的旋钮和题干的目标背道而驰。claude-sonnet-4-5-20250929 with thinking: {"type": "enabled", "budget_tokens": 12000}. Its post-processor reads response.content[0].thinking to build an audit trail. The team moves to claude-opus-5. Every request now returns 400, and a colleague's quick patch of thinking: {"type": "adaptive"} makes the 400 go away but the post-processor starts throwing on some requests. What is the correct fix?type: "enabled" 配 budget_tokens 在 Claude 4.7 及之后的模型上不被支持、以 400 拒绝。迁移映射:去掉 budget_tokens、设 adaptive、改用 output_config: {effort: ...} 控制深度。post-processor 抛异常的原因也在同一段——adaptive 下 Claude 自行决定要不要思考,低 effort 时简单输入上可能完全跳过思考;再加上新模型上 display 默认 "omitted",不显式要 summarized 就只拿到空的 thinking 字段。adaptive 是真实存在的取值,只是被放错了载体——它属于 thinking 参数,不属于 effort。这不是本书捏造,而是官方专门写过警告的一类错用:不要把 adaptive 当成 effort 值传,adaptive 是一种 thinking mode,不是 effort 档位。而且不设 display 时,Opus 5 这类 thinking 默认开的型号默认 "omitted",「像以前一样读 thinking 文本」也读不到。type: "enabled",调小预算不会让它被接受。后半句的容错是对的,但建立在一个修不好的前提上。max_tokens 作用在一个题干没有指认的变量上,那个假定「必有 thinking block」的解析器一行没改。claude-sonnet-5 sends a 900k-token corpus plus max_tokens: 128000. Monitoring shows two distinct failure shapes: a small set of requests returns HTTP 400 with "prompt is too long", while a much larger set returns HTTP 200 whose JSON payload is truncated mid-object and whose stop_reason the client never reads. Which change addresses both?invalid_request_error("prompt is too long")——用 token counting 端点在发送前估算可以挡住它。第二类:Claude 4.5 及更新模型上 input + max_tokens 超窗的请求会被接受,生成触到窗口上限时以 stop_reason: "model_context_window_exceeded" 停止——那正是「HTTP 200 但 JSON 半截」的来源,只有读 stop_reason 才发现。max_tokens 确实能让第二类不再因 input + max_tokens 超窗而触顶,但它对第一类(输入本身就超窗的 400)毫无作用,也没让下游开始读 stop_reason——那个把截断 JSON 当完整结果吞下去的解析器一行没改。type,内容块由 content_block_start / 若干 content_block_delta / content_block_stop 包住;client SDK 对 streaming、retries、error handling 有内建支持。后端与浏览器之间用什么传输是另一层的架构决定,与调 Claude 用什么无关,评审只被要求改 Claude 那一侧。