CCDV-F 备考手册 · Claude Certified Developer – Foundations
Chapter 02 · Domain 2(本章 skill 权重合计 6.8%)

Claude API Mechanics
一次调用的解剖,与 realtime / batch 之间那条线

blueprint 把 messages、tools、streaming、vision、thinking、caching、经第三方 vendor 调用、Messages API data access patterns、batch API 使用、realtime 与 batch 的选型取舍,一并压进了同一条 skill。这些名词看起来是十个话题,实际只有两个问题:一次请求里数据怎么进、结果怎么出,以及这份工作该走同步那条路还是批处理那条路。本章按这两个问题组织,核心交付是第七节的对照表。

读法提示 — 本章几乎每个断言都是产品行为,因此逐条挂了 vb: 出处(登记表见 reference/verified-behaviors.md,含官方原句与查证日期)。考纲只点了这些机制的名字、没给行为定义,所以「名词在考纲里」与「行为细节可写」是两件事——这正是 ch01 P5 的边界警告对本书自己的要求。

一、一次 Messages API 调用的解剖

先把一次调用拆成七个零件:请求侧四个、响应侧三个。后面所有机制都是往这七个零件上挂东西。

请求侧:model、max_tokens、顶层 system、messages 数组。这里有一条最常被写错的结构约束,而且它比多数人以为的更细——问的不是「system 能不能进 messages」,是「这条指令要从什么时候开始生效」。

从一开始就要生效的全局指令,走顶层 system 字段。API 参考页的说法是:想加 system prompt 就用顶层 system 参数;而 messages 的第一条不能是带内容的 system message。从别家 SDK 迁过来的代码最容易在这里翻车——把一条 {"role": "system", …} 摆在 messages[0],那不是「system prompt 生效了」,而是把它放错了位置。

对话中途才需要生效的指令,可以用 mid-conversation system message。在 Fable 5.1 / Mythos 5.1 / Fable 5 / Mythos 5 / Opus 5.5 / Opus 4.8 / Opus 5 / Sonnet 5.5 / Haiku 5.5 上(以官方列表为准),你可以在一个 user turn 之后放 "role": "system" 的消息,在对话进行到一半时追加新的 system 指令(受 placement rules 约束,仍然不能是第一条)。它的权威性不打折——官方明说 mid-conversation system message 与顶层 system 字段同等权威。

别把这两件事混成一件 — 「messages[0] 不能是 system」与「messages 里放 system 一定报错」是两句完全不同的话,只有前一句成立。做题时要抓的判据是指令的生效时点:从头生效 → 顶层 system;中途生效 → mid-conversation system message。

messages 本身是回合序列,不是消息列表。模型被训练成在 user 与 assistant 交替的对话回合上工作;如果你在请求里连着放两条同 role 的消息,它们会被合并成一轮,而不是被当成两轮。工程含义:想让模型「看见两段独立的用户输入」,靠连发两条 user 消息是做不到的,得靠一条消息里的两个 content block,或者显式的分隔标注。

响应侧:content(content block 数组)、stop_reason、usage。

响应字段它回答的问题集成层该拿它做什么
content模型产出了哪些块(text / thinking / tool_use / …)按 type 分派,不假定只有一个 text 块
stop_reason为什么停下来先读它再读 content:tool_use 表示要跑工具,max_tokens 表示输出被截断
usage这次调用记了多少 token成本与预算的唯一权威读数

stop_reason 有两个容易踩的性质。其一,取值 "max_tokens" 的官方释义是「触到了请求的 max_tokens 或模型上限」——也就是说,一个看起来完整的 JSON 响应,如果 stop_reason 是 max_tokens,那它极可能是被切断的半截。其二,非流式响应里这个字段恒非空;流式下它在 message_start 事件里是 null,其余情况非空。所以「流式收完第一帧就判断 stop_reason」的代码,永远读到 null。

usage 的算法也不是直觉的那样。官方定义:一次请求的总输入 token 等于 input_tokens、cache_creation_input_tokens、cache_read_input_tokens 三项之和。只读 input_tokens 做成本报表的团队,会在开启缓存之后突然「发现输入 token 大幅下降」——那不是省了,是漏读了两个字段。

