prompt().user().call().content() 背后的完整链路

0 阅读11分钟

Spring AI 源码解读 - 第 2 篇:ChatClient 调用链路

prompt().user().call()content() 的完整执行路径

📖 开篇引言

上一篇文章我们分析了 ChatModel 接口,它定义了发送 Prompt 并返回 ChatResponse 的核心契约。

但日常开发中,我们更多使用的是 ChatClient

// 这种方式
String answer = chatClient.prompt()
    .user("你好")
    .call()
    .content();

// 而不是这种方式
Prompt prompt = new Prompt(new UserMessage("你好"));
ChatResponse response = chatModel.call(prompt);
String answer = response.getResult().getOutput().getContent();

ChatClient 相比 ChatModel 封装了更多能力:

  • 链式 Builder API:更友好的编程体验
  • Advisor 拦截器链:自动注入记忆、日志增强等
  • Prompt 模板处理:支持 System Prompt 的灵活配置
  • 流式/非流式统一call()stream() 两种调用方式

本篇将深入 ChatClient 的内部实现,理解这条优雅链式调用的背后机制。


一、ChatClient 体系结构

1.1 接口定义

// org.springframework.ai.chat.client.ChatClient
public interface ChatClient {
    
    // 默认 PromptBuilder
    PromptBuilder prompt();
    
    // 通用的 prompt() 重载
    PromptBuilder prompt(Prompt prompt);
    
    // ========== 非流式调用 ==========
    
    // 返回完整响应
    ResponseEntity<ChatResponse> call(Prompt prompt);
    
    // 返回单个文本(便捷方法)
    ResponseEntity<String> call(String message);
    
    // 返回自定义类型(通过 ChatResponseMapper 转换)
    <T> ResponseEntity<T> call(Prompt prompt, Class<T> responseClass);
    
    // ========== 流式调用 ==========
    
    // 返回流式响应
    Flux<ChatResponse> stream(Prompt prompt);
    
    // 返回流式文本
    Flux<String> stream(String message);
}

1.2 PromptBuilder 子接口

// Prompt 构建器接口
public interface PromptBuilder 
    extends Builder<PromptBuilder>, 
            RequestBuilder<PromptBuilder, Prompt> {
    
    // 继承自 RequestBuilder:消息构建
    PromptBuilder user(String userMessage);
    PromptBuilder user(Object userMessage);  // 支持复杂对象
    PromptBuilder system(String systemMessage);
    PromptBuilder system(SystemPromptTemplate systemPromptTemplate);
    
    // 继承自 Builder:选项配置
    PromptBuilder options(ChatOptions options);
    PromptBuilder defaultOptions(ChatOptions options);
    
    // 构建并触发调用
    ResponseEntity<ChatResponse> call();
    <T> ResponseEntity<T> call(Class<T> responseClass);
    
    Flux<ChatResponse> stream();
    Flux<String> stream();
}

继承关系

PromptBuilder
    │
    ├─── user(String)              添加用户消息
    ├─── system(String)           添加系统消息
    ├─── options(ChatOptions)      设置本次请求参数
    ├─── defaultOptions(ChatOptions) 设置默认参数
    ├─── call()                   同步调用
    └─── stream()                 流式调用

1.3 ResponseEntity 与 ChatResponse

ChatClient 返回 ResponseEntity<ChatResponse> 而不是直接返回 ChatResponse

// ResponseEntity 包装了 ChatResponse,额外提供 HTTP 状态码等信息
public class ResponseEntity<T> {
    private final T body;
    private final HttpStatusCode statusCode;
    
    public static <T> ResponseEntity<T> of(T body) {
        return new ResponseEntity<>(body, HttpStatusCode.OK);
    }
    
    public T body() { return body; }
    public HttpStatusCode statusCode() { return statusCode; }
}

// 便捷方法:直接提取 body
public class ResponseEntity<T> {
    public T getBody() {
        return this.body;
    }
}

