Day 4:结构化输出基础

学习目标

今天要解决一个很实际的问题:怎么让大模型的输出能被程序可靠地处理。

如果你昨天认真思考了 Prompt 的局限,你应该已经意识到一个关键问题——在 Prompt 里写”请输出 JSON 格式”,模型大部分时候会照做,但偶尔也会出错。可能是多了一段解释性文字,可能是少了一个字段,可能是 JSON 格式根本不合法。当人在阅读时,这些小问题无所谓;但当程序需要解析输出时,一个格式错误就会导致整个流程崩溃。

今天学习的内容就是解决这个问题的。你要理解为什么自然语言输出在企业应用中不够用,什么是结构化输出,怎么用 JSON Schema 来定义输出结构,怎么用 Pydantic 来校验输出内容,怎么处理输出格式错误。

完成今天的学习后,你应该能说明为什么企业应用必须使用结构化输出,能设计基本的 JSON Schema,能理解结构化输出和自动化流程之间的关系。

核心概念

一、自然语言输出的问题

先搞清楚为什么自然语言输出在大多数企业应用场景中不够用。

自然语言是人类沟通的媒介,不是机器处理的格式。人在阅读一段文字时,能自然地提取关键信息、理解隐含含义、忽略无关内容。但程序做不到这些——程序需要精确的、格式化的、可预测的输入。

具体来说,自然语言输出在企业应用中有以下问题:

解析困难。程序要从一个自然语言段落中提取特定信息,比如行业名称、市场规模、增长率等。没有固定格式的情况下,程序需要用正则表达式或另一个模型来解析,这增加了复杂性和不确定性。

格式不稳定。同一种信息,模型今天可能输出”市场规模:100 亿”,明天可能输出”该行业市场约为100亿元”,后天可能输出”规模约在百亿级别”。格式的不稳定让程序无法可靠地提取数据。

信息遗漏。你要求模型分析五个维度,它可能只分析了三个,因为模型认为另外两个”不太相关”。人在阅读时能发现遗漏并追问,但程序通常不会自动追问。

边界模糊。什么时候算”分析完成”?自然语言没有一个明确的结束标志。程序怎么判断模型已经输出了所有内容?

扩展性差。当业务需求变化,你需要在输出中增加新字段时,自然语言输出没有固定的结构让你”加一个字段”。你只能重新设计 Prompt,然后重新测试,祈祷模型能稳定输出新格式。

这些问题归结为一点:自然语言输出缺乏结构化约束,导致程序无法可靠地处理。

二、结构化输出的价值

结构化输出就是把模型的输出限制在一个预定义的数据结构中。

最常见的结构化输出格式是 JSON。JSON 是一种轻量级的数据交换格式,由键值对和数组组成。它既是人能读懂的,也是程序能解析的。

结构化输出的核心价值:

可解析性。JSON 有标准的解析规则,程序可以可靠地从 JSON 中提取字段值。不需要正则表达式,不需要额外的解析模型。

可校验性。你可以定义 JSON 的结构规范(Schema),然后校验模型输出的 JSON 是否符合规范。字段是否齐全?类型是否正确?值是否在允许范围内?

可组合性。结构化数据可以轻松地传递给下游系统——存入数据库、传给 API、生成报告、触发工作流。每个下游系统只需要知道 JSON 的结构,就能正确处理数据。

可测试性。有了固定的输出结构,你就可以编写自动化测试来验证输出是否符合预期。这在自然语言输出中几乎不可能做到。

可迭代性。当需要增加新字段时,只需修改 Schema,然后调整 Prompt。所有的测试用例和下游处理逻辑都可以基于新的 Schema 来更新。

在 AI 应用开发的实际工作中,结构化输出是大模型能力与工程系统之间的桥梁。大模型擅长理解和生成自然语言,工程系统需要结构化数据来执行后续流程。结构化输出把两者连接起来。

三、JSON 输出

JSON 是结构化输出的最基本形式。

JSON 的结构很简单:它由对象(用花括号包围的键值对集合)和数组(用方括号包围的值列表)组成。键是字符串,值可以是字符串、数字、布尔值、null、对象或数组。

