Spring AI ChatClient API 介绍

ChatClient 是 Spring AI 提供的核心 API,它是一个流畅的接口,旨在简化 Java 应用程序与 AI 模型的交互。ChatClient 支持同步和流式编程模型,提供了构建提示(Prompt)和处理 AI 响应的便捷方式。

核心概念

1. 提示(Prompt)结构

  • 用户消息:用户直接输入的内容
  • 系统消息:系统生成的指令,用于指导对话
  • 占位符:运行时基于用户输入进行替换的变量
  • 提示选项:如 AI 模型名称、温度设置等

2. 消息类型

  • UserMessage:用户输入的消息
  • SystemMessage:系统级别的指令
  • AssistantMessage:AI 助手的回复

创建 ChatClient

1. 使用自动配置的 ChatClient.Builder

@RestController
class MyController {

    private final ChatClient chatClient;

    public MyController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/ai")
    String generation(String userInput) {
        return this.chatClient.prompt()
            .user(userInput)
            .call()
            .content();
    }
}

2. 多模型配置

禁用默认自动配置

# 禁用默认的 ChatClient.Builder 自动配置
spring.ai.chat.client.enabled=false

单一模型类型的多个 ChatClient

// 程序化创建 ChatClient 实例
ChatModel myChatModel = ... // 已由 Spring Boot 自动配置
ChatClient chatClient = ChatClient.create(myChatModel);

// 或使用 Builder 获得更多控制
ChatClient.Builder builder = ChatClient.builder(myChatModel);
ChatClient customChatClient = builder
    .defaultSystemPrompt("You are a helpful assistant.")
    .build();

不同模型类型的 ChatClients

@Configuration
public class ChatClientConfig {

    @Bean
    public ChatClient openAiChatClient(OpenAiChatModel chatModel) {
        return ChatClient.create(chatModel);
    }

    @Bean
    public ChatClient anthropicChatClient(AnthropicChatModel chatModel) {
        return ChatClient.create(chatModel);
    }
}

使用 @Qualifier 注入特定模型

@Configuration
public class ChatClientExample {

    @Bean
    CommandLineRunner cli(
            @Qualifier("openAiChatClient") ChatClient openAiChatClient,
            @Qualifier("anthropicChatClient") ChatClient anthropicChatClient) {

        return args -> {
            var scanner = new Scanner(System.in);
            ChatClient chat;

            // 模型选择
            System.out.println("\nSelect your AI model:");
            System.out.println("1. OpenAI");
            System.out.println("2. Anthropic");
            System.out.print("Enter your choice (1 or 2): ");

            String choice = scanner.nextLine().trim();

            if (choice.equals("1")) {
                chat = openAiChatClient;
                System.out.println("Using OpenAI model");
            } else {
                chat = anthropicChatClient;
                System.out.println("Using Anthropic model");
            }

            // 使用选定的模型
            String response = chat.prompt()
                .user("Tell me a joke")
                .call()
                .content();
            
            System.out.println("Response: " + response);
        };
    }
}

基础使用

1. 简单聊天示例

@RestController
@RequestMapping("/api/chat")
public class ChatController {
    
    private final ChatClient chatClient;
    
    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }
    
    @PostMapping("/simple")
    public String simpleChat(@RequestBody String message) {
        return chatClient.prompt(message).call().content();
    }
}

2. 使用用户和系统消息

@PostMapping("/advanced")
public String advancedChat(@RequestBody ChatRequest request) {
    return chatClient.prompt()
        .system("你是一个专业的编程助手,请用简洁明了的方式回答问题。")
        .user(request.getMessage())
        .call()
        .content();
}

高级功能

1. 提示模板与占位符

@PostMapping("/template")
public String templateChat(@RequestBody TemplateRequest request) {
    return chatClient.prompt()
        .user(u -> u
            .text("请为 {language} 语言编写一个 {function} 函数,要求:{requirements}")
            .param("language", request.getLanguage())
            .param("function", request.getFunction())
            .param("requirements", request.getRequirements())
        )
        .call()
        .content();
}

2. 流式处理