二、Messages API data access patterns:无状态是它最重要的性质

blueprint 单独点了「Messages API data access patterns」这个短语。要抓的第一件事不是某个字段,而是这个 API 的状态模型。

官方对它的定位是:可用于单次查询,或者无状态的多轮对话。「无状态」这三个字决定了一整类工程后果——服务端不替你记住上一轮,会话历史是你每次重新提交的。于是:

数据进模型的通道有三条,都在 messages[].content 这一层:

通道形态典型用途与代价
内联内容块text / image / document 块直接写进请求体最简单;体积算进每一轮的输入
引用型来源URL 或 Files API 的 file_id上传一次、多次引用;payload 不随轮数膨胀
工具回传tool_result 块,作为 user 回合送回活数据只能靠调用去取(ch01 P4)

还有一个历史上常用、现在已经不能再用的输出侧模式:prefill。API 参考页至今仍写着它的通用行为——最后一条消息用 assistant role 时,响应会直接从那条内容往下续写,可以用来约束模型回复的一部分。很多老代码正是靠它把输出钉成 JSON 的。

但在当前的主力模型上,这么写会直接失败。从 Claude 4.6 各模型与 Claude Mythos Preview 起,最后一条 assistant turn 上的 prefill 不再被支持,带 prefill 的请求返回 400。两条边界要一起记:更早的模型仍然支持 prefill;而在对话的其他位置添加 assistant 消息不受影响——所以「把那段半截 JSON 挪到倒数第二条」既不会报错,也不再起约束作用。官方给的理由是模型的智能与指令遵循已经进步到多数 prefill 用例不再需要它。

迁移到哪儿去:官方给了一整套替代路径,它们的完整取舍由 ch10 第四节主讲,本章不重复。本章要你带走的只有上一段那条硬事实——在 4.6 及之后的模型上,最后一条 assistant turn 的 prefill 返回 400,所以读到任何「用 prefill 把输出钉成 JSON」的老代码或老选项,先按这条判。

为什么这一段值得留着而不是删掉:它是本书的一个方法论样本。/en/api/messages 这类 API 参考页的原句是真的,但它给的结论可能被更专门的页面推翻——system role 与 prefill 已经各踩过一次。凡是从 API 参考页登记的行为,落笔前先找同主题的专题页;冲突时以专题页为准。

另一层连接:即使换成 Structured Outputs,它保证的也只是形状而不是取值——模型仍可能在合法 JSON 里给出错误内容,所以出口处的 parse + validate 一步不能省(ch01 P7)。完整的出口纪律见 ch10 第五节。这一段是本书的连接推论,不在 guide 措辞里。

三、tools 与 thinking:把「一次调用」撑成「一轮往返」的两个机制

tools。客户端工具的往返是固定三拍:模型返回 stop_reason: "tool_use" 与一个或多个 tool_use 块 → 你的代码执行那个操作 → 你把结果作为 tool_result 块送回去。注意第三拍的方向:tool_result 是你回传给 API 的内容,要随着下一次请求重新进入 messages,它不是「响应的一部分」。工具的设计与描述写法在 ch12 展开,这里只需要记住这个往返的形状,因为下面 thinking 的约束就挂在它上面。

thinking。up-front thinking(先想再答)的响应里,一个或多个 thinking content block 会排在 text 块之前。这带来两条硬约束,而且都是集成层的坑:

  1. 回传 tool result 时,assistant 消息里的 thinking block 要原样完整传回,与它伴随的 tool_use 块一起。改动过的 thinking block 会被以 400 拒绝。为了「省 token」把 thinking 剥掉再回传,是这一型最常见的自伤。
  2. 在 manual extended thinking(thinking: {type: "enabled"})下,tool use 只支持 tool_choice 为 auto 或 none;写成 any 或指定某个 tool 会直接报错,因为这两种写法强制用工具,与 manual extended thinking 不兼容。

