
前言
现在不少业务系统都用上了 Spring AI,但是这两天和小伙伴们沟通,发现很多人还是停留在使用上,对于调用链甚至底层逻辑不甚了解。
正好最近也没有要输出的内容,于是开个新坑:完整读一遍 Spring AI 2.0 的源码,同时完整分享内部源码链路。
这个系列暂定 24 篇。
文章会同步发在微信公众号和掘金,个别的精华内容会同步到 X,也会把每章的内容同步到 github 仓库方便归档和阅读。后续对于适合演示的部分,我会再录成短视频进行分享。
自动大模型进入 Java 项目后,各种 Agent 框架层出不穷,现在调用模型已经是最简单的一步了。
但是随着代码量逐步增加,问题也会慢慢出现。比如上下文到底放在哪里、tool 如何实现注册、多轮调用由谁控制、换一家模型厂商要改多少代码等问题,还有出了问题又该从哪里看日志......
注入此类的问题,相信大家也有不少疑问。
Spring AI 针对这些问题,实际是做了一套非常 "Spring" 的封装。用起来确实还算顺手,但也把不少细节藏到了接口、自动配置和 Advisor 里面。
所以我想对它做个拆解,就向雷总拆车一样,由外向内逐层拆解,还原最真实的代码逻辑。
先从这几行代码开始
Spring AI 的对话调用非常简短:
scss
String content = chatClient.prompt()
.user("你好")
.call()
.content();
4 行代码,就能实现和大模型对话。
但是继续往下问,问题就凸显出来了:
ChatClient是谁创建的?prompt()返回了什么,前面的配置保存在哪里?call()为什么没有直接调用模型?- Advisor 在哪个位置介入?
Prompt怎样转换成模型厂商 SDK 需要的请求?- 流式调用、工具调用和结构化输出走的是不是同一条链路?
对于 Hello World 而言,这些问题毫不影响 ,但作为真实业务系统呢?
加上 Chat Memory 后,历史消息要进入请求。接入 Tool Calling 后,一次请求可能变成多次"模型调用---执行工具---再次调用模型"。RAG 还会在请求发给模型前增加检索和上下文拼装。MCP 又把工具、资源和提示词放到了远程协议里。
如果主调用链没有理清,这些功能很容易被理解成几个互不相关的 Starter。
看似代码能跑,结果出了问题都不知道应该在哪一层打断点。
为什么是 2.0.0
系列固定使用 Spring AI v2.0.0,对应 Commit 是 ef502da。
截止到这篇文章时,这是最新的正式版本。
2.0.0 也正好适合作为新的起点。它基于 Spring Boot 4 和 Spring Framework 7,重新整理了 Options、空安全和模型接入方式。工具调用循环也进入了 Advisor Chain。Tool Calling、结构化输出重试这类需要多轮执行的能力,可以沿同一套链路进行组织。
后续版本如果改动了关键实现,我会单独补充差异,不会把不同版本的代码混在一篇文章里。
怎么读
肯定不会按照项目的目录从第一个 package 讲到最后一个 package。源码的目录只能作用于模块边界,没办法说明一次请求进来后究竟是如何运行的。
我会先把功能跑起来,从业务入口打断点,跟着实际的执行路径逐层往下走。
原则上讲,一篇文章只处理一个问题。
如果能用一张调用链图就表达清楚,那就避免通过众多类图的层叠堆砌。
源码也只会保留影响流程的部分,不把整段的类文件直接复制进文章。同时尽量把源码片段通过代码块粘贴进来,方便检索和阅读,减少直接截图贴图片。
Spring 家族的产品特点用抽象来收缩复杂度,但抽象多了,会变成黑盒------完整调用链被隐藏的太深。因此解析时还得具体看每一层解决了什么问题、增加了多少理解成本,以及在普通业务项目里有没有必要直接使用。
内容顺序
目前初步的内容结构已经规划完成。
前面几篇先围绕一次对话请求,从头跑到底,包括 Starter 自动配置、ChatClient、Prompt、Message、Options、ChatModel,以及 OpenAiChatModel 怎样调用供应商 SDK。再把流式响应和 Structured Output 也放在这一段。
主链理清之后,再看 Advisor、Chat Memory 和 Tool Calling。这里会重点来来讲解 ToolCallingAdvisor:模型返回工具请求后,Java 方法究竟在何处执行,执行结果怎样重新变成消息,以及什么条件下会结束循环。
紧接着就是 MCP 。先看 Client 怎样发现并注册远程工具,再看 Server 怎样暴露 Tool、Resource 和 Prompt。这样能直接看到 MCP 接入前后,Spring AI 的调用链发生了什么变化。
最后一段围绕 RAG 和工程能力:Document Reader、文档切分、EmbeddingModel、VectorStore、检索增强、Retry、Micrometer,以及如何扩展新的模型供应商,尤其是国内的这些大模型接入。
按照目前的规划,总共分为 3 个大段,总计预估是 24 篇,但是在撰写的过程中肯定会有些许的变更,但是总的路线不变。
优先保证内容质量,以内容来反推章节,不做字数的堆砌。
第一篇
第一篇的标题已经想好了:
《Spring AI 2.0 源码解析(一):一次 ChatClient 调用到底经历了什么?》
我会从前面的五行代码开始,先找到 ChatClient 的创建过程,再跟进 prompt()、call() 和 content(),一直追到 ChatModel 与供应商 SDK。
后面的 Advisor、Memory、Tool Calling 和 RAG,也都是逐步回归到这条调用链上。
保持主线不跑偏。
第一篇就从这里开始。
不积跬步,无以至千里。
感兴趣的朋友们可以关注我,让我们一起投身到 Spring AI 的宏大蓝图里,感受 Spring 家族的技术美学。