让模型输出 JSON 的方式有几种:

在 Prompt 中要求。最简单的方式是在 Prompt 里写”请以 JSON 格式输出”。这是一种软约束——模型大多数时候会照做,但不保证 100% 遵守。适合对格式要求不那么严格的场景。

使用 JSON Mode。一些模型 API 提供了 JSON Mode,它会强制模型输出合法的 JSON。这是比 Prompt 要求更强的一种约束,但仍然不能保证 JSON 的结构(字段名、类型、值范围)符合你的期望。

使用 JSON Schema 约束。这是最强的约束方式——你不仅要求模型输出 JSON,还告诉它这个 JSON 必须长什么样。哪些字段必须有、每个字段是什么类型、值的范围是什么。

在 Enterprise AI 应用中,JSON 输出几乎是标配。原因很简单:大模型生成的自然语言内容,最终需要被程序处理——存到数据库、渲染到页面、传递给其他服务。JSON 是这个过程的标准数据格式。

JSON 输出也有一些需要注意的点:

字段命名要有意义。不要用 “f1”、“f2” 这种缩写,要用 “industry_name”、“market_size” 这种自解释的名称。

嵌套不要太深。三层以上的嵌套会让模型更容易出错,也让下游处理更复杂。尽量保持扁平结构。

数组长度的控制。模型有时候会生成过长的数组(比如你要求分析 5 个场景,它生成了 15 个),有时候数组为空。需要在 Prompt 中明确指定数组长度或在 Schema 中设置约束。

字段的可选与必选。不是所有字段都能保证每次都有值。要区分哪些字段是必填的(缺失就是错误),哪些是选填的(缺失可以接受)。

四、JSON Schema

JSON Schema 是定义 JSON 数据结构的规范语言。

如果把 JSON 比作一个填好的表格,那 JSON Schema 就是这个表格的模板——它定义了表格有哪些字段、每个字段填什么类型的内容、哪些字段必填、哪些可以选填。

JSON Schema 本身也是一个 JSON 文档,它描述了另一个 JSON 文档应该长什么样。

一个简单的例子来说明。假设你需要模型输出一个行业分析结果,包含行业名称、市场规模、增长率和主要挑战四个字段。对应的 JSON Schema 会定义:行业名称必须是字符串类型、必填;市场规模必须是数字类型、必填、最小值为 0;增长率必须是数字类型、选填;主要挑战必须是字符串数组、至少包含一项。

JSON Schema 能表达的约束远不止类型和必填。它还支持:

字符串长度限制(最少多少字符、最多多少字符)。

数字范围限制(最小值、最大值、是否允许小数)。

枚举值约束(字段值只能是预定义的几个选项之一,比如行业类型只能是”制造业”、“服务业”、“金融业”等)。

正则表达式匹配(字段值必须匹配特定的格式,比如日期格式)。

条件约束(如果字段 A 有值,则字段 B 也必须有值)。

JSON Schema 的价值在于:它把”期望的输出格式”从自然语言描述变成了机器可读的规范。这意味着你可以用程序来校验模型的输出是否符合要求,而不需要人工检查。

在 AI 应用开发中,JSON Schema 的作用可以分为两层:

设计层面——它帮你理清楚你到底需要模型输出什么。设计 JSON Schema 的过程就是梳理业务需求的过程:需要哪些字段?每个字段代表什么?类型是什么?约束是什么?这些问题的答案构成了完整的数据需求定义。

运行层面——它在运行时校验模型输出。模型生成 JSON 后,系统用 JSON Schema 来校验:格式是否合法?字段是否齐全?类型是否正确?校验通过的才能进入后续流程,校验不通过的需要重试或降级处理。

五、Pydantic

Pydantic 是 Python 中用于数据校验的库,它和 JSON Schema 配合使用非常方便。

Pydantic 的核心思想是:用 Python 的类型注解来定义数据结构,然后自动完成数据校验和序列化。

使用 Pydantic 的好处:

声明式定义。你只需要定义数据结构(这个字段是字符串、那个字段是数字),Pydantic 自动处理校验逻辑。不需要手写一堆 if-else 来检查每个字段。

