使用 Spring AI 打造企业级 RAG 知识库第一部分:核心基础

1.1 稳固开发基础:Spring Boot 核心机制

自动配置原理(Auto-Configuration)

Spring Boot 的自动配置机制是其"约定优于配置"理念的核心实现。它通过 @EnableAutoConfiguration 注解触发,利用 SpringFactoriesLoader 机制加载自动配置类。
⚠️ 重要更新(Spring Boot 2.7+) :从 Spring Boot 2.7 开始,传统的 META-INF/spring.factories 配置方式已标记为弃用,Spring Boot 3.x 版本已完全移除对该方式的支持。新的配置方式使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,每行一个全限定类名,不再使用键值对格式。
2.7+
2.6及以前


启动类 @SpringBootApplication
包含 @EnableAutoConfiguration
SpringFactoriesLoader 扫描
Spring Boot 版本判断
读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
读取 META-INF/spring.factories

key=EnableAutoConfiguration
加载所有 AutoConfiguration 类
条件评估: @ConditionalOnClass / OnProperty / OnMissingBean
条件是否满足?
配置类生效
配置类跳过
自动注入 Bean 到 Spring 容器

自动配置的完整生命周期:

  1. 启动触发@SpringBootApplication 是一个组合注解,包含 @EnableAutoConfiguration,它通过 @Import(AutoConfigurationImportSelector.class) 触发自动配置流程

  2. 配置加载AutoConfigurationImportSelector 实现 ImportSelector 接口,在容器刷新阶段调用 selectImports() 方法,通过 SpringFactoriesLoader.loadFactoryNames() 读取类路径下的配置文件

  3. 条件评估 :每个自动配置类都带有条件注解(Condition),Spring 使用 ConditionEvaluator 在运行时评估:

    • @ConditionalOnClass:类路径存在指定类时生效(如 OpenAiApi.class 存在才加载 OpenAI 自动配置)
    • @ConditionalOnProperty:配置文件中指定属性为特定值时生效(如 spring.ai.openai.api-key 存在)
    • @ConditionalOnMissingBean:容器中不存在指定 Bean 时才生效(避免覆盖用户自定义)
    • @ConditionalOnWebApplication:仅在 Web 应用环境下生效
  4. 属性绑定 :通过 @EnableConfigurationProperties@ConfigurationProperties 标注的类注册为 Bean,实现外部配置(application.yml)到结构化 Java 对象的类型安全绑定

  5. 执行顺序 :通过 @AutoConfigureOrder@AutoConfigureAfter 控制配置类加载顺序,确保依赖关系正确处理

优化替代方案 - 自定义自动配置:

java 复制代码
// 1. 创建配置属性类
@ConfigurationProperties(prefix = "custom.ai")
public class CustomAiProperties {
    private String apiKey;
    private String baseUrl = "https://api.default.com";
    private Duration timeout = Duration.ofSeconds(30);
    
    // getters and setters
}

// 2. 创建自动配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CustomAiClient.class)  // 类路径存在才生效
@ConditionalOnProperty(prefix = "custom.ai", name = "api-key")  // 配置了api-key才生效
@EnableConfigurationProperties(CustomAiProperties.class)
@AutoConfigureAfter({RestTemplateAutoConfiguration.class})  // 在RestTemplate配置之后
public class CustomAiAutoConfiguration {
    
    private final CustomAiProperties properties;
    
    public CustomAiAutoConfiguration(CustomAiProperties properties) {
        this.properties = properties;
    }
    
    @Bean
    @ConditionalOnMissingBean  // 用户未定义时才创建
    public CustomAiClient customAiClient(RestTemplateBuilder restTemplateBuilder) {
        return new CustomAiClient(
            properties.getApiKey(),
            properties.getBaseUrl(),
            properties.getTimeout(),
            restTemplateBuilder.build()
        );
    }
}

