Skip to content

Spring AI 1.1.3 手术刀式拆解

当前主线:先把 Spring AI Core 的请求生命周期看透,再进入 Tool Calling、Spring AI Alibaba、Agent Framework 与 Graph Core。

为什么使用三个月仍然容易模糊

下面的业务代码很短:

java
String answer = chatClient.prompt()
    .user(question)
    .call()
    .content();

但它隐藏了完整执行链:

text
ChatClient
→ RequestSpec 收集配置
→ ChatClientRequest / Prompt 组装
→ Advisor Around Chain
→ ChatModel 统一抽象
→ DeepSeekChatModel 协议适配
→ DeepSeekApi HTTP 调用
→ ChatResponse / Generation / Message 包装
→ content() / chatResponse() / entity() 提取

如果没有形成方法级地图,遇到下面的问题时就只能猜:

  • prompt()call()content() 分别做了什么?
  • 默认 system 和本次 system 是覆盖还是追加?
  • 默认 Advisor 和本次 Advisor 会不会同时存在?
  • Advisor 的 before 和 after 为什么方向相反?
  • Memory 在什么时候读历史、写 UserMessage、写 AssistantMessage?
  • DeepSeek 的非流式与流式真正调用点在哪里?
  • contentreasoningContent 被放到了哪里?
  • Tool Calling 为什么可能再次请求模型?
  • 流式取消后为什么可能留下半轮对话?

这套课程只做一件事:

把 Spring AI 隐藏在流式 API 背后的对象、状态、调用顺序和失败边界全部显影。


精确版本基准

本阶段固定为:

text
JDK 17
Spring AI Core 1.1.3
Spring AI 官方 DeepSeek 模块
DeepSeek 作为主要模型提供方

必须区分:

text
Spring AI Core 1.1.3
Spring AI Alibaba 的版本
Agent Framework / Graph Core 的版本
DeepSeek 供应商协议版本

本阶段源码结论以:

text
spring-projects/spring-ai
v1.1.3 标签

为准,不把主分支或更高版本行为直接套进来。


Spring AI 九层剖面

第 1 层:业务入口

业务层决定:

  • conversationId 和租户边界;
  • 是否启用 Memory、RAG、Tool;
  • 使用非流式还是流式;
  • 超时、取消、重试和幂等语义;
  • 对外返回字符串、结构化对象还是事件流。

第 2 层:ChatClient

ChatClient 是业务门面,不是模型。

它负责:

text
收集调用配置
组织 Advisor
创建响应操作对象
提供 content()、chatResponse()、entity() 等结果视图

第 3 层:DefaultChatClientRequestSpec

执行:

java
chatClient.prompt()
    .system(...)
    .user(...)
    .messages(...)
    .advisors(...)
    .options(...)
    .tools(...);

主要是在可变 RequestSpec 中收集配置,还没有调用 DeepSeek。

第 4 层:ChatClientRequestPrompt

执行 call()stream() 时,框架才会正式:

text
渲染模板
创建 SystemMessage / UserMessage
合并显式 messages
处理工具选项
创建 Prompt
创建 ChatClientRequest
构建 Advisor Chain

消息基本顺序:

text
SystemMessage
→ 显式 messages
→ UserMessage

第 5 层:Advisor Chain

Advisor 不是简单的前置拦截器,而是环绕链:

text
Advisor A before
→ Advisor B before
→ ChatModel
→ Advisor B after
→ Advisor A after

每个 Advisor 都可以:

text
修改请求
继续调用下游
短路模型调用
处理响应

第 6 层:ChatModel

ChatModel 是聊天模型统一抽象:

java
ChatResponse call(Prompt prompt);
Flux<ChatResponse> stream(Prompt prompt);

实际实现可能是:

text
DeepSeekChatModel
OpenAiChatModel
DashScopeChatModel
OllamaChatModel

第 7 层:DeepSeekChatModel

它负责:

  • 合并 DeepSeek 默认参数和运行时参数;
  • 转换 Spring AI Message;
  • 创建 DeepSeek ChatCompletionRequest
  • 转换工具定义;
  • 执行或递归处理工具调用;
  • 把 DeepSeek 返回转换成 Spring AI 响应;
  • 保存普通回答和 reasoningContent

第 8 层:DeepSeekApi

真正远程调用发生在:

java
deepSeekApi.chatCompletionEntity(request);

以及:

java
deepSeekApi.chatCompletionStream(request);

第 9 层:响应包装

text
DeepSeek ChatCompletion / Chunk
→ Choice
→ Generation
→ DeepSeekAssistantMessage
→ ChatResponse
→ ChatClientResponse
→ content() / chatResponse() / entity()

其中:

text
普通回答 → DeepSeekAssistantMessage.getText()
思考内容 → DeepSeekAssistantMessage.getReasoningContent()