类型安全。Pydantic 会在数据传入时自动检查类型,如果类型不匹配就报错。比如你期望一个数字,但传入了字符串”abc”,Pydantic 会抛出明确的错误信息。

自动生成 JSON Schema。Pydantic 模型可以直接生成对应的 JSON Schema,不需要手动编写。这减少了出错的可能,也减少了维护两份文档的工作量。

友好的错误信息。当校验失败时,Pydantic 会明确告诉你哪个字段出了什么问题——类型错误、缺失必填字段、值超出范围等。这对于调试和日志记录非常有用。

与 FastAPI 等框架深度集成。如果你用 Python 做后端开发,Pydantic 几乎是标配。FastAPI 直接使用 Pydantic 模型来定义请求和响应的数据结构。

在 AI 应用开发中,Pydantic 的工作流程通常是这样的:

定义输出模型。用 Pydantic 定义你期望的模型输出结构,包括字段名、类型、约束、默认值等。

生成 JSON Schema。从 Pydantic 模型自动生成 JSON Schema,传给大模型作为输出的格式约束。

解析模型输出。模型返回 JSON 后,用 Pydantic 模型来解析和校验。如果校验通过,得到一个类型安全的 Python 对象;如果校验失败,得到明确的错误信息。

处理校验失败。根据错误信息决定下一步——是重新调用模型,还是降级处理,还是报错。

六、字段校验

字段校验是确保模型输出数据质量的最后一道关卡。

即使模型输出了合法的 JSON、结构符合 Schema,数据的”质量”仍然可能有问题。比如行业增长率字段,模型输出了一个合法的数字 500,但 500% 的增长率明显不合理。或者主要挑战列表中,模型输出了三项一模一样的内容。

字段校验就是在 Schema 校验的基础上,进一步检查数据的语义合理性。常见的校验规则包括:

值域校验——数字是否在合理范围内?增长率一般不会超过 1000%,市场规模一般不会是负数。

唯一性校验——数组中的元素是否重复?列表项不应该出现完全相同的内容。

一致性校验——相关字段之间是否一致?如果”市场规模”写的是”100 亿”,“增长率”写的是”50%“,这两个数字是否匹配该行业的一般情况?

完整性校验——关键字段是否有实际内容(不是”待补充”或”暂无数据”这样的占位符)?

格式校验——日期是否是有效日期?邮箱是否是合法格式?URL 是否能访问?

字段校验的实现方式可以是 Pydantic 的自定义校验器,也可以是独立的校验函数。关键是要把校验规则和业务逻辑绑定在一起,而不是散落在代码各处。

七、结构化输出失败处理

结构化输出不是万能的,模型仍然可能输出不符合要求的格式。你需要一个健壮的失败处理机制。

常见的失败场景:

JSON 格式不合法。模型输出的不是合法的 JSON,解析器无法解析。可能是多了逗号、少了引号、花括号不匹配等。

结构不符合 Schema。JSON 格式合法,但结构不对——缺少必填字段、字段类型错误、嵌套层级不对等。

内容质量不达标。格式和结构都对,但内容有问题——值不合理、内容空洞、与输入无关等。

输出截断。模型在生成过程中达到了最大 Token 限制,导致 JSON 不完整。

处理这些失败的方式:

格式修复。对于简单的格式错误(比如多了或少了逗号),可以尝试用程序自动修复。一些 JSON 修复库可以处理常见的格式问题。也可以把格式错误的 JSON 和错误信息一起发给模型,让它修正格式后重新输出。

重试机制。对于偶尔的格式错误,最简单的处理方式是重试——重新调用模型,让它在同样的 Prompt 下重新生成。但重试不是无限次的,通常设置一个最大重试次数(比如 3 次)。

降级处理。如果重试多次仍然失败,可以降级到更宽松的要求。比如原本要求 JSON 格式输出,降级后允许自然语言输出,然后用另一个模型来从自然语言中提取结构化信息。

错误记录。所有失败案例都应该被记录下来,用于分析失败原因和优化 Prompt。如果某种失败模式反复出现,说明需要调整 Prompt 或 Schema 设计。