为什么用 ResponseEntity?

与 Spring MVC 风格保持一致,方便在异常情况下返回不同的 HTTP 状态码(如 429 限流、500 服务错误等)。


二、ChatClient 默认实现:DefaultChatClient

2.1 类结构

// org.springframework.ai.chat.client.DefaultChatClient
public class DefaultChatClient implements ChatClient {
    
    private final ChatModel chatModel;                        // 核心模型
    private final ChatClientOptions defaultOptions;           // 默认选项
    private final List<ChatModelOptionsTransformer> optionTransformers; // 选项转换器
    private final ChatModelChatMemoryManager chatMemoryManager; // 记忆管理器
    private final Map<String, ChatModel> toolTimeReturnChatModelCache; // 工具模型缓存
    private final Set<CallAroundAdvisor> aroundAdvisors;      // 拦截器链
    private final RequestResponseAdvisorManager advisorManager; // 拦截器管理器
}

DefaultChatClient 的核心职责

  1. 管理 ChatModel 实例
  2. 维护 Advisor 拦截器链
  3. 管理 ChatMemory 记忆
  4. 提供 PromptBuilder 实现

2.2 Builder 模式:ChatClient.builder()

// DefaultChatClient 的 Builder
public class DefaultChatClientBuilder implements ChatClientBuilder {
    
    private ChatModel chatModel;                     // 必填
    private ChatClientOptions defaultOptions;        // 可选
    private List<CallAroundAdvisor> aroundAdvisors;  // 拦截器
    private ChatMemory chatMemory;                   // 记忆
    private MessageWindowChatMemory defaultChatMemory; // 默认记忆
    
    // 基础构建
    public ChatClientBuilder chatModel(ChatModel chatModel) {
        this.chatModel = chatModel;
        return this;
    }
    
    // 添加 Advisor(拦截器)
    public ChatClientBuilder defaultAdvisors(CallAroundAdvisor... advisors) {
        this.aroundAdvisors = Arrays.asList(advisors);
        return this;
    }
    
    // 添加记忆
    public ChatClientBuilder chatMemory(ChatMemory chatMemory) {
        this.chatMemory = chatMemory;
        return this;
    }
    
    // 构建最终对象
    public ChatClient build() {
        // 1. 如果没有显式设置 ChatMemory,使用默认的 MessageWindowChatMemory
        if (this.chatMemory == null) {
            this.chatMemory = MessageWindowChatMemory.create();
        }
        
        // 2. 构建 ChatModelChatMemoryManager(管理记忆注入)
        ChatModelChatMemoryManager chatMemoryManager = 
            new ChatModelChatMemoryManager(this.chatMemory);
        
        // 3. 构建 Advisor 管理器
        RequestResponseAdvisorManager advisorManager = 
            new RequestResponseAdvisorManager(this.aroundAdvisors);
        
        // 4. 创建 DefaultChatClient 实例
        return new DefaultChatClient(
            this.chatModel,
            this.defaultOptions,
            this.optionTransformers,
            chatMemoryManager,
            this.toolTimeReturnChatModelCache,
            advisorManager
        );
    }
}

典型的构建方式

ChatClient chatClient = ChatClient.builder(chatModel)
    // 设置默认系统提示词
    .defaultSystem("你是一个专业的 Java 工程师")
    // 添加记忆管理(自动注入历史消息)
    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
    // 添加工具拦截器
    .defaultAdvisors(myToolAdvisor)
    .build();

三、PromptBuilder 实现:prompt() 方法

3.1 prompt() 方法源码

// DefaultChatClient.prompt()
@Override
public PromptBuilder prompt() {
    return new PromptUserMessageBuilder(this);  // 每次创建新的 Builder
}

// 内部 Builder 实现
class PromptUserMessageBuilder implements PromptBuilder {
    