还有一条跨机制的联动值得先记下:切换 thinking 模式会让 prompt cache 失效。也就是说「这一轮开思考、下一轮关掉省钱」的做法,代价不止是那一轮的 thinking token。

与 ch06 / ch07 的分工 — blueprint 在 TS 5.1 与 TS 5.3 另外点了 fast mode、extended thinking、adaptive thinking、effort levels 这一组 model options,那是选哪档思考的问题,放在 ch06 与 ch07。本章只管思考产物在 API 上长什么样、怎么传回去。

四、streaming:事件序列,以及什么时候不流就会出事

流式响应走 server-sent events。官方对事件形态的描述是:每个事件带一个 SSE 事件名(例如 event: message_stop),并在它的 data 里带上同名的 type;每个内容块由一个 content_block_start、一个或多个 content_block_delta、一个 content_block_stop 包住。整条流的骨架是:

streaming · message_start        ✔ 真实存在 · 流的开头
streaming · content_block_start  ✔ 真实存在 · 内容块开始
streaming · content_block_delta  ✔ 真实存在 · 可重复多次
streaming · content_block_stop   ✔ 真实存在 · 内容块结束
streaming · message_delta        ✔ 真实存在 · 顶层变更与累计 usage
streaming · message_stop         ✔ 真实存在 · 流的结尾
streaming · content_block_flush  ✘ 不存在 · 自制干扰项

两个专门用来出题的细节。第一,message_delta 事件 usage 字段里的 token 计数是累计值,不是增量——把每个 message_delta 的 output_tokens 加起来做账,会得到一个远大于真实值的数字。第二,thinking 内容在 content_block_stop 之前会先发一个 signature_delta 事件,这个 signature 用来校验 thinking block 的完整性;上一节「原样回传」的要求,在流式侧的落点就是它。

「什么时候用流式」不是体验问题,是可靠性问题。官方对长请求的建议是:超过 10 minutes 的长任务,考虑改用 streaming Messages API 或 Message Batches API;并且 SDK 会校验非流式的 Messages API 请求不应超过 10-minute timeout。更根本的差别在网络假设上——Batches 靠轮询取结果,因此不需要一条不中断的连接。

把三条路径并排:同步调用押注「一条连接撑到底」,流式让数据在这条连接上持续到达,批处理干脆不要长连接。选哪条,取决于你的工作能不能等,以及你的网络能不能撑。

五、vision:三种 source type,选的是成本形态

图片以 image content block 传入,官方给了三种 source type:请求体里内嵌的 base64、指向线上图片的 URL、Files API 返回的 file_id(上传一次、多次引用)。三者在模型侧看到的是同一张图,差别全在数据怎么进来。

source type数据在哪多轮里的行为第三方 vendor 上
base64请求体内每轮随历史重发全部字节可用
url你托管的地址请求体只带 URL不可用
file_idFiles API上传一次,后续按 id 引用不可用

第二节那句「无状态」在这里收账。官方的描述很直白:多轮与 agentic 流程里每次请求都重发完整对话历史;如果图片是 base64 编码的,完整的图片字节会随每一轮进入 payload,随着对话增长显著抬高请求体积与延迟。所以在一个走十几轮的 agent 里,把截图换成 file_id 引用,改的不是画质而是请求体积的增长曲线。

另一条要提前记住:在 Amazon Bedrock 与 Google Cloud 上,目前只有 base64 source 可用。这条会在第八节和本章第 7 题再出现一次——同一段能跑通的代码,换个接入点就未必。

六、caching:前缀有层级,失效也有层级

blueprint 在 TS 2.3 只写了 caching 这个词。缓存的经济学(省多少、什么时候值得)在 ch07;本章只讲它在 API 结构上怎么生效、怎么失效——因为这一半才是集成 bug 的来源。

