格式化输出Structured Output

1.什么是格式化输出

如果您想从 LLM 接收结构化输出,Structured Output 可以协助将 ChatModel/ChatClient 方法的返回类型从 String 更改为其他类型。

LLM 生成结构化输出的能力对于依赖可靠解析输出值的下游应用程序非常重要。开发人员希望快速将 AI 模型的结果转换为可以传递给其他应用程序函数和方法的数据类型,例如 JSON、XML 或 Java 类。Spring AI 结构化输出转换器有助于将 LLM 输出转换为结构化格式。

在 LLM 调用之前,转换器会将期望的输出格式(output format instruction)附加到 prompt 中,为模型提供生成所需输出结构的明确指导,这些指令充当蓝图,塑造模型的响应以符合指定的格式。以下是此类格式说明的示例

java 复制代码
Your response should be in JSON format.
The data structure for the JSON should match this Java class: java.util.HashMap
Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.

在 LLM 调用之后,转换器获取模型的输出文本并将其转换为结构化类型的实例,此转换过程涉及解析原始文本输出并将其映射到相应的结构化数据表示,例如 JSON、XML 或特定于域的数据结构添加链接描述

2. 新建子模块SAA-07StructuredOutput

其余与前几个子模块中内容大同小异,pom文件中去除lombok,07子模块中使用Record。

2.1. pom文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.hf.hong</groupId>
        <artifactId>SpringAIAlibaba-v1</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>SAA-07StructuredOutput</artifactId>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 引入 springai alibaba DashScope 模型适配的 Starter -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--lombok-->
<!--        <dependency>-->
<!--            <groupId>org.projectlombok</groupId>-->
<!--            <artifactId>lombok</artifactId>-->
<!--            <optional>true</optional>-->
<!--        </dependency>-->
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

2.2. application.properties

yaml 复制代码
server.port=8007

#大模型对话中文乱码UTF8编码处理
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-07StructuredOutput

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}

2.3.配置类直接复制粘贴

2.4. StuRecord

java 复制代码
package com.hf.hong.entity;

public record StuRecord(String sId,String sName,Integer sAge,String sMajor,String sEmail) {
}

2.5. StructuredOutputController

java 复制代码
package com.hf.hong.controller;

import com.hf.hong.entity.StuRecord;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.function.Consumer;

/**
 * 
 * @author admin
 * @date 2026/3/13 20:39
 * @description: 结构化输出,本代码以Record为例
 */

@RestController
public class StructuredOutputController {
    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekChatClient;
    @Resource(name = "qwenChatClient")
    private ChatClient qwenChatClient;
    @Resource(name = "deepSeekChatModel")
    private ChatModel deepSeekChatModel;


    @GetMapping("/structuredOutput/testRecord")
    public StuRecord testRecord(String sname, String email) {
        return qwenChatClient.prompt()
                .user(new Consumer<ChatClient.PromptUserSpec>() {
                    @Override
                    public void accept(ChatClient.PromptUserSpec promptUserSpec)
                    {
                        promptUserSpec.text("学号001,我叫{sname},今年18,大学专业是计算机科学与技术,邮箱{email}")
                                .param("sname",sname)
                                .param("email",email);
                    }
                }).call().entity(StuRecord.class);
    }

}

3. 测试

bash 复制代码
http://localhost:8007/structuredOutput/testRecord?sname=%E5%B0%8F%E7%94%9F%E4%B8%8D%E6%89%8D&email=hst14**@163.com

切换lambda版

java 复制代码
    @GetMapping("/structuredOutput/testRecord2")
    public StuRecord testRecord2(@RequestParam(name = "sname") String sname, @RequestParam(name = "email") String email) {
        String stringTemplate = """
                学号002,我叫{sname},大学专业是软件工程,邮箱{email}
                """;
        return qwenChatClient.prompt()
                .user(promptUserSpec -> promptUserSpec.text(stringTemplate)
                        .param("sname",sname)
                        .param("email",email))
                .call()
                .entity(StuRecord.class);
    }
}

测试

bash 复制代码
http://localhost:8007/structuredOutput/testRecord2?sname=%E5%B0%8F%E7%94%9F%E4%B8%8D%E6%89%8D&email=hst14**@163.com

4. 完整代码

java 复制代码
package com.hf.hong.controller;

import com.hf.hong.entity.StuRecord;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
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;

/**
 *
 * @author admin
 * @date 2026/3/13 20:39
 * @description: 结构化输出,本代码以Record为例
 */

@RestController
public class StructuredOutputController {
    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekChatClient;
    @Resource(name = "qwenChatClient")
    private ChatClient qwenChatClient;
    @Resource(name = "deepSeekChatModel")
    private ChatModel deepSeekChatModel;


    @GetMapping("/structuredOutput/testRecord")
    public StuRecord testRecord(String sname, String email) {
        return qwenChatClient.prompt()
                .user(new Consumer<ChatClient.PromptUserSpec>() {
                    @Override
                    public void accept(ChatClient.PromptUserSpec promptUserSpec)
                    {
                        promptUserSpec.text("学号001,我叫{sname},今年18,大学专业是计算机科学与技术,邮箱{email}")
                                .param("sname",sname)
                                .param("email",email);
                    }
                }).call().entity(StuRecord.class);
    }

    @GetMapping("/structuredOutput/testRecord2")
    public StuRecord testRecord2(@RequestParam(name = "sname") String sname, @RequestParam(name = "email") String email) {
        String stringTemplate = """
                学号002,我叫{sname},大学专业是软件工程,邮箱{email}
                """;
        return qwenChatClient.prompt()
                .user(promptUserSpec -> promptUserSpec.text(stringTemplate)
                        .param("sname",sname)
                        .param("email",email))
                .call()
                .entity(StuRecord.class);
    }
}
相关推荐
海兰13 小时前
【第21篇】 Chat Memory Example
人工智能·spring ai
海兰14 小时前
【第22篇】Evaluation Example
人工智能·spring boot·log4j·alibaba·spring ai
梵得儿SHI1 天前
(第二篇)Spring AI 架构设计与优化:可观察性体系,打造全链路可视化的 AI 运维方案
人工智能·微服务·grafana·prometheus·监控·可观察性·spring ai
Java小生不才2 天前
spring AI文生图
java·人工智能·spring ai
Java小生不才3 天前
Chat Memory连续对话保存和持久化
spring ai
java1234_小锋3 天前
Spring AI 2.0 开发Java Agent智能体 - Spring AI 2.0简介
java·人工智能·spring·spring ai
yyk的萌5 天前
Spring AI + 智谱大模型实战:打造有记忆功能的智能天气助手
java·人工智能·spring·agent·spring ai
少许极端5 天前
AI修炼记1-Tool Calling
人工智能·ai·spring ai·tool calling
中间件XL6 天前
spring ai alibaba原理源码分析(一)-架构
人工智能·ai·alibaba·spring ai·agent框架
QC·Rex8 天前
Spring AI MCP Apps 实战:打造聊天与富 UI 融合的智能化应用
人工智能·spring·ui·spring ai·mcp