让 AI 直接返回 Java 对象:Spring AI 结构化输出 + 校验失败自动自纠正(StructuredOutputValidationAdvisor) 让 AI 直接返回 Java 对象Spring AI 结构化输出 校验失败自动自纠正StructuredOutputValidationAdvisor系列Spring AI 深入浅出第 8 篇版本Spring AI 2.0.1 / Spring Boot 4.1.x / JDK 21标签#SpringAI #结构化输出 #StructuredOutput #Java对象前置阅读第 2 篇《ChatClient 全拆解》预计字数6500 字可复制源码 自纠正时序图 踩坑 FAQ本文将解决别再手动JSON.parse大模型的返回了。用.entity()直接把回复绑定成 Java 对象绑不上还能靠StructuredOutputValidationAdvisor自动自纠正。你将学到为什么需要结构化输出下游要的是对象不是字符串.entity()绑定 POJO 与泛型集合JSON Schema 如何约束模型输出校验失败自动自纠正的完整机制provider 原生结构化输出 vs 通用自纠正的取舍Jackson 3 序列化底座变化一、版本标注本文基于 Spring AI 2.0.1 / Boot 4.1 / JDK 21。2.0 相关点序列化底座升级到 Jackson 3提供了统一的JsonMapper/JsonHelper自纠正能力由StructuredOutputValidationAdvisor提供精确签名以 2.0.1 官方文档为准。二、为什么需要结构化输出大模型默认返回的是自由文本但下游业务要的是结构化对象。传统做法StringrawchatClient.prompt().user(抽取订单信息...).call().content();// 手动解析脆且易错OrderordermanualParse(raw);// ← 这里最脆弱一旦模型返回格式稍有变化少个引号、多个字段manualParse就崩。结构化输出把这个过程交给框架OrderorderchatClient.prompt().user(从这段文本抽取订单信息text).call().entity(Order.class);// ← 直接得到对象三、entity() 绑定 POJO3.1 绑定单个对象// 定义一个目标结构Java record 或普通 POJO 均可publicrecordOrder(StringorderId,StringcustomerName,doubletotalAmount){}RestControllerpublicclassExtractController{privatefinalChatClientchatClient;publicExtractController(ChatClient.Builderbuilder){this.chatClientbuilder.build();}GetMapping(/extract)publicOrderextract(RequestParamStringtext){returnchatClient.prompt().user(从以下文本抽取订单的 orderId、customerName、totalAmount只返回 JSON\ntext).call().entity(Order.class);}}3.2 绑定泛型集合要抽取多个对象如一组订单用ParameterizedTypeReferenceimportorg.springframework.core.ParameterizedTypeReference;importjava.util.List;ListOrderorderschatClient.prompt().user(从以下文本抽取所有订单text).call().entity(newParameterizedTypeReferenceListOrder(){});四、JSON Schema框架如何约束模型.entity(Order.class)背后框架做了两件事把Order的结构生成JSON Schema注入到 prompt 里告诉模型按这个结构输出。模型返回的 JSON 用该 Schema 校验再反序列化成Order对象。这就是为什么能直接返回 Java 对象——不是魔法是 Schema 约束 反序列化。entity()的更多微调如控制 Schema 生成、字段描述可用EntityParamSpec写作时核对 2.0.1 具体用法。五、校验失败自动自纠正2.0 亮点模型偶尔会返回不合规的 JSON字段缺失、类型不对、多了解释文字。2.0 的StructuredOutputValidationAdvisor会把校验错误回喂给模型让它重新生成直到合规或达到次数上限importorg.springframework.ai.chat.client.advisor.StructuredOutputValidationAdvisor;BeanpublicChatClientchatClient(ChatClient.Builderbuilder){returnbuilder.defaultAdvisors(StructuredOutputValidationAdvisor.builder().build()).build();}挂上之后entity()遇到不合规返回会自动重试业务代码无需关心模型这次是不是又输出错了。模型StructuredOutputValidationAdvisor业务代码模型StructuredOutputValidationAdvisor业务代码alt[校验失败且未超次数]entity(Order.class)请求带 JSON Schema返回 JSON校验回喂错误重新生成新的 JSON再校验反序列化后的 Order六、两条路线provider 原生 vs 通用自纠正路线机制优点缺点provider 原生用模型厂商自带的结构化输出如 OpenAI 的 JSON mode / function calling更稳定、更省 token依赖厂商支持切换模型可能不支持通用自纠正StructuredOutputValidationAdvisor校验-重试模型无关、可移植多一次往返可能更耗 token取舍追求稳定用原生但要评估厂商锁定追求可移植用通用自纠正。多数项目用通用自纠正 合理重试次数就能覆盖大部分场景。七、Jackson 3 提示2.0 序列化底座2.0 把序列化升级到 Jackson 3AI 模块提供了统一的JsonMapper/JsonHelper。影响如果你有自定义的ObjectMapper/序列化器升级 2.0 时要排查 Jackson 2→3 的兼容性详见第 4 篇迁移。结构化输出的反序列化也走这套字段命名建议用JsonProperty显式声明避免大小写/命名歧义。八、实战AI 抽取表单/订单为对象完整例子——把一段自然语言的订单描述抽取成可反序列化的对象列表RestControllerpublicclassOrderController{privatefinalChatClientchatClient;publicOrderController(ChatClient.Builderbuilder){this.chatClientbuilder.build();}GetMapping(/orders)publicListOrderextractOrders(RequestParamStringdesc){returnchatClient.prompt().user(从以下描述中抽取所有订单orderId、customerName、totalAmount\ndesc).call().entity(newParameterizedTypeReferenceListOrder(){});}}请求/orders?desc张三下单订单号A001金额99.9李四下单订单号A002金额199.0返回一个包含两条Order的 JSON 数组可直接反序列化使用。九、踩坑 FAQ坑 1返回 JSON 字段对不上 POJO反序列化报错现象模型返回的字段名和 POJO 不一致报反序列化异常。原因字段命名/大小写不一致或模型多返回了字段。解决用JsonProperty显式声明字段映射提示词里明确字段名挂自纠正 Advisor。坑 2自纠正超次数仍失败现象挂自纠正后仍然拿到不合规对象或报错。原因模型持续输出不合规 JSON超过重试上限。解决收紧 Schema/提示词描述、降低 temperature严谨任务用 0~0.3、换更强的模型。坑 3泛型集合用错 entity 重载现象想取ListOrder却拿到类型擦除错误。原因.entity(Order.class)拿不到泛型要用ParameterizedTypeReference。解决集合用entity(new ParameterizedTypeReferenceListOrder() {})。坑 4模型在 JSON 外多输出了解释文字现象返回内容前面多了好的以下是结果“之类的文字解析失败。原因模型没严格按 JSON 输出。解决提示词强调只返回 JSON”或挂自纠正 Advisor 处理。坑 5Jackson 3 下自定义 ObjectMapper 失效现象升级 2.0 后自定义序列化配置不生效。原因Jackson 2→3 升级部分 API 变了。解决排查自定义序列化代码适配 Jackson 3 与框架统一的JsonMapper。十、小结与合集导航本篇核心用.entity()让 AI 直接返回 Java 对象用StructuredOutputValidationAdvisor兜住模型偶尔不听话。.entity(Order.class)/entity(ParameterizedTypeReference)绑定对象与集合JSON Schema 约束 反序列化是底层机制自纠正 校验失败回喂重试provider 原生 vs 通用自纠正的取舍下一篇《RAG 原理与 ETL 管线》——进入知识库流量担当从喂资料讲起。十一、互动钩子投票你更倾向哪种结构化输出路线provider 原生通用自纠正 Advisor手动解析还在手工评论区聊聊你让 AI 输出过什么结构、踩过什么格式坑源码见置顶评论。