官方定义的前缀顺序是 tools → system → messages,后一层建立在前一层之上;并且某一层的改动会让该层及其之后的所有层失效。层内还有一条同样硬的规则:缓存条目的 hash 是累积的,覆盖到断点为止的全部前缀,所以断点处或断点之前的任何一块只要变了,下一次请求就是另一个 hash;带变动后缀(时间戳、每请求上下文、这一轮的来信)的 prompt,断点该放在静态前缀的末尾,而不是放在那个每次都变的块上。把这句话翻译成排查动作:缓存命中率突然掉到零,先问「今天有没有人动过 tool 定义」——改一个工具的 description,整条链上的缓存一起作废。

第二个坑更隐蔽。短于最小可缓存长度的 prompt不会被缓存,而且不返回任何错误——官方原话是「will be processed without caching, and no error is returned」。你的日志里不会有任何异常,账单上也没有明显信号。验证办法只有一个:看 usage 里的 cache_creation_input_tokens 与 cache_read_input_tokens,两个都是 0 就说明这次没缓存上。

剩下三条各占一行:缓存默认 5-minute lifetime,每次被用到都会免费刷新一次;一个缓存条目要等第一个响应开始之后才可用,所以并发打出去的首批请求全部落空,官方建议是先等第一个响应回来再发后续请求;切换 thinking 模式会让缓存失效。

把这个层级和第一节的两种 system 指令放在一起看,会得到一个很实用的工程取舍。长会话跑到一半才想加一条指令,改顶层 system 是最贵的做法——system 在前缀链的第二层,动它会让该层及其之后的 messages 层一起失效,整段几万 token 的前缀要重写一次。mid-conversation system message 走的是另一条路:它追加在消息历史的末尾,因此不会让它之前的 cached prefix 失效。既然它与顶层 system 同等权威,「中途加指令」这件事就有了一个不炸缓存的做法。

诊断顺序 — 缓存不生效时按这个顺序排:① 前缀里有没有每次都变的内容(时间戳、请求 id、用户名)落在了断点处或断点之前?② 内容长度够不够最小值(查 usage 两个字段)?③ 上游有没有人改了 tools / system?④ 是不是并发首批?把顺序记住比记住任何一个数字有用——这是 ch01 P6「先分源头」在单机制上的应用。

七、batch vs. realtime:本章的核心对照表

这是 TS 2.3 里唯一被写成「取舍」的条目,也是官方样题 Q1 的考点。先把两条路径逐轴摊开,再谈怎么选。

判断轴realtime(同步 / 流式 Messages API)Message Batches API
结果何时可得本次调用内返回多数批次 1 小时内完成;结果在「全部完成」或「满 24 小时」先到者时可取;24 小时内没跑完即过期
提交与取回一次 HTTP 往返,或一条 SSE 流POST /v1/messages/batches 提交,创建后立即开始处理;processing_status 由 in_progress 转 ended 后结果就绪
结果形态响应体 / 事件流.jsonl,每行是一个请求的结果
结果与请求的对应天然一对一靠 custom_id 匹配,结果可能不按提交顺序给出
网络假设需要一条不中断的连接轮询取结果,不依赖长连接
失败爆炸半径本次调用失败即本次失败批内单个请求失败不影响其他请求
参数面完整少数参数不支持,带上直接 validation error;stream: true 即其一,因为批结果是一个文件而不是流
规模上限受 rate limit 约束单批 100,000 条请求或 256 MB,先触到哪个算哪个
与 caching 的关系cache_control 按第六节的规则生效支持,但异步并发处理使命中是尽力而为
计费标准价全部用量按标准 API 价格的 50% 计
第三方 vendorBedrock / Google Cloud 上可用Google Cloud 与 Bedrock 两页的 Features not supported 小节都列着它
两个数字,两层来源,别混 — 「24-hour window / at reduced cost」是 Exam Guide 的措辞,属于考试事实层;「按标准价格的 50% 计」是官方文档的产品行为,属于第二层(vb:vb-batch-price-o28)。guide 本身没有给出折扣数字。考场上遇到只写 "at reduced cost" 的选项,它并不因为没写百分比而变弱。

