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 系统就无法自动化执行。
从工程角度看,结构化输出是把大模型从”聊天工具”变成”系统组件”的关键一步。只有当模型的输出可以被程序可靠地处理时,大模型才能真正嵌入到企业的自动化流程中。
自测问题
-
为什么自然语言输出在企业应用中不够用?至少说出三个具体的问题。
-
结构化输出相比自然语言输出有哪些核心优势?
-
JSON Schema 解决什么问题?它和”在 Prompt 里要求输出 JSON”有什么本质区别?
-
Pydantic 在结构化输出流程中扮演什么角色?它的核心优势是什么?
-
常见的结构化输出失败场景有哪些?每种场景的处理策略是什么?
-
什么是”自我修正”重试策略?它比盲目重试好在哪里?
-
设计 JSON Schema 时,应该从哪个方向出发——模型能输出什么,还是下游系统需要什么?为什么?
-
如果一个 Prompt 的首次 JSON 格式成功率只有 50%,你会怎么优化?
-
为什么说结构化输出是大模型从”聊天工具”变成”系统组件”的关键?
-
在 Schema 设计中,如何平衡”完整性”和”简洁性”?
关键词
- 结构化输出:让大模型输出可被程序可靠处理的数据格式(如 JSON)
- JSON:轻量级数据交换格式,由键值对和数组组成
- JSON Schema:定义 JSON 数据结构的规范语言,用于校验 JSON 的合法性
- Pydantic:Python 数据校验库,用类型注解定义数据结构并自动校验
- 字段校验:检查数据值是否在合理范围内的过程
- 格式重试:当模型输出格式错误时重新调用的机制
- 自我修正(Self-correction):把错误信息反馈给模型让它自我修正的策略
- JSON Mode:模型 API 提供的强制输出合法 JSON 的模式
- 降级处理:当结构化输出多次失败后,退而求其次使用更宽松的处理方式
- 幂等性:同一操作执行多次产生的结果和执行一次相同
- 下游系统衔接:结构化输出传递给数据库、API、前端等后续系统的过程