格式化输出是大模型应用开发中的核心能力,它解决了大模型原生输出格式不固定、难以被程序解析的问题,是实现 AI 与业务系统无缝集成的关键。
一、概述
Spring AI Alibaba 提供了多种强大的格式化输出能力,包括:- 结构化输出(JSON/XML)
- 流式输出(SSE)
- Markdown 格式化输出
- 函数调用(Function Calling)输出
- 自定义格式转换器
二、环境准备
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. 配置文件在 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 之间。三、结构化输出(JSON/XML)
结构化输出是指让大模型按照指定的 schema 生成符合格式要求的 JSON 或 XML 数据。Spring AI Alibaba 实现结构化输出的核心原理是:
1. Schema 自动生成:通过反射将 Java 类转换为 JSON Schema
2. 提示词注入:自动将 Schema 信息添加到系统提示词中
3. 输出解析:自动将模型返回的字符串解析为 Java 对象
4. 容错处理:内置了多种容错机制,处理模型输出不严格符合格式的情况
Spring AI Alibaba 支持两种结构化输出方式:
基于 @StructuredOutput 注解:简单易用,适合大多数场景
基于 StructuredOutputConverter 接口:灵活强大,支持自定义格式
四、流式输出(SSE)
流式输出(Server-Sent Events, SSE)是指大模型在生成内容的过程中,将结果逐字逐句地返回给客户端。这种方式可以显著提升用户体验,特别是在生成长文本时。Spring AI Alibaba 流式输出的核心特性:
1. 基于 Flux 的响应式编程模型:支持背压和异步处理
2. Token 级别的流式返回:可以实时展示模型生成的每个字
3. 完整的事件类型支持:包括内容事件、完成事件和错误事件
4. 与 Spring WebFlux 无缝集成:支持响应式 Web 应用
五、函数调用(Function Calling)输出
1. 知识点剖析
函数调用是指大模型根据用户的问题,自动决定是否调用外部工具/函数来获取信息,然后基于工具返回的结果生成最终回答。这是实现智能代理(Agent)的核心技术。
1. Spring AI Alibaba 函数调用的工作流程:
1) 开发者定义函数并注册到 Spring 容器
2) Spring AI 自动将函数转换为 JSON Schema 并发送给模型
3) 模型根据用户问题判断是否需要调用函数
4) 如果需要调用,模型返回函数调用指令
5) Spring AI 自动执行对应的函数
6) 将函数返回结果发送给模型
7) 模型基于函数结果生成最终回答
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=明天北京的天气怎么样六、最佳实践与常见问题
1. 最佳实践1) 结构化输出最佳实践:
使用简单清晰的数据模型,避免过深的嵌套
为字段添加明确的注释,帮助模型理解字段含义
使用较低的 temperature 值(0.1-0.3)提高输出稳定性
总是添加异常处理,应对模型输出不符合格式的情况
2) 流式输出最佳实践:
MediaType.TEXT_EVENT_STREAM_VALUE作为响应类型
在前端使用 EventSource 处理 SSE 响应
添加超时处理和重连机制
对于长文本生成,考虑分段处理
3) 函数调用最佳实践:
函数名称和描述要清晰准确
函数参数要尽可能简单
限制函数调用的次数,避免无限循环
对函数返回结果进行验证和清洗
2. 常见问题与解决方案
问题 1:结构化输出偶尔不符合 JSON 格式
解决方案:
降低 temperature 值
在提示词中强调格式要求
使用更简单的数据模型
添加容错解析逻辑,例如使用 JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES
问题 2:流式输出在某些浏览器中不工作
解决方案:
确保服务器没有启用压缩
添加 Cache-Control: no-cache 响应头
在前端使用 polyfill 支持旧浏览器
问题 3:函数调用总是不被触发
解决方案:
检查函数名称和描述是否清晰
确保函数参数类型正确
在提示词中明确说明可以使用哪些函数
尝试使用更明确的问题
Spring AI Alibaba 格式化输出详解
