<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>结构化输出 - DJJ</title><link>https://blog.pdjjq.org/tags/%E7%BB%93%E6%9E%84%E5%8C%96%E8%BE%93%E5%87%BA/</link><description>反抗吧，朋友！</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 07 Jul 2026 14:30:41 +0800</lastBuildDate><atom:link href="https://blog.pdjjq.org/tags/%E7%BB%93%E6%9E%84%E5%8C%96%E8%BE%93%E5%87%BA/index.xml" rel="self" type="application/rss+xml"/><item><title>Tager: 格式化输出</title><link>https://blog.pdjjq.org/post/tager-formatted-output-z2frdhg.html</link><pubDate>Tue, 07 Jul 2026 14:30:41 +0800</pubDate><guid>https://blog.pdjjq.org/post/tager-formatted-output-z2frdhg.html</guid><description>&lt;h2 id="第一阶段prompt-only-json">第一阶段：Prompt-only JSON&lt;a class="heading-anchor" href="#%e7%ac%ac%e4%b8%80%e9%98%b6%e6%ae%b5prompt-only-json" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>最早的做法不是 API 级能力，而是纯 prompt 约束。开发者会在 system prompt 或 user prompt 中写：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">请严格输出 JSON。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">不要输出 Markdown。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">不要解释。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">不要添加额外文字。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">必须包含 name、email、age 三个字段。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种方式的本质是用自然语言约束自然语言模型。它在 demo 中通常有效，但在生产环境中不稳定。模型可能输出 Markdown code block：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alice&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可能在 JSON 前后添加解释&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="err">下面是你要的&lt;/span> &lt;span class="err">JSON：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alice&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>还可能出现漏字段、字段类型错误、枚举值幻觉、数组结构不稳定、字符串转义错误等问题。&lt;/p>
&lt;p>Prompt-only JSON 的问题在于，它只是软约束。模型知道你“希望”它输出 JSON，但推理过程本身没有被硬性限制。它仍然可以在任意位置输出任意 token。也就是说，JSON 在这个阶段只是一种格式建议，而不是协议。&lt;/p>
&lt;p>这个阶段的典型工程补救包括：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">1. 尝试 JSON.parse
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 如果失败，要求模型重试
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 如果仍失败，使用正则或 JSON repair
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 再用 schema validator 检查字段
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. 如果字段错误，再 retry
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这套流程可以工作，但本质上是事后补救。模型先可能犯错，然后应用程序再修复。它没有从生成机制上阻止错误发生。&lt;/p>
&lt;p>参考来源：&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs 对 JSON mode 与 Structured Outputs 的区分&lt;/a>、&lt;a href="https://json-schema.org/learn/getting-started-step-by-step">JSON Schema 入门文档&lt;/a>&lt;/p>
&lt;h2 id="第二阶段json-mode">第二阶段：JSON Mode&lt;a class="heading-anchor" href="#%e7%ac%ac%e4%ba%8c%e9%98%b6%e6%ae%b5json-mode" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>JSON Mode 是 Prompt-only JSON 之后的一个关键进步。它不再完全依赖 prompt，而是让模型平台在输出层面保证结果是合法 JSON。&lt;/p>
&lt;p>JSON Mode 解决的是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">模型输出能不能被 JSON.parse 解析？
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它不能完整解决的是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">字段是否齐全？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">字段类型是否正确？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">枚举值是否有效？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">是否添加了多余字段？
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">是否满足业务约束？
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenAI 文档中明确区分了 JSON Mode 和 Structured Outputs：两者都能确保输出是 valid JSON，但只有 Structured Outputs 能确保输出遵循指定 schema。OpenAI 也建议在可用时优先使用 Structured Outputs，而不是 JSON Mode。&lt;/p>
&lt;p>因此可以这样理解：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">Prompt-only JSON:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 希望模型输出 JSON，但不保证。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">JSON Mode:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 保证输出是合法 JSON，但不保证符合业务结构。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Structured Outputs:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 保证输出是合法 JSON，并且符合开发者提供的 JSON Schema。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>JSON Mode 是一个中间阶段。它让输出从“可能不可解析”变成“通常可解析”，但还没有把模型输出升级为真正的数据契约。&lt;/p>
&lt;p>参考来源：&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs vs JSON mode&lt;/a>&lt;/p>
&lt;h2 id="第三阶段json-schema-与-structured-outputs">第三阶段：JSON Schema 与 Structured Outputs&lt;a class="heading-anchor" href="#%e7%ac%ac%e4%b8%89%e9%98%b6%e6%ae%b5json-schema-%e4%b8%8e-structured-outputs" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>JSON Schema 的作用不是简单地告诉模型“请输出 JSON”，而是定义这个 JSON 应该长什么样。&lt;/p>
&lt;p>例如：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;object&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;properties&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;action&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enum&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;search&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;reply&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;required&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;action&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;additionalProperties&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个 schema 传达了几个信息：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">输出必须是 object。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">必须包含 action 和 query。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">action 必须是 string。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">action 只能是 search 或 reply。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">query 必须是 string。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">不能添加 schema 之外的字段。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>JSON Schema 把 JSON Object 从“数据格式”提升为“数据契约”。这正是结构化输出真正有用的地方：应用程序不再只是拿到一个能 parse 的 JSON，而是拿到一个符合预期字段、类型和约束的数据对象。&lt;/p>
&lt;p>JSON Schema 官方文档也说明，JSON Schema 是一套可以用于标注和验证 JSON 文档的词汇表。它可以描述 JSON 数据的结构、约束和类型。&lt;code>properties&lt;/code>​ 用于定义对象字段，&lt;code>required&lt;/code>​ 用于声明必需字段，&lt;code>additionalProperties: false&lt;/code> 用于禁止额外字段。&lt;/p>
&lt;p>参考来源：&lt;a href="https://json-schema.org/learn/getting-started-step-by-step">JSON Schema 入门&lt;/a>、&lt;a href="https://json-schema.org/understanding-json-schema/reference/object">JSON Schema object / properties / required / additionalProperties&lt;/a>&lt;/p>
&lt;h2 id="description-的特殊位置它不是硬约束而是语义说明">&lt;code>description&lt;/code> 的特殊位置：它不是硬约束，而是语义说明&lt;a class="heading-anchor" href="#description-%e7%9a%84%e7%89%b9%e6%ae%8a%e4%bd%8d%e7%bd%ae%e5%ae%83%e4%b8%8d%e6%98%af%e7%a1%ac%e7%ba%a6%e6%9d%9f%e8%80%8c%e6%98%af%e8%af%ad%e4%b9%89%e8%af%b4%e6%98%8e" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>在 JSON Schema 中，&lt;code>description&lt;/code>​ 和 &lt;code>title&lt;/code> 属于 annotation keywords，也就是注释型关键词。JSON Schema 官方文档明确说明，这类关键词不是用来做 validation 的，而是用来描述 schema 的含义。&lt;/p>
&lt;p>也就是说：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;The search query to send to the web search engine.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里的 &lt;code>type: &amp;quot;string&amp;quot;&lt;/code> 是硬约束。它告诉 validator 或 constrained decoder：这个字段必须是字符串。&lt;/p>
&lt;p>但 &lt;code>description&lt;/code> 不是硬约束。它不会阻止模型输出某个字符串。它真正的作用是告诉模型：这个字段在语义上应该填什么。&lt;/p>
&lt;p>这就是为什么 &lt;code>description&lt;/code> 在 LLM 时代突然变得非常重要。传统程序读取 schema 时，主要关心的是类型、必填项、枚举、额外字段等机器可验证信息。但大语言模型读取 schema 时，还会利用字段名和 description 来理解任务意图。&lt;/p>
&lt;p>因此可以把 schema 分成两层：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">结构层：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> type
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> properties
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> required
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> enum
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> items
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> additionalProperties
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">语义层：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> field description
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> examples
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>结构层负责“能不能填”，语义层负责“应该怎么理解”。&lt;/p>
&lt;p>参考来源：&lt;a href="https://json-schema.org/understanding-json-schema/reference/annotations">JSON Schema annotations&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/function-calling">OpenAI Function Calling function definition&lt;/a>、&lt;a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">Anthropic Tool Use&lt;/a>&lt;/p>
&lt;h2 id="structured-outputs-的实现方式constrained-decoding">Structured Outputs 的实现方式：Constrained Decoding&lt;a class="heading-anchor" href="#structured-outputs-%e7%9a%84%e5%ae%9e%e7%8e%b0%e6%96%b9%e5%bc%8fconstrained-decoding" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>Structured Outputs 的关键实现思想通常是 constrained decoding，也叫 constrained sampling。它不是模型完整输出之后再接一个小模型修复 JSON，而是在模型生成每一个 token 时，根据 schema 判断哪些 token 仍然合法。&lt;/p>
&lt;p>普通解码大致是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">模型根据上下文预测下一个 token 的概率分布
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">从整个词表中选择一个 token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">继续生成
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Constrained decoding 则是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">模型预测下一个 token 的概率分布
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Schema / grammar engine 判断当前状态下哪些 token 合法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">非法 token 被 mask，概率变成 0
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">重新采样或选择下一个 token
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">重复直到输出完成
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenAI 在 Structured Outputs 技术说明中明确说，他们会把 JSON Schema 转换成 context-free grammar，推理引擎在每个 token 生成之后动态判断下一步哪些 token 合法，然后把非法 token 的概率降为 0。OpenAI 还说明，CFG 相比 FSM 或 regex 能表达更复杂的递归和嵌套结构。&lt;/p>
&lt;p>这意味着 schema 不再只是事后验证器，而是变成了生成时约束器。&lt;/p>
&lt;p>一句话概括：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">Structured Outputs 把 JSON Schema 从“模型说完以后检查对不对”，推进为“模型每说一个 token 时就限制它不能说错格式”。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>但 constrained decoding 也有边界。它主要保证结构合法，不保证语义正确。例如 schema 能保证：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;city&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Paris&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>中的 &lt;code>city&lt;/code> 是字符串，但不能保证 Paris 就是用户真正想问的城市。事实正确性、业务正确性和用户意图匹配仍然依赖模型理解、上下文、工具结果以及应用侧校验。&lt;/p>
&lt;p>参考来源：&lt;a href="https://openai.com/index/introducing-structured-outputs-in-the-api/">OpenAI Structured Outputs 技术博客&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs 文档&lt;/a>&lt;/p>
&lt;h2 id="7-主流厂商如何实现结构化输出">7. 主流厂商如何实现结构化输出&lt;a class="heading-anchor" href="#7-%e4%b8%bb%e6%b5%81%e5%8e%82%e5%95%86%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e7%bb%93%e6%9e%84%e5%8c%96%e8%be%93%e5%87%ba" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;h3 id="71-openai">7.1 OpenAI&lt;a class="heading-anchor" href="#71-openai" aria-label="本节链接">#&lt;/a>
&lt;/h3>&lt;p>OpenAI 当前把结构化输出分为两类：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">1. response_format / text.format:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制模型最终响应的结构。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. function calling / tools:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制模型调用工具时的参数结构。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>OpenAI 文档明确建议：如果你是在连接模型到工具、函数、数据或系统能力，应使用 function calling；如果只是希望模型最终回答符合某个结构，应使用 structured response format。&lt;/p>
&lt;p>OpenAI 还区分了 JSON Mode 和 Structured Outputs。JSON Mode 只保证合法 JSON，Structured Outputs 才保证 schema adherence。对于 strict mode，OpenAI 要求 function parameters 中每个 object 设置 &lt;code>additionalProperties: false&lt;/code>，并要求 properties 中所有字段都被列入 required。&lt;/p>
&lt;p>参考来源：&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/function-calling">OpenAI Function Calling strict mode&lt;/a>&lt;/p>
&lt;h3 id="72-anthropic-claude">7.2 Anthropic Claude&lt;a class="heading-anchor" href="#72-anthropic-claude" aria-label="本节链接">#&lt;/a>
&lt;/h3>&lt;p>Anthropic 的 Claude Tool Use 文档强调：Claude 会基于用户请求和工具 description 判断是否调用工具，然后返回一个结构化调用。客户端工具由应用程序执行，服务端工具由 Anthropic 执行。&lt;/p>
&lt;p>Claude 文档还明确说明，工具 token 成本来自工具名称、description、schema、tool_use blocks 和 tool_result blocks。也就是说，工具定义并不是系统外部的隐藏元数据，而是会进入模型可见的上下文，并影响模型判断。&lt;/p>
&lt;p>参考来源：&lt;a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">Anthropic Tool Use with Claude&lt;/a>&lt;/p>
&lt;h3 id="73-google-gemini">7.3 Google Gemini&lt;a class="heading-anchor" href="#73-google-gemini" aria-label="本节链接">#&lt;/a>
&lt;/h3>&lt;p>Gemini 文档同样区分了 Structured Outputs 和 Function Calling。Gemini 的 Structured Outputs 用于让模型最终响应遵循 JSON Schema；Function Calling 用于让模型在对话过程中请求外部函数执行真实动作。&lt;/p>
&lt;p>Google 文档中明确写到：Structured Outputs 适合数据抽取、结构化分类和 agentic workflows；Function Calling 则让模型连接外部工具和 API，不再只是生成文本，而是决定何时调用函数并提供执行参数。&lt;/p>
&lt;p>参考来源：&lt;a href="https://ai.google.dev/gemini-api/docs/structured-output">Gemini Structured Outputs&lt;/a>、&lt;a href="https://ai.google.dev/gemini-api/docs/function-calling">Gemini Function Calling&lt;/a>&lt;/p></description></item><item><title>Tager: JsonOutput 到 Tools Intro</title><link>https://blog.pdjjq.org/post/tager-jsonoutput-dao-tools-intro-sujla.html</link><pubDate>Tue, 07 Jul 2026 14:30:02 +0800</pubDate><guid>https://blog.pdjjq.org/post/tager-jsonoutput-dao-tools-intro-sujla.html</guid><description>&lt;h1 id="从-json-object-到-tools大语言模型工程接口的演进">从 JSON Object 到 Tools：大语言模型工程接口的演进&lt;a class="heading-anchor" href="#%e4%bb%8e-json-object-%e5%88%b0-tools%e5%a4%a7%e8%af%ad%e8%a8%80%e6%a8%a1%e5%9e%8b%e5%b7%a5%e7%a8%8b%e6%8e%a5%e5%8f%a3%e7%9a%84%e6%bc%94%e8%bf%9b" aria-label="本节链接">#&lt;/a>
&lt;/h1>&lt;h2 id="为什么需要结构化输出">为什么需要结构化输出&lt;a class="heading-anchor" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e7%bb%93%e6%9e%84%e5%8c%96%e8%be%93%e5%87%ba" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>OpenAI 文档中有一句适合作为本章开场的话：&lt;/p>
&lt;blockquote>
&lt;p>“JSON is one of the most widely used formats in the world for applications to exchange data.”&lt;/p>&lt;/blockquote>
&lt;p>这句话的关键点是：JSON 不是为了大语言模型发明的，它本来就是现代软件系统之间交换数据的通用格式。Web API、数据库中间层、前后端通信、配置文件、日志系统、消息队列，都大量依赖 JSON。问题在于，大语言模型原本输出的是自然语言文本，而不是程序可以稳定消费的数据对象。&lt;/p>
&lt;p>早期 LLM 应用的典型形态是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">应用程序发送 prompt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">模型返回一段自然语言
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">应用程序尝试解析这段自然语言
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这在聊天场景中没有问题，但在工程系统中会很脆弱。因为下游程序真正需要的是字段、类型、枚举、数组、对象，而不是“看起来像答案”的文本。&lt;/p>
&lt;p>例如一个信息抽取任务，用户希望从简历中抽取：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-JSON" data-lang="JSON">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Alice&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;email&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;alice@example.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;years_of_experience&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;skills&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Python&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;React&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;SQL&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果模型返回：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">姓名是 Alice，邮箱是 alice@example.com，大概有 5 年经验，技能包括 Python、React 和 SQL。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>人类当然能读懂，但程序不能稳定消费。程序要继续做正则、字符串切割、异常处理、补字段、类型转换。这样一来，LLM 的输出就没有真正进入软件系统的“类型世界”。&lt;/p>
&lt;p>因此，结构化输出的第一层价值是：把模型输出从“人读的文本”转成“程序读的数据”。&lt;/p>
&lt;p>OpenAI 官方文档对 Structured Outputs 的定义是：让模型生成始终符合开发者提供的 JSON Schema 的响应，从而避免遗漏必需字段或生成无效枚举值。它列出的优势包括类型安全、可程序化检测拒绝、更少依赖强提示词来维持格式一致性。&lt;/p>
&lt;p>参考来源：&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs 文档&lt;/a>&lt;/p>
&lt;h2 id="从-json-object-到-tools-的完整演进图">从 JSON Object 到 Tools 的完整演进图&lt;a class="heading-anchor" href="#%e4%bb%8e-json-object-%e5%88%b0-tools-%e7%9a%84%e5%ae%8c%e6%95%b4%e6%bc%94%e8%bf%9b%e5%9b%be" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;p>可以把整条演进线压缩成七个阶段：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">阶段 1：Natural Language Output
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 模型返回自由文本。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 适合聊天，不适合程序消费。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 2：Prompt-only JSON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 用 prompt 要求模型输出 JSON。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 解决部分格式问题，但不稳定。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 3：JSON Mode
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 平台保证输出是 valid JSON。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 解决 parseability，但不保证业务 schema。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 4：Structured Outputs / JSON Schema
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 平台保证 schema adherence。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 输出成为类型安全的数据对象。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 5：Tools / Function Calling
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 模型不只是返回数据，而是请求外部动作。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> JSON Schema 成为函数参数契约。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 6：ReAct-style Tool Use
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 推理与行动交替。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Tool result 成为 observation。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">阶段 7：Agent Loop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 多轮感知、决策、工具调用、反馈和终止。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 系统从问答模型变成任务执行体。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这条路线可以用一句话概括：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">JSON Object 是数据格式。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">JSON Schema 是数据契约。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Structured Outputs 是生成时结构保证。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Tools 是动作契约。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent Loop 是围绕动作契约展开的执行系统。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参考来源：&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/function-calling">OpenAI Function Calling&lt;/a>、&lt;a href="https://ai.google.dev/gemini-api/docs/structured-output">Gemini Structured Outputs vs Function Calling&lt;/a>、&lt;a href="https://arxiv.org/abs/2210.03629">ReAct 论文&lt;/a>&lt;/p>
&lt;h2 id="三类能力的边界对比">三类能力的边界对比&lt;a class="heading-anchor" href="#%e4%b8%89%e7%b1%bb%e8%83%bd%e5%8a%9b%e7%9a%84%e8%be%b9%e7%95%8c%e5%af%b9%e6%af%94" aria-label="本节链接">#&lt;/a>
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>能力&lt;/th>
&lt;th>解决的问题&lt;/th>
&lt;th>输出对象&lt;/th>
&lt;th>是否执行外部动作&lt;/th>
&lt;th>典型场景&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>JSON Mode&lt;/td>
&lt;td>保证输出是合法 JSON&lt;/td>
&lt;td>JSON 文本&lt;/td>
&lt;td>否&lt;/td>
&lt;td>简单结构化返回&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Structured Outputs&lt;/td>
&lt;td>保证输出符合 JSON Schema&lt;/td>
&lt;td>类型安全对象&lt;/td>
&lt;td>否&lt;/td>
&lt;td>信息抽取、分类、UI 生成、结构化报告&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Tools / Function Calling&lt;/td>
&lt;td>让模型请求外部能力&lt;/td>
&lt;td>工具调用请求&lt;/td>
&lt;td>是，由 runtime 执行&lt;/td>
&lt;td>查数据库、搜索、代码执行、调用业务 API&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Agent Loop&lt;/td>
&lt;td>多步执行与反馈修正&lt;/td>
&lt;td>多轮 tool call + tool result&lt;/td>
&lt;td>是&lt;/td>
&lt;td>复杂任务执行、自动化办公、代码修改、数据分析&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>关键区别是：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-Plain" data-lang="Plain">&lt;span class="line">&lt;span class="cl">Structured Outputs:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制最终回答的形状。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Tools:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制模型请求动作的形状。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent Loop:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 控制动作、观察、再决策的过程。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>参考来源：&lt;a href="https://ai.google.dev/gemini-api/docs/structured-output">Gemini Structured outputs vs function calling&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/structured-outputs">OpenAI Structured Outputs：function calling vs response_format&lt;/a>、&lt;a href="https://developers.openai.com/api/docs/guides/function-calling">OpenAI Function Calling&lt;/a>&lt;/p>
&lt;h1 id="homework">HomeWork&lt;a class="heading-anchor" href="#homework" aria-label="本节链接">#&lt;/a>
&lt;/h1>&lt;ol>
&lt;li>调用任意模型, 返回一个固定的Json Schema&lt;/li>
&lt;li>制作 Read / Write / Update / List 三个工具, 并且验证使用&lt;/li>
&lt;li>尝试基于以上四个工具, 实现一个最小版本的 Agent&lt;/li>
&lt;/ol></description></item></channel></rss>