    private final DefaultChatClient client;  // 持有 client 引用
    private final List<Message> messages;   // 消息列表
    private ChatOptions options;             // 请求选项
    private SystemPromptTemplate systemPromptTemplate; // 系统提示模板
    
    PromptUserMessageBuilder(DefaultChatClient client) {
        this.client = client;
        this.messages = new ArrayList<>();
    }
    
    @Override
    public PromptBuilder user(String userMessage) {
        this.messages.add(new UserMessage(userMessage));
        return this;
    }
    
    @Override
    public PromptBuilder system(String systemMessage) {
        this.systemPromptTemplate = new SystemPromptTemplate(systemMessage);
        return this;
    }
    
    @Override
    public PromptBuilder system(SystemPromptTemplate systemPromptTemplate) {
        this.systemPromptTemplate = systemPromptTemplate;
        return this;
    }
    
    @Override
    public PromptBuilder options(ChatOptions options) {
        this.options = options;
        return this;
    }
    
    // ... 其他方法
}

3.2 链式调用示例

chatClient.prompt()           // → PromptUserMessageBuilder
    .system("你是一个助手")    // 设置系统提示词
    .user("你好")              // 添加用户消息
    .user("今天天气如何?")     // 再次添加用户消息
    .options(options)          // 设置请求选项
    .call()                    // 触发调用
    .content();                // 提取文本

Builder 内部状态变化

初始状态
  ↓ prompt()
messages: []
systemPrompt: null
options: null

  ↓ system("你是一个助手")
messages: []
systemPrompt: SystemPromptTemplate("你是一个助手")
options: null

  ↓ user("你好")
messages: [UserMessage("你好")]
systemPrompt: SystemPromptTemplate("你是一个助手")
options: null

  ↓ user("今天天气如何?")
messages: [UserMessage("你好"), UserMessage("今天天气如何?")]
systemPrompt: SystemPromptTemplate("你是一个助手")
options: null

  ↓ options(options)
messages: [UserMessage("你好"), UserMessage("今天天气如何?")]
systemPrompt: SystemPromptTemplate("你是一个助手")
options: ChatOptions{...}

四、call() 方法执行链路

4.1 call() 的完整流程

当调用 call() 时,背后经历了以下步骤:

// 用户代码
chatClient.prompt().user("你好").call().content()

// 步骤分解
PromptUserMessageBuilder builder = chatClient.prompt();       // 1. 获取 Builder
builder.user("你好");                                         // 2. 添加消息
ResponseEntity<ChatResponse> response = builder.call();       // 3. 执行调用
String content = response.getBody().getResult()...;          // 4. 提取结果

4.2 PromptBuilder.call() 源码

// PromptUserMessageBuilder.call()
public ResponseEntity<ChatResponse> call() {
    
    // 1. 构建完整的 Prompt(包含系统消息和用户消息)
    Prompt prompt = buildPrompt();
    
    // 2. 触发实际调用
    return this.doCall(prompt);
}

// 内部方法
private ResponseEntity<ChatResponse> doCall(Prompt prompt) {
    
    // 3. 获取 DefaultChatClient 实例
    DefaultChatClient client = getClient();
    
    // 4. 调用 client 的 doChatMethod
    // 这里会触发 Advisor 链的执行
    return client.doChatMethod(
        Prompt.toUserMessages(prompt),  // 只传用户消息
        Prompt.toSystemMessage(prompt),  // 单独传系统消息
        prompt.getOptions()              // 请求选项
    );
}

4.3 DefaultChatClient.doChatMethod() 源码

这是真正触发 Advisor 链的方法:

// DefaultChatClient.doChatMethod()
ResponseEntity<ChatResponse> doChatMethod(
    List<UserMessage> userMessages,       // 用户消息列表
    String systemPromptText,              // 系统提示词文本
    ChatOptions requestOptions            // 请求级选项
) {
    
    // 1. 构建 AdvisedRequest(携带完整上下文)
    AdvisedRequest advisedRequest = AdvisedRequest.builder()
        .userMessage(userMessages)              // 用户消息
        .systemText(systemPromptText)          // 系统提示词
        .options(mergeOptions(requestOptions)) // 合并后的选项
        .chatMemoryContext(this.chatMemoryManager.getChatMemoryContext(
            Thread.currentThread().threadId()  // 获取当前线程的会话 ID
        ))
        .build();
    
    // 2. 通过 AdvisorManager 执行拦截器链
    // before(): 前置处理(注入记忆等)
    // doChat(): 实际调用 ChatModel
    // after(): 后置处理(日志等)
    AdvisedResponse<ChatResponse> advisedResponse = 
        this.advisorManager.execute(advisedRequest, 
            advisedRequest1 -> {
                // 实际的 ChatModel 调用
                return doChat(advisedRequest1);
            });
    
    // 3. 返回响应
    return ResponseEntity.of(advisedResponse.getChatResponse());
}

4.4 doChat() 方法:实际调用 ChatModel

// 执行实际的 ChatModel 调用
ChatResponse doChat(AdvisedRequest request) {
    
    // 1. 注入 ChatMemory 记忆到用户消息
    // 这一步会把历史消息插入到当前消息之前
    UserMessage userMessageWithMemory = 
        this.chatMemoryManager.appendHistoryContext(request);
    
    // 2. 构建完整的 Prompt(系统消息 + 记忆消息 + 当前消息)
    Prompt prompt = new Prompt(
        userMessageWithMemory,
        request.getOptions()
    );
    
    // 3. 调用底层的 ChatModel
    return this.chatModel.call(prompt);
}

4.5 完整调用链路图

┌─────────────────────────────────────────────────────────────────┐
│ chatClient.prompt().user("你好").call().content()                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│ 1. PromptUserMessageBuilder 实例化                               │
│    messages = []                                                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓ .user("你好")
┌─────────────────────────────────────────────────────────────────┐
│ 2. 添加用户消息                                                  │
│    messages = [UserMessage("你好")]                             │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓ .call()
┌─────────────────────────────────────────────────────────────────┐
│ 3. buildPrompt() - 构建完整 Prompt                               │
│    - systemPromptTemplate → SystemMessage                        │
│    - userMessages → UserMessage                                 │
│    Prompt{messages: [SystemMessage, UserMessage]}               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓ doChatMethod()
┌─────────────────────────────────────────────────────────────────┐
│ 4. 构建 AdvisedRequest(携带上下文)                             │
│    - userMessage                                                 │
│    - systemText                                                  │
│    - options (合并后的)                                          │
│    - chatMemoryContext                                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓ advisorManager.execute()
┌─────────────────────────────────────────────────────────────────┐
│ 5. Advisor 链执行                                                │
│    before() → doChat() → after()                                │
│    ├── LoggerAdvisor.before()    记录请求日志                    │
│    ├── MemoryAdvisor.before()     注入历史消息 ← 关键!          │
│    ├── doChat()                  调用 ChatModel                  │
│    │     ├── 注入记忆消息                                      │
│    │     └── chatModel.call(prompt)                             │
│    ├── MemoryAdvisor.after()      更新记忆                       │
│    └── LoggerAdvisor.after()     记录响应日志                    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│ 6. 返回 ChatResponse                                             │
│    ResponseEntity.of(chatResponse)                               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ↓ .content()
┌─────────────────────────────────────────────────────────────────┐
│ 7. 提取文本内容                                                  │
│    chatResponse.getResult().getOutput().getContent()             │
└─────────────────────────────────────────────────────────────────┘

五、ChatMemory 记忆注入机制

5.1 为什么要注入记忆?

多轮对话时,AI 需要知道之前的上下文:

# 第 1 轮
用户:我想学 Java
AI:Java 是一门面向对象的编程语言...

