Spring AI 实战系列 | 第 3.1 篇:结构化输出 系列说明AI 返回一大段文字你要从中提取数据别用正则表达式了。Spring AI 的结构化输出功能让 AI 直接返回你需要的对象。前置知识已掌握 ChatClient 和 Prompt 基础用法。前言我做过的第一个 AI 项目需要让 AI 从用户评论中提取情感评分和关键词。当时的做法是这样的StringresponsechatClient.prompt().user(分析这条评论的情感和关键词comment).call().content();// 然后用正则表达式从 response 里提取数据...// 结果 AI 每次格式都不一样正则根本写不完后来才发现 Spring AI 有BeanOutputConverter能让 AI 直接返回 JSON自动映射成 Java 对象。这篇把这个功能讲透。一、BeanOutputConverterAI 返回 POJO1.1 定义输出实体publicrecordSentimentAnalysis(Stringsentiment,// positive / negative / neutralintscore,// 1-10ListStringkeywords,Stringsummary){}1.2 使用 BeanOutputConverterimportorg.springframework.ai.converter.BeanOutputConverter;ServicepublicclassCommentService{privatefinalChatClientchatClient;publicCommentService(ChatClient.Builderbuilder){this.chatClientbuilder.build();}publicSentimentAnalysisanalyze(Stringcomment){BeanOutputConverterSentimentAnalysisconverternewBeanOutputConverter(SentimentAnalysis.class);StringresponsechatClient.prompt().user(分析以下评论的情感倾向\ncomment).call().content();returnconverter.convert(response);}}BeanOutputConverter会自动把SentimentAnalysis.class的 JSON Schema 注入到 Prompt 中要求 AI 按 Schema 返回 JSON把 AI 返回的 JSON 映射成 Java 对象1.3 更简洁的写法Spring AI 1.1 支持直接在call()后链式转换publicSentimentAnalysisanalyze(Stringcomment){BeanOutputConverterSentimentAnalysisconverternewBeanOutputConverter(SentimentAnalysis.class);returnchatClient.prompt().user(分析以下评论\ncomment).call().entity(converter);}二、MapOutputConverter 与 ListOutputConverter2.1 MapOutputConverter如果你不想定义实体类直接用 Mapimportorg.springframework.ai.converter.MapOutputConverter;publicMapString,ObjectextractInfo(Stringtext){MapOutputConverterconverternewMapOutputConverter();StringjsonchatClient.prompt().user(从以下文本中提取人物信息\ntext).call().content();returnconverter.convert(json);}返回的 Map 结构可能是{name:张三,age:30,city:北京}2.2 ListOutputConverter让 AI 返回一个列表importorg.springframework.ai.converter.ListOutputConverter;publicListStringextractKeywords(Stringtext){ListOutputConverterconverternewListOutputConverter(newDefaultConversionService());StringresultchatClient.prompt().user(从以下文章中提取 5 个关键词\ntext).call().content();returnconverter.convert(result);}三、自定义输出转换器如果内置的转换器不够用可以实现OutputConverter接口自定义。3.1 实现自定义转换器publicclassCustomOutputConverterimplementsOutputConverterCustomResult{privatefinalObjectMapperobjectMappernewObjectMapper();OverridepublicCustomResultconvert(Stringtext){try{// 清理 AI 可能添加的 markdown 代码块标记StringcleanJsontext.replaceAll(json\\s*,).replaceAll(\\s*,);returnobjectMapper.readValue(cleanJson,CustomResult.class);}catch(JsonProcessingExceptione){thrownewRuntimeException(解析 AI 输出失败: text,e);}}OverridepublicStringgetFormat(){return以 JSON 格式返回不要添加 markdown 标记;}}3.2 使用自定义转换器publicCustomResultanalyze(Stringtext){CustomOutputConverterconverternewCustomOutputConverter();returnchatClient.prompt().user(分析以下内容\ntext).call().entity(converter);}四、JSON Schema 约束输出对于复杂的输出结构可以显式指定 JSON Schema让 AI 严格按 Schema 输出。4.1 定义复杂实体publicrecordProductReview(StringproductName,intrating,// 1-5ListStringpros,ListStringcons,Stringverdict,ListAspectScoreaspectScores){}publicrecordAspectScore(Stringaspect,// 外观、性能、性价比...intscore// 1-10){}4.2 生成 JSON SchemaBeanOutputConverterProductReviewconverternewBeanOutputConverter(ProductReview.class);// 获取自动生成的 SchemaStringschemaconverter.getJsonSchema();System.out.println(schema);生成的 Schema 大概长这样{type:object,properties:{productName:{type:string},rating:{type:integer,minimum:1,maximum:5},pros:{type:array,items:{type:string}},cons:{type:array,items:{type:string}},verdict:{type:string},aspectScores:{type:array,items:{type:object,properties:{aspect:{type:string},score:{type:integer}}}}}}4.3 手动注入 Schema有时候你想更精细地控制 Schema可以手动构造StringcustomSchema { type: object, properties: { title: {type: string, maxLength: 100}, tags: {type: array, items: {type: string}, maxItems: 5} }, required: [title, tags] } ;// 结合 Prompt 使用StringresponsechatClient.prompt().user(u-u.text(生成文章标题和标签\n主题{topic})).system(请严格按照以下 JSON Schema 输出\ncustomSchema).call().content();五、实战智能表单填充一个完整的实战示例让用户用自然语言描述需求AI 自动填充表单。5.1 表单实体publicrecordTravelPlan(Stringdestination,LocalDatestartDate,LocalDateendDate,inttravelers,ListStringpreferences,// 美食、购物、景点...Budgetbudget){}publicrecordBudget(inttotalBudget,// 总预算元Stringcurrency){}5.2 服务层ServicepublicclassTravelPlanner{privatefinalChatClientchatClient;publicTravelPlanner(ChatClient.Builderbuilder){this.chatClientbuilder.build();}publicTravelPlanplan(StringuserInput){BeanOutputConverterTravelPlanconverternewBeanOutputConverter(TravelPlan.class);returnchatClient.prompt().system( 你是一位旅行规划助手。请从用户的自然语言描述中提取旅行信息 并按指定格式返回。如果信息缺失使用合理的默认值。 ).user(userInput).call().entity(converter);}}5.3 调用示例// 用户输入Stringinput我想下个月带老婆孩子去三亚玩一周预算大概一万五 主要是想海边放松吃点海鲜;TravelPlanplantravelPlanner.plan(input);// 输出// TravelPlan[destination三亚, startDate2026-07-01, endDate2026-07-08,// travelers3, preferences[海边, 美食],// budgetBudget[totalBudget15000, currencyCNY]]六、常见问题Q1AI 返回的 JSON 格式不对怎么办在 System Message 里明确说明只返回 JSON不要添加 markdown 标记使用BeanOutputConverter它会自动注入 Schema如果还是不行在 Prompt 里给 Few-Shot 示例Q2字段类型不匹配怎么办确保实体类的类型和 AI 返回的数据一致。比如 AI 返回age: 30字符串但你的字段是int age就会解析失败。可以在 Prompt 里明确要求类型注意age 必须是整数不要加引号Q3中文乱码Spring AI 内部使用 UTF-8一般不会有乱码问题。如果出现检查项目编码是否为 UTF-8数据库编码是否为 UTF-8前端请求头Content-Type: application/json; charsetutf-8写在最后结构化输出是 Spring AI 最实用的功能之一。它把 AI 从聊天工具变成了可编程接口让 AI 的输出可以直接被代码消费。下一篇讲多模态输入包括图片理解、文档解析等能力。系列目录第 1 篇Spring AI 概述与快速上手第 1.2 篇环境准备与第一个项目第 1.3 篇核心概念速览第 2.1 篇ChatClient 详解第 2.2 篇多模型提供商接入第 2.3 篇Prompt 工程化第 3.1 篇结构化输出 ✅本文第 3.2 篇多模态输入第 3.3 篇文生图与语音第 4 篇Embedding 与向量数据库第 5 篇RAG 检索增强生成第 6 篇Tool Calling 工具调用第 7 篇Advisor 机制与对话管理第 8 篇MCP 模型上下文协议第 9 篇AI Agent 开发实战第 10 篇企业级应用与最佳实践如果这篇文章对你有帮助欢迎点赞收藏关注系列持续更新中