当前课程目录

课程不是平均铺开,而是按“主链 → 横切增强 → 模型适配 → 工程故障”推进。

第一批:建立主调用链

课程核心问题状态
L01:ChatClient 请求解剖prompt()call()、终结方法分别做什么?已完成校准
L02:Advisor 与 MemoryAdvisor 在什么时候执行,Memory 何时读写?下一实际学习
L03:DeepSeek 调用与返回真正 HTTP 调用点和流式/非流式返回如何包装?待学习

第二批:从“知道主链”进入“能排错”

课程核心问题真实产出
L04:默认与运行时配置合并哪些覆盖、哪些追加、哪些会二次合并?最终 Prompt 观察 Advisor
L05:Advisor 环绕顺序order 怎样决定 before/after,异常和短路会怎样?Advisor 顺序实验
L06:Memory 失败边界模型失败或流式取消后为什么会留下半轮对话?Memory 故障矩阵

当前校准结论

截至 2026-07-31,已经确认:

已掌握

  • prompt() 创建本次 RequestSpec,而不是直接创建最终模型请求;
  • .system().user() 主要先把数据收集到 RequestSpec;
  • call() 会组装请求和 Advisor Chain,但真正执行由 content()chatResponse() 等终结方法触发;
  • ChatClient 是业务组织门面,ChatModel 是模型统一抽象;
  • Advisor 链包围模型调用,链底由 ChatModelCallAdvisorChatModelStreamAdvisor 接入模型;
  • 运行时 system 覆盖默认 system;
  • 默认 Advisor 与本次 Advisor 会合并存在。

当前需要继续加深

  • 不同类型配置为什么采用不同合并策略;
  • Advisor order 和 before/after 的真实顺序;
  • 同步异常、流式错误和取消时哪些 after 不会执行;
  • Memory 的半轮对话、conversationId 隔离和持久化一致性;
  • DeepSeek options 二次合并和返回对象包装。

这说明当前不是 API 入门水平,而是正在从“会用框架”转向“能解释执行过程并定位故障”。


每节固定学习方法

text
提出一个核心问题
→ 先预测
→ 运行最小实验
→ 观察日志和对象状态
→ 找到源码入口
→ 追踪正常路径
→ 追踪失败、取消和边界路径
→ 回到工程方案
→ 完成 3~5 个验证问题

不从源码第一行开始读,也不把源码抄一遍当成理解。


本阶段真实产出

完成 L01~L06 后,需要形成一份:

text
Spring AI 1.1.3 + DeepSeek 请求生命周期与故障地图

至少包含:

  • RequestSpec 中有哪些状态;
  • Prompt 和 Message 的组装点;
  • 默认配置与运行时配置合并点;
  • Advisor 排序、进入、退出和短路位置;
  • Memory 读取、UserMessage 写入和 AssistantMessage 写入位置;
  • ChatModel 调用点;
  • DeepSeek 非流式与流式 HTTP 调用点;
  • contentreasoningContent 存放位置;
  • Tool Calling 递归入口;
  • 同步失败、流式取消和半轮对话风险。

没有完成这张图,不判定为完全掌握。


后续主线

L01~L06 完成后,按当前工作优先级进入:

text
Tool Calling 完整循环
→ DeepSeek 思考模式与工具调用兼容
→ 自定义 Advisor 与可观测性
→ ChatMemory 持久化和一致性
→ Spring AI Alibaba 如何扩展 Spring AI
→ Agent Framework
→ Graph Core
→ 主子 Agent、中断、恢复与取消

不会因为一次临时问题推翻主线,但真实工作中的问题会被吸收为后续案例。


源码阅读地图

text
spring-ai-client-chat
├─ ChatClient.java
├─ DefaultChatClient.java
├─ DefaultChatClientBuilder.java
├─ DefaultChatClientUtils.java
├─ ChatClientRequest.java
├─ ChatClientResponse.java
├─ ChatClientMessageAggregator.java
└─ advisor/
   ├─ DefaultAroundAdvisorChain.java
   ├─ ChatModelCallAdvisor.java
   ├─ ChatModelStreamAdvisor.java
   ├─ MessageChatMemoryAdvisor.java
   └─ api/
      ├─ BaseAdvisor.java
      └─ BaseChatMemoryAdvisor.java

spring-ai-model
└─ chat/memory/ChatMemory.java

models/spring-ai-deepseek
├─ DeepSeekChatModel.java
├─ DeepSeekChatOptions.java
├─ DeepSeekAssistantMessage.java
└─ api/DeepSeekApi.java

当前状态

官方源码基准

官网稳定文档可能已经描述更高版本行为。课程中的执行结论以 v1.1.3 标签源码为准。

Built with VitePress. Deployed on Cloudflare Pages.