人工介入。对于关键的、高价值的任务,可以设置人工审核节点。当自动处理无法满足要求时,转交给人工处理。

八、格式重试机制

格式重试机制是结构化输出中最重要的工程实践之一。

一个好的重试机制需要考虑以下几个问题:

重试策略——是立即重试还是延迟重试?是使用相同的 Prompt 重试还是调整 Prompt?通常的做法是立即重试,并把上一次的错误信息包含在新的 Prompt 中,让模型知道它上一次哪里做错了。

重试次数——重试几次后放弃?通常设为 2-3 次。太多重试会浪费时间和 Token,太少可能错过本来可以成功的尝试。

重试判断——什么情况下应该重试?格式错误要重试,内容质量问题要不要重试?格式错误可以通过程序判断,内容质量问题可能需要另一个模型来评估。

幂等性——重试不会产生副作用。如果第一次调用已经触发了一些操作(比如写了数据库),重试时不会重复触发。这需要在系统设计中考虑。

重试机制不应该只是简单地重新调用模型。一个更优雅的方式是把错误信息反馈给模型,让它知道哪里出了问题,然后自我修正。这种方式叫做”自我修正”(Self-correction),在结构化输出中效果通常比盲目重试好。

九、结构化输出与下游系统衔接

结构化输出的最终目的是与下游系统衔接。一个常见的错误是:花了很多精力设计 JSON Schema 和校验逻辑,但没有想清楚校验通过的数据要怎么用。

下游系统的衔接方式取决于业务需求:

存入数据库——结构化数据直接映射到数据库表。JSON 的字段对应表的列,JSON 的对象对应表的行。这里需要考虑的是字段名映射(JSON 的 snake_case 和数据库的命名规范可能不同)和数据类型转换。

渲染到页面——结构化数据传给前端,前端根据数据渲染页面。需要考虑的是数据格式是否符合前端的期望(比如日期格式、数字精度)。

传递给 API——结构化数据作为参数调用其他服务的 API。需要考虑的是数据格式是否符合目标 API 的要求。

生成报告——结构化数据填入报告模板,生成最终报告。需要考虑的是数据到文本的转换逻辑(一个数字如何变成”约 100 亿元”这样的文字)。

触发工作流——结构化数据中的某些字段值可以触发下游的自动化流程。比如”风险等级”字段值为”高”时,自动发送预警通知。

在设计 JSON Schema 时,就要考虑这些下游需求。Schema 不应该只考虑模型能不能输出,还应该考虑下游系统能不能用。

概念关系图

结构化输出体系

自然语言输出的问题          结构化输出的价值
+--------------------+     +--------------------+
| 解析困难            |     | 可解析              |
| 格式不稳定          |     | 可校验              |
| 信息遗漏            | --> | 可组合              |
| 边界模糊            |     | 可测试              |
| 扩展性差            |     | 可迭代              |
+--------------------+     +--------------------+

结构化输出的技术栈
+------------------+     +------------------+     +------------------+
| JSON Schema      |     | Pydantic         |     | 格式重试机制     |
| 定义输出结构      | --> | 校验输出内容      | --> | 处理输出失败     |
| 字段、类型、约束  |     | 类型安全、友好报错|     | 重试、降级、记录 |
+------------------+     +------------------+     +------------------+
         |                        |                         |
         v                        v                         v
+----------------------------------------------------------------+
|                    下游系统衔接                                 |
|                                                                |
|  数据库  |  前端页面  |  外部API  |  报告生成  |  工作流触发   |
+----------------------------------------------------------------+

结构化输出流程
+----------+   +----------+   +----------+   +----------+
| 设计     |   | 生成     |   | 校验     |   | 处理     |
| JSON     | -> | 模型调用 | -> | Pydantic | -> | 下游衔接 |
| Schema   |   | JSON输出 |   | 字段校验 |   |          |
+----------+   +----------+   +----------+   +----------+
                                  | 失败
                                  v
                            +----------+
                            | 格式修复 |
                            | 重试     |
                            | 降级     |
                            | 人工介入 |
                            +----------+

