Spring AI 从入门到精通-ChatClient你与 AI 对话的终极武器

3. ChatClient:你与 AI 对话的终极武器

3.1 为什么需要 ChatClient?

直接调 ChatModel 也可以,但很啰嗦。看看对比:

只用 ChatModel(啰嗦版):

java 复制代码
@Autowired
private ChatModel chatModel;

// 构建一个 Prompt,手动处理各种细节
Prompt prompt = new Prompt(
    List.of(new UserMessage("讲个笑话")),
    OpenAiChatOptions.builder().temperature(0.7).build()
);
ChatResponse response = chatModel.call(prompt);
String text = response.getResult().getOutput().getText();

用 ChatClient(简洁版):

java 复制代码
String text = chatClient.prompt()
    .user("讲个笑话")
    .call()
    .content();

ChatClient 用流式 API 把常见的操作都串联起来了,读起来像自然语言。

3.2 创建 ChatClient

有三种方式:

方式一:自动注入(最常用)

java 复制代码
@RestController
class MyController {

    private final ChatClient chatClient;

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

    @GetMapping("/ai")
    String chat(@RequestParam String input) {
        return chatClient.prompt()
                .user(input)
                .call()
                .content();
    }
}

Spring Boot 自动配置了一个 ChatClient.Builder 的 prototype Bean。注入它,.build() 一下就完事了。

方式二:手动创建

java 复制代码
ChatClient chatClient = ChatClient.create(chatModel);

方式三:带默认配置的 Builder

java 复制代码
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
    return builder
            .defaultSystem("你是一个有帮助的助手,回答简洁明了。")
            .defaultOptions(OpenAiChatOptions.builder().temperature(0.3).build())
            .build();
}

3.3 三种 prompt() 方式

java 复制代码
// 方式 1:无参,后面慢慢拼
chatClient.prompt()
    .user("你好")
    .system("你是一个助手")
    .call()
    .content();

// 方式 2:直接传字符串(最简)
chatClient.prompt("你好").call().content();

// 方式 3:传一个完整的 Prompt 对象
Prompt prompt = new Prompt(new UserMessage("你好"));
chatClient.prompt(prompt).call().content();

3.4 call() 的返回值

call() 之后,你有四种选择:

java 复制代码
// 1. 只要文本
String content = chatClient.prompt().user("讲个笑话").call().content();

// 2. 要完整的 ChatResponse(含元数据)
ChatResponse chatResponse = chatClient.prompt()
        .user("讲个笑话")
        .call()
        .chatResponse();
// 可以拿到 token 用量、模型名称等
System.out.println("用了 " + chatResponse.getMetadata().getUsage().getTotalTokens() + " 个 token");

// 3. 要 ChatClientResponse(含执行上下文,RAG 场景有用)
ChatClientResponse clientResponse = chatClient.prompt()
        .user("讲个笑话")
        .call()
        .chatClientResponse();

// 4. 映射成 Java 对象
ActorFilms result = chatClient.prompt()
        .user("生成一个随机演员的电影作品列表")
        .call()
        .entity(ActorFilms.class);

3.5 流式响应(Streaming)

不想等 AI 一句话说完才显示?用 stream()

java 复制代码
Flux<String> flux = chatClient.prompt()
        .user("用中文讲一个关于程序员的长笑话")
        .stream()
        .content();

flux.subscribe(
    chunk -> System.out.print(chunk),  // 一个字一个字出来
    error -> System.err.println("出错了: " + error),
    () -> System.out.println("\n--- 讲完了 ---")
);

效果: 你会看到文字像打字机一样一个字一个字(准确说是 token by token)地输出。

🎯 什么时候用 call(),什么时候用 stream()

  • 简短问答、结构化输出、Tool Calling → call(),简单直接
  • 长文本生成、聊天界面、需要打字机效果 → stream(),用户体验更好
  • 流式响应需要额外引入 spring-boot-starter-webflux 依赖
    💡 关于 Flux: Flux 是 Spring WebFlux 提供的响应式流类型,用来表示"随时间推移陆续到达的多个数据"。如果你在 Spring MVC 环境做演示,需要调用 .subscribe() 来触发流开始执行;如果你在 WebFlux 环境直接把 Flux 返回给浏览器,框架会自动处理,不需要手动 subscribe。流式响应需要额外引入 spring-boot-starter-webflux 依赖。