怎么选:三个问题就够

  1. 调用方能不能等?有人正盯着屏幕等结果 → realtime。次晨要、报表要、离线评测要 → batch。这一问已经决定了大多数题。
  2. 量有多大、优化的是什么?题干把「成本」写成首要约束、把体量写成几万条时,batch 是那条同时通过两道筛子的路径(ch01 P1)。
  3. 集成层扛不扛得住?需要长连接的场合(严格的网络策略、不稳的出口、超过 10 minutes 的单次生成),batch 的轮询模型本身就是可靠性优势。

官方样题 Q1 精讲

Q1(Domain 2 / TS 2.3)给的场景是:过夜处理一万份文档、成本是首要考虑、次晨才要结果。正确项是 Message Batches API,guide 的措辞是 "processes large asynchronous workloads within a 24-hour window at reduced cost"。真正值钱的是它的三条排除理由,每一条都是一台可复用的裁决机器:

三条合起来就是一句读题动作:先认出题干指认的那个变量,再看每个选项作用在哪个变量上。

参数真伪速查(batch 相关)

batch API · custom_id          ✔ 真实存在 · 结果回匹请求用的标识
batch API · processing_status  ✔ 真实存在 · 批次状态字段
batch API · results_url        ✔ 真实存在 · 结果下载地址
prompt caching · cache_control ✔ 真实存在 · 显式缓存断点
batch API · batch_priority     ✘ 不存在 · 自制干扰项
batch API · sequence_index     ✘ 不存在 · 自制干扰项

下面两个捏造物挑得很有针对性:batch_priority 迎合「我想让某几条先跑」的直觉,sequence_index 迎合「结果总该有个顺序号吧」的直觉——而后者恰恰与 custom_id 存在的理由相反。看到没见过的字段,先按不存在处理(ch01 D1 / P5)。

八、经第三方 vendor 调用 Claude:差异清单

blueprint 把「经第三方 vendor 调用 Claude」写进了 TS 2.3。考点不是某家云的控制台怎么点,而是同一段代码换个接入点之后,哪些东西不再成立。

差异面直连 Claude API经第三方 vendor
请求格式model 在 body,版本走 headerGoogle Cloud Agent Platform 上 model 写在端点 URL 里,anthropic_version 写在 body 里
图片来源base64 / URL / file_id 三选一Bedrock 与 Google Cloud 上目前只有 base64
可用端点含 Message BatchesGoogle Cloud 与 Bedrock 两页的 Features not supported 小节都列着 Message Batches 在内的一组 API endpoints

这张表最该带走的判断是第三行。「用 batch 省钱」这个结论,是绑定接入点的。一个在直连 API 上完全正确的架构决定,搬到 Bedrock 或 Google Cloud 上会变成一个 404——而这类题的干扰项通常写成「调大超时」「拆小批量」「换个 region 重试」,全是在一个根本不存在的端点上做优化(ch01 D2)。

本章刻意没写各 vendor 的鉴权方式、region 选择、model id 前缀规则:它们随平台演进的速度远快于一本备考书的修订周期,而 blueprint 的措辞停在「经第三方 vendor 调用 Claude」这一层。考场上真正会被问的,是差异存在且要在设计时被考虑,不是某个前缀今天长什么样。

本章题组

