鞍注塔 发表于 2025-11-20 21:55:03

Spring AI 代码分析(四)--Client Chat

Spring AI Client Chat 分析

请关注微信公众号:阿呆-bot
1. 工程结构概览

spring-ai-client-chat 是 Spring AI 的高级 API 模块,提供了类似 WebClient 的流式 API,让 AI 对话编程变得更加简单和直观。
spring-ai-client-chat/
├── ChatClient.java            # 核心接口,流式 API
├── DefaultChatClient.java       # 默认实现
├── ChatClientRequest.java       # 请求对象
├── ChatClientResponse.java      # 响应对象

└── advisor/                     # Advisor 机制
    ├── api/                     # Advisor 接口定义
    │   ├── Advisor.java
    │   ├── CallAdvisor.java
    │   ├── StreamAdvisor.java
    │   └── BaseAdvisor.java
    ├── ChatModelCallAdvisor.java    # 调用模型的 Advisor
    ├── ChatModelStreamAdvisor.java# 流式调用的 Advisor
    ├── ToolCallAdvisor.java         # 工具调用 Advisor
    └── SimpleLoggerAdvisor.java    # 日志 Advisor2. 技术体系与模块关系

ChatClient 建立在 ChatModel 之上,通过 Advisor 机制提供了强大的扩展能力:

3. 关键场景示例代码

3.1 基础使用

ChatClient 提供了非常友好的流式 API:
@Autowired
private ChatModel chatModel;

public void basicUsage() {
    ChatClient chatClient = ChatClient.create(chatModel);
   
    String response = chatClient.prompt()
      .user("什么是 Spring AI?")
      .call()
      .content();
   
    System.out.println(response);
}3.2 流式调用

流式调用非常简单:
public void streamUsage() {
    ChatClient chatClient = ChatClient.create(chatModel);
   
    chatClient.prompt()
      .user("讲一个故事")
      .stream()
      .content()
      .doOnNext(chunk -> System.out.print(chunk))
      .blockLast();
}3.3 使用 Advisor

Advisor 是 ChatClient 的核心特性,可以增强请求和响应:
public void advisorUsage() {
    // 创建 RAG Advisor
    RetrievalAugmentationAdvisor ragAdvisor =
      RetrievalAugmentationAdvisor.builder()
            .documentRetriever(vectorStoreRetriever)
            .build();
   
    ChatClient chatClient = ChatClient.builder(chatModel)
      .defaultAdvisors(ragAdvisor)
      .build();
   
    String response = chatClient.prompt()
      .user("查询文档中的信息")
      .call()
      .content();
}3.4 结构化输出

ChatClient 支持直接将响应转换为 POJO:
public void structuredOutput() {
    BeanOutputConverter<WeatherInfo> converter =
      new BeanOutputConverter<>(WeatherInfo.class);
   
    WeatherInfo weather = chatClient.prompt()
      .user("查询北京天气,返回 JSON:" + converter.getFormat())
      .call()
      .entity(converter);
}4. 核心时序图

4.1 同步调用流程


4.2 流式调用流程


5. 入口类与关键类关系


6. 关键实现逻辑分析

6.1 流式 API 设计

ChatClient 的流式 API 设计非常巧妙,使用了 Builder 模式和函数式接口:
public interface ChatClient {
    ChatClientRequestSpec prompt();
   
    interface ChatClientRequestSpec {
      PromptUserSpec user(String text);
      PromptSystemSpec system(String text);
      AdvisorSpec advisors(Advisor... advisors);
      CallResponseSpec call();
      StreamResponseSpec stream();
    }
   
    interface CallResponseSpec {
      String content();
      <T> T entity(Class<T> type);
      ChatResponse chatResponse();
    }
}这种设计让 API 既类型安全又易于使用。
6.2 Advisor 机制

Advisor 是 ChatClient 的核心扩展机制,类似于 Spring AOP 的切面:
public interface CallAdvisor extends Advisor {
    ChatClientResponse adviseCall(
      ChatClientRequest request,
      CallAdvisorChain chain
    );
}Advisor 链的执行流程:

[*]Before 阶段:每个 Advisor 可以在调用前修改请求
[*]Call 阶段:调用下一个 Advisor 或最终调用模型
[*]After 阶段:每个 Advisor 可以在调用后修改响应
public class DefaultAroundAdvisorChain implements CallAdvisorChain {
    public ChatClientResponse nextCall(ChatClientRequest request) {
      if (currentIndex >= advisors.size()) {
            // 链的末尾,调用模型
            return chatModelCallAdvisor.adviseCall(request, this);
      }
      
      CallAdvisor currentAdvisor = advisors.get(currentIndex);
      return currentAdvisor.adviseCall(request,
            new DefaultAroundAdvisorChain(currentIndex + 1, ...));
    }
}6.3 工具调用处理

ToolCallAdvisor 负责处理工具调用,它会在检测到工具调用时自动执行:
public class ToolCallAdvisor implements CallAdvisor {
    @Override
    public ChatClientResponse adviseCall(...) {
      boolean isToolCall = false;
      do {
            // 调用模型
            chatClientResponse = callAdvisorChain.nextCall(request);
            
            // 检查是否有工具调用
            isToolCall = chatClientResponse.chatResponse().hasToolCalls();
            
            if (isToolCall) {
                // 执行工具调用
                ToolExecutionResult result =
                  toolCallingManager.executeToolCalls(...);
               
                // 将工具结果作为新的请求再次调用
                request = new Prompt(result.getConversationHistory());
            }
      } while (isToolCall);
      
      return chatClientResponse;
    }
}这种设计实现了自动工具调用循环,直到模型不再请求工具调用。
6.4 流式处理实现

流式处理通过 ChatModelStreamAdvisor 实现:
public class ChatModelStreamAdvisor implements StreamAdvisor {
    @Override
    public Flux<ChatClientResponse> adviseStream(...) {
      Flux<ChatResponse> responseFlux =
            chatModel.stream(request.prompt());
      
      return responseFlux.map(chunk ->
            ChatClientResponse.builder()
                .chatResponse(chunk)
                .context(request.context())
                .build()
      );
    }
}流式 Advisor 链的处理更复杂,因为需要在流式响应中插入处理逻辑。
7. 外部依赖

spring-ai-client-chat 的依赖:

[*]spring-ai-model:核心模型抽象
[*]Reactor Core:响应式流式处理
[*]Spring Framework:IoC 和核心功能
[*]Jackson:JSON 处理(用于结构化输出)
[*]MCP SDK:Model Context Protocol 支持
8. 工程总结

spring-ai-client-chat 模块的设计体现了几个重要的设计原则:
流式 API 设计。ChatClient 的 API 风格和 Spring WebClient 很像,用起来很顺手。开发者不需要学习新的 API,就能快速上手。
Advisor 机制。这是 ChatClient 的核心能力。通过 Advisor,我们可以给请求和响应"加料",比如做 RAG、记录对话历史、打日志、安全检查等,而且不需要改核心代码。
责任链模式。多个 Advisor 按顺序执行,每个 Advisor 都能修改请求和响应,就像过滤器链一样。
工具调用自动化。ToolCallAdvisor 会自动处理工具调用循环,模型说要调用工具,它就自动调用,然后把结果再传给模型,直到模型不再需要工具。整个过程对用户是透明的。
类型安全。通过泛型和函数式接口,API 既灵活又安全,编译期就能发现很多错误。
总的来说,spring-ai-client-chat 是一个设计得很好的高级 API。它让 AI 对话编程变得简单,同时通过 Advisor 机制提供了强大的扩展能力。简单场景用起来很顺手,复杂场景也能通过 Advisor 灵活扩展。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

劳暄美 发表于 2025-12-4 21:26:50

谢谢分享,辛苦了

劳暄美 发表于 3 天前

喜欢鼓捣这些软件,现在用得少,谢谢分享!

副我 发表于 10 分钟前

收藏一下   不知道什么时候能用到
页: [1]
查看完整版本: Spring AI 代码分析(四)--Client Chat