格式化输出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);
    }
}
相关推荐
kong79069284 天前
Spring AI简介
人工智能·spring ai
最初的↘那颗心5 天前
Spring AI Prompt 工程与结构化输出实战
spring ai·结构化输出·prompt engineering
小小工匠18 天前
大模型开发 - SpringAI之MCP Client开发:让Agent动态调用远程工具服务
spring ai·mcp·mcp client
@SmartSi20 天前
Spring AI 实战:通过 ChatMemory 构建有记忆的智能对话应用
llm·spring ai
小小工匠21 天前
大模型开发 - SpringAI之RAG应用效果评估
spring ai·rag效果评估
小小工匠21 天前
大模型开发 - SpringAI 之高级 RAG 组件
rag·spring ai
小楼v22 天前
⭐解锁RAG与Spring AI的实战应用(万字详细教学与完整步骤流程实践)
java·后端·rag·spring ai·ai大模型应用
小小工匠22 天前
大模型开发 - SpringAI之MySQL存储ChatMemory
mysql·spring ai
腾飞开源23 天前
104_Spring AI 干货笔记之开发时服务
人工智能·docker compose·容器管理·spring ai·testcontainers·开发时服务·ssl支持