3.6 同时使用多个 Chat Model

现实场景中,你可能需要:

  • 一个便宜模型处理简单任务,一个贵模型处理复杂推理
  • 针对不同用户提供不同模型选择
  • A/B 测试两个模型
java 复制代码
@Configuration
public class ChatClientConfig {

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

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

// 使用时注入
@Service
public class MultiModelService {

    @Autowired
    @Qualifier("openAiChatClient")
    private ChatClient openAiClient;

    @Autowired
    @Qualifier("anthropicChatClient")
    private ChatClient anthropicClient;

    public String routeToModel(String task, String input) {
        if (task.equals("code")) {
            return anthropicClient.prompt(input).call().content(); // Claude 擅长代码
        } else {
            return openAiClient.prompt(input).call().content();     // GPT 做通用
        }
    }
}

3.7 使用不同的 OpenAI 兼容端点

很多服务(Groq、DeepSeek、通义千问)都兼容 OpenAI API 格式。Spring AI 允许你"派生"一个 API 实例:

java 复制代码
@Service
public class MultiEndpointService {

    @Autowired
    private OpenAiApi baseApi;

    @Autowired
    private OpenAiChatModel baseModel;

    public String callGroq(String prompt) {
        // 派生一个指向 Groq 的 API
        OpenAiApi groqApi = baseApi.mutate()
                .baseUrl("https://api.groq.com/openai")
                .apiKey(System.getenv("GROQ_API_KEY"))
                .build();

        OpenAiChatModel groqModel = baseModel.mutate()
                .openAiApi(groqApi)
                .defaultOptions(OpenAiChatOptions.builder()
                        .model("llama3-70b-8192")
                        .temperature(0.5)
                        .build())
                .build();

        return ChatClient.create(groqModel).prompt(prompt).call().content();
    }
}

3.8 完整 Demo:一个带 Web 界面的聊天机器人

java 复制代码
@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是一个友好的助手,回答简洁,不超过 100 字。")
                .build();
    }

    // 同步接口
    @PostMapping
    public Map<String, String> chat(@RequestBody ChatRequest request) {
        String reply = chatClient.prompt()
                .user(request.message())
                .call()
                .content();
        return Map.of("reply", reply);
    }

    // 流式接口(SSE)
    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chatStream(@RequestBody ChatRequest request) {
        return chatClient.prompt()
                .user(request.message())
                .stream()
                .content();
    }
}

record ChatRequest(String message) {}

相关推荐
蓝速科技1 小时前
蓝速科技丨立式全面屏 AI 数字人交互一体机落地实战指南
人工智能·科技·交互
暮雪倾风1 小时前
【AI】CC switch安装与使用教程:告别繁琐配置,解锁 AI 编程 CLI 一键管理
人工智能·chatgpt·claudecode·ccswitch
linge_sun1 小时前
Sping AI 使用 Ollama 快速搭建本地知识库
java·人工智能·ai编程
海鸥-w1 小时前
用python (fastapi)做项目第二天实现新闻列表和新闻详情接口
开发语言·python·fastapi
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第四章 Item 25 - 26)
开发语言·人工智能·经验分享·笔记·python·学习方法
KaMeidebaby1 小时前
卡梅德生物技术快报|抗原如何自己检测?FAdV-4 重组抗原制备与 ELISA 体系技术调试指南
前端·人工智能·物联网·算法·百度
呆呆敲代码的小Y1 小时前
Understand Anything入门指南: 代码库、知识库 转化为交互式知识图谱
人工智能·ai·知识图谱·知识库·代码库·understand
fthux1 小时前
「装闭」-AI驱动的开源装修闭坑系统
人工智能·docker·开源
Esaka_Forever1 小时前
AI Agent的ReAct Loop(Reasoning and Acting 循环)
人工智能