# 第 2 轮(AI 需要知道用户在讨论 Java)
用户:它和 Python 有什么区别?
AI:Java 和 Python 的主要区别是...

MessageChatMemoryAdvisor 负责在两次调用之间保存和注入记忆。

5.2 MessageChatMemoryAdvisor 源码

// org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor
public class MessageChatMemoryAdvisor 
    implements CallAroundAdvisor, 
               StreamAroundAdvisor {
    
    private final ChatMemory chatMemory;  // 记忆存储
    
    // 1. before() - 在调用前注入记忆
    @Override
    public AdvisedRequest before(AdvisedRequest advisedRequest) {
        
        // 获取会话 ID(如果没有则生成新 ID)
        String sessionId = getOrCreateSessionId(advisedRequest);
        
        // 从 ChatMemory 中获取该会话的历史消息
        List<Message> historyMessages = this.chatMemory.get(
            new ConversationId(sessionId)
        );
        
        // 将历史消息注入到请求中
        // 会插入到 SystemMessage 之后、当前 UserMessage 之前
        advisedRequest = AdvisedRequest.from(advisedRequest)
            .userMessage(
                insertHistoryMessages(
                    advisedRequest.getUserMessage(),  // 当前消息
                    historyMessages                   // 历史消息
                )
            )
            .build();
        
        return advisedRequest;
    }
    
    // 2. after() - 在调用后保存消息到记忆
    @Override
    public ChatResponse after(AdvisedRequest advisedRequest, 
                               AdvisedResponse<ChatResponse> advisedResponse) {
        
        String sessionId = getOrCreateSessionId(advisedRequest);
        
        // 保存用户消息和 AI 回复到记忆
        this.chatMemory.add(
            new ConversationId(sessionId),
            advisedRequest.getUserMessage()  // 用户消息
        );
        this.chatMemory.add(
            new ConversationId(sessionId),
            advisedResponse.getChatResponse()  // AI 回复
        );
        
        return advisedResponse;
    }
}

5.3 记忆注入时机

调用前 (before)
┌──────────────────────────────────────────────────────────┐
│ ChatMemory.get(sessionId)                                 │
│     ↓ 获取历史消息                                        │
│ [UserMessage("我想学 Java"), AssistantMessage("Java 是...")] │
│     ↓ 插入到当前请求                                      │
│ Prompt.messages = [                                      ││     SystemMessage("你是一个助手"),                        ││     UserMessage("我想学 Java"),     ← 历史消息            ││     AssistantMessage("Java 是..."), ← 历史消息           ││     UserMessage("它和 Python 的区别?") ← 当前消息         ││ ]                                                         │
└──────────────────────────────────────────────────────────┘

调用后 (after)
┌──────────────────────────────────────────────────────────┐
│ ChatMemory.add(sessionId, [UserMessage, AssistantMessage])│
│     ↓ 保存到记忆                                          │
│ 下次调用时,ChatMemory.get() 会返回这些消息                │
└──────────────────────────────────────────────────────────┘

六、stream() 流式调用链路

6.1 stream() 方法签名

// ChatClient.stream()
Flux<String> stream(String userMessage);

// PromptBuilder.stream()
Flux<ChatResponse> stream();
Flux<String> stream();

6.2 流式调用的实现

// DefaultChatClient.PromptUserMessageBuilder.stream()
public Flux<ChatResponse> stream() {
    
    // 1. 构建 Prompt
    Prompt prompt = buildPrompt();
    
    // 2. 获取 Client
    DefaultChatClient client = getClient();
    
    // 3. 调用流式方法
    return client.streamChat(prompt);
}

// DefaultChatClient.streamChat()
Flux<ChatResponse> streamChat(Prompt prompt) {
    
    // 构建 AdvisedRequest
    AdvisedRequest advisedRequest = buildAdvisedRequest(prompt);
    
    // 通过 AdvisorManager 执行流式拦截器链
    return this.advisorManager.streamExecute(
        advisedRequest,
        advisedRequest1 -> {
            // 流式调用 ChatModel
            return doStreamChat(advisedRequest1);
        }
    );
}

