CCAR-F 备考手册 · Claude Certified Architect – Foundations
Chapter 09 · Domain 4(权重 20%)

结构化输出
tool_use + JSON Schema、tool_choice 三态、validation-retry

本章有一条贯穿全篇的主线,也是整个 Domain 4 最重要的一句话:schema 消灭语法错误,但不消灭语义错误。把这句话记牢,本章一半的题可以直接排除两个选项——凡是宣称「用了 strict schema 就不会再出错」的选项,都错。

一、为什么是 tool_use 而不是「让它输出 JSON」

要拿到保证符合 schema 的结构化输出,最可靠的路径是定义一个以 JSON Schema 为入参的工具,让模型去「调用」它,然后从 tool_use 响应里取参数。它彻底消灭了 JSON 语法错误——不会再有截断的花括号、多余的逗号、把 JSON 包在 markdown 代码块里这类问题。

tools = [{
  "name": "extract_invoice",
  "description": "Record the structured fields extracted from an invoice document.",
  "input_schema": {
    "type": "object",
    "properties": {
      "invoice_number": {"type": "string"},
      "vendor_name":    {"type": ["string", "null"]},   ✔ nullable · 源文档没有就返回 null,不编造
      "line_items":     {"type": "array", "items": {…}},
      "stated_total":   {"type": "number"},
      "calculated_total": {"type": "number"},           ✔ 自纠字段 · 与 stated_total 比对可暴露语义错误
      "doc_category":   {"type": "string",
                         "enum": ["invoice","receipt","statement","unclear","other"]},
      "category_detail":{"type": ["string","null"]}     ✔ "other" 的配套 detail 字段,保持可扩展
    },
    "required": ["invoice_number", "stated_total"]
  }
}]

Schema 设计的三条硬规则

规则解决什么反面
可能不存在的字段设为 optional / nullable 防止模型为了满足 required 而编造值 把所有字段设成 required → 空缺变杜撰,错误从可见变不可见
enum 加 "unclear" 给歧义案例一个诚实的出口 只给正常取值 → 模型被迫在几个都不对的值里挑一个
enum 加 "other" + detail string 类别可扩展,新类型不必改 schema 就能被记录下来 封闭 enum → 新类别被硬塞进最接近的旧类别

另有一条配套 skill:在 prompt 里附上格式归一化规则,与严格 schema 配合使用,用来处理源文档格式不一致(日期写法、单位、大小写)。 schema 约束的是结构,归一化规则约束的是取值形态——两者管的不是一回事,考试可能把它们做成一组对照。

二、tool_choice 三态

tool_choice = {"type": "auto"}                                ✔ 真实存在 · 模型可以调工具,也可以直接返回纯文本
tool_choice = {"type": "any"}                                 ✔ 真实存在 · 必须调某个工具,但由模型选调哪个
tool_choice = {"type": "tool", "name": "extract_metadata"}    ✔ 真实存在 · 强制调用指定的那一个工具
取值保证什么典型用法
"auto"什么都不保证——模型可能返回纯文本对话型场景,工具是可选辅助
"any"保证一定调用某个工具(不会返回纯文本),但选哪个由模型定有多个抽取 schema、且文档类型未知时——保证拿到结构化输出
forced {"type":"tool","name":…}保证调用指定的那一个确保 extract_metadata 先于 enrichment 步骤运行,后续步骤放到下一轮
考场辨析 — 「保证一定有结构化输出」→ "any";「保证某个特定工具先跑」→ forced。二者常在同一题里做对照:如果题干只关心「别给我返回散文」,用 "any" 就够,forced 反而多余地限死了选择(在文档类型未知时甚至会用错 schema)。

三、schema 挡不住的那一半

这是本章的题眼。strict schema 保证:字段齐、类型对、JSON 能解析。它不保证:

这类语义错误需要单独的验证层。guide 给了两个具体的自纠设计:

  1. calculated_total 与 stated_total 并列抽取——让模型既抄下文档声称的总额,又自己把明细加一遍。两者不等就是一个可程序化检测的信号。
  2. conflict_detected 布尔字段——源数据自身矛盾时置真,把「文档有问题」与「抽取有问题」区分开。

