Spring AI 2.0.0 结构化输出教程ChatClient.entity 把模型结果转成 Java 对象Spring AI 2.0.0 写 AI 接口时很多人会先用.content()StringanswerchatClient.prompt().user(分析这段代码有什么风险code).call().content();如果只是把回答展示在页面上这样可以。但如果模型结果后面还要进入业务流程比如风险打标、人工审核、工单分流就不适合只拿一段字符串回来解析。这种场景更适合用 Spring AI 的结构化输出.entity(CodeReviewResult.class)后端拿到的是 Java 对象不是模型吐出来的一整段文本。本文环境示例环境Spring AI: 2.0.0 Spring Boot: 4.1.0 Chat Model: deepseek-v4-flash 接口: /code/review需要配置exportDEEPSEEK_API_KEY你的 DeepSeek API Key1. 定义返回对象示例做一个 Java 代码风险分析接口。先定义后端要接收的结构packagecom.example.springaideepseekdemo.dto;importjava.util.List;publicrecordCodeReviewResult(StringriskLevel,Stringsummary,ListStringsuggestions,booleanneedHumanReview){}这几个字段可以直接进入后续业务riskLevel风险等级 summary摘要 suggestions修改建议 needHumanReview是否需要人工确认字段名不要写得太泛。比如result、detail这种字段后端和模型都容易理解不一致。2. 添加依赖pom.xmldependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-deepseek/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency3. 配置 DeepSeek 和提示词application.yamlspring:application:name:springai-deepseek-demoai:deepseek:api-key:${DEEPSEEK_API_KEY}chat:model:deepseek-v4-flashapp:code-review:system-prompt:|你是一个资深 Java 代码审查助手。 只根据用户提供的代码做判断不要编造不存在的上下文。 riskLevel 只能返回 LOW、MEDIUM、HIGH 三种值。user-template:|请审查下面这段 Java 代码。代码{code}判断要求-如果可能导致线上异常riskLevel 返回 HIGH-如果只是可读性或小问题riskLevel 返回 LOW 或 MEDIUM-suggestions 给出可执行的修改建议-needHumanReview 表示是否需要人工确认后再上线提示词放到配置文件里后面改风险等级、审查口径、输出要求时不用进 Controller 翻字符串。4. Controller用 entity 返回对象packagecom.example.springaideepseekdemo.controller;importcom.example.springaideepseekdemo.dto.CodeReviewResult;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RestController;RestControllerpublicclassCodeReviewController{privatefinalChatClientchatClient;privatefinalStringuserTemplate;publicCodeReviewController(ChatClient.Builderbuilder,Value(${app.code-review.system-prompt})StringsystemPrompt,Value(${app.code-review.user-template})StringuserTemplate){this.chatClientbuilder.defaultSystem(systemPrompt).build();this.userTemplateuserTemplate;}PostMapping(/code/review)publicCodeReviewResultreview(RequestBodyStringcode){returnchatClient.prompt().user(u-u.text(userTemplate).param(code,code)).call().entity(CodeReviewResult.class);}}重点是.entity(CodeReviewResult.class)对比一下.content()返回 String .entity(...)返回指定 Java 类型接口返回CodeReviewResult后Spring MVC 会继续把它序列化成 JSON 返回给客户端。5. 运行测试编译./mvnw-DskipTestscompile启动./mvnw spring-boot:run调用curl-XPOSThttp://localhost:8080/code/review\-HContent-Type: text/plain\--data-binarypublic String getName(User user) { return user.getName(); }返回示例{riskLevel:HIGH,summary:user 可能为空直接调用 getName() 会触发空指针异常,suggestions:[在调用 user.getName() 前增加空值判断,根据业务需要返回默认值或抛出明确异常],needHumanReview:true}后端可以直接按字段处理if(HIGH.equals(result.riskLevel())){// 标记高风险}if(result.needHumanReview()){// 创建人工审核任务}6. entity 底层原理.entity(CodeReviewResult.class)本质上不是让模型返回 Java 对象。模型仍然生成文本。Spring AI 会在中间加一层StructuredOutputConverter。简化链路如下CodeReviewResult.class → BeanOutputConverter → 生成 JSON Schema 和格式要求 → 调用模型拿到 JSON 文本 → 清理文本 → Jackson 反序列化 → CodeReviewResult 对象BeanOutputConverter主要做三件事根据目标类型生成 JSON Schema 生成格式提示 把模型文本转换成 Java 对象Spring AI 会把格式要求放进请求上下文this.request.context().put(ChatClientAttributes.OUTPUT_FORMAT.getKey(),outputConverter.getFormat());默认调用链里的ChatModelCallAdvisor会读取这份格式要求并追加到用户消息中。模型返回后Spring AI 先拿到文本varstringResponsegetContentFromChatResponse(chatResponse);再调用 ConverterreturnoutputConverter.convert(stringResponse);convert(String text)会清理 markdown 代码块、前后空白等内容再反序列化成目标对象return(T)this.jsonMapper.readValue(text,this.jsonMapper.constructType(this.type));7. 常见 ConverterSpring AI 结构化输出里常见 ConverterBeanOutputConverter转 Java Bean / record MapOutputConverter转 Map ListOutputConverter转 List普通业务接口建议优先用明确的 record 或 class。字段明确后续做校验、日志、审计都更方便。8. provider structured output 和 validateSchemaSpring AI 2.0.0 还提供两个可选能力。provider 原生结构化输出.entity(CodeReviewResult.class,spec-spec.useProviderStructuredOutput())默认方式是把 schema 当成文本指令放进 prompt。useProviderStructuredOutput()则是把 schema 交给底层模型服务让 provider 用原生能力约束输出。注意不同模型和 provider 支持情况不一样所以它不是默认开启。schema 校验.entity(CodeReviewResult.class,spec-spec.validateSchema())这个选项会加入StructuredOutputValidationAdvisor。如果返回 JSON 不符合目标结构Spring AI 会把错误信息追加回 prompt再让模型重试。默认最多重试 3 次。使用时注意会增加耗时 validateSchema() 启用时不支持 streaming9. 常见问题结构化输出是不是一定稳定不是。它仍然依赖模型按要求输出再由 Spring AI 转换。生产环境要做业务校验if(!List.of(LOW,MEDIUM,HIGH).contains(result.riskLevel())){thrownewIllegalStateException(invalid riskLevel: result.riskLevel());}枚举字段怎么写在 prompt 里写清楚riskLevel 只能返回 LOW、MEDIUM、HIGH 三种值。结构化输出和 Tool Calling 有什么区别Tool Calling模型要调用哪个工具参数是什么 结构化输出模型最终回答要转成什么数据结构Tool Calling 是调能力结构化输出是接结果。业务规则能不能交给模型不要全交给模型。比如团队规则是 HIGH 风险必须人工确认就要在后端兜底booleanneedReviewresult.needHumanReview()||HIGH.equals(result.riskLevel());模型负责分析后端负责规则。总结Spring AI 2.0.0 结构化输出的核心不是“让模型返回 JSON”而是让模型结果能进入 Java 工程系统。如果结果只是展示给人看.content()就够。如果结果要继续被程序处理优先定义返回对象再用.entity(...)接住模型输出。配套代码按文章编号放在对应分支方便对照运行。