实战分析

设计行业分析 JSON Schema

行业分析需要输出哪些信息?从业务角度倒推:你需要行业名称、行业类别、市场规模(包含金额和单位)、增长率、产业链结构(上游、中游、下游各自的主要环节)、关键痛点列表、AI 机会列表。

设计思路:先用扁平结构满足基本需求,每个字段都标注类型和是否必填。市场规模用嵌套对象来表示(金额和单位分开),因为直接用一个字符串”100亿元”不利于程序处理。产业链结构用嵌套对象,包含上中下游三个数组。痛点列表和 AI 机会列表用字符串数组。

设计岗位拆解 JSON Schema

岗位拆解需要输出:岗位名称、所属部门、岗位目标、日常任务列表(每个任务包含任务名称、频率、耗时、是否有 AI 机会)、核心痛点列表、AI 机会列表(每个机会包含场景描述、AI 方案、预期效果、实施难度)。

设计思路:日常任务是一个对象数组,每个任务有多个属性。AI 机会也是对象数组,包含更详细的信息。注意实施难度字段用枚举值(低/中/高),而不是让模型自由发挥。

设计流程拆解 JSON Schema

流程拆解需要输出:流程名称、触发条件、参与角色列表、步骤列表(每个步骤包含步骤名称、输入、输出、执行角色、是否有人工判断、耗时估计、AI 优化建议)、整体 AI 优化点列表。

设计思路:步骤列表是核心数据,每个步骤需要详细的属性。AI 优化建议放在步骤级别和流程级别两层——步骤级别标注每个环节的 AI 优化可能,流程级别给出整体的优化方向。

模拟模型输出格式错误并修正

这个任务是要你体验真实场景中的错误处理。

假设模型输出了一个不合法的 JSON(比如在 JSON 之前加了一段”好的,以下是分析结果:”),你的处理流程应该是:先尝试提取 JSON 部分(通过正则匹配找到第一个花括号到最后一个花括号之间的内容),然后解析 JSON,再用 Schema 校验。如果提取失败或解析失败,把错误信息和原始输出一起反馈给模型,让它修正。

如果模型输出的 JSON 格式合法但内容有问题(比如某个字段缺失),你的处理方式应该是:记录错误信息,把缺失的字段和错误信息反馈给模型,让它补充缺失的字段。

当日产物说明

《行业分析 JSON Schema》

一个完整的行业分析输出结构定义。至少包含以下字段:行业名称、行业类别、市场规模、增长率、产业链结构、关键痛点、AI 机会。每个字段有明确的类型和约束。质量标准:Schema 本身合法,字段覆盖行业分析的核心维度,类型和约束合理。

《岗位拆解 JSON Schema》

一个完整的岗位拆解输出结构定义。至少包含:岗位名称、岗位目标、日常任务列表、痛点列表、AI 机会列表。任务和机会都用对象数组表示,每个对象有详细的属性。质量标准:结构清晰,嵌套不超过三层,枚举值定义明确。

《流程拆解 JSON Schema》

一个完整的流程拆解输出结构定义。至少包含:流程名称、触发条件、参与角色、步骤列表、AI 优化点。步骤列表是核心,每个步骤有完整的属性。质量标准:步骤属性覆盖了流程分析的所有关键维度。

《结构化输出异常处理方案》

一份描述如何处理结构化输出异常的文档。包含:常见异常类型(格式错误、结构错误、内容错误、截断)、每种异常的处理策略、重试机制设计、降级策略、错误记录格式。质量标准:覆盖了主要的异常场景,处理策略清晰可执行。

常见误区与避坑

误区一:过度信任 JSON Mode

JSON Mode 只是保证输出合法的 JSON,不保证结构符合你的要求。模型可能在 JSON 里放了你没要求的字段,或者遗漏了你要求的字段。JSON Mode 是基础保障,不是完整解决方案。你仍然需要 JSON Schema 校验。

误区二:Schema 设计过于复杂

有些人喜欢设计非常复杂的 Schema——多层嵌套、大量条件约束、复杂的枚举值。Schema 越复杂,模型遵守的可能性越低。好的 Schema 设计应该在满足业务需求和保持简洁之间找到平衡。能用扁平结构就不要嵌套,能用简单类型就不要复杂约束。