四、Validation-retry:什么时候重试有用

重试的正确形态是带错误反馈的重试:后续请求里同时包含①原文档、②失败的那次抽取结果、③具体的验证错误。三者缺一,模型就只能盲猜自己哪里错了。

重试有效重试无效
错误类型格式不匹配、结构性输出错误、值放错字段所需信息根本不在源文档里
为什么信息在文档中存在,模型只是没组织对重试一百次也变不出不存在的信息
正确处置带三件套重试返回 null / "unclear",路由人工或标记数据缺口

识别信号很直接:题干说「该信息只存在于一份未提供的外部文档中」——重试就是无效的,选它的都是干扰项。

detected_pattern:给误报做归因

还有一个容易被跳过的字段设计:在结构化 findings 里加一个 detected_pattern,记录是哪一类代码构造触发了这条发现。当开发者大量 dismiss 某些 findings 时,你可以按 pattern 聚合,系统性地分析误报模式——而不是逐条读评论猜规律。它把「感觉这个 bot 有点吵」变成了可统计的数据。

工程实践中,语义验证层通常用 Pydantic 之类的模型做(Appendix 点名了 Pydantic:schema validation、semantic validation errors、validation-retry loops)。写法上有一个常见取舍:把「明细求和 == 总额」写成 schema 层的 validator,还是留在业务层?考试不会考实现位置,只考你是否知道语法层挡不住它。
外部课程 · CertSafari — 本章 2 个考点(展开查看)

用于补读和错题复盘;第三方 AI 生成练习材料,非官方题库。核验答案时以 Exam Guide 和官方文档为依据。复盘方法 · 链接核对:2026-10-16。

本章题组