// 流式调用 ChatModel
Flux<ChatResponse> doStreamChat(AdvisedRequest request) {
    
    // 1. 注入记忆
    UserMessage userMessageWithMemory = 
        this.chatMemoryManager.appendHistoryContext(request);
    
    // 2. 构建 Prompt
    Prompt prompt = new Prompt(userMessageWithMemory, request.getOptions());
    
    // 3. 调用流式 ChatModel(返回 Flux)
    return this.chatModel.stream(prompt);
}

6.3 call() vs stream() 对比

维度call()stream()
返回类型ResponseEntity<ChatResponse>Flux<ChatResponse>
响应时机完整响应后返回流式返回(边生成边返回)
使用场景简单对话、工具调用长文本生成、实时展示
性能需要等待完整生成首字符更快
内存一次性加载完整响应流式处理,内存占用小

6.4 流式响应的使用示例

// 非流式:等待完整响应
String answer = chatClient.prompt()
    .user("写一篇 5000 字的文章")
    .call()
    .content();

// 流式:边生成边打印
chatClient.prompt()
    .user("写一篇 5000 字的文章")
    .stream()
    .subscribe(
        chunk -> System.out.print(chunk),  // 每个 token 到达时打印
        error -> error.printStackTrace(),
        () -> System.out.println("\n[完成]")
    );

七、ChatModelChatMemoryManager 记忆上下文管理

7.1 为什么需要上下文管理器?

每个用户会话需要独立的记忆空间:

// 用户 A 的会话
session-A: [A的历史消息...]

// 用户 B 的会话
session-B: [B的历史消息...]

7.2 上下文管理机制

// DefaultChatClient 中的 ChatModelChatMemoryManager
class ChatModelChatMemoryManager {
    
    private final ChatMemory chatMemory;  // 统一存储
    private final Map<String, String> conversationIdIndex;  // 线程→会话ID 映射
    
    // 获取当前线程对应的会话 ID
    String getOrCreateSessionId(AdvisedRequest request) {
        long threadId = Thread.currentThread().getId();
        
        return conversationIdIndex.computeIfAbsent(
            String.valueOf(threadId),
            k -> UUID.randomUUID().toString()  // 新线程 = 新会话
        );
    }
    
    // 获取指定会话的历史消息
    List<Message> getHistoryMessages(String sessionId) {
        return this.chatMemory.get(new ConversationId(sessionId));
    }
    
    // 添加消息到指定会话
    void addMessage(String sessionId, Message message) {
        this.chatMemory.add(new ConversationId(sessionId), message);
    }
}

多会话管理

ChatMemory (统一存储)
    │
    ├── session-A: [msg1, msg2, msg3, ...]  ← 用户 A
    ├── session-B: [msg1, msg2, ...]        ← 用户 B
    └── session-C: [msg1, ...]             ← 用户 C

ConversationIdIndex (线程→会话映射)
    │
    ├── thread-1session-A
    ├── thread-2session-B
    └── thread-3session-C

八、Options 合并策略

8.1 三级 Options 优先级

优先级 1:请求级 Options(PromptBuilder.options() 设置)
         ↓ 覆盖
优先级 2:Client 默认级 Options(builder.defaultOptions() 设置)
         ↓ 覆盖
优先级 3:模型级 Options(application.properties 配置)

8.2 合并源码

// DefaultChatClient.mergeOptions()
ChatOptions mergeOptions(ChatOptions requestOptions) {
    
    // 1. 从 Client 构建时传入的默认选项
    ChatOptions defaultOptions = this.defaultOptions;
    
    // 2. 如果请求指定了选项,合并到默认选项
    if (requestOptions != null) {
        return ChatModelOptionsUtils.merge(
            requestOptions,   // 请求级(高优先级)
            defaultOptions    // 默认级(低优先级)
        );
    }
    
    // 3. 如果没有请求选项,返回默认选项
    return defaultOptions;
}