QUESTION 01 · TS 2.3 · BATCH REQUEST SHAPE
A team moves a nightly document-tagging job onto the Message Batches API. They reuse the request builder from their live endpoint, so every entry in requests[].params still carries "stream": true. The submission is rejected with a validation error before anything is processed, and the console shows no batch at all. The nightly window is six hours and the job must not become interactive. What should the team change?
正确 Cbatch 请求不支持 stream: true,带上它会返回 validation error——因为批结果是一个文件,不是一条流。正确的取回方式是轮询到 processing_status 变为 ended,再读 .jsonl 结果。题干的两个约束(六小时窗口、不得变成交互式)都由这条路径满足。
D2 无关杠杆违反 P6A:题干已经指认了源头——请求在处理开始前就被拒,而且拒的是校验而不是容量。重试作用的变量是「瞬时失败」,这里的失败是稳定的、由请求形状决定的,重试一百次得到的是同一个错误,只是把它记录了一百遍。
D6 半对半错违反 P1B:「单一代码路径」听起来是工程美德,但它把题干里最硬的两条约束一起丢了——回到同步端点意味着放弃 batch 的计费与轮询模型,24 路并发压的是 wall clock 而不是成本,而这正是官方样题 Q1 排除同型选项的理由。前半句真,后半句换了轴。
D4 换模型当解法违反 P1D:题干里没有一条约束说模型能力过剩或单次生成过慢。更要命的是它明说「其余请求形状不动」——那个引发校验失败的 stream: true 一个字都没碰,换完模型仍然提交不上去。
QUESTION 02 · TS 2.3 · BATCH RESULT MATCHING
A batch of 4,000 invoice-extraction requests finishes with request_counts reporting 4,000 succeeded and 0 errored. The ingestion script reads the results file line by line and writes result n onto the invoice at index n of the original submission array. Spot checks find that about 12% of invoices now carry another invoice's totals. What is wrong?
正确 A官方对 custom_id 的定义就是为这件事存在的:它用来把结果匹配回请求,因为结果可能不按请求顺序给出。按下标 zip 在小批量上可能一直「碰巧对」,规模一大就开始错位——12% 这个数字正是这种错位的典型形态,而不是失败率。
D2 无关杠杆违反 P6B:题干已经给了诊断材料——request_counts 报 4,000 succeeded、0 errored,说明批次已结束且结果齐全。「快照不全」会表现为行数缺失,而现象是行数够、内容对错了位。这个选项去修一个数据已经证明运转正常的组件。
D1 凭空捏造违反 P5C:sequence_index 是本书自制的干扰项,没有官方出处。它的可信度来自「结果总该有个顺序号」的直觉,而这个直觉与 custom_id 存在的理由正好相反——如果顺序是可靠的,官方就不必特意说明结果可能乱序。没见过的字段先按不存在处理。
D7 过度工程违反 P10D:为一个改一行匹配逻辑就能解决的问题,换来 4,000 次提交、4,000 个批次状态要轮询、4,000 条对账记录要维护。轻的一档(按 custom_id 匹配)连试都没试过,而且这个方案还顺手丢掉了单批的规模优势。
QUESTION 03 · TS 2.3 · CACHE BREAKPOINT PLACEMENT
A support assistant puts a 30 KB policy handbook in system and marks the last system block with cache_control. That block ends with the line Generated at 2026-08-22T09:14:03Z, regenerated on every request. After a week in production, cache_read_input_tokens is 0 on every call, cache_creation_input_tokens is non-zero on every call, and nothing has ever been logged as an error. What explains it?
正确 B缓存 hash 覆盖到断点为止的全部前缀,断点处或之前任何一块改了就换一个 hash——于是每次都写、从来不读,与观察到的现象(write 非零、read 恒零)完全吻合。官方给的做法正是把 cache_control 放在跨请求完全相同的那个块上,把变动后缀留在断点之后。
D2 无关杠杆违反 P6A:TTL 作用的变量是「同一个前缀能活多久」,而这里的前缀根本不相同。而且题干给的信号排除了过期:缓存默认 5-minute lifetime 且每次命中都会免费刷新,若真是过期,高峰期应该能看到零星 read,现实是恒为 0。换更长的 TTL 只会让一堆永远读不到的条目活得更久。
D1 凭空捏造违反 P5C:cache_status 是本书自制的干扰项,没有官方出处。它之所以像真的,是因为它填上了一个真实存在的空白——低于最小长度时确实不返回任何错误。但官方给的验证手段是读 usage 的两个 cache 字段,而题干已经告诉你 write 非零,说明长度门槛早就过了。
D4 换模型当解法违反 P1D:题干指认的变量是 read 恒为 0,而 write 非零已经证明这份 prompt 够长、缓存写入一直在发生。换一个门槛更低的模型解决的是一个这里并不存在的问题,那行每次都变的时间戳一个字也没动。
QUESTION 04 · TS 2.3 · THINKING BLOCKS IN A TOOL LOOP
An agent runs a two-leg tool loop with thinking enabled. To keep requests small, the client strips thinking blocks out of the assistant message before echoing it back together with the tool_result. The first leg succeeds and the tool returns valid JSON, but every second-leg request now fails with a 400 before the model is reached. Which change is correct?
正确 D官方要求很直接:回传 tool result 时,assistant 消息里的 thinking block 要完整、未经改动地传回,与它伴随的 tool_use 块一起;改动过的 thinking block 会被以 400 拒绝。「剥掉 thinking 省 token」正是本型最常见的自伤——省下的那点输入 token 换来了整条第二腿不可用。
D5 错层承载违反 P4A:thinking 是assistant 回合的结构化内容块,不是可以搬进 user 消息的一段文本。换个 role 重新叙述一遍,既不满足回传要求(400 照旧),又把模型自己的推理降级成了一段它得重新解读的外来文字。载体错了,内容再对也不成立。
D1 凭空捏造违反 P5B:tool_choice 是真实存在的机制,但这里被安上了它职责之外的能力——在 manual extended thinking 下,tool use 只支持 auto 或 none,指定具体 tool 会直接报错,因为那是强制用工具。名字真、职责假,是 D1 更难认的那个子型。
D2 无关杠杆违反 P6C:题干已经把源头指得很清楚——失败是 100% 的、发生在模型之前、且第一腿正常。重试作用的变量是瞬时失败,而这里是稳定的请求形状问题。另外 Messages API 是无状态的,不存在「assistant 回合在服务端落盘」这回事,这个选项的因果解释本身就是编的。
QUESTION 05 · TS 2.3 · STREAMING USAGE ACCOUNTING
A billing dashboard consumes the SSE stream and adds usage.output_tokens from every message_delta event into a running total. Finance reports the dashboard bills roughly nine times what the console shows for the same traffic. The product has a strict first-token latency budget, so dropping streaming is not acceptable. What is the fix?
正确 Amessage_delta 的 usage 里的 token 计数是累计值,把每一帧相加等于把同一批 token 数了很多遍——九倍这个量级正是「事件数量」而不是「token 数量」的影子。改法是记录最后一次读到的值——流的事件序列以一个 message_stop 收口,所以「最后一次」有明确的边界。顺带一提(不是本题判据):这个看板的输入侧同样会算错——一次请求的总输入 token 是 input_tokens、cache_creation_input_tokens、cache_read_input_tokens 三项之和,只读第一项会在开启缓存后凭空「省」出一大截。
D1 凭空捏造违反 P5B:「四个字符一个 token」是一条广为流传但没有官方出处的换算,用它做账等于拿一个估算值替换掉 API 已经如实给出的计量字段。官方对成本的权威读数是 usage,且它明确包含缓存相关的两项——自制换算连这两项的存在都表达不了。
D7 过度工程违反 P10C:为了读一个已经躺在流里的字段,把每次请求变成两次——账单先翻一倍,还引入了「两次调用结果不一致时以谁为准」的新问题。最轻的一档(改一行累加逻辑)根本没试过。
D6 半对半错违反 P5D:message_start 确实带 usage,这半句是真的;但它是流开始时的快照,输出还没生成完,不可能是「整个响应的权威总数」。同一个事件里 stop_reason 也还是 null——把开场快照当终局读数,是给真实机制安错了职责。
QUESTION 06 · TS 2.3 · IMAGE SOURCE TYPES
A QA agent walks a tester through a web form. Each turn appends a fresh 1200×800 screenshot as a base64 image block, and the client resends the whole conversation every turn. By turn 12 the request body is 38 MB, p95 latency has tripled, and per-turn input tokens keep climbing. Earlier screenshots are still referenced by later turns. Which change addresses the growth?
正确 C问题的根在「无状态 + base64」的组合:每次请求都重发完整历史,base64 图片因此每轮把完整字节塞进 payload,请求体积与延迟随对话增长。三种 source type 里,file_id 正是「上传一次、多次引用」那一种,它改的是增长曲线本身。
D2 无关杠杆违反 P1A:官方给的单图尺寸上限是 8000x8000 px,1200×800 早就远在其内,这个「优化」什么都不会改变。它作用的变量是单张图的尺寸上限,而题干指认的变量是累积重发——第 12 轮的 38 MB 是十二张图叠出来的,不是任何一张太大。
D3 措辞冒充控制违反 P9B:那些截图已经不在上下文里了——模型不是「不肯参考」,是看不见。要求它记住一个不在场的东西,只会让它把界面状态编出来。把丢信息变成造信息,比多花点 payload 危险得多。
D6 半对半错违反 P9D:缓存确实能把重复前缀的计费压下来,这半句是真的。但缓存不改变请求体积——那 38 MB 的字节仍然每轮走一遍网络,p95 延迟一分不降。而且图片块在断点之前,任何一轮新增内容的位置安排稍有不慎就会让整段前缀失效。它只覆盖了题干三个症状里的一个。
QUESTION 07 · TS 2.3 · THIRD-PARTY VENDOR ACCESS
A team is porting a working integration from the direct Claude API to Google Cloud's Agent Platform. Two things break in staging: every request returns an error about the model, and the nightly job that submits to /v1/messages/batches returns 404. The nightly job processes 60,000 records inside a ten-hour window. What is the accurate reading of these two failures?
正确 B两个失败来自两条不同的差异。请求格式上,Agent Platform 的 model 写在端点 URL 里而不是 body,anthropic_version 则写在 body 里而不是 header。端点可用性上,Message Batches 被列在该平台的 Features not supported 小节里(Bedrock 页有同样一条)——404 不是配置问题,是这条路在这里不存在。
D1 凭空捏造违反 P5A:把版本信息当 header 发,恰恰是这个平台不接受的写法——它要求 anthropic_version 出现在 body 里。选项还额外编了一条「版本协商成功后端点就会解析」的因果,一个真实存在的 header 被安上了开通端点的能力。
D2 无关杠杆违反 P1C:重试与轮询间隔作用的变量是「服务暂时不可达」。这里的端点不在该平台的支持清单里,等多久都不会出现。在一个不存在的端点上调参数,是这一型最典型的形态。
D6 半对半错违反 P1D:30 MB 的 payload 上限在该平台确实存在,所以前半句读起来很硬。但 404 是「找不到这个端点」,不是「请求太大」;模型错误也与体积无关。拆小批量解决的是一个题干没有出现的症状,两个真实失败一个都没碰。
QUESTION 08 · TS 2.3 · REALTIME VERSUS BATCH
A ticket-classification service handles two workloads through one code path: 40 live tickets per minute that must be classified before the agent console renders, and a nightly re-classification of 90,000 archived tickets whose output feeds a report read at 09:00. Cost is the stated constraint on the nightly workload; the live path has a 900 ms budget. Which design fits both?
正确 D两个工作负载在「调用方能不能等」这一轴上处在相反的位置,因此它们本来就该走两条路径。夜间那条完全落在 batch 的适用面里:延迟可容忍、体量大、成本是写明的约束,而结果在次晨 09:00 才被读,远在 24 小时窗口之内。这与官方样题 Q1 的判据是同一条。
D5 错层承载违反 P1A:控制台是有人正盯着的界面,900 ms 预算与 batch 的交付模型直接冲突——结果在全部完成或满 24 小时先到者时才可取,轮询多勤都改不了这件事。把一个交互式需求塞进异步载体,省下的钱要用产品可用性去付。
D2 无关杠杆违反 P1B:并发压的是 wall clock,不是 per-token 成本——这是官方样题 Q1 对同型选项的原话式判据。九万条在同步端点上跑完,账单与串行跑完一模一样,只是跑得快些,而题干指认的变量恰恰是成本。
D6 半对半错违反 P1C:给共享 rubric 加缓存确实能压低重复前缀的成本,这半句站得住。但它只作用在输入的一小部分上,而且没有碰题干指认的那条轴——batch 与 realtime 的取舍。压 max_tokens 在官方样题 Q1 里被排除,用的正是这个理由:省钱是真的,跑错轴也是真的。