Spring AI:结构化输出

Spring AI 结构化输出转换器 将 LLM 输出转换为结构化格式。

使用通用完成 API 从大型语言模型 (LLM) 生成结构化输出需要仔细处理输入和输出。结构化输出转换器在 LLM 调用之前和之后都起着关键作用,确保实现所需的输出结构。 在 LLM 调用之前,转换器将格式指令附加到提示中,为模型提供生成所需输出结构的明确指导。这些指令充当蓝图,塑造模型的响应以符合指定的格式。

可用转换器

目前,Spring AI 提供了 AbstractConversionServiceOutputConverterAbstractMessageOutputConverterBeanOutputConverterMapOutputConverterListOutputConverter 实现

其中BeanOutputConverter<T>MapOutputConverterListOutputConverter配置了默认的 FormatProvider 实现

转换器的两种种使用方式

BeanOutputConverter<T>为例子演示

表示演员电影作品的目标记录

arduino 复制代码
record ActorsFilms(String actor, List<String> movies) {
}

以下是使用ChatClient API 应用 BeanOutputConverter 的方法

scss 复制代码
ActorsFilms actorsFilms = ChatClient.create(chatModel).prompt()
        .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
                    .param("actor", "Tom Hanks"))
        .call()
        .entity(ActorsFilms.class);

或直接使用低级 ChatModel API

ini 复制代码
BeanOutputConverter<ActorsFilms> beanOutputConverter =
    new BeanOutputConverter<>(ActorsFilms.class);

String format = this.beanOutputConverter.getFormat();

String actor = "Tom Hanks";

String template = """
        Generate the filmography of 5 movies for {actor}.
        {format}
        """;

Generation generation = chatModel.call(
    PromptTemplate.builder().template(this.template).variables(Map.of("actor", this.actor, "format", this.format)).build().create()).getResult();

ActorsFilms actorsFilms = this.beanOutputConverter.convert(this.generation.getOutput().getText());

对比使用结构化输出对返回内容的影响(使用chatclientapi)

BeanOutputConverter<T>

调用代码:

scss 复制代码
    record ActorsFilms(String actor, List<String> movies) {
    }
String structuredPrompt = "Generate the 5 movies that {actor} has performed in. ";
ActorsFilms actorsFilms = ChatClient.create(dashscopeChatModel).prompt()
        .user(u -> u.text(structuredPrompt)
                .param("actor", "Robert Downey Jr."))
        .call()
        .entity(ActorsFilms.class);
String plainText = ChatClient.create(dashscopeChatModel).prompt()
        .user(u -> u.text(structuredPrompt)
                .param("actor", "Robert Downey Jr."))
        .call()
        .content();
printComparison("beanOutputConverterWithChatClient", actorsFilms, plainText);

输出结果对比:

MapOutputConverter

调用代码

vbnet 复制代码
        Map<String, Object> result = ChatClient.create(dashscopeChatModel).prompt()
                .user(u -> u.text("Provide me a List of {subject}")
                        .param("subject", "Books You Must Read at Different Stages of Life."))
                .call()
                .entity(new ParameterizedTypeReference<Map<String, Object>>() {
                });
        String plainText = ChatClient.create(dashscopeChatModel).prompt()
                .user(u -> u.text("Provide me a List of {subject}")
                        .param("subject", "Books You Must Read at Different Stages of Life."))
                .call()
                .content();
        printComparison("mapOutputConverterWithChatClient", result, plainText);

输出结果对比:

结构化输出:result

非结构化输出:

ListOutputConverter

调用代码:

scss 复制代码
        List<String> flavors = ChatClient.create(dashscopeChatModel).prompt()
                .user(u -> u.text("List five {subject}")
                        .param("subject", "ice cream flavors"))
                .call()
                .entity(new ListOutputConverter(new DefaultConversionService()));
        String plainText = ChatClient.create(dashscopeChatModel).prompt()
                .user("List five ice cream flavors.")
                .call()
                .content();
        printComparison("listOutputConverterWithChatClient", flavors, plainText);

输出结果对比:

相关推荐
就叫_这个吧7 分钟前
Java递归方法实现面包屑导航
java·开发语言
To_OC9 分钟前
面试被问了三回三栏布局,这次我终于把 BFC 那层窗户纸捅破了
前端·css·面试
fīɡЙtīиɡ ℡23 分钟前
AI 应用系统设计
java·开发语言·人工智能
城管不管31 分钟前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅31 分钟前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
To_OC39 分钟前
啃完 TS 工具类型我发现:Pick 和 Omit 原来就是一层窗户纸
前端·面试·typescript
AI绘画哇哒哒44 分钟前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
最强小杰1 小时前
gpt-5.6-sol 频繁报 503 怎么办?区分容量熔断和限速 429 的排查方法 + 可复用 retry wrapper
java·人工智能·gpt·ai
唐青枫2 小时前
一个值多种形态:Zig union、Tagged Union 与内存布局实战
后端
卷无止境2 小时前
Windows 上丝滑开发 Python,并稳定构建 Docker 镜像
后端·python·docker