合并示例

// application.properties
spring.ai.ollama.chat.options.temperature=0.8

// 代码中
chatClient.builder(chatModel)
    .defaultOptions(OllamaOptions.builder()
        .temperature(0.5)    // Client 默认:0.5
        .maxTokens(1000)    // Client 默认:1000
        .build())
    .build();

// 请求时
.prompt()
    .options(OllamaOptions.builder()
        .temperature(0.3)    // 请求指定:0.3
        // maxTokens 未指定,沿用 Client 默认:1000
        .build())
    .call();

// 最终合并结果
// temperature = 0.3(请求覆盖了默认)
// maxTokens = 1000(沿用默认)

九、小结

9.1 本篇要点

主题核心要点
ChatClient vs ChatModelChatClient 是更高层的封装,提供 Builder API 和 Advisor 链支持
Builder 模式prompt().user().system().call() 链式调用的实现原理
Advisor 拦截链before()doChat()after() 的执行顺序
记忆注入MessageChatMemoryAdvisor 在调用前注入历史消息、调用后保存新消息
call() vs stream()完整响应 vs 流式响应,返回类型和执行流程的差异
Options 合并三级优先级:请求 > Client 默认 > 配置文件

9.2 关键类清单

类 / 接口职责
ChatClient顶层接口,定义 prompt()call()stream()
DefaultChatClient默认实现,管理 Advisor 链和记忆
PromptBuilderPrompt 构建器,提供链式 API
PromptUserMessageBuilderPromptBuilder 实现,持有消息列表
AdvisedRequest携带完整上下文的请求对象
AdvisedResponseAdvisor 链处理后的响应对象
RequestResponseAdvisorManagerAdvisor 链的执行管理器
MessageChatMemoryAdvisor记忆注入拦截器
ChatModelChatMemoryManager会话 ID 到记忆的映射管理

9.3 完整调用链路总结

用户代码
  chatClient.prompt().user("你好").call().content()
                │
                ↓
        ┌───────────────┐
        │ ChatClient    │  提供 prompt() 方法
        │ .prompt()     │
        └───────┬───────┘
                ↓
        ┌───────────────┐
        │ PromptBuilder │  构建 Prompt
        │ .user("...")  │  添加用户消息
        │ .call()       │  触发调用
        └───────┬───────┘
                ↓
        ┌───────────────┐
        │ AdvisorManager│  执行拦截器链
        │ .execute()    │
        └───────┬───────┘
                │
                ├──→ LoggerAdvisor.before()  记录请求
                │
                ├──→ MemoryAdvisor.before()  注入历史消息
                │
                ├──→ doChat()                调用 ChatModel
                │         │
                │         ├──→ 注入记忆消息
                │         ├──→ chatModel.call(prompt)
                │         └──→ 返回 ChatResponse
                │
                ├──→ MemoryAdvisor.after()   保存新消息到记忆
                │
                └──→ LoggerAdvisor.after()   记录响应
                │
                ↓
        ┌───────────────┐
        │ ChatResponse  │  返回完整响应
        │ .content()    │  提取文本
        └───────────────┘

9.4 下一篇预告

第 3 篇:Prompt 与 Message 体系

  • PromptTemplate 模板引擎的实现
  • 变量替换机制 ${name} → 实际值
  • SystemPromptTemplate 的高级用法
  • 不同模型的消息格式映射(Role Mapping)

系列目录

  • 第 1 篇:整体架构与核心抽象
  • 第 2 篇:ChatClient 调用链路(本篇)

需要Spring AI系列学习代码的同学 欢迎关注公众号「AI日撰」,点击菜单「获取源码」获取完整代码(Gitee 仓库)。