@PostMapping(value = "/stream", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<StreamingResponseBody> streamChat(@RequestBody String message) {
    return ResponseEntity.ok()
        .contentType(MediaType.TEXT_PLAIN)
        .body(outputStream -> {
            chatClient.prompt()
                .user(message)
                .stream()
                .content()
                .forEach(content -> {
                    try {
                        outputStream.write(content.getBytes());
                        outputStream.flush();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
        });
}

3. 结构化输出

// 定义输出结构
public class CodeResponse {
    private String language;
    private String code;
    private String explanation;
    
    // getters and setters
}

@PostMapping("/structured")
public CodeResponse structuredChat(@RequestBody String request) {
    return chatClient.prompt()
        .user("请为以下需求生成代码:" + request)
        .call()
        .entity(CodeResponse.class);
}

4. 多轮对话

@Service
public class ConversationService {
    
    private final ChatClient chatClient;
    private final List<ChatMessage> conversationHistory = new ArrayList<>();
    
    public ConversationService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }
    
    public String continueConversation(String userMessage) {
        // 添加用户消息到历史
        conversationHistory.add(new UserMessage(userMessage));
        
        // 构建包含历史的提示
        String response = chatClient.prompt()
            .system("你是一个友好的助手,请基于对话历史回答问题。")
            .messages(conversationHistory)
            .call()
            .content();
        
        // 添加助手回复到历史
        conversationHistory.add(new AssistantMessage(response));
        
        return response;
    }
}

Advisors API

Advisors API 提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。

1. Advisor 配置接口

interface AdvisorSpec {
    AdvisorSpec param(String k, Object v);
    AdvisorSpec params(Map<String, Object> p);
    AdvisorSpec advisors(Advisor... advisors);
    AdvisorSpec advisors(List<Advisor> advisors);
}

2. 使用 Advisors

ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

3. 日志 Advisor

ChatResponse response = ChatClient.create(chatModel).prompt()
        .advisors(new SimpleLoggerAdvisor())
        .user("Tell me a joke?")
        .call()
        .chatResponse();

启用日志

logging.level.org.springframework.ai.chat.client.advisor=DEBUG

自定义日志格式

SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
    request -> "Custom request: " + request.prompt().getUserMessage(),
    response -> "Custom response: " + response.getResult(),
    0
);

Chat Memory

1. MessageWindowChatMemory

@Configuration
public class ChatMemoryConfig {
    
    @Bean
    public ChatMemory chatMemory() {
        return new MessageWindowChatMemory(20); // 最多保留 20 条消息
    }
}

2. 使用 Chat Memory

@Service
public class ConversationService {
    
    private final ChatClient chatClient;
    private final ChatMemory chatMemory;
    
    public ConversationService(ChatClient.Builder builder, ChatMemory chatMemory) {
        this.chatClient = builder.build();
        this.chatMemory = chatMemory;
    }
    
    public String continueConversation(String userMessage) {
        // 添加用户消息到记忆
        chatMemory.add(new UserMessage(userMessage));
        
        // 使用记忆进行对话
        String response = chatClient.prompt()
            .advisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
            .user(userMessage)
            .call()
            .content();
        
        // 添加助手回复到记忆
        chatMemory.add(new AssistantMessage(response));
        
        return response;
    }
}

错误处理

1. 基础错误处理

@PostMapping("/safe-chat")
public ResponseEntity<String> safeChat(@RequestBody String message) {
    try {
        String response = chatClient.prompt(message).call().content();
        return ResponseEntity.ok(response);
    } catch (Exception e) {
        log.error("AI 服务调用失败", e);
        return ResponseEntity.status(500)
            .body("AI 服务暂时不可用,请稍后重试");
    }
}

2. 重试机制

@Service
public class ResilientChatService {
    
    private final ChatClient chatClient;
    private final RetryTemplate retryTemplate;
    
    public ResilientChatService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
        this.retryTemplate = RetryTemplate.builder()
            .maxAttempts(3)
            .exponentialBackoff(1000, 2, 10000)
            .retryOn(Exception.class)
            .build();
    }
    
    public String chatWithRetry(String message) {
        return retryTemplate.execute(context -> {
            return chatClient.prompt(message).call().content();
        });
    }
}

实际应用示例

1. 智能客服系统

@RestController
@RequestMapping("/api/support")
public class SupportController {
    
    private final ChatClient chatClient;
    
    @PostMapping("/chat")
    public SupportResponse handleSupportRequest(@RequestBody SupportRequest request) {
        String response = chatClient.prompt()
            .system("你是一个专业的客服助手,请根据用户问题提供帮助。")
            .user("用户问题:" + request.getQuestion() + 
                  "\n用户类型:" + request.getUserType() + 
                  "\n问题分类:" + request.getCategory())
            .call()
            .content();
        
        return new SupportResponse(response, "ai", System.currentTimeMillis());
    }
}

2. 代码生成助手

@Service
public class CodeGenerationService {
    
    private final ChatClient chatClient;
    
    public CodeSuggestion generateCode(String requirement, String language) {
        String prompt = String.format(
            "请为以下需求生成 %s 代码:\n%s\n\n要求:\n1. 代码要简洁高效\n2. 添加必要的注释\n3. 遵循最佳实践",
            language, requirement
        );
        
        String code = chatClient.prompt()
            .system("你是一个专业的编程助手,擅长生成高质量的代码。")
            .user(prompt)
            .call()
            .content();
        
        return new CodeSuggestion(code, language, requirement);
    }
}

3. 检索增强生成(RAG)

@Service
public class RAGService {
    
    private final ChatClient chatClient;
    private final VectorStore vectorStore;
    
    public String answerQuestion(String question) {
        return chatClient.prompt()
            .advisors(QuestionAnswerAdvisor.builder(vectorStore).build())
            .user(question)
            .call()
            .content();
    }
}

配置选项

application.yml 配置

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4
          temperature: 0.7
          max-tokens: 2000
          top-p: 0.9
          frequency-penalty: 0.0
          presence-penalty: 0.0
          stop: ["Human:", "AI:"]
    chat:
      client:
        enabled: true  # 启用 ChatClient 自动配置

application.properties 配置

# OpenAI 配置
spring.ai.openai.api-key=${OPENAI_API_KEY:your-api-key-here}
spring.ai.openai.chat.options.model=gpt-4
spring.ai.openai.chat.options.temperature=0.7
spring.ai.openai.chat.options.max-tokens=2000

# ChatClient 配置
spring.ai.chat.client.enabled=true

实现注意事项

1. 命令式和响应式编程模型

  • HTTP 客户端配置:RestClient 和 WebClient 都必须配置
  • 流式处理:仅通过响应式堆栈支持
  • 非流式处理:仅通过 Servlet 堆栈支持
  • 工具调用:是命令式的,导致阻塞工作流

2. 重要配置

# 由于 Spring Boot 3.4 的 bug,必须设置此属性
spring.http.client.factory=jdk

3. 性能优化

异步处理

@Service
public class AsyncChatService {
    
    private final ChatClient chatClient;
    private final AsyncTaskExecutor taskExecutor;
    
    public CompletableFuture<String> asyncChat(String message) {
        return CompletableFuture.supplyAsync(() -> {
            return chatClient.prompt(message).call().content();
        }, taskExecutor);
    }
}

缓存机制

@Service
public class CachedChatService {
    
    private final ChatClient chatClient;
    private final CacheManager cacheManager;
    
    @Cacheable(value = "chat-responses", key = "#message")
    public String cachedChat(String message) {
        return chatClient.prompt(message).call().content();
    }
}

总结

Spring AI 的 ChatClient API 提供了强大而灵活的接口来与 AI 模型交互。通过其流畅的 API 设计,开发者可以轻松实现:

  • 简单的聊天功能
  • 复杂的多轮对话
  • 流式响应处理
  • 结构化输出
  • 多模型支持
  • 检索增强生成(RAG)
  • 对话记忆管理
  • 自定义 Advisor 链
  • 错误处理和重试机制

这使得 ChatClient 成为构建现代 AI 驱动应用程序的理想选择。