Spring AI Alibaba 格式化输出

1. 概述

Spring AI Alibaba 是阿里云基于 Spring AI 生态打造的 AI 应用开发框架,为开发者提供了统一、简洁的 API 来调用阿里云的各类 AI 服务。格式化输出是大模型应用开发中的核心能力,它解决了大模型原生输出格式不固定、难以被程序解析的问题,是实现 AI 与业务系统无缝集成的关键。

Spring AI Alibaba 提供了多种强大的格式化输出能力,包括:

  • 结构化输出(JSON/XML)
  • 流式输出(SSE)
  • Markdown 格式化输出
  • 函数调用(Function Calling)输出
  • 自定义格式转换器

本文档将深入剖析这些格式化输出技术的实现原理,并提供完整的可运行案例。

2. 环境准备

2.1 依赖配置

pom.xml 中添加以下依赖(以 Spring Boot 3.2.x 为例):

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2023.0.1.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- Spring AI Alibaba 通义千问核心依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>
    
    <!-- Web 依赖(用于流式输出和接口测试) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Lombok(简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2.2 配置文件

application.yml 中配置通义千问 API 密钥:

spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY:你的API密钥}
      chat:
        options:
          model: qwen-max
          temperature: 0.1 # 格式化输出建议使用较低的温度值

注意:格式化输出对模型的确定性要求较高,建议将 temperature 设置在 0.1-0.3 之间。

3. 结构化输出(JSON/XML)

3.1 知识点剖析

结构化输出是指让大模型按照指定的 schema 生成符合格式要求的 JSON 或 XML 数据。Spring AI Alibaba 实现结构化输出的核心原理是:

  1. Schema 自动生成:通过反射将 Java 类转换为 JSON Schema
  2. 提示词注入:自动将 Schema 信息添加到系统提示词中
  3. 输出解析:自动将模型返回的字符串解析为 Java 对象
  4. 容错处理:内置了多种容错机制,处理模型输出不严格符合格式的情况

Spring AI Alibaba 支持两种结构化输出方式:

  • 基于 @StructuredOutput 注解:简单易用,适合大多数场景
  • 基于 StructuredOutputConverter 接口:灵活强大,支持自定义格式

4. 流式输出(SSE)

4.1 知识点剖析

流式输出(Server-Sent Events, SSE)是指大模型在生成内容的过程中,将结果逐字逐句地返回给客户端。这种方式可以显著提升用户体验,特别是在生成长文本时。

Spring AI Alibaba 流式输出的核心特性:

  • 基于 Flux 的响应式编程模型:支持背压和异步处理
  • Token 级别的流式返回:可以实时展示模型生成的每个字
  • 完整的事件类型支持:包括内容事件、完成事件和错误事件
  • 与 Spring WebFlux 无缝集成:支持响应式 Web 应用

6. 函数调用(Function Calling)输出

6.1 知识点剖析

函数调用是指大模型根据用户的问题,自动决定是否调用外部工具/函数来获取信息,然后基于工具返回的结果生成最终回答。这是实现智能代理(Agent)的核心技术。

Spring AI Alibaba 函数调用的工作流程:

  1. 开发者定义函数并注册到 Spring 容器
  2. Spring AI 自动将函数转换为 JSON Schema 并发送给模型
  3. 模型根据用户问题判断是否需要调用函数
  4. 如果需要调用,模型返回函数调用指令
  5. Spring AI 自动执行对应的函数
  6. 将函数返回结果发送给模型
  7. 模型基于函数结果生成最终回答

6.2 案例:天气查询函数

步骤 1:定义函数参数和返回值
import lombok.Data;

@Data
public class WeatherRequest {
    private String city;
    private String date;
}

@Data
public class WeatherResponse {
    private String city;
    private String date;
    private String weather;
    private int temperature;
    private int humidity;
    private String wind;
}
步骤 2:实现函数
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.model.function.FunctionCallbackWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.function.Function;

@Configuration
public class FunctionConfig {

    @Bean
    public FunctionCallback weatherFunction() {
        return FunctionCallbackWrapper.builder(new WeatherFunction())
                .withName("getWeather")
                .withDescription("查询指定城市指定日期的天气信息")
                .withInputType(WeatherRequest.class)
                .build();
    }

    public static class WeatherFunction implements Function<WeatherRequest, WeatherResponse> {
        @Override
        public WeatherResponse apply(WeatherRequest request) {
            // 这里模拟调用天气API
            WeatherResponse response = new WeatherResponse();
            response.setCity(request.getCity());
            response.setDate(request.getDate());
            response.setWeather("晴");
            response.setTemperature(25);
            response.setHumidity(60);
            response.setWind("东北风 3级");
            return response;
        }
    }
}
步骤 3:使用函数调用
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;

@RestController
@RequestMapping("/api/weather")
public class WeatherController {

    private final ChatClient chatClient;

    public WeatherController(ChatModel chatModel) {
        this.chatClient = ChatClient.create(chatModel);
    }

    @GetMapping("/query")
    public String queryWeather(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .functions("getWeather")
                .call()
                .content();
    }
}

测试:访问 http://localhost:8080/api/weather/query?question=明天北京的天气怎么样

7. 最佳实践与常见问题

7.1 最佳实践

  1. 结构化输出最佳实践

    • 使用简单清晰的数据模型,避免过深的嵌套
    • 为字段添加明确的注释,帮助模型理解字段含义
    • 使用较低的 temperature 值(0.1-0.3)提高输出稳定性
    • 总是添加异常处理,应对模型输出不符合格式的情况
  2. 流式输出最佳实践

    • 使用 MediaType.TEXT_EVENT_STREAM_VALUE 作为响应类型
    • 在前端使用 EventSource 处理 SSE 响应
    • 添加超时处理和重连机制
    • 对于长文本生成,考虑分段处理
  3. 函数调用最佳实践

    • 函数名称和描述要清晰准确
    • 函数参数要尽可能简单
    • 限制函数调用的次数,避免无限循环
    • 对函数返回结果进行验证和清洗

7.2 常见问题与解决方案

问题 1:结构化输出偶尔不符合 JSON 格式

解决方案

  • 降低 temperature
  • 在提示词中强调格式要求
  • 使用更简单的数据模型
  • 添加容错解析逻辑,例如使用 JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES

问题 2:流式输出在某些浏览器中不工作

解决方案

  • 确保服务器没有启用压缩
  • 添加 Cache-Control: no-cache 响应头
  • 在前端使用 polyfill 支持旧浏览器

问题 3:函数调用总是不被触发

解决方案

  • 检查函数名称和描述是否清晰
  • 确保函数参数类型正确
  • 在提示词中明确说明可以使用哪些函数
  • 尝试使用更明确的问题

8. 总结

Spring AI Alibaba 提供了全面而强大的格式化输出能力,极大地简化了 AI 应用的开发过程。通过本文档介绍的结构化输出、流式输出、Markdown 输出和函数调用技术,开发者可以快速构建出高质量、用户体验良好的 AI 应用。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