前言
现在不少业务系统都用上了 Spring AI,但是这两天和小伙伴们沟通,发现很多人还是停留在使用上,对于调用链甚至底层逻辑不甚了解。
正好最近也没有要输出的内容,于是开个新坑:完整读一遍 Spring AI 2.0 的源码,同时完整分享内部源码链路。
这个系列暂定 24 篇。
文章会同步发在微信公众号和掘金,个别的精华内容会同步到 X,也会把每章的内容同步到 github 仓库方便归档和阅读。后续对于适合演示的部分,我会再录成短视频进行分享。
自动大模型进入 Java 项目后,各种 Agent 框架层出不穷,现在调用模型已经是最简单的一步了。
但是随着代码量逐步增加,问题也会慢慢出现。比如上下文到底放在哪里、tool 如何实现注册、多轮调用由谁控制、换一家模型厂商要改多少代码等问题,还有出了问题又该从哪里看日志……
注入此类的问题,相信大家也有不少疑问。
Spring AI 针对这些问题,实际是做了一套非常 ”Spring“ 的封装。用起来确实还算顺手,但也把不少细节藏到了接口、自动配置和 Advisor 里面。
所以我想对它做个拆解,就向雷总拆车一样,由外向内逐层拆解,还原最真实的代码逻辑。
先从这几行代码开始
Spring AI 的对话调用非常简短:
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 家族的技术美学。