005-Spring AI Alibaba Structured Output 功能完整案例
本案例将引导您一步步构建一个 Spring Boot 应用,演示如何利用 Spring AI Alibaba 的 Structured Output 功能,实现将大模型输出转换为结构化数据(如 Java Bean、JSON、Map 和 List)。

本案例将引导您一步步构建一个 Spring Boot 应用,演示如何利用 Spring AI Alibaba 的 Structured Output 功能,实现将大模型输出转换为结构化数据(如 Java Bean、JSON、Map 和 List)。
1. 案例目标
我们将创建一个包含多个结构化输出功能的 Web 应用:
- Java Bean 输出 (
/bean/*):将大模型输出转换为指定的 Java Bean 对象。 - JSON 格式输出 (
/json/*):使用 DashScope 的 JSON 模式,确保输出为有效的 JSON 格式。 - Map 和 List 输出 (
/map-list/*):将大模型输出转换为 Map 或 List 集合。
2. 技术栈与核心依赖
- Spring Boot 3.x
- Spring AI Alibaba (用于对接阿里云 DashScope 通义大模型)
- Maven (项目构建工具)
在 pom.xml 中,你需要引入以下核心依赖:
<dependencies>
<!-- Spring AI Alibaba 核心启动器,集成 DashScope -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Spring Web 用于构建 RESTful API -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3. 项目配置
在 src/main/resources/application.yml 文件中,配置你的 DashScope API Key 和响应格式。
server:
port: 10007
spring:
application:
name: spring-ai-alibaba-structured-example
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
chat:
response-format: json
重要提示:请将 AI_DASHSCOPE_API_KEY 环境变量设置为你从阿里云获取的有效 API Key。配置中的 response-format: json 表示默认使用 JSON 格式响应。
4. 定义实体类
首先,我们定义一个用于接收结构化输出的实体类 BeanEntity:
package com.alibaba.cloud.ai.example.outparser.entity;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"title", "date", "author", "content"}) // 指定属性的顺序
public class BeanEntity {
private String title;
private String author;
private String date;
private String content;
public BeanEntity() {
}
// Getter 和 Setter 方法
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "StreamToBeanEntity{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", date='" + date + '\'' +
", content='" + content + '\'' +
'}';
}
}
5. 编写控制器代码
5.1 BeanController.java - Java Bean 输出
实现将大模型输出转换为 Java Bean 对象的功能。
package com.alibaba.cloud.ai.example.outparser.controller;
import com.alibaba.cloud.ai.example.outparser.entity.BeanEntity;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.template.st.StTemplateRenderer;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;
import java.util.Objects;
@RestController
@RequestMapping("/bean")
public class BeanController {
private static final Logger log = LoggerFactory.getLogger(BeanController.class);
private final ChatClient chatClient;
private final ChatModel chatModel;
private final BeanOutputConverter<BeanEntity> converter;
private final String format;
public BeanController(ChatClient.Builder builder, ChatModel chatModel) {
this.chatModel = chatModel;
this.converter = new BeanOutputConverter<>(
new ParameterizedTypeReference<BeanEntity>() {
}
);
this.format = converter.getFormat();
log.info("format: {}", format);
this.chatClient = builder
.build();
}
@GetMapping("/chat")
public String simpleChat(@RequestParam(value = "query", defaultValue = "以影子为作者,写一篇200字左右的有关人工智能诗篇") String query) {
String result = chatClient.prompt(query)
.call().content();
log.info("result: {}", result);
assert result != null;
try {
BeanEntity convert = converter.convert(result);
log.info("反序列成功,convert: {}", convert);
} catch (Exception e) {
log.error("反序列化失败");
}
return result;
}
@GetMapping("/chat-format")
public BeanEntity simpleChatFormat(@RequestParam(value = "query", defaultValue = "以影子为作者,写一篇200字左右的有关人工智能诗篇") String query) {
return chatClient.prompt(query)
.call().entity(BeanEntity.class);
}
@GetMapping("/chat-model-format")
public String chatModel(@RequestParam(value = "query", defaultValue = "以影子为作者,写一篇200字左右的有关人工智能诗篇") String query) {
String template = query + "{format}";
PromptTemplate promptTemplate = PromptTemplate.builder()
.template(template)
.variables(Map.of("format", format))
.renderer(StTemplateRenderer.builder().build())
.build();
Prompt prompt = promptTemplate.create();
String result = chatModel.call(prompt)
.getResult().getOutput().getText();
log.info("result: {}", result);
assert result != null;
try {
BeanEntity convert = converter.convert(result);
log.info("反序列成功,convert: {}", convert);
} catch (Exception e) {
log.error("反序列化失败");
}
return result;
}
/**
* @return {@link BeanEntity}
*/
@GetMapping("/play")
public BeanEntity simpleChat(HttpServletResponse response) {
Flux<String> flux = this.chatClient.prompt()
.user(u -> u.text("""
requirement: 请用大概 120 字,作者为 牧生 ,为计算机的发展历史写一首现代诗;
format: 以纯文本输出 json,请不要包含任何多余的文字——包括 markdown 格式;
outputExample: {
"title": {title},
"author": {author},
"date": {date},
"content": {content}
};
"""))
.stream()
.content();
String result = String.join("\n", Objects.requireNonNull(flux.collectList().block()))
.replaceAll("\\n", "")
.replaceAll("\\s+", " ")
.replaceAll("\"\\s*:", "\":")
.replaceAll(":\\s*\"", ":\"");
log.info("LLMs 响应的 json 数据为:{}", result);
return converter.convert(result);
}
}
5.2 JsonController.java - JSON 格式输出
实现使用 DashScope 的 JSON 模式,确保输出为有效的 JSON 格式。
package com.alibaba.cloud.ai.example.outparser.controller;
import com.alibaba.cloud.ai.dashscope.api.DashScopeResponseFormat;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/json")
public class JsonController {
private final ChatClient chatClient;
private final DashScopeResponseFormat responseFormat;
public JsonController(ChatClient.Builder builder) {
// AI模型内置支持JSON模式
DashScopeResponseFormat responseFormat = new DashScopeResponseFormat();
responseFormat.setType(DashScopeResponseFormat.Type.JSON_OBJECT);
this.responseFormat = responseFormat;
this.chatClient = builder
.build();
}
@GetMapping("/chat")
public String simpleChat(@RequestParam(value = "query", defaultValue = "请以JSON格式介绍你自己") String query) {
return chatClient.prompt(query).call().content();
}
@GetMapping("/chat-format")
public String simpleChatFormat(@RequestParam(value = "query", defaultValue = "请以JSON格式介绍你自己") String query) {
return chatClient.prompt(query)
.options(
DashScopeChatOptions.builder()
.withTopP(0.7)
.withResponseFormat(responseFormat)
.build()
)
.call().content();
}
}
5.3 MapListController.java - Map 和 List 输出
实现将大模型输出转换为 Map 或 List 集合的功能。
package com.alibaba.cloud.ai.example.outparser.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.ChatClientAttributes;
import org.springframework.ai.converter.ListOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/map-list")
public class MapListController {
private static final Logger log = LoggerFactory.getLogger(BeanController.class);
private final ChatClient chatClient;
private final MapOutputConverter mapConverter;
private final ListOutputConverter listConverter;
public MapListController(ChatClient.Builder builder) {
// map转换器
this.mapConverter = new MapOutputConverter();
// list转换器
this.listConverter = new ListOutputConverter(new DefaultConversionService());
this.chatClient = builder
.build();
}
@GetMapping("/chatMap")
public Map<String, Object> chatMap(@RequestParam(value = "query", defaultValue = "请为我描述下影子的特性") String query) {
return chatClient.prompt(query)
.advisors(
a -> a.param(ChatClientAttributes.OUTPUT_FORMAT.getKey(), mapConverter.getFormat())
).call().entity(mapConverter);
}
@GetMapping("/chatList")
public List<String> chatList(@RequestParam(value = "query", defaultValue = "请为我描述下影子的特性") String query) {
return chatClient.prompt(query)
.advisors(
a -> a.param(ChatClientAttributes.OUTPUT_FORMAT.getKey(), listConverter.getFormat())
).call().entity(listConverter);
}
}
6. 运行与测试
- 启动应用:运行你的 Spring Boot 主程序。
- 使用浏览器或 API 工具(如 Postman, curl)进行测试。
测试 1:Java Bean 输出
访问以下 URL,测试基本对话功能:
GET http://localhost:10007/bean/chat
访问以下 URL,测试格式化输出为 Java Bean:
GET http://localhost:10007/bean/chat-format
访问以下 URL,测试使用 ChatModel 格式化输出:
GET http://localhost:10007/bean/chat-model-format
访问以下 URL,测试流式输出并转换为 Bean:
GET http://localhost:10007/bean/play
测试 2:JSON 格式输出
访问以下 URL,测试基本 JSON 输出:
GET http://localhost:10007/json/chat
访问以下 URL,测试使用 DashScope 的 JSON 模式:
GET http://localhost:10007/json/chat-format
测试 3:Map 和 List 输出
访问以下 URL,测试输出为 Map:
GET http://localhost:10007/map-list/chatMap
访问以下 URL,测试输出为 List:
GET http://localhost:10007/map-list/chatList
7. 实现思路与扩展建议
实现思路
本案例的核心思想是"结构化输出转换"。我们将大模型的非结构化文本输出转换为结构化数据对象,这使得:
- 数据一致性高:通过预定义的结构,确保输出数据的一致性和可预测性。
- 易于集成:结构化数据可以轻松集成到现有的业务系统中。
- 类型安全:使用强类型的 Java 对象,减少运行时错误。
扩展建议
- 自定义输出转换器:可以根据业务需求,实现自定义的输出转换器,支持更复杂的数据结构。
- 输出验证:添加输出验证机制,确保转换后的数据符合业务规则。
- 多格式支持:扩展支持更多输出格式,如 XML、YAML 等。
- 错误处理:增强错误处理机制,当输出无法正确转换时提供有意义的错误信息。
- 性能优化:对于大规模数据转换,可以考虑使用缓存或批量处理来提高性能。
更多推荐


所有评论(0)