// 3. 在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中注册
// 文件内容:com.example.ai.autoconfigure.CustomAiAutoConfiguration
三层架构(Controller-Service-Repository)

企业级应用的标准分层架构确保关注点分离(Separation of Concerns)。实际生产环境中,这个架构通常扩展为五层或六层,以处理更复杂的业务需求。
基础设施层 Infrastructure Layer
数据访问层 Data Access Layer
业务层 Business Layer
表现层 Presentation Layer
接入层 API Gateway
客户端层 Client Layer
Web/App/第三方系统
API网关

鉴权/限流/路由
Controller

处理 HTTP 请求/响应

参数校验/转换

调用 Service

返回统一响应格式
Request DTO

入参对象
Response DTO

出参对象
View Object

视图对象
Service Interface

业务逻辑接口定义
Service Impl

业务逻辑实现

事务管理

跨领域协调
Business Object

业务对象
Repository/Mapper

数据持久化接口
Repository Impl

查询实现

数据库交互
Entity/PO

持久化对象
Database
Redis
Message Queue
File Storage

分层架构详解与补充:

  1. Controller 层(表现层)

    • 核心职责:接收 HTTP 请求、参数校验、DTO 转换、调用 Service、封装统一响应、处理异常转换
    • 禁止事项绝不包含业务逻辑,不进行事务控制,不直接操作数据库
    • DTO(Data Transfer Object):用于层间数据传输,Controller 接收 Request DTO,返回 Response DTO,避免直接暴露 Entity 结构
    • 参数校验 :使用 @Valid@Validated 触发 Bean Validation,配合全局异常处理器统一返回错误格式
  2. Service 层(业务层)

    • 核心职责 :承载核心业务逻辑,管理事务边界(@Transactional),协调多个 Repository 或外部服务,实现业务规则的完整性校验
    • 设计原则:接口与实现分离(基于接口编程),便于单元测试和 AOP 代理
    • 事务管理 :在 Service 层通过 @Transactional 控制事务边界,确保 ACID 特性。涉及多数据源时使用分布式事务(Seata)或 Saga 模式
    • 领域对象:使用 BO(Business Object)封装复杂业务逻辑,避免 Entity 直接参与业务计算
  3. Repository/Mapper 层(数据访问层)

    • 核心职责:封装数据访问细节,对外暴露领域对象的持久化操作,屏蔽底层数据库差异
    • 技术选型
      • Spring Data JPA:适合复杂对象关系、快速开发场景,通过方法名派生查询
      • MyBatis/MyBatis-Plus:适合复杂 SQL、高性能优化、遗留系统改造
    • Entity/PO(Persistent Object) :与数据库表结构一一映射,仅包含字段和 getter/setter,禁止包含业务逻辑
    • 查询优化:复杂查询使用 CQRS(命令查询职责分离)模式,读操作直接返回 DTO 避免 Entity 转换开销

分层间的数据流转:
DB Entity Repository BO Service DTO Controller Client DB Entity Repository BO Service DTO Controller Client HTTP Request (JSON) 反序列化为 RequestDTO @Valid 参数校验 调用方法,传入 DTO DTO 转换为 BO/Entity 调用持久化方法 操作 Entity 执行 SQL 返回结果 返回 Entity 业务逻辑处理 Entity/BO 转换为 ResponseDTO 返回 DTO HTTP Response (JSON)

1.2 观念与实践:依赖注入与验证机制

依赖注入(Dependency Injection, DI)

Spring 的 DI 容器通过 控制反转(IoC) 模式管理对象生命周期和依赖关系。IoC 是一种设计原则,DI 是其具体实现方式之一。

三种注入方式深度对比:

注入方式 代码示例 适用场景 优点 缺点 Spring 推荐度
构造器注入 public Service(UserRepo repo) {...} 强制依赖、不可变依赖 1. 依赖不可变(final) 2. 确保对象创建时完整 3. 便于单元测试 4. 循环依赖可检测 参数过多时构造器臃肿 ⭐⭐⭐⭐⭐ 首选
Setter 注入 @Autowired setUserRepo(...) 可选依赖、配置属性 1. 灵活性高 2. 可在创建后重新配置 1. 对象可能处于不完整状态 2. 难以追踪依赖 ⭐⭐⭐ 特定场景
字段注入 @Autowired private UserRepo repo 快速开发、遗留代码 代码简洁 1. 隐藏依赖(违反单一职责) 2. 无法单元测试(需反射) 3. NPE 风险 ⭐ 不推荐

构造器注入最佳实践(Lombok 版本):

java 复制代码
@Service
@RequiredArgsConstructor  // Lombok 生成包含 final 字段的构造器
public class KnowledgeBaseService {
    
    // final 确保不可变性
    private final DocumentRepository documentRepository;
    private final VectorStore vectorStore;
    private final EmbeddingModel embeddingModel;
    
    // 可选依赖使用 Setter 注入
    private AuditLogger auditLogger;
    
    @Autowired(required = false)  // required=false 表示可选
    public void setAuditLogger(AuditLogger auditLogger) {
        this.auditLogger = auditLogger;
    }
    
    @Transactional
    public Document saveDocument(DocumentDTO dto) {
        // 所有强制依赖通过构造器传入,确保对象创建时即完整可用
        // 无需担心 NPE
        Document doc = convertToEntity(dto);
        Document saved = documentRepository.save(doc);
        
        // 异步处理向量嵌入
        vectorStore.add(List.of(
            new Document(saved.getContent(), 
                Map.of("docId", saved.getId().toString()))
        ));
        
        // 可选依赖使用前检查
        if (auditLogger != null) {
            auditLogger.log("DOCUMENT_SAVED", saved.getId());
        }
        
        return saved;
    }
}

循环依赖问题与解决方案:

当 Bean A 依赖 B,B 又依赖 A 时,构造器注入会抛出 BeanCurrentlyInCreationException
构造器依赖
构造器依赖
Service A
Service B

解决方案(按推荐度排序):

  1. 重构代码(推荐):通过抽取公共逻辑到第三个 Service C,打破循环依赖
  2. Setter 注入:将其中一个依赖改为 Setter 方式,延迟注入
  3. @Lazy 延迟加载 :在构造器参数上使用 @Lazy,注入代理对象而非真实实例
java 复制代码
@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    // 使用 @Lazy 延迟注入,解决循环依赖
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
  1. ObjectFactory/Provider :使用 ObjectFactory<ServiceB> 延迟获取实例
数据验证(Validation)

Spring 使用 Bean Validation 3.0(Jakarta Validation,原 JSR-380) 规范,通过 Hibernate Validator 实现声明式校验。Spring Boot 3.x 已迁移到 Jakarta EE 命名空间(jakarta.validation),不再使用 javax.validation

常用约束注解分类:

分类 注解 说明 适用类型
空值检查 @NotNull 值不能为 null 任意
@NotEmpty 字符串/集合非 null 且非空(长度>0) String, Collection
@NotBlank 字符串非 null 且至少包含一个非空白字符 String
范围检查 @Size(min, max) 长度/大小在范围内 String, Collection, Map, Array
@Min / @Max 数值最小/最大值 Number
@Range(min, max) 数值范围(Hibernate 扩展) Number
@DecimalMin / @DecimalMax 小数范围 BigDecimal 等
模式检查 @Pattern(regexp) 正则表达式匹配 String
@Email 邮箱格式验证 String
@URL URL 格式验证(Hibernate 扩展) String
时间检查 @Past / @Future 过去/未来时间 Date, Temporal
业务逻辑 @AssertTrue / @AssertFalse 自定义验证逻辑 Boolean
@Valid 级联验证嵌套对象 Object

分组验证与顺序控制:

java 复制代码
// 定义验证分组接口
public interface ValidationGroups {
    interface Create {}  // 创建时验证
    interface Update {}  // 更新时验证
    interface Delete {}  // 删除时验证
}

public class DocumentDTO {
    @Null(groups = ValidationGroups.Create.class)  // 创建时ID必须为null
    @NotNull(groups = ValidationGroups.Update.class)  // 更新时ID必须存在
    private Long id;
    
    @NotBlank(message = "文档标题不能为空")
    @Size(max = 100, message = "标题长度不能超过100字符")
    private String title;
    
    @NotBlank(groups = ValidationGroups.Create.class)  // 仅创建时验证内容
    private String content;
    
    @Valid  // 级联验证嵌套对象
    private List<TagDTO> tags;
}

// Controller 中使用分组
@RestController
public class DocumentController {
    
    @PostMapping("/documents")
    public ResponseEntity<?> create(
            @Validated(ValidationGroups.Create.class) @RequestBody DocumentDTO dto) {
        // 仅执行 Create 组的验证
        return ResponseEntity.ok(service.create(dto));
    }
    
    @PutMapping("/documents/{id}")
    public ResponseEntity<?> update(
            @Validated(ValidationGroups.Update.class) @RequestBody DocumentDTO dto) {
        // 仅执行 Update 组的验证
        return ResponseEntity.ok(service.update(dto));
    }
}

统一异常处理机制(增强版):

java 复制代码
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    // 处理参数校验失败异常(MethodArgumentNotValidException)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(
            MethodArgumentNotValidException ex, 
            WebRequest request) {
        
        List<FieldErrorDetail> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(error -> new FieldErrorDetail(
                error.getField(),
                error.getRejectedValue(),
                error.getDefaultMessage(),
                error.getCode()  // 错误类型码
            ))
            .collect(Collectors.toList());
        
        log.warn("参数校验失败: URI={}, Errors={}", 
            request.getDescription(false), errors);
            
        ErrorResponse response = ErrorResponse.builder()
            .code("VALIDATION_ERROR")
            .message("请求参数校验失败")
            .errors(errors)
            .timestamp(Instant.now())
            .path(request.getDescription(false))
            .build();
            
        return ResponseEntity.badRequest().body(response);
    }
    
    // 处理请求体缺失或格式错误
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ErrorResponse> handleMessageNotReadable(
            HttpMessageNotReadableException ex) {
        
        String message = "请求体格式错误";
        if (ex.getCause() instanceof InvalidFormatException ife) {
            message = String.format("字段'%s'类型不匹配,期望%s", 
                ife.getPath().get(0).getFieldName(),
                ife.getTargetType().getSimpleName());
        }
        
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("FORMAT_ERROR", message));
    }
    
    // 处理约束违反异常(通常发生在 Service 层 @Validated)
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResponse> handleConstraintViolation(
            ConstraintViolationException ex) {
        
        List<String> errors = ex.getConstraintViolations()
            .stream()
            .map(v -> v.getPropertyPath() + ": " + v.getMessage())
            .collect(Collectors.toList());
            
        return ResponseEntity.badRequest()
            .body(new ErrorResponse("CONSTRAINT_ERROR", "约束违反", errors));
    }
    
    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        log.error("业务异常: {}", ex.getMessage(), ex);
        return ResponseEntity.status(ex.getHttpStatus())
            .body(ErrorResponse.builder()
                .code(ex.getErrorCode())
                .message(ex.getMessage())
                .timestamp(Instant.now())
                .build());
    }
    
    // 兜底异常处理
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception ex) {
        log.error("系统异常", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(new ErrorResponse("INTERNAL_ERROR", "系统内部错误,请联系管理员"));
    }
}

// 统一错误响应结构
@Data
@Builder
public class ErrorResponse {
    private String code;           // 业务错误码
    private String message;        // 错误描述
    private List<?> errors;        // 详细错误列表
    private Instant timestamp;     // 时间戳
    private String path;           // 请求路径
    
