第七章 结构化输出与对象映射版本标注Spring AI:1.1.2Spring AI Alibaba:1.1.2.0章节定位结构化输出在1.1.2.x中的价值更高除了对象映射还常用于 Agent 路由决策、参数解析、工作流分类与节点控制。s01 s02 s03 s04 s05 s06 [ s07 ] s08 s09 s10 s11 s12 s13 s14 s15 s16 s17 s18让模型返回对象, 比让人手动拆字符串靠谱得多-- 结构化输出的核心价值是稳定和可编程。一、为什么需要结构化输出1.1 自然输出的问题默认情况下AI 的输出是纯文本的比如你问帮我返回一个学生信息AI 会返回学生信息如下 姓名张三 学号1001 专业计算机科学与技术 邮箱zzyybs126.com但是在实际开发中我们往往需要把返回值存到数据库在前端展示成表格传递给其他接口如果让 AI 返回一堆文字我们需要手动写解析代码非常麻烦。1.2 结构化输出的价值让 AI 直接返回一个结构化的对象JSON、Java对象{ name: 张三, studentId: 1001, major: 计算机科学与技术, email: zzyybs126.com }这样我们可以直接用 Jackson 解析成 Java 对象在前端用 Vue/React 的表格组件展示传递给其他微服务接口存入数据库二、核心概念Record 类型2.1 什么是 RecordJava 14 引入了Record记录类它是一种特殊的类专门用于存储数据。// 普通类 public class Student { private String name; private String studentId; private String major; private String email; // 需要 getter/setter/构造函数... } // Record简洁多了 public record StudentRecord( String name, // 自动生成 final 字段 String studentId, // 自动生成构造函数 String major, // 自动生成 getter 方法但叫 getName() 而是 name() String email // 自动生成 equals(), hashCode(), toString() ) {}Record 的特点所有字段默认public final自动生成构造函数自动生成equals()、hashCode()、toString()代码超级简洁2.2 Spring AI 中的结构化输出Spring AI 提供了.entity(Class)方法可以直接将 AI 的输出映射到 Java 对象// 调用 AI并指定返回类型为 StudentRecord StudentRecord student chatClient.prompt() .user(学号1001我叫张三专业计算机邮箱zzyybs126.com) .call() .entity(StudentRecord.class); // 直接得到 Java 对象三、项目代码详解3.1 定义 Record 类首先创建两个 Record 类来接收 AI 返回的数据// 文件位置src/main/java/com/atguigu/study/records/StudentRecord.java package com.atguigu.study.records; public record StudentRecord( String name, // 姓名 String studentId, // 学号 String major, // 专业 String email // 邮箱 ) {} // 文件位置src/main/java/com/atguigu/study/records/Book.java package com.atguigu.study.records; public record Book( String title, // 书名 String author, // 作者 double price, // 价格 String publishDate // 出版日期 ) {}3.2 控制器代码package com.atguigu.study.controller; import com.atguigu.study.records.StudentRecord; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.function.Consumer; /** * 结构化输出控制器 * 展示如何让 AI 返回结构化的 Java 对象Record */ RestController public class StructuredOutputController { // 注入 Qwen 的 ChatClient Resource(name qwenChatClient) private ChatClient qwenChatClient; /** * 方式一使用 Consumer 风格的参数设置推荐 * * 核心方法.entity(RecordClass.class) * 自动把 AI 返回的 JSON 解析成指定的 Java Record 对象 * * 接口http://localhost:8007/structuredoutput/chat?sname李四emailzzyybs126.com */ GetMapping(/structuredoutput/chat) public StudentRecord chat(RequestParam(name sname) String sname, RequestParam(name email) String email) { // 使用 Consumer 风格的 API 设置用户消息 // promptUserSpec.text() 定义模板.param() 替换变量 return qwenChatClient.prompt().user(new ConsumerChatClient.PromptUserSpec() { Override public void accept(ChatClient.PromptUserSpec promptUserSpec) { // text() 方法中使用 {sname} {email} 作为占位符 // .param() 方法将实际参数值填充进去 promptUserSpec.text(学号1001我叫{sname},大学专业计算机科学与技术,邮箱{email}) .param(sname, sname) // 替换 {sname} .param(email, email); // 替换 {email} } }) // .entity(StudentRecord.class) 是关键 // 告诉 AI 返回 JSON 格式然后自动映射成 StudentRecord 对象 .call() .entity(StudentRecord.class); } /** * 方式二更简洁的 Lambda 写法 * * 接口http://localhost:8007/structuredoutput/chat2?sname孙伟emailzzyybs126.com */ GetMapping(/structuredoutput/chat2) public StudentRecord chat2(RequestParam(name sname) String sname, RequestParam(name email) String email) { // 定义模板字符串使用占位符 String stringTemplate 学号1002我叫{sname},大学专业软件工程,邮箱{email} ; // 使用 Lambda 简化版的 param 设置 return qwenChatClient.prompt() // text() 设置模板.param() 替换变量 .user(promptUserSpec - promptUserSpec.text(stringTemplate) .param(sname, sname) .param(email, email)) .call() // 关键将 AI 返回的数据映射成 StudentRecord 对象 .entity(StudentRecord.class); } // 下面是 Book 的示例类似 // GetMapping(/structuredoutput/book) // public Book getBook(...) { ... } }3.3 Consumer 匿名类 vs Lambda 表达式详解在上面的代码中方式一和方式二在功能上完全等价只是语法形式不同。理解它们的区别有助于你更好地掌握 Java 8 引入的 Lambda 特性。3.3.1. 核心区别维度方式一方式二语法匿名内部类传统 Java 写法Lambda 表达式Java 8 写法代码量多包含new、Override、accept等样板代码极少一行搞定底层对象都是ConsumerPromptUserSpec的实现实例同上3.3.2. 为什么方式二不用写方法名就能自动识别原理在于函数式接口Functional Interface。在方式一中user()方法要求的参数类型是ConsumerPromptUserSpec。而Consumer接口的定义长这样FunctionalInterface public interface ConsumerT { void accept(T t); // 只有一个抽象方法 }Java 规定只要一个接口只有一个抽象方法Single Abstract Method, SAM它就可以用 Lambda 表达式代替。编译器在解析方式二时会自动做以下推断这个位置需要一个Consumer类型的参数Consumer接口只有一个抽象方法叫accept所以 Lambda 的参数promptUserSpec就是accept方法的参数Lambda 的箭头右边- ...就是accept的方法体因此你不需要写方法名编译器会自动把 Lambda 映射到accept方法上。底层通常会通过invokedynamic指令在运行时生成对应的实现类。3.3.3. 一句话总结方式二是方式一的Lambda 语法糖。因为Consumer是函数式接口Java 允许你省略接口名和方法名直接写“参数 - 逻辑”编译器会自动补全成accept方法。日常开发强烈推荐方式二。四、底层原理分析4.1 AI 返回 JSON 的原理当你调用.entity(StudentRecord.class)时Spring AI 内部会发送请求时在 Prompt 中自动加上请以JSON格式返回这样的指令接收响应时AI 返回的文本假设是 JSON 格式解析时使用 Jackson 或其他 JSON 库把 JSON 解析成指定的 Record 对象整个过程对你来说是透明的你只需要关心输入给 AI 描述清楚需要哪些字段输出直接拿到 Java 对象4.2 为什么用 Record 而不用普通类// 普通类 public class Student { private String name; private String studentId; // 需要手动写 // private 字段 // public getter/setter // 无参构造函数 // 全参构造函数 // equals/hashCode/toString // ... 一大坨代码 } // Record自动帮你生成 public record StudentRecord( String name, String studentId ) {} // 一行搞定Spring AI 的结构化输出功能会自动把 JSON 映射到字段名称匹配的 Record 上特别方便五、结构化输出的最佳实践5.1 Prompt 中明确字段要求// ❌ 模糊的描述AI可能返回不完整的结构 prompt: 返回一个学生信息 // ✅ 明确的描述AI会按照要求的字段返回 prompt: 返回一个学生信息JSON格式包含以下字段 - name: 姓名字符串 - studentId: 学号字符串 - major: 专业字符串 - email: 邮箱字符串 5.2 字段命名建议// AI 返回 JSON 通常是 camelCase {name: 张三, studentId: 1001} // 使用 Record 时字段名要和 JSON 的 key 对应 public record StudentRecord( String name, // 对应 name String studentId, // 对应 studentId String major, String email ) {} // 如果 JSON 用 snake_case需要加 JsonProperty public record StudentRecord( JsonProperty(student_id) String studentId // 对应 student_id ) {}六、本章小结6.1 核心技能技能说明RecordJava 的简洁数据类自动生成 equals/toString.entity(Class)Spring AI 的核心方法将 AI 输出映射为 Java 对象JsonPropertyJackson 注解处理 JSON 和 Java 字段名不一致问题6.2 使用流程定义Record类 ── 构建Prompt ── 调用.entity(RecordClass) ── 获得Java对象 ↓ AI智能理解需求返回JSON ── 自动解析 ── 注入到Record的字段中本章重点Java Record 的基本使用如何让 AI 返回结构化数据JSON使用.entity()方法实现自动映射下章剧透s08学会了让 AI 返回结构化数据后下一章我们将学习持久化会话——如何用 Redis 保存对话历史让 AI 记住之前的上下文。编辑者Flittly更新时间2026年4月相关资源Spring AI Structured Output