文章目录
- [1. 核心概念](#1. 核心概念)
-
- [1.1 什么是结构化输出](#1.1 什么是结构化输出)
- [1.2 Spring AI Alibaba 实现方式](#1.2 Spring AI Alibaba 实现方式)
- [2. 快速入门](#2. 快速入门)
-
- [2.1 定义输出 POJO](#2.1 定义输出 POJO)
- [2.2 使用 outputType](#2.2 使用 outputType)
- [2.3 使用 outputSchema](#2.3 使用 outputSchema)
- [3. 复杂嵌套结构](#3. 复杂嵌套结构)
-
- [3.1 商品评价分析(嵌套对象)](#3.1 商品评价分析(嵌套对象))
- [3.2 文本实体分析](#3.2 文本实体分析)
- [4. 实现原理](#4. 实现原理)
-
- [4.1 模型原生结构化输出(最优)](#4.1 模型原生结构化输出(最优))
- [4.2 ToolCall 兜底(通用)](#4.2 ToolCall 兜底(通用))
- [4.3 指令增强机制](#4.3 指令增强机制)
- [5. 错误处理策略](#5. 错误处理策略)
-
- [5.1 Try-Catch 基础处理](#5.1 Try-Catch 基础处理)
- [5.2 数据验证模式](#5.2 数据验证模式)
- [5.3 重试模式](#5.3 重试模式)
1. 核心概念
1.1 什么是结构化输出
结构化输出允许 Agent 按照预定义的格式返回数据,替代传统的自然语言响应:
- 输入:用户指令 + 输出格式规则
- 输出:标准
JSON字符串 / 直接映射为Java POJO - 优势:类型安全、无歧义、程序可直接消费
结构化输出是 AI Agent 工程化落地的核心能力,它让 Agent 摆脱自然语言的模糊性,以固定格式返回数据,应用程序可直接解析使用,无需复杂的文本提取逻辑。
1.2 Spring AI Alibaba 实现方式
ReactAgent.Builder 提供两个核心方法实现结构化输出:
outputSchema(String schema):手动指定JSON Schema字符串outputType(Class<?> type):传入Java类,自动生成JSON Schema(推荐)
outputType 直接传入 Java POJO 类,框架通过 BeanOutputConverter 自动生成 JSON Schema,无需手动编写 ,编译期类型校验。
方法签名:
java
Builder outputType(Class<?> outputType)
outputSchema 手动传入 JSON Schema 字符串,支持完全自定义输出格式。
方法签名:
java
Builder outputSchema(String outputSchema)
| 特性 | outputType | outputSchema |
|---|---|---|
| 使用方式 | 直接传入 Java 类 | 传入 JSON Schema 字符串 |
| 类型安全 | ✅ 编译期校验 | ❌ 运行时校验 |
| 维护成本 | 低(自动生成) | 高(手动维护) |
| 灵活性 | 标准格式 | 完全自定义 |
| 推荐场景 | 绝大多数业务场景 | 极端自定义格式场景 |
2. 快速入门
2.1 定义输出 POJO
java
/**
* 联系方式输出实体(标准Java POJO)
*/
public static class ContactInfo {
private String name;
private String email;
private String phone;
// Getter & Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
2.2 使用 outputType
java
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import org.springframework.ai.chat.messages.AssistantMessage;
// 构建Agent,直接指定输出类型
ReactAgent agent = ReactAgent.builder()
.name("contact_extractor")
.model(chatModel) // 注入ChatModel
.outputType(ContactInfo.class) // 核心:指定输出POJO
.saver(new MemorySaver())
.build();
// 执行调用
AssistantMessage result = agent.call("从以下信息提取联系方式:张三,zhangsan@example.com,(555) 123-4567");
// 输出标准JSON
System.out.println(result.getText());
输出结果
json
{"name": "张三", "email": "zhangsan@example.com", "phone": "(555) 123-4567"}
2.3 使用 outputSchema
java
import org.springframework.ai.converter.BeanOutputConverter;
// 自动从POJO生成JSON Schema
BeanOutputConverter<ContactInfo> converter = new BeanOutputConverter<>(ContactInfo.class);
String schema = converter.getFormat();
// 构建Agent
ReactAgent agent = ReactAgent.builder()
.name("contact_extractor")
.model(chatModel)
.outputSchema(schema) // 传入Schema
.build();
3. 复杂嵌套结构
结构化输出完美支持嵌套对象、数组、复杂实体,适用于文本分析、评价提取、实体识别等场景。
3.1 商品评价分析(嵌套对象)
java
/**
* 商品评价复杂输出结构
*/
public static class ProductReview {
private int rating;
private String sentiment;
private String[] keyPoints;
private ReviewDetails details;
// 嵌套子类
public static class ReviewDetails {
private String[] pros;
private String[] cons;
// Getter & Setter
}
// Getter & Setter
}
// 使用
BeanOutputConverter<ProductReview> converter = new BeanOutputConverter<>(ProductReview.class);
ReactAgent agent = ReactAgent.builder()
.name("review_analyzer")
.model(chatModel)
.outputSchema(converter.getFormat())
.build();
AssistantMessage result = agent.call("分析评价:这个产品很棒,5星好评。配送快速,但价格稍贵。");
3.2 文本实体分析
java
/**
* 文本分析输出结构
*/
public static class TextAnalysis {
private String summary;
private String[] keywords;
private String sentiment;
private Entities entities;
public static class Entities {
private String[] persons;
private String[] locations;
private String[] organizations;
// Getter & Setter
}
// Getter & Setter
}
4. 实现原理
4.1 模型原生结构化输出(最优)
支持原生结构化输出的模型(OpenAI、通义千问 DashScope),框架自动启用模型内置能力:
- 严格保证
JSON格式 - 模型服务端自动校验格式
示例(DashScope):
java
ChatOptions options = DashScopeChatOptions.builder()
.withResponseFormat(DashScopeResponseFormat.builder()
.type(DashScopeResponseFormat.Type.JSON_OBJECT)
.build())
.build();
Spring AI Alibaba 框架会增强系统 Prompt,引导模型输出格式化内容
增强用户消息方法示例:
java
/ In AgentLlmNode.augmentUserMessage() method
public void augmentUserMessage(List<Message> messages, String outputSchema) {
if (!StringUtils.hasText(outputSchema)) {
return;
}
for (int i = messages.size() - 1; i >= 0; i--) {
Message message = messages.get(i);
if (message instanceof UserMessage userMessage) {
messages.set(i, userMessage.mutate()
.text(userMessage.getText() + System.lineSeparator() + outputSchema)
.build());
break;
}
}
}
注意 ,相比于 DashScope 模型是通过增强 Prompt 提示词实现最终的 JSON 格式,实现的是一个尽最大努力的效果,OpenAI 模型则是在模型 API 层面支持 Json 格式,提供格式的严格保证支持。
4.2 ToolCall 兜底(通用)
对于不支持原生结构化输出的模型,Spring AI Alibaba 支持通过调用工具来实现相同效果。此方法适用于所有支持工具调用的模型,即大多数现代模型。
4.3 指令增强机制
框架会自动在用户消息末尾追加格式指令,引导模型输出规范内容:
java
// 核心增强逻辑:为用户消息追加Schema指令
public void augmentUserMessage(List<Message> messages, String outputSchema) {
for (int i = messages.size() - 1; i >= 0; i--) {
Message message = messages.get(i);
if (message instanceof UserMessage userMessage) {
messages.set(i, userMessage.mutate()
.text(userMessage.getText() + "\n" + outputSchema)
.build());
break;
}
}
}
5. 错误处理策略
模型可能偶尔返回格式异常的 JSON,Spring AI Alibaba 提供三种可靠的错误处理方案。
5.1 Try-Catch 基础处理
java
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
try {
AssistantMessage result = agent.call("提取数据");
// JSON 转 Java 对象
ContactInfo data = mapper.readValue(result.getText(), ContactInfo.class);
} catch (JsonProcessingException e) {
System.err.println("JSON 解析失败:" + e.getMessage());
System.err.println("原始响应:" + result.getText());
}
5.2 数据验证模式
java
/**
* 带校验逻辑的输出实体
*/
public class ValidatedOutput {
private String title;
private Integer rating;
// 自定义校验规则
public void validate() {
if (title == null || title.isEmpty()) {
throw new IllegalArgumentException("标题不能为空");
}
if (rating < 1 || rating > 5) {
throw new IllegalArgumentException("评分必须在1-5之间");
}
}
}
AssistantMessage result = agent.call("生成评价");
ValidatedOutput output = mapper.readValue(result.getText(), ValidatedOutput.class);
output.validate(); // 如果无效则抛出异常
5.3 重试模式
java
int maxRetries = 3;
ContactInfo data = null;
ObjectMapper mapper = new ObjectMapper();
for (int i = 0; i < maxRetries; i++) {
try {
AssistantMessage result = agent.call("提取联系方式");
data = mapper.readValue(result.getText(), ContactInfo.class);
break;
} catch (Exception e) {
if (i == maxRetries - 1) {
throw new RuntimeException("重试" + maxRetries + "次后失败", e);
}
System.out.println("第" + (i+1) + "次失败,正在重试...");
}
}