结构化输出与Spring AI实现原理深度解析

29 阅读19分钟

1. 什么是结构化输出?

1.1 结构化输出的定义与价值

结构化输出是指大[语言模型]按照预定义的格式和 schema 生成数据,而不是自由形式的文本。这种输出方式让AI的响应变得可预测、可解析,便于程序化处理。

自然语言查询

大语言模型

非结构化输出
自由文本

结构化输出
JSON/XML/Object

需要复杂解析

直接程序使用

传统非结构化输出 vs 结构化输出对比:

# 非结构化输出示例
traditional_output = """
张三是一名软件工程师,今年28岁,在北京工作。
他的邮箱是zhangsan@email.com,电话是13800138000。
他擅长Java和Python编程。
"""

# 结构化输出示例
structured_output = {
    "name": "张三",
    "age": 28,
    "profession": "软件工程师",
    "location": "北京",
    "contact": {
        "email": "zhangsan@email.com",
        "phone": "13800138000"
    },
    "skills": ["Java", "Python"]
}

AI写代码python
运行
12345678910111213141516171819

1.2 结构化输出的核心优势

public class StructuredOutputBenefits {
    /**
     * 结构化输出的主要优势
     */
    public enum Benefit {
        PREDICTABILITY("可预测性", "输出格式固定,便于程序处理"),
        VALIDATION("可验证性", "可进行数据校验和完整性检查"),
        INTEGRATION("易集成性", "直接与现有系统集成,无需复杂解析"),
        AUTOMATION("自动化友好", "适合工作流和自动化任务"),
        CONSISTENCY("一致性", "保证多次调用的输出格式一致");
        
        private final String name;
        private final String description;
        
        Benefit(String name, String description) {
            this.name = name;
            this.description = description;
        }
    }
    
    // 使用示例对比
    public static void demonstrateBenefits() {
        // 非结构化数据处理
        String unstructuredText = "价格是299元,库存剩余15件,产品编号A123";
        
        // 需要复杂的文本解析
        Pattern pattern = Pattern.compile("价格是(\d+)元,库存剩余(\d+)件,产品编号(\w+)");
        Matcher matcher = pattern.matcher(unstructuredText);
        if (matcher.find()) {
            double price = Double.parseDouble(matcher.group(1));
            int stock = Integer.parseInt(matcher.group(2));
            String productId = matcher.group(3);
            System.out.println("解析结果: " + price + ", " + stock + ", " + productId);
        }
        
        // 结构化数据处理
        ProductInfo structuredOutput = new ProductInfo(299.0, 15, "A123");
        System.out.println("直接使用: " + structuredOutput.getPrice());
    }
    
    static class ProductInfo {
        private double price;
        private int stock;
        private String productId;
        
        public ProductInfo(double price, int stock, String productId) {
            this.price = price;
            this.stock = stock;
            this.productId = productId;
        }
        
        // getters and setters
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354

2. Spring AI 结构化输出实现原理

2.1 Spring AI 架构概览

应用程序

Spring AI

结构化输出模块

Output Parser

Prompt Template

Schema 定义

Bean Output Parser

JSON Output Parser

自定义 Parser

参数化模板

Schema 注入

Java Bean

JSON Schema

自定义 Schema

AI Provider

OpenAI

Azure OpenAI

本地模型

2.2 核心组件详解

2.2.1 Bean Output Parser
// 1. 定义输出数据结构
@Data
@Builder
public class CharacterInfo {
    @Description("角色姓名")
    @NotBlank
    private String name;
    
    @Description("角色年龄")
    @Min(0)
    @Max(150)
    private int age;
    
    @Description("角色职业")
    private String profession;
    
    @Description("技能列表")
    private List<String> skills;
    
    @Description("角色属性")
    private Attributes attributes;
    
    @Data
    @Builder
    public static class Attributes {
        @Description("力量值 1-100")
        @Range(min = 1, max = 100)
        private int strength;
        
        @Description("智力值 1-100")
        @Range(min = 1, max = 100)
        private int intelligence;
        
        @Description("敏捷值 1-100")
        @Range(min = 1, max = 100)
        private int agility;
    }
}

// 2. 配置 Bean Output Parser
@Configuration
public class StructuredOutputConfig {
    
    @Bean
    public BeanOutputParser<CharacterInfo> characterOutputParser() {
        return new BeanOutputParser<>(CharacterInfo.class);
    }
    
    @Bean
    public PromptTemplate characterPromptTemplate() {
        return new PromptTemplate("""
            请根据以下描述创建一个游戏角色:
            {description}
            
            {format}
            """);
    }
}

// 3. 使用 Bean Output Parser
@Service
public class CharacterService {
    
    @Autowired
    private ChatClient chatClient;
    
    @Autowired
    private BeanOutputParser<CharacterInfo> characterOutputParser;
    
    @Autowired
    private PromptTemplate characterPromptTemplate;
    
    public CharacterInfo createCharacter(String description) {
        // 构建提示词
        String formatInstructions = characterOutputParser.getFormat();
        Prompt prompt = characterPromptTemplate.create(
            Map.of(
                "description", description,
                "format", formatInstructions
            )
        );
        
        // 调用AI并解析结果
        String content = chatClient.call(prompt).getResult().getOutput().getContent();
        return characterOutputParser.parse(content);
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
2.2.2 JSON Output Parser
// JSON 结构化输出实现
@Configuration
public class JsonOutputConfiguration {
    
    // 定义JSON Schema
    private static final String PRODUCT_SCHEMA = """
        {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "产品名称"
                },
                "price": {
                    "type": "number",
                    "description": "产品价格"
                },
                "category": {
                    "type": "string",
                    "description": "产品分类"
                },
                "specifications": {
                    "type": "object",
                    "properties": {
                        "color": {"type": "string"},
                        "size": {"type": "string"},
                        "weight": {"type": "number"}
                    }
                },
                "inStock": {
                    "type": "boolean",
                    "description": "是否有库存"
                }
            },
            "required": ["name", "price", "inStock"]
        }
        """;
    
    @Bean
    public JsonOutputParser productOutputParser() {
        return new JsonOutputParser(PRODUCT_SCHEMA);
    }
    
    @Bean
    public PromptTemplate productPromptTemplate() {
        return new PromptTemplate("""
            请根据用户描述提取产品信息:
            {userInput}
            
            请严格按照以下JSON格式输出:
            {format}
            """);
    }
}

@Service
public class ProductExtractionService {
    
    @Autowired
    private ChatClient chatClient;
    
    @Autowired
    private JsonOutputParser productOutputParser;
    
    @Autowired
    private PromptTemplate productPromptTemplate;
    
    public JsonNode extractProductInfo(String userInput) {
        String formatInstructions = productOutputParser.getFormat();
        
        Prompt prompt = productPromptTemplate.create(
            Map.of(
                "userInput", userInput,
                "format", formatInstructions
            )
        );
        
        String content = chatClient.call(prompt).getResult().getOutput().getContent();
        return productOutputParser.parse(content);
    }
    
    // 处理复杂的产品信息提取
    public ProductDetails extractDetailedProduct(String description) {
        String complexSchema = """
            {
                "type": "object",
                "properties": {
                    "products": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "name": {"type": "string"},
                                "price": {"type": "number"},
                                "features": {
                                    "type": "array",
                                    "items": {"type": "string"}
                                },
                                "rating": {
                                    "type": "object",
                                    "properties": {
                                        "score": {"type": "number"},
                                        "reviews": {"type": "integer"}
                                    }
                                }
                            }
                        }
                    },
                    "summary": {
                        "type": "object",
                        "properties": {
                            "totalProducts": {"type": "integer"},
                            "priceRange": {
                                "type": "object",
                                "properties": {
                                    "min": {"type": "number"},
                                    "max": {"type": "number"}
                                }
                            }
                        }
                    }
                }
            }
            """;
            
        JsonOutputParser parser = new JsonOutputParser(complexSchema);
        String format = parser.getFormat();
        
        Prompt prompt = new Prompt("""
            分析以下产品描述,提取所有产品信息并生成摘要:
            {description}
            
            输出格式:
            {format}
            """.replace("{description}", description).replace("{format}", format));
            
        String content = chatClient.call(prompt).getResult().getOutput().getContent();
        return parseProductDetails(parser.parse(content));
    }
    
    private ProductDetails parseProductDetails(JsonNode jsonNode) {
        // 将JSON解析为业务对象
        // 实现细节...
        return new ProductDetails();
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146

3. Spring AI 结构化输出实战

3.1 完整的企业级应用示例

// 电商产品信息提取系统
@Data
@Builder
public class EcommerceProduct {
    @Description("产品唯一标识")
    @NotBlank
    private String productId;
    
    @Description("产品名称")
    @NotBlank
    private String name;
    
    @Description("产品描述")
    private String description;
    
    @Description("价格信息")
    private PriceInfo price;
    
    @Description("库存状态")
    private StockStatus stock;
    
    @Description("产品分类路径")
    private List<String> categories;
    
    @Description("产品属性")
    private Map<String, Object> attributes;
    
    @Description("图片链接列表")
    private List<String> imageUrls;
    
    @Description("用户评价摘要")
    private ReviewSummary reviewSummary;
    
    @Data
    @Builder
    public static class PriceInfo {
        @Description("当前价格")
        @Min(0)
        private BigDecimal currentPrice;
        
        @Description("原价")
        @Min(0)
        private BigDecimal originalPrice;
        
        @Description("货币单位")
        @Pattern(regexp = "CNY|USD|EUR")
        private String currency;
        
        @Description("折扣信息")
        private Discount discount;
    }
    
    @Data
    @Builder
    public static class StockStatus {
        @Description("库存数量")
        @Min(0)
        private Integer quantity;
        
        @Description("库存状态")
        private StockLevel level;
        
        @Description("预计补货时间")
        private String restockDate;
    }
    
    @Data
    @Builder
    public static class Discount {
        @Description("折扣百分比")
        @Range(min = 0, max = 100)
        private Integer percentage;
        
        @Description("折扣金额")
        @Min(0)
        private BigDecimal amount;
        
        @Description("折扣类型")
        private String type;
    }
    
    @Data
    @Builder
    public static class ReviewSummary {
        @Description("平均评分")
        @Range(min = 0, max = 5)
        private Double averageRating;
        
        @Description("评价总数")
        @Min(0)
        private Integer totalReviews;
        
        @Description("评分分布")
        private Map<Integer, Integer> ratingDistribution;
    }
    
    public enum StockLevel {
        IN_STOCK, LOW_STOCK, OUT_OF_STOCK, PRE_ORDER
    }
}

// 高级输出解析器
@Service
public class AdvancedProductParser {
    
    private final BeanOutputParser<EcommerceProduct> productParser;
    private final ChatClient chatClient;
    private final PromptTemplate productExtractionTemplate;
    
    public AdvancedProductParser(ChatClient chatClient) {
        this.chatClient = chatClient;
        this.productParser = new BeanOutputParser<>(EcommerceProduct.class);
        
        this.productExtractionTemplate = new PromptTemplate("""
            你是一个专业的产品信息提取AI。请从以下文本中提取完整的电商产品信息:
            
            〖文本内容〗
            {textContent}
            
            〖提取要求〗
            1. 尽可能提取所有提到的产品信息
            2. 对于缺失的信息,根据上下文合理推断或标记为null
            3. 价格信息需要统一转换为{currency}货币单位
            4. 分类信息需要按照电商标准分类整理
            5. 确保所有数字字段格式正确
            
            〖输出格式〗
            {formatInstructions}
            
            〖示例参考〗
            {examples}
            """);
    }
    
    public EcommerceProduct extractProductInfo(String textContent, String currency) {
        String formatInstructions = productParser.getFormat();
        String examples = generateExamples();
        
        Map<String, Object> variables = Map.of(
            "textContent", textContent,
            "currency", currency,
            "formatInstructions", formatInstructions,
            "examples", examples
        );
        
        Prompt prompt = productExtractionTemplate.create(variables);
        
        try {
            String content = chatClient.call(prompt).getResult().getOutput().getContent();
            EcommerceProduct product = productParser.parse(content);
            validateProduct(product);
            return product;
        } catch (Exception e) {
            throw new ProductExtractionException("产品信息提取失败: " + e.getMessage(), e);
        }
    }
    
    public List<EcommerceProduct> extractMultipleProducts(String textContent, String currency) {
        // 批量提取多个产品信息
        String batchSchema = """
            {
                "type": "array",
                "items": %s
            }
            """.formatted(productParser.getSchema());
            
        JsonOutputParser batchParser = new JsonOutputParser(batchSchema);
        
        Prompt prompt = new Prompt("""
            从以下文本中提取所有提到的产品信息:
            {textContent}
            
            要求:
            - 货币单位统一为{currency}
            - 每个产品信息尽可能完整
            - 输出格式:{format}
            """.replace("{textContent}", textContent)
              .replace("{currency}", currency)
              .replace("{format}", batchParser.getFormat()));
              
        String content = chatClient.call(prompt).getResult().getOutput().getContent();
        JsonNode jsonNode = batchParser.parse(content);
        
        return parseProductList(jsonNode);
    }
    
    private void validateProduct(EcommerceProduct product) {
        // 实现验证逻辑
        if (product.getPrice() != null && product.getPrice().getCurrentPrice() == null) {
            throw new ValidationException("产品价格信息不完整");
        }
    }
    
    private String generateExamples() {
        // 生成示例数据
        return """
            示例输出:
            {
                "productId": "P123456",
                "name": "智能手机X1",
                "description": "最新款智能手机,配备高清摄像头",
                "price": {
                    "currentPrice": 2999.00,
                    "originalPrice": 3499.00,
                    "currency": "CNY",
                    "discount": {
                        "percentage": 14,
                        "amount": 500.00,
                        "type": "季节折扣"
                    }
                },
                "stock": {
                    "quantity": 150,
                    "level": "IN_STOCK",
                    "restockDate": null
                },
                "categories": ["电子产品", "手机", "智能手机"],
                "attributes": {
                    "品牌": "示例品牌",
                    "颜色": "黑色",
                    "内存": "128GB"
                },
                "imageUrls": ["https://example.com/image1.jpg"],
                "reviewSummary": {
                    "averageRating": 4.5,
                    "totalReviews": 1280,
                    "ratingDistribution": {
                        "5": 800,
                        "4": 300,
                        "3": 100,
                        "2": 50,
                        "1": 30
                    }
                }
            }
            """;
    }
    
    private List<EcommerceProduct> parseProductList(JsonNode jsonNode) {
        // 实现JSON到对象列表的转换
        List<EcommerceProduct> products = new ArrayList<>();
        // 解析逻辑...
        return products;
    }
}

// 自定义异常类
public class ProductExtractionException extends RuntimeException {
    public ProductExtractionException(String message, Throwable cause) {
        super(message, cause);
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252

3.2 响应式编程支持

// 响应式结构化输出
@Configuration
public class ReactiveStructuredOutputConfig {
    
    @Bean
    public ReactiveChatClient reactiveChatClient() {
        return new ReactiveChatClient(/* 配置 */);
    }
    
    @Bean
    public BeanOutputParser<ProductInfo> reactiveProductParser() {
        return new BeanOutputParser<>(ProductInfo.class);
    }
}

@Service
public class ReactiveProductService {
    
    @Autowired
    private ReactiveChatClient reactiveChatClient;
    
    @Autowired
    private BeanOutputParser<ProductInfo> productParser;
    
    public Flux<ProductInfo> extractProductsFromStream(Flux<String> textStream) {
        return textStream
            .buffer(10) // 每10个文本一批处理
            .flatMap(this::processBatch)
            .flatMapIterable(Function.identity());
    }
    
    private Flux<List<ProductInfo>> processBatch(List<String> texts) {
        List<Mono<ProductInfo>> productMonos = texts.stream()
            .map(this::extractSingleProduct)
            .collect(Collectors.toList());
            
        return Flux.combineLatest(
            productMonos,
            objects -> Arrays.stream(objects)
                .map(obj -> (ProductInfo) obj)
                .collect(Collectors.toList())
        );
    }
    
    public Mono<ProductInfo> extractSingleProduct(String text) {
        String formatInstructions = productParser.getFormat();
        
        Prompt prompt = new Prompt("""
            提取产品信息:
            {text}
            
            格式:
            {format}
            """.replace("{text}", text).replace("{format}", formatInstructions));
            
        return reactiveChatClient
            .call(prompt)
            .map(chatResponse -> chatResponse.getResult().getOutput().getContent())
            .map(productParser::parse)
            .onErrorResume(throwable -> {
                // 错误处理
                return Mono.just(createFallbackProduct());
            });
    }
    
    private ProductInfo createFallbackProduct() {
        return ProductInfo.builder()
            .name("未知产品")
            .price(BigDecimal.ZERO)
            .build();
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172

4. 高级特性与最佳实践

4.1 自定义 Output Parser

// 自定义输出解析器
@Component
public class CustomXmlOutputParser implements OutputParser<Map<String, Object>> {
    
    private final ObjectMapper xmlMapper;
    
    public CustomXmlOutputParser() {
        this.xmlMapper = new XmlMapper();
        this.xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
    
    @Override
    public Map<String, Object> parse(String text) {
        try {
            // 提取XML部分(可能包含在markdown代码块中)
            String xmlContent = extractXmlContent(text);
            return xmlMapper.readValue(xmlContent, new TypeReference<Map<String, Object>>() {});
        } catch (Exception e) {
            throw new OutputParseException("XML解析失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String getFormat() {
        return """
            请使用以下XML格式输出:
            <response>
                <status>success|error</status>
                <data>
                    <!-- 具体数据内容 -->
                </data>
                <timestamp>当前时间戳</timestamp>
            </response>
            """;
    }
    
    private String extractXmlContent(String text) {
        // 从文本中提取XML内容
        Pattern pattern = Pattern.compile("```xml\n(.*?)\n```", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            return matcher.group(1);
        }
        
        // 如果没有代码块,尝试直接查找XML标签
        pattern = Pattern.compile("<response>.*?</response>", Pattern.DOTALL);
        matcher = pattern.matcher(text);
        if (matcher.find()) {
            return matcher.group(0);
        }
        
        throw new OutputParseException("未找到有效的XML内容");
    }
}

// 复合解析器
@Component  
public class CompositeOutputParser implements OutputParser<Object> {
    
    private final List<OutputParser<?>> parsers;
    
    public CompositeOutputParser(List<OutputParser<?>> parsers) {
        this.parsers = parsers;
    }
    
    @Override
    public Object parse(String text) {
        for (OutputParser<?> parser : parsers) {
            try {
                return parser.parse(text);
            } catch (Exception e) {
                // 尝试下一个解析器
                continue;
            }
        }
        throw new OutputParseException("所有解析器都无法处理该文本");
    }
    
    @Override
    public String getFormat() {
        return """
            请选择以下任意一种格式输出:
            
            格式1(JSON):
            {jsonFormat}
            
            格式2(XML):
            {xmlFormat}
            
            格式3(YAML):
            {yamlFormat}
            """.replace("{jsonFormat}", getJsonFormat())
              .replace("{xmlFormat}", getXmlFormat())
              .replace("{yamlFormat}", getYamlFormat());
    }
    
    private String getJsonFormat() {
        return "{"key": "value"}";
    }
    
    private String getXmlFormat() {
        return "<root><key>value</key></root>";
    }
    
    private String getYamlFormat() {
        return "key: value";
    }
}

AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108

4.2 验证与错误处理

// 结构化输出验证器
@Component
public class StructuredOutputValidator {
    
    private final Validator validator;
    
    public StructuredOutputValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        this.validator = factory.getValidator();
    }
    
    public <T> ValidationResult validate(T object) {
        Set<ConstraintViolation<T>> violations = validator.validate(object);
        
        if (violations.isEmpty()) {
            return ValidationResult.valid();
        } else {
            List<String> errorMessages = violations.stream()
                .map(violation -> 
                    String.format("%s: %s", violation.getPropertyPath(), violation.getMessage()))
                .collect(Collectors.toList());
            return ValidationResult.invalid(errorMessages);
        }
    }
    
    public <T> T validateAndFix(T object, Class<T> clazz) {
        ValidationResult result = validate(object);
        
        if (result.isValid()) {
            return object;
        }
        
        // 尝试自动修复常见问题
        return attemptAutoFix(object, clazz, result.getErrors());
    }
    
    private <T> T attemptAutoFix(T object, Class<T> clazz, List<String> errors) {
        try {
            T fixedObject = cloneObject(object, clazz);
            
            for (String error : errors) {
                if (error.contains("不能为null")) {
                    applyDefaultValues(fixedObject, error);
                } else if (error.contains("数值范围")) {
                    applyRangeCorrection(fixedObject, error);
                }
            }
            
            // 验证修复后的对象
            if (validate(fixedObject).isValid()) {
                return fixedObject;
            }
        } catch (Exception e) {
            // 修复失败
        }
        
        throw new ValidationException("无法自动修复数据: " + errors);
    }
    
    // 实现克隆和修复逻辑...
}

// 验证结果封装
@Data
@AllArgsConstructor
class ValidationResult {
    private final boolean valid;
    private final List<String> errors;
    
    public static ValidationResult valid() {
        return new ValidationResult(true, Collections.emptyList());
    }
    
    public static ValidationResult invalid(List<String> errors) {
        return new ValidationResult(false, errors);
    }
}

AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677

4.3 性能优化与缓存

// 结构化输出缓存
@Service
public class StructuredOutputCache {
    
    private final Cache<String, Object> cache;
    
    public StructuredOutputCache() {
        this.cache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .build();
    }
    
    public <T> Optional<T> get(String key, Class<T> clazz) {
        Object value = cache.getIfPresent(key);
        if (value != null && clazz.isInstance(value)) {
            return Optional.of(clazz.cast(value));
        }
        return Optional.empty();
    }
    
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    
    public String generateCacheKey(String prompt, Class<?> outputType) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            String input = prompt + "::" + outputType.getName();
            byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception e) {
            return Integer.toHexString(input.hashCode());
        }
    }
}

// 带缓存的结构化输出服务
@Service
public class CachedStructuredOutputService {
    
    @Autowired
    private ChatClient chatClient;
    
    @Autowired
    private StructuredOutputCache cache;
    
    @Autowired
    private StructuredOutputValidator validator;
    
    public <T> T getStructuredOutput(String prompt, OutputParser<T> parser, Class<T> clazz) {
        String cacheKey = cache.generateCacheKey(prompt, clazz);
        
        // 尝试从缓存获取
        Optional<T> cached = cache.get(cacheKey, clazz);
        if (cached.isPresent()) {
            return cached.get();
        }
        
        // 调用AI生成
        T result = generateStructuredOutput(prompt, parser, clazz);
        
        // 验证并缓存结果
        ValidationResult validation = validator.validate(result);
        if (validation.isValid()) {
            cache.put(cacheKey, result);
            return result;
        } else {
            throw new ValidationException("输出验证失败: " + validation.getErrors());
        }
    }
    
    private <T> T generateStructuredOutput(String prompt, OutputParser<T> parser, Class<T> clazz) {
        String formatInstructions = parser.getFormat();
        
        String fullPrompt = """
            {prompt}
            
            请严格按照以下格式输出:
            {format}
            """.replace("{prompt}", prompt).replace("{format}", formatInstructions);
            
        Prompt aiPrompt = new Prompt(fullPrompt);
        String content = chatClient.call(aiPrompt).getResult().getOutput().getContent();
        
        return parser.parse(content);
    }
}

AI写代码java
运行
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

5. 实际应用场景

5.1 电商数据分析

// 电商评论情感分析
@Data
@Builder
public class ProductReview {
    @Description("评论ID")
    private String reviewId;
    
    @Description("情感极性")
    private Sentiment sentiment;
    
    @Description("情感得分 -1.0到1.0")
    @Range(min = -1, max = 1)
    private Double sentimentScore;
    
    @Description("提取的产品特征")
    private List<ProductFeature> features;
    
    @Description("评论分类")
    private List<ReviewCategory> categories;
    
    @Description("建议改进点")
    private List<String> suggestions;
    
    @Data
    @Builder
    public static class ProductFeature {
        @Description("特征名称")
        private String name;
        
        @Description("提及情感")
        private Sentiment sentiment;
        
        @Description("具体评价")
        private String comment;
    }
    
    public enum Sentiment {
        POSITIVE, NEUTRAL, NEGATIVE, MIXED
    }
    
    public enum ReviewCategory {
        QUALITY, PRICE, DELIVERY, SERVICE, USABILITY, DESIGN
    }
}

@Service
public class ReviewAnalysisService {
    
    @Autowired
    private CachedStructuredOutputService outputService;
    
    @Autowired
    private BeanOutputParser<ProductReview> reviewParser;
    
    public ProductReview analyzeReview(String reviewText) {
        String prompt = """
            请分析以下产品评论:
            "{reviewText}"
            
            要求:
            1. 判断整体情感倾向
            2. 提取提到的产品特征及对应情感
            3. 对评论进行分类
            4. 总结改进建议
            """.replace("{reviewText}", reviewText);
            
        return outputService.getStructuredOutput(prompt, reviewParser, ProductReview.class);
    }
    
    public ReviewSummary analyzeBatchReviews(List<String> reviews) {
        List<ProductReview> analyzedReviews = reviews.parallelStream()
            .map(this::analyzeReview)
            .collect(Collectors.toList());
            
        return generateSummary(analyzedReviews);
    }
    
    private ReviewSummary generateSummary(List<ProductReview> reviews) {
        // 生成评论摘要统计
        // 实现细节...
        return new ReviewSummary();
    }
}

AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283

5.2 智能客服系统

// 客服工单自动分类
@Data
@Builder
public class SupportTicket {
    @Description("工单ID")
    private String ticketId;
    
    @Description("问题分类")
    private ProblemCategory category;
    
    @Description("紧急程度")
    private UrgencyLevel urgency;
    
    @Description("问题描述摘要")
    private String summary;
    
    @Description("建议解决方案")
    private List<String> suggestedSolutions;
    
    @Description("需要的信息")
    private List<String> requiredInformation;
    
    @Description("自动回复模板")
    private String autoResponse;
    
    @Description("预计解决时间")
    private String estimatedResolutionTime;
    
    public enum ProblemCategory {
        BILLING, TECHNICAL, ACCOUNT, FEATURE_REQUEST, BUG_REPORT
    }
    
    public enum UrgencyLevel {
        LOW, MEDIUM, HIGH, CRITICAL
    }
}

@Service
public class SupportTicketService {
    
    @Autowired
    private CachedStructuredOutputService outputService;
    
    @Autowired
    private BeanOutputParser<SupportTicket> ticketParser;
    
    public SupportTicket classifyTicket(String userMessage) {
        String prompt = """
            用户反馈:
            {message}
            
            请分析:
            1. 问题分类
            2. 紧急程度
            3. 自动生成回复
            4. 建议解决方案
            5. 需要用户提供的额外信息
            """.replace("{message}", userMessage);
            
        return outputService.getStructuredOutput(prompt, ticketParser, SupportTicket.class);
    }
}

AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162

6. 总结

Spring AI 的结构化输出功能通过以下方式实现:

6.1 核心实现机制

  1. Output Parser 体系:提供统一的输出解析接口
  2. Schema 驱动:基于 Java Bean 或 JSON Schema 定义输出结构
  3. 提示词工程:自动生成格式指令并注入到提示词中
  4. 类型安全:编译时类型检查与运行时验证结合

6.2 主要优势

  • 开发效率:减少数据解析和转换代码
  • 维护性:集中管理输出格式定义
  • 可靠性:内置验证和错误处理机制
  • 灵活性:支持自定义解析器和复杂数据结构

6.3 最佳实践

  1. 合理设计数据模型:使用清晰的字段描述和验证注解
  2. 分层错误处理:结合验证器实现健壮的错误恢复
  3. 性能优化:利用缓存减少重复的AI调用
  4. 监控可观测性:记录解析成功率和数据质量指标

Spring AI 的结构化输出让AI集成变得更加工程化和可维护,是现代AI应用开发的重要基础设施。