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    # 日志 Advisor

2. 技术体系与模块关系

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

image.png

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 同步调用流程

image.png

4.2 流式调用流程

image.png

5. 入口类与关键类关系

aaa.png

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 链的执行流程

  1. Before 阶段:每个 Advisor 可以在调用前修改请求
  2. Call 阶段:调用下一个 Advisor 或最终调用模型
  3. 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 灵活扩展。

posted @ 2025-11-20 21:32  wasp  阅读(7)  评论(0)    收藏  举报