1. 概述

本文将深入探讨 Spring AI 1.0.0 M1 版本中 ChatClient 的流式 API 设计。这个接口作为 Spring AI 模块的核心组件,提供了与 AI 模型交互的标准化方式,支持发送提示词并接收结构化响应。其设计遵循构建器模式,API 风格与 WebClientRestClientJdbcClient 保持一致,让 Spring 开发者能快速上手。

2. 通过 ChatClient 执行提示词

在 Spring Boot 中,我们可以直接使用自动配置的 bean,或通过编程方式创建实例。首先添加依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

然后在组件中注入 ChatClient.Builder

@RestController
@RequestMapping("api/articles")
class BlogsController {

    private final ChatClient chatClient;
  
    public BlogsController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    // ...
}

创建一个简单接口接收用户问题并转发给 AI:

@GetMapping("v1")
String askQuestion(@RequestParam(name = "question") String question) {
    return chatClient.prompt()
      .user(question)
      .call()
      .chatResponse()
      .getResult()
      .getOutput()
      .getContent();
}

核心优势:流式 API 让我们能用链式调用完成从用户输入到响应提取的全流程。如果只需要响应文本内容,可以用 content() 方法简化最后四步:

@GetMapping("v1")
String askQuestion(@RequestParam(name = "question") String question) {
    return chatClient.prompt()
      .user(question)
      .call()
      .content();
}

发送 GET 请求后,会收到类似 ChatGPT 浏览器界面的非结构化响应:

request article recommedation

3. 将响应映射为特定格式

实际开发中,我们通常需要结构化输出(如 JSON)。ChatClient 提供了 entity() 方法实现这个需求。下面改造代码返回 Article 对象列表:

record Article(String title, Set<String> tags) {
}

@GetMapping("v2")
List<Article> askQuestionAndRetrieveArticles(@RequestParam(name = "question") String question) {
    return chatClient.prompt()
      .user(question)
      .call()
      .entity(new ParameterizedTypeReference<List<Article>>() {});
}

现在接口会返回符合规范的 JSON 列表:

request aritcles as json

4. 提供额外上下文

直接调用 AI 模型可能返回虚构数据。解决方案是使用 RAG(检索增强生成)模式,结合向量存储提供真实数据。实现步骤:

4.1 初始化向量存储

@RestController
@RequestMapping("api/articles")
public class BlogsController {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public BlogsController(ChatClient.Builder chatClientBuilder, EmbeddingModel embeddingModel) throws IOException {
        this.chatClient = chatClientBuilder.build();
        this.vectorStore = new SimpleVectorStore(embeddingModel);
        initContext();
    }

    void initContext() throws IOException {
        List<Document> documents = Files.readAllLines(Path.of("src/main/resources/articles.txt"))
          .stream()
          .map(Document::new)
          .toList();

        vectorStore.add(documents);
    }
  
    // ...
}

⚠️ 注意:这里从本地文件加载文档,实际项目中可替换为数据库/API 等数据源。

4.2 集成 RAG 顾问

@GetMapping("v3")
List<Article> askQuestionWithContext(@RequestParam(name = "question") String question) {
    return chatClient.prompt()
      .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
      .user(question)
      .call()
      .entity(new ParameterizedTypeReference<List<Article>>() {});
}

现在响应完全基于提供的上下文数据:

request articles using rag

5. 总结

本文系统介绍了 Spring AI 的 ChatClient 使用方法:

  1. ✅ 基础用法:发送简单提示词并获取文本响应
  2. ✅ 结构化输出:通过 entity() 方法映射响应到特定格式
  3. ✅ 上下文增强:使用 RAG 模式结合向量存储提供真实数据

核心优势在于其流式 API 设计,既保持了 Spring 生态的统一性,又简化了 AI 集成开发。实际应用中,建议优先考虑 RAG 模式避免"一本正经地胡说八道"的问题。


原始标题:ChatClient Fluent API in Spring AI | Baeldung