误区三:忽视重试成本

每次重试都意味着额外的模型调用和 Token 消耗。如果一个 Prompt 的首次成功率是 70%,平均需要 1.5 次调用才能成功。在设计重试机制时,要把重试成本纳入考虑——不是所有场景都值得无限重试。低价值任务可以接受较低的输出质量,高价值任务才值得多次重试。

误区四:只关注格式,不关注内容

结构化输出不只是让模型输出正确格式的 JSON,更重要的是让 JSON 里的内容准确、有价值。一个格式完全正确但内容空洞的 JSON,比一个格式有瑕疵但内容丰富的自然语言回答更没用。在追求格式合规的同时,不要牺牲内容质量。

误区五:不考虑下游系统的实际需求

设计 Schema 时只从模型能输出什么的角度出发,不考虑下游系统需要什么。这会导致输出的 JSON 虽然格式正确,但下游系统用不了。正确的方式是先明确下游系统的数据需求,再据此设计 Schema,确保每一个字段都是下游需要的。

延伸思考

今天的结构化输出学习为后续的工程化实践打下了基础。

在后续的 API 调用学习中,你会把结构化输出和 API 结合起来——后端接收用户请求,调用大模型,获取结构化输出,校验后返回给前端或存入数据库。整个流程中,结构化输出是连接大模型和工程系统的桥梁。

在 RAG 系统中,结构化输出同样重要。用户提问后,系统检索相关文档,调用模型生成回答。如果回答是结构化的,就可以做更多后续处理——比如自动提取关键信息生成摘要、把结构化结果存入数据库供后续查询、根据回答中的某些字段触发工作流。

在 Agent 系统中,结构化输出更是不可或缺。Agent 的每一步输出都需要被系统解析和处理——规划步骤、工具调用的参数和结果、中间状态、最终报告——都需要结构化。没有结构化输出,Agent 系统就无法自动化执行。

从工程角度看,结构化输出是把大模型从”聊天工具”变成”系统组件”的关键一步。只有当模型的输出可以被程序可靠地处理时,大模型才能真正嵌入到企业的自动化流程中。

自测问题

  1. 为什么自然语言输出在企业应用中不够用?至少说出三个具体的问题。

  2. 结构化输出相比自然语言输出有哪些核心优势?

  3. JSON Schema 解决什么问题?它和”在 Prompt 里要求输出 JSON”有什么本质区别?

  4. Pydantic 在结构化输出流程中扮演什么角色?它的核心优势是什么?

  5. 常见的结构化输出失败场景有哪些?每种场景的处理策略是什么?

  6. 什么是”自我修正”重试策略?它比盲目重试好在哪里?

  7. 设计 JSON Schema 时,应该从哪个方向出发——模型能输出什么,还是下游系统需要什么?为什么?

  8. 如果一个 Prompt 的首次 JSON 格式成功率只有 50%,你会怎么优化?

  9. 为什么说结构化输出是大模型从”聊天工具”变成”系统组件”的关键?

  10. 在 Schema 设计中,如何平衡”完整性”和”简洁性”?

关键词

  • 结构化输出:让大模型输出可被程序可靠处理的数据格式(如 JSON)
  • JSON:轻量级数据交换格式,由键值对和数组组成
  • JSON Schema:定义 JSON 数据结构的规范语言,用于校验 JSON 的合法性
  • Pydantic:Python 数据校验库,用类型注解定义数据结构并自动校验
  • 字段校验:检查数据值是否在合理范围内的过程
  • 格式重试:当模型输出格式错误时重新调用的机制
  • 自我修正(Self-correction):把错误信息反馈给模型让它自我修正的策略
  • JSON Mode:模型 API 提供的强制输出合法 JSON 的模式
  • 降级处理:当结构化输出多次失败后,退而求其次使用更宽松的处理方式
  • 幂等性:同一操作执行多次产生的结果和执行一次相同
  • 下游系统衔接:结构化输出传递给数据库、API、前端等后续系统的过程