ch09 · Q1 · 准则 P1
Your extraction pipeline uses tool_use with a strict JSON schema. Schema validation passes on 100% of documents. Downstream accounting still rejects about 4% of records: on those, the line items do not sum to the stated total, and in some cases the vendor name appears in the bill_to field. What should you add?
正确 CTS 4.3 的核心断言:strict JSON schema 消灭语法错误,但不消灭语义错误——guide 举的两个例子正是题干的两个症状(明细不等于总额、值放错字段)。TS 4.4 给了配套的自纠设计:并列抽取 calculated_total 与 stated_total 以暴露差异。
D3 治标不治本违反 P1A:类型与正则仍然是语法层。vendor 名和 bill_to 都是合法字符串,任何正则都拦不住「放错格子」;求和关系更不是单字段约束能表达的。
D4 甩锅错位违反 P1B:tool_choice 控制的是调不调工具、调哪个工具,与工具入参填得对不对无关。题干也说 schema 校验 100% 通过——工具选择显然没出问题。
D3 治标不治本违反 P3D:few-shot 能降低这类错误的发生率,但它是概率性改进,而 4% 的记录会流向下游会计系统。需要的是能检测出来的机制,不是「更不容易出错」。
ch09 · Q2 · 准则 P5
You have four extraction tools, one per document type (invoice, purchase order, packing slip, credit note). Incoming documents arrive unlabeled. In about 12% of requests the model returns a conversational summary instead of calling any extraction tool, breaking the downstream parser. Which configuration fixes this?
正确 BTS 4.3 的原话级 skill:当存在多个抽取 schema 且文档类型未知时,设 tool_choice: "any" 以保证结构化输出。"any" 的语义正是「必须调某个工具、但由模型选哪个」——恰好匹配题干的两个约束。
D6 概率当保证违反 P3A:"auto" 的定义就是模型可以返回纯文本。用 prompt 措辞去覆盖一个 API 层的开关语义,是拿概率赌确定性——而 API 直接提供了确定性选项。
D5 吞错或炸链违反 P5C:forced 确实保证有工具调用,但它锁死了哪一个。文档类型未知时强制走 invoice schema,会把 packing slip 硬塞进发票结构——把一个可见的失败换成了静默的错误抽取。
D2 过度工程违反 P5D:全字段 optional 的大一统 schema 放弃了类型特异的约束,等于让 schema 层几乎不再校验任何东西;而现成的 "any" 一个参数就能解决问题。
ch09 · Q3 · 准则 P6
Validation fails on 9% of contracts because the required governing_law field is missing. Investigation shows those contracts incorporate governing law by reference to a master services agreement that is not included in the request. Your current retry loop re-sends the document with the validation error, three times, then gives up. What should change?
正确 DTS 4.4 的 retry 边界:当所需信息根本不在源文档中(guide 的原例正是「信息只存在于一份未提供的外部文档里」),重试无效——这与格式/结构错误可修是两回事。配套 TS 4.3:字段设为 nullable 以防模型为满足 required 而编造。
D2 过度工程违反 P2A:加大重试次数是把一个注定失败的循环跑得更久更贵。「更仔细地找」也无法找到文档里不存在的内容。
D3 治标不治本违反 P1B:采样参数影响的是措辞稳定性,与信息是否存在无关。降温只会让模型更稳定地返回同样的缺失——或更稳定地编造。
D3 治标不治本违反 P1C:few-shot 治的是「文档里有但认不出」(TS 4.2 的适应症)。题干已经调查清楚:这些合同确实没有这一条,只是引用了另一份文件。诊断已经排除了这个方向。
ch09 · Q4 · 准则 P6
Your document classifier uses an enum with values ["invoice", "receipt", "statement"]. Analysts find that shipping manifests and tax forms — which the corpus does contain — are being labeled "statement," and genuinely ambiguous scans are also labeled "statement." How should you redesign the enum?
正确 CTS 4.3 的 schema 设计规则,两个症状对应两个机制:歧义案例需要 "unclear",已知集合之外的新类别需要 "other" + detail string 以保持可扩展。题干恰好同时展示了这两种被硬塞进 "statement" 的情形。
D3 治标不治本违反 P1A:只补当前发现的两类,下一个新类型出现时同样问题重演;而且它完全没有处理「真正模糊的扫描件」这一半——那些不属于任何具体类别。
D4 甩锅错位违反 P1B:放弃 enum 等于放弃这一层结构约束,把归一化负担推给下游,且引入了无限的取值空间——正是 "other"+detail 模式要在受控前提下解决的问题。
D6 概率当保证违反 P8D:未校准的自报置信(这里是布尔版本)不可作路由依据(TS 5.5);而且它没有解决「新类别无处可放」——shipping manifest 仍然会被标成 statement,只是多了个标记。
ch09 · Q5 · 准则 P5
Your pipeline must always run extract_metadata (document type, date, source system) before any enrichment tool, because enrichment routing depends on the metadata. Currently the model sometimes calls an enrichment tool first. What is the correct configuration?
正确 CTS 4.3 / TS 2.3 的原话级 skill:用 forced tool selection 确保某个特定工具先跑(guide 的原例正是「强制 extract_metadata 先于 enrichment tools」),后续步骤在接下来的回合里处理。这是 tool_choice 三态里唯一能保证「调用指定的那一个」的取值。
D1 凭空捏造违反 P5A:"any" 只保证调某个工具,不保证调哪个;而「tools 数组顺序决定调用优先级」是编造的语义——数组顺序不构成顺序约束。
D6 概率当保证违反 P3B:把一个确定性的顺序需求写进工具描述,仍然是概率性合规。题干说的是「必须总是先跑」,而 "auto" 连「是否调工具」都不保证。
D2 过度工程违反 P2D:动态增删工具集需要额外的编排逻辑,而一个 tool_choice 参数就能达成同样效果。它也把顺序约束藏进了应用代码,可读性更差。
ch09 · Q6 · 准则 P8
Developers dismiss roughly 40% of your CI reviewer's findings. You want to know which kinds of code constructs are producing the dismissed findings, so you can fix the prompt for those specifically rather than lowering overall sensitivity. What should you add to the structured finding schema?
正确 ATS 4.4 的 feedback loop 设计:加 detected_pattern 字段记录是哪一类代码构造触发了发现,从而在开发者 dismiss 时对误报模式做系统性分析。它把逐条读评论猜规律,变成了可按 pattern 聚合的数据。
D3 治标不治本违反 P1B:自由文本 rationale 每条都不一样,无法聚合统计——你会得到 40% × N 段散文,仍然要人工归纳。题干要的正是可归类的维度。
D6 概率当保证违反 P8C:未校准的自报 confidence 与实际正确性关系薄弱(TS 4.1 / TS 5.5);即使相关,它也只告诉你「哪些条不确定」,不告诉你哪类构造在产生误报。
D3 治标不治本违反 P1D:记录 dismissal 结果是必要的一半,但没有 pattern 维度就没法分组——你知道 40% 被丢弃,仍然不知道该修哪条准则。它记录了结果,缺的是自变量。
ch09 · Q7 · 准则 P6
Extraction on utility bills fails validation for about 7% of documents: the model returns a total that does not match the sum of charges. Manual inspection shows the documents themselves are internally inconsistent — the printed total is wrong on the bill. How should the pipeline handle this class?
正确 DTS 4.4 的自纠验证流设计,两个机制正好对应:并列抽取 calculated_total 与 stated_total 以标记差异,加 conflict_detected 布尔表示源数据自身不一致。它把「文档有问题」与「抽取有问题」清晰地分开——这正是 P6 信息保全的形态。
D5 吞错或炸链违反 P6A:静默改写源文档声称的数值,是把一个客户账单上的真实矛盾抹平成看似正常的记录。下游再也无法知道这份账单本身有问题——最彻底的信息销毁。
D2 过度工程违反 P2B:抽取本身没有错——它忠实反映了一份自相矛盾的文档。重试只会让模型在「照抄错误总额」和「改成正确总额」之间反复,两者都不是问题所在。
D5 吞错或炸链违反 P6C:丢弃把 7% 的数据连同「这些账单有对账问题」这个有价值的发现一起扔掉。可解析、可标注的冲突不应被当成不可处理。
ch09 · Q8 · 准则 P5
Everything that lands in your ap@ mailbox — roughly 11,000 messages a week — goes through one extraction step backed by a single extract_invoice tool. About 44% of that traffic is not an invoice at all: vendor questions, out-of-office replies, carrier delivery notices. Since tool_choice: "any" was set last quarter, those messages still produce invoice records — about 21,000 of them landed in the ledger last month, and 1,900 of those carry vendor names and amounts that appear nowhere in the email rather than empty fields. The intake worker already has a branch for responses that arrive without a tool call — it forwards the message to the vendor-correspondence queue — and that branch has been dead code ever since. What should you change?
正确 ATS 4.3 对三态的定义里,"auto" 的语义正是「模型可以调工具、也可以返回纯文本」。本题的判断轴不是「调哪一个工具」,而是这条输入到底该不该调工具——44% 的邮件里没有任何可抽取的内容,而 "any" 保证每条都产出一次工具调用,模型只能把字段填满,于是编出 vendor 名与金额。与本章 Q2 是同一根轴的两侧:那里每份文档都有东西可抽、只是类型未知,所以要用 "any" 把纯文本这条路堵死;这里输入可能什么都不是,纯文本恰恰是正确的回答形态,而 intake worker 早就为它留好了分支。
D3 治标不治本违反 P1B:nullable 是 TS 4.3 的真机制,治的是「为满足 required 而编造」,它确实能把那 1,900 条里编出来的金额换成 null。但题干的两个量级说明根因在更前面一环:"any" 保证了每一封邮件都产出一条发票记录,剩下的近两万条即便字段全空也照样进 ledger,下游照样把它当发票。要消除的是记录本身,不是记录里的字段值。
D1 凭空捏造违反 P5C:给 "any" 安上它没有的语义。它的定义就是必调某个工具、不返回纯文本,prompt 里写什么都改不了这个 API 层的保证。这个选项正好是本章 Q2 的 A 的镜像——那里想用 prompt 让 "auto" 保证调工具,这里想用 prompt 让 "any" 允许不调,两个方向都是拿措辞去改开关的语义。
D2 过度工程违反 P2D:加一个哨兵工具,是在工具集里重造一个 API 已经定义好的开关(同本章 Q2 的 D、Q5 的 D 一样的工艺)。它还把「没有可抽取内容」伪装成一次工具调用,逼下游多学一种表达方式,而题干里那条现成的「无工具调用」分支继续闲置。