    @Data
    @AllArgsConstructor
    public static class FieldErrorDetail {
        private String field;      // 字段名
        private Object rejectedValue;  // 拒绝的值
        private String message;    // 错误消息
        private String code;       // 验证器类型
    }
}

1.3 安全配置:API Key 管理策略

企业级 AI 应用必须妥善管理大模型 API Key,避免硬编码泄露风险。API Key 属于高敏感凭证,泄露可能导致严重的财务损失和数据安全问题。

安全风险等级评估:
API Key 管理方式
硬编码

❌ 极高风险
配置文件明文

⚠️ 高风险
环境变量

✅ 中等风险
密钥管理系统

✅ 低风险
动态凭证

✅ 极低风险

推荐方案演进:

阶段一:环境变量 + Spring 配置属性(开发/测试环境)

yaml 复制代码
# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY:}  # 从环境变量读取,无默认值,缺失时为空字符串
      base-url: ${OPENAI_BASE_URL:https://api.openai.com}
    azure:
      openai:
        api-key: ${AZURE_OPENAI_KEY:}
        endpoint: ${AZURE_OPENAI_ENDPOINT:}
    # 国产大模型配置
    zhipuai:
      api-key: ${ZHIPUAI_API_KEY:}
    dashscope:  # 通义千问
      api-key: ${DASHSCOPE_API_KEY:}

阶段二:配置加密(生产环境基础防护)

使用 Jasypt 或 Spring Cloud Config 加密敏感配置:

yaml 复制代码
# 加密后的配置(ENC(...) 为加密标记)
spring:
  ai:
    openai:
      api-key: ENC(AQOqC8W7hBv3x8f4cWQwQzYxNjc5...)
java 复制代码
@Configuration
public class JasyptConfig {
    @Bean("jasyptStringEncryptor")
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        // 从环境变量读取主密钥,而非配置文件
        config.setPassword(System.getenv("JASYPT_ENCRYPTOR_PASSWORD"));
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }
}

阶段三:密钥管理系统(KMS)- 生产环境标准方案

使用云厂商提供的密钥管理服务,实现动态注入和自动轮换:

java 复制代码
@Configuration
public class CloudSecretConfig {
    
    @Bean
    @RefreshScope  // 支持配置刷新
    public OpenAiApi openAiApi(SecretClient secretClient) {
        // 从 Azure Key Vault 获取密钥
        String apiKey = secretClient.getSecret("openai-api-key").getValue();
        
        return new OpenAiApi(
            "https://api.openai.com",
            apiKey,
            RestClient.builder()
        );
    }
}

阶段四:完整的密钥安全治理方案
技术实现层
密钥生命周期管理
密钥创建
密钥分发
密钥使用
密钥轮换
密钥吊销
密钥审计
Vault

HashiCorp/AWS/Azure
IAM 角色绑定

Pod Identity
内存安全

不在磁盘落盘
传输加密

TLS 1.3

生产环境强化方案完整清单:

  1. 密钥轮换机制

    • 实现双密钥并发机制(新旧密钥同时有效过渡期)
    • 定期自动轮换(建议 90 天周期)
    • 紧急吊销能力(应对泄露场景)
  2. 最小权限原则

    • 为不同环境(dev/staging/prod)使用独立的 API Key
    • 限制 API Key 的模型访问范围(如仅允许 GPT-4,禁止 GPT-3.5)
    • 设置调用频率限制和额度上限
  3. 监控与审计

    • 记录所有 API Key 的使用日志(调用时间、IP、模型、token 数)
    • 异常流量检测(突增的调用量或错误率)
    • 成本告警(日消费限额告警)
  4. 安全传输与存储

    • 传输层强制 TLS 1.3
    • 内存中存储(避免写入日志或堆转储)
    • 使用 char[] 而非 String 存储密钥(便于清零)

1.4 Spring AI 核心价值:统一 AI 模型接入层

Spring AI 的核心价值主张是 "解耦 AI 模型与业务代码",它通过抽象层屏蔽底层大模型(OpenAI、Azure、Anthropic、Ollama 等)的差异,让开发者像使用标准数据源(JdbcTemplate、JPA)一样使用 AI 能力。
底层大模型
模型提供商 Adapters
Spring AI 抽象层 Core Abstractions
应用层 Application Layer
业务代码

ChatClient / ChatModel
ChatClient API

Fluent Interface

统一入口
Model 抽象

ChatModel / EmbeddingModel

ImageModel / AudioModel
Prompt 管理

PromptTemplate

Message 体系
顾问机制

Advisor API

横切关注点
输出解析

OutputParser

Bean 提取
OpenAI

GPT-4/3.5
Azure OpenAI

企业级 GPT
Anthropic

Claude 3.5
Ollama

本地模型
智谱 AI

GLM-4
通义千问

Qwen
百度文心

ERNIE
DeepSeek

R1/V3
GPT-4o / o1
Claude 3.5 Sonnet
Llama 3 / Mistral

Gemma
GLM-4 / ChatGLM3
Qwen-Max / Qwen-Plus

Spring AI 架构核心组件:

  1. ChatClient(推荐入口) :Fluent API,采用 Builder 模式 构建请求,支持链式调用配置 Prompt、上下文、工具等。它是高层次的抽象,简化了大多数场景的使用。

  2. ChatModel(底层接口):直接操作模型通信,适合需要精细控制请求/响应的场景。

  3. Prompt 体系

    • Prompt:包含 Message 列表和模型选项的完整请求
    • Message:角色(System/User/Assistant/Tool) + 内容 + 元数据
    • PromptTemplate:支持占位符 ${variable} 的模板,用于动态构建 Prompt
  4. Advisor 机制(横切关注点):类似 Spring AOP,在请求处理前后插入逻辑,实现日志、重试、缓存、安全过滤等功能。

ChatClient 核心设计详解:

java 复制代码
// 基础用法 - 简单调用
String response = chatClient.prompt()
    .system("你是一位专业的企业知识库助手")  // 系统消息:设定 AI 角色和行为准则
    .user("查询请假流程")                    // 用户消息:用户输入
    .call()                                 // 执行同步调用
    .content();                             // 提取文本响应

// 进阶用法 - 完整配置
ChatResponse response = chatClient.prompt()
    .system(s -> s.text("你是一位{role},使用{tone}语气回答")
        .param("role", "企业知识库助手")
        .param("tone", "专业且友好"))        // 系统消息支持参数化模板
    .user("查询请假流程")
    .advisors(a -> a                       // 添加顾问链
        .param("chat_memory_conversation_id", "user-123")
        .param("chat_memory_response_size", 10))
    .options(OpenAiChatOptions.builder()    // 模型参数配置
        .withModel("gpt-4-turbo-preview")
        .withTemperature(0.7)               // 创造性 vs 确定性 (0-2)
        .withMaxTokens(2000)                // 最大生成 token 数
        .withTopP(1.0)                      // 核采样
        .withFrequencyPenalty(0.0)          // 频率惩罚(避免重复)
        .withPresencePenalty(0.0)           // 存在惩罚(鼓励新话题)
        .build())
    .call()
    .chatResponse();                        // 获取完整响应对象(包含元数据)

// 流式调用(SSE)
Flux<String> stream = chatClient.prompt()
    .user("请详细解释 Spring Boot 自动配置原理")
    .stream()                               // 改为 stream 方法
    .content();

输出解析与结构化数据提取:

Spring AI 提供 OutputParser 将模型输出转换为 Java 对象,避免手动解析 JSON:

java 复制代码
// 定义 Bean 结构
public record EmployeeInfo(
    String name,
    String department,
    double salary,
    List<String> skills
) {}

// 使用 BeanOutputParser 自动解析
BeanOutputParser<EmployeeInfo> parser = new BeanOutputParser<>(EmployeeInfo.class);

EmployeeInfo info = chatClient.prompt()
    .system("从以下文本中提取员工信息,按指定格式返回 JSON")
    .user("张三,研发部高级工程师,月薪25000,精通Java和Spring")
    .call()
    .entity(parser);  // 自动解析为 EmployeeInfo 对象

// 或使用 MapOutputParser 动态解析
MapOutputParser mapParser = new MapOutputParser();
Map<String, Object> result = chatClient.prompt()
    .user("提取关键信息")
    .call()
    .entity(mapParser);

顾问(Advisor)机制详解:

Advisor 是 Spring AI 的强大扩展点,用于封装横切关注点:
ChatModel RetryAdvisor MemoryAdvisor LoggerAdvisor Client ChatModel RetryAdvisor MemoryAdvisor LoggerAdvisor Client 发送 Prompt 记录请求日志 传递 Prompt 检索历史消息 合并上下文到 Prompt 传递增强 Prompt 设置重试策略 调用模型 返回响应 传递响应(失败时重试) 保存新消息到记忆 传递响应 记录响应日志 返回最终结果

常用内置 Advisor:

Advisor 功能 使用场景
MessageChatMemoryAdvisor 对话记忆管理 多轮对话保持上下文
QuestionAnswerAdvisor RAG 检索增强 结合向量数据库回答
SafeGuardAdvisor 内容安全检查 过滤敏感词和有害内容
RetryAdvisor 自动重试 模型调用失败时重试
LoggerAdvisor 日志记录 记录请求/响应详情

配置示例:

java 复制代码
@Bean
public ChatClient chatClient(ChatClient.Builder builder, 
                             ChatMemory chatMemory,
                             VectorStore vectorStore) {
    return builder
        .defaultSystem("你是一位专业的企业助手")
        .defaultOptions(OpenAiChatOptions.builder()
            .withTemperature(0.7)
            .build())
        .defaultAdvisors(
            new MessageChatMemoryAdvisor(chatMemory),  // 记忆管理
            new QuestionAnswerAdvisor(vectorStore),     // RAG 增强
            new RetryAdvisor(3, Duration.ofSeconds(1)) // 失败重试
        )
        .build();
}

通过这套架构,业务代码只需关心 Prompt 设计业务逻辑,底层模型切换(从 OpenAI 切换到 Azure 或国产模型)只需修改配置,无需改动业务代码。


相关推荐
爱上珍珠的贝壳2 小时前
ESP32-S3-CAM:豆包语音识别文字后控制小车(三)——SD卡本地音频识别转文字
人工智能·音频·语音识别·智能硬件·esp32-s3
恼书:-(空寄2 小时前
责任链模式实现流程动态编排
java·责任链模式
星原望野2 小时前
java:volatile关键字的作用
java·开发语言·volatile
春末的南方城市2 小时前
CVPR 2026 | 复旦开源首个端到端多模态矢量动画生成框架OmniLottie:UI动效革命,文本/图像一键转Lottie动画!
人工智能·深度学习·机器学习·计算机视觉·aigc
禹笑笑-AI食用指南2 小时前
AI 团队协作下的工作日志系统:痛点、场景与技术解决方案
人工智能
新缸中之脑2 小时前
用Gemma 4构建自托管OCR
人工智能·ocr
ai_xiaogui2 小时前
凌晨3点的重构局:从遗漏“用户中心”看AI客户端前后端分离架构的深水区
人工智能·aistarter·panelai·ai客户端架构设计·桌面端前后端分离·本地大模型api接入·独立开发者踩坑实录
XiYang-DING2 小时前
【Java】Map和Set
java·开发语言
不才小强2 小时前
CUDA编程与API详解
人工智能