掘金 人工智能 07月09日 11:14
Spring AI 骚操作:让大模型乖乖听话,直接返回 Java 对象!
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了 Spring AI 的结构化输出转换器,它能将大语言模型(LLM)的非结构化文本精准转换为 JSON、XML 或 Java 对象等结构化数据,简化了开发者处理 AI 输出的流程。文章详细阐述了其工作原理,包括在提示词中加入格式指令和将文本转换为指定 Java 类实例。此外,文章还提供了多种开箱即用的转换器,并给出了实战案例,展示了如何通过几行代码实现 AI 旅游报告的生成,并总结了使用结构化输出的四大技巧。

💡结构化输出转换器通过在提示词中添加格式指令,明确告知模型输出数据的格式,确保输出符合预期。

✨Spring AI 提供了多种转换器,如 BeanOutputConverter、MapOutputConverter 和 ListOutputConverter,满足不同场景的需求,简化了开发者的数据处理工作。

✅结构化输出转换器支持主流 AI 模型,包括 OpenAI、Azure OpenAI、Ollama 和 Mistral AI 等,且部分模型提供内置 JSON 模式,增强了输出的可靠性。

🚀实战案例演示了如何使用结构化输出生成旅游报告,只需定义 Java Record 类,并使用 .entity() 方法即可将 AI 输出转换为结构化对象。

💡结构化输出的四大心法包括:指令清晰、验证兜底、选对模型、巧用泛型,帮助开发者更好地利用该功能。

还在为解析大模型返回的非结构化文本而头疼吗?还在用一堆 if-else 和正则表达式做着繁琐的字符串切割吗?现在,有了 Spring AI 的 结构化输出转换器(Structured Output Converter),这一切都将成为过去式!

这个神器能将大语言模型(LLM)返回的原始文本,精准地转换为你想要的任何结构化数据,无论是 JSON、XML 还是一个具体的 Java 对象。对于需要稳定、可靠地处理 AI 输出的应用程序来说,这简直是天降福音!

工作原理:AI 如何秒懂你的数据需求?

结构化输出转换器的魔法主要分两步:

图1: 结构化输出工作流:先“约法三章”,再“格式转换”

温馨提示:结构化输出转换器会尽最大努力(Best-Effort)完成任务。但 AI 模型偶尔也会“调皮”,不完全按指令办事。因此,在代码中加入验证和异常处理机制,是保证程序健壮性的好习惯。

深入探秘:揭开 StructuredOutputConverter 的神秘面纱

想知道这背后的技术实现吗?核心在于 StructuredOutputConverter<T> 接口,它像一个多面手,同时扮演两个角色:

public interface StructuredOutputConverter<T> extends Converter<String, T>, FormatProvider {}
public interface FormatProvider {    String getFormat();}

Spring AI 已经内置了多种开箱即用的转换器,满足你不同场景的需求:

它们的家族关系如下图所示:

图2: StructuredOutputConverter 核心类图

现在,我们再来梳理一遍完整的工作流程:

    生成格式指令FormatProvider 会生成类似下面的指令,并附加到你的提示词中。这相当于给 AI 划重点,告诉它必须按这个 JSON Schema 来回答。
Your response should be in JSON format.The data structure for the JSON should match this Java class: java.util.HashMapDo not include any explanations, only provide a RFC8259 compliant JSON response...

通常,我们会用 PromptTemplate 来优雅地实现这一点:

StructuredOutputConverter outputConverter = ...String userInputTemplate = """        ... 你的业务提示词 ....        {format}         """; // 预留一个 {format} 占位符Prompt prompt = new Prompt(        new PromptTemplate(                userInputTemplate,                Map.of(..., "format", outputConverter.getFormat()) // 将格式指令填入占位符        ).createMessage());
    转换输出Converter 将模型返回的 JSON 字符串,反序列化为你指定的 Java 对象。

整个过程无缝衔接,对开发者极其友好。

图3: 提示词与转换器协同工作流程

上手实战:三行代码,让 AI 输出“言听计从”

官方文档提供了丰富的示例,我们来看几个最经典的。

    BeanOutputConverter:将 AI 输出直接转换为自定义 Java 类。
// 1. 定义一个简单的 Java Recordrecord ActorsFilms(String actor, List<String> movies) {}// 2. 一行代码调用并完成转换!ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()        .user("Generate 5 movies for Tom Hanks.")        .call()        .entity(ActorsFilms.class); // 指定目标类型,搞定!

处理复杂的泛型列表也同样简单,只需使用 ParameterizedTypeReference

// 轻松转换为对象列表List<ActorsFilms> actorsFilms = ChatClient.create(chatModel).prompt()        .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")        .call()        .entity(new ParameterizedTypeReference<List<ActorsFilms>>() {});
    MapOutputConverter:将输出转换为 Map。
Map<String, Object> result = ChatClient.create(chatModel).prompt()        .user(u -> u.text("Provide me a List of {subject}")                    .param("subject", "an array of numbers from 1 to 9 under they key name 'numbers'"))        .call()        .entity(new ParameterizedTypeReference<Map<String, Object>>() {});
    ListOutputConverter:将输出转换为字符串列表。
List<String> flavors = ChatClient.create(chatModel).prompt()                .user(u -> u.text("List five {subject}")                            .param("subject", "ice cream flavors"))                .call()                .entity(new ListOutputConverter(new DefaultConversionService()));

兼容性王者:主流模型全支持

根据官方文档,以下主流 AI 模型均已通过测试,完美支持 ListMapBean 结构化输出:

AI 模型示例测试代码
OpenAIOpenAiChatModelIT
Anthropic Claude 3AnthropicChatModelIT.java
Azure OpenAIAzureOpenAiChatModelIT.java
Mistral AIMistralAiChatModelIT.java
OllamaOllamaChatModelIT.java
Vertex AI GeminiVertexAiGeminiChatModelIT.java

更棒的是,许多模型提供了内置 JSON 模式,这让结构化输出的可靠性更上一层楼。启用后,模型会保证输出严格符合 JSON 格式。

实战演练:AI 变身“旅游规划师”,自动生成旅行报告

下面,我们来构建一个实用的功能:让 AI 为用户生成一份包含标题和建议列表的旅游报告。

    引入 JSON Schema 依赖:这是让转换器理解 Java 类结构的关键。
<dependency>    <groupId>com.github.victools</groupId>    <artifactId>jsonschema-generator</artifactId>    <version>4.38.0</version></dependency>
    定义旅游报告类:使用 Java Record,代码简洁优雅。
record SightSeeingReport(String title, List<String> suggestions) {}
    编写业务代码:在原有的 ChatClient 基础上,只需一行 .entity() 即可实现结构化输出。
/** * 为用户生成一份专属的旅游报告 * @param message 用户的问题 * @param chatId  会话ID * @return 结构化的旅游报告对象 */public SightSeeingReport doChatWithReport(String message, String chatId) {    SightSeeingReport sightSeeingReport = chatClient            .prompt()            // 强化系统提示,要求生成包含标题和建议的报告            .system(SYSTEM_PROMPT + "每次对话后都要生成旅游结果,标题为{用户名}的旅游报告,内容为建议列表")            .user(message)            .advisors(spec ->                    spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)            )            .call()            // 魔法发生的地方!直接将结果转换为 SightSeeingReport 对象            .entity(SightSeeingReport.class);    log.info("AI 生成的旅游报告: {}", sightSeeingReport);    return sightSeeingReport;}
    编写单元测试
@Resourceprivate App app;
@Testvoid doChatWithReport() {    String chatId = UUID.randomUUID().toString();    String message = "你好,我想去北京旅游,请帮我规划一下";    App.SightSeeingReport sightSeeingReport = app.doChatWithReport(message, chatId);    Assertions.assertNotNull(sightSeeingReport);}

运行测试,通过 Debug 我们可以清晰地看到,框架自动将我们的 SightSeeingReport 类转换为了详细的 JSON Schema,并添加到了提示词中,指导 AI 生成了我们期望的 JSON 格式数据,并最终成功转换为了 SightSeeingReport 对象实例。整个过程如丝般顺滑!

图4: Debug 模式下查看自动生成的格式指令和最终转换的对象

格式指令的完整内容如下,我们发现对象被转换为了 JSON Schema 描述语言:

Your response should be in JSON format.Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.Do not include markdown code blocks in your response.Remove the ```json markdown from the output.Here is the JSON Schema instance your output must adhere to:```{  "$schema" : "https://json-schema.org/draft/2020-12/schema",  "type" : "object",  "properties" : {    "suggestions" : {      "type" : "array",      "items" : {        "type" : "string"      }    },    "title" : {      "type" : "string"    }  },  "additionalProperties" : false}```

AI 生成的内容如图,是 JSON 格式文本:

转换器成功将 JSON 文本转换为了对象:

高手秘籍:用好结构化输出的四大心法

    指令清晰:给模型的格式指导越清晰、越具体越好。验证兜底:务必实现输出验证和异常处理逻辑,应对 AI 的“小脾气”。选对模型:优先选择官方支持或提供内置 JSON 模式的模型,可靠性更高。巧用泛型:处理复杂数据结构(如 List<Map<String, MyObject>>)时,ParameterizedTypeReference 是你的得力助手。

Spring AI 的结构化输出功能,极大地简化了与大模型交互的复杂度,让开发者能更专注于业务逻辑,而不是繁琐的数据解析。它就像一座桥梁,无缝连接了 AI 的创造力与 Java 应用的严谨性。

赶快在你的项目中试试吧!体验一下让 AI “言听计从”的快感。

你对 Spring AI 的哪个特性最感兴趣?欢迎在评论区留言讨论! 如果觉得本文对你有帮助,别忘了点赞、在看、分享三连哦!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Spring AI 结构化输出 大模型 Java AI
相关文章