Spring AI 学习地图
截至 2026-03-30,Spring AI 推荐先按稳定线学习:Spring AI 1.1.x + Spring Boot 3.5.x 。
2.x 目前属于预览线,适合后续进阶时了解,不建议作为入门主线。
1. 这门学科是干什么的
Spring AI 是一个 面向 AI 应用开发的 Spring 风格框架 。
它的目标不是"训练模型",而是让你像写 Spring Boot 应用一样,把:
- 大语言模型(LLM)
- Embedding
- Vector Store
- Tool Calling
- RAG
- MCP
- Observability
这些 AI 能力组织成一个可维护、可扩展、可监控的 Java 工程。
一句话理解:
Spring AI = 用 Spring 的方式开发 AI 应用。
2. 它解决什么问题
Spring AI 主要解决以下工程问题:
2.1 不同模型厂商接入方式不统一
不同模型提供商的 SDK、参数、调用方式不同。
Spring AI 提供统一抽象,减少业务代码和具体厂商 SDK 的强耦合。
2.2 AI 应用不只是"调一次接口"
真实 AI 系统通常不仅有聊天,还会涉及:
- Prompt 模板
- 会话上下文
- 工具调用
- 结构化输出
- 知识库检索(RAG)
- 监控与排错
Spring AI 把这些能力整合进 Spring 生态。
2.3 企业项目需要"工程化"
企业不是只关心"能回答",还关心:
- 如何配置
- 如何切换模型
- 如何记录日志
- 如何监控调用链
- 如何控制成本
- 如何定位报错
Spring AI 的价值就在这里。
3. 它和哪些相关技术有关系
3.1 上游模型与平台
- OpenAI
- Anthropic
- Ollama
- AWS Bedrock
- Google Vertex AI
- Azure OpenAI / Foundry
3.2 AI 应用核心概念
- Prompt Engineering
- Tool Calling
- Structured Output
- RAG
- Embedding
- Vector Search
- MCP(Model Context Protocol)
3.3 Spring 生态
- Spring Boot
- Spring Web MVC / WebFlux
- Spring Bean / IOC
- Auto Configuration
- Actuator
- Micrometer
3.4 常见外部组件
- PostgreSQL + pgvector
- Redis
- MongoDB
- OpenSearch / Elasticsearch
- Milvus / Qdrant / Pinecone
4. 学它之前需要哪些前置知识
必须掌握
- Java 基础
- Maven 或 Gradle
- Spring Boot 基础
- IOC / DI 基本理解
- REST API 基础
- YAML / properties 配置
最好具备
- HTTP / JSON
- Docker 基础
- SQL 基础
- 面向接口编程思想
先知道即可
- 响应式编程(Reactive)
- 各厂商模型差异细节
- 向量数据库底层实现
- MCP 协议规范细节
5. 真正重要的 20% 核心内容
这些内容决定你能不能真正上手 Spring AI。
必须吃透
ChatModelChatClientPromptChatOptionsChatResponse- Spring Boot starter 与自动配置
EmbeddingModelVectorStore- RAG 的基本链路
- Tool Calling
- Advisor 机制
- 基础可观测性与排错方法
一句话理解主干
先掌握:
聊天调用主线 -> 配置主线 -> RAG 主线 -> 工程排错主线
6. 哪些内容初学者容易陷入,但不值得一开始深挖
不建议一开始深挖
- 多智能体(Multi-Agent)
- 复杂工作流编排
- 所有模型厂商对比
- 所有向量库对比
- MCP 全部协议细节
- 深入源码所有模块
- 复杂 Prompt 技巧流派
原因
这些内容要么太大,要么变化快,要么依赖你先掌握核心抽象。
初学阶段深挖它们,很容易"懂很多名词,但不会落地"。
学习顺序
推荐按下面顺序学习:
-
入门认知
- Spring AI 是什么
- 它在 AI 技术栈中的位置
- 跑通第一个最小示例
-
核心概念
ChatModelChatClientPromptChatOptionsChatResponse
-
核心机制
- 自动配置
- Provider 抽象
- 请求构建
- 响应封装
- Streaming 基本机制
-
底层原理
- 消息模型
- Prompt 渲染
- Advisor 链
- 工程设计哲学
-
工程实践
- OpenAI / Ollama 接入
- 结构化输出
- Tool Calling
- RAG
- 会话记忆
- MCP 入门
-
常见问题与排错
- API Key 问题
- 超时 / 限流
- Token / 成本
- 模型参数错误
- 向量维度不匹配
- 检索效果差
-
面试与评估
- 高频面试题
- 系统设计题
- 实战表达题
- 项目回答套路
-
学习总结与知识闭环
- 做一个完整小项目
- 用项目巩固核心抽象
- 形成自己的排错方法论
第一阶段:入门认知
1. 学什么
这一阶段只学最关键的三件事:
- Spring AI 到底是什么
- Spring AI 在整个 AI 技术栈中处于什么位置
- 如何跑通一个最小可运行示例
你要先建立一个正确的认知:
Spring AI 不是模型,不是向量库,不是 AI 产品,而是一个开发框架。
2. 为什么重要
很多人一开始学 Spring AI,会把几个东西混在一起:
- Spring AI
- OpenAI / Ollama
- RAG
- Agent
- 向量数据库
一旦混淆,就会出现两个问题:
问题 1:概念学乱
你会记住很多名词,但不知道它们彼此是什么关系。
问题 2:工程落地学偏
你会急着做复杂能力,比如 Agent、RAG、多轮记忆,但连最基础的聊天调用链都没搞清楚。
所以第一阶段最重要的任务,不是"会用很多功能",而是:
先把整个系统的骨架搭起来。
3. 核心概念
先用类比理解,再给技术定义。
3.1 Spring AI 是什么
类比
如果说:
- Spring JDBC 是"数据库访问的工程化抽象"
- Spring Data 是"数据访问的统一抽象"
那么:
Spring AI 就是"AI 能力接入与编排的 Spring 风格抽象"。
技术定义
Spring AI 是一个面向 AI 应用开发的 Spring 生态项目,提供统一 API 和 Spring Boot 自动配置,帮助开发者以统一方式接入聊天模型、嵌入模型、向量存储、工具调用、RAG 和 MCP 等能力。
3.2 它与模型的关系
| 对象 | 它是什么 | 例子 |
|---|---|---|
| 模型提供商 | 提供模型能力的平台 | OpenAI、Anthropic、Ollama |
| 模型 | 真正执行推理的能力 | GPT、Claude、Llama、Mistral |
| Spring AI | 调用与组织这些能力的框架 | Spring AI |
所以要记住:
模型负责"生成能力",Spring AI 负责"工程组织"。
3.3 核心组件初识
| 组件 | 作用 | 当前阶段理解要求 |
|---|---|---|
ChatModel |
对接具体聊天模型的统一接口 | 知道它是底层模型抽象 |
ChatClient |
面向业务层更友好的调用入口 | 知道业务代码通常直接用它 |
Prompt |
一次完整请求的输入描述 | 知道它包含消息和参数 |
ChatOptions |
模型参数配置 | 知道它控制模型行为 |
ChatResponse |
模型返回结果封装 | 知道它比字符串更完整 |
4. 原理解释
4.1 先讲直觉
你可以把 Spring AI 的一次调用理解成:
"业务代码写一个统一请求,Spring AI 负责把它翻译成具体模型厂商能听懂的话,再把结果统一包装回来。"
4.2 调用流程
text
Controller / Service
↓
ChatClient
↓
ChatModel
↓
具体 Provider 实现(OpenAI / Ollama / Anthropic)
↓
模型返回结果
↓
Spring AI 统一封装响应
↓
返回给业务代码
4.3 为什么这样设计
这是典型的"抽象隔离变化"的设计思想。
因为变化最大的地方是:
- 模型厂商不同
- 参数不同
- 返回格式不同
- 协议差异大
如果业务代码直接依赖厂商 SDK,那么:
- 切换模型麻烦
- 测试麻烦
- 维护麻烦
- 统一治理困难
Spring AI 通过统一抽象,把变化挡在底层。
4.4 必须吃透:三层职责
| 层级 | 职责 | 你怎么理解 |
|---|---|---|
| 业务层 | 表达业务意图 | "我要问一个问题" |
ChatClient |
方便你构造请求 | "帮你组织请求" |
ChatModel |
真正执行模型调用 | "帮你翻译成厂商调用" |
4.5 学习本质
Spring AI 的学习本质,不是背 API,而是理解这两个核心思想:
- 统一抽象
- 工程化集成
5. 示例
下面给你一个最小可运行示例。
入门推荐先用 Ollama,因为本地可跑、门槛低、不需要先申请云平台 Key。
5.1 Maven 依赖
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
</dependencies>
5.2 配置文件
yaml
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: mistral
5.3 控制器代码
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AiController {
private final ChatClient chatClient;
public AiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/ai/ask")
public String ask(@RequestParam(defaultValue = "用一句话解释 Spring AI") String q) {
return chatClient.prompt()
.user(q)
.call()
.content();
}
}
5.4 运行步骤
- 安装 Ollama
- 执行:
bash
ollama pull mistral
- 启动 Ollama 服务
- 启动 Spring Boot 应用
- 浏览器访问:
text
http://localhost:8080/ai/ask?q=什么是Spring AI
6. 工程实践
6.1 企业中通常怎么用它
企业里很少一上来就做复杂 Agent。
更常见的落地路径是:
- 先做一个稳定的聊天接口
- 外置模型配置
- 加日志与调用监控
- 再做结构化输出
- 再接工具调用
- 最后做 RAG / MCP / 更复杂能力
6.2 当前阶段你要做的工程动作
你现在只需要完成这几个动作:
- 创建一个 Spring Boot 项目
- 引入 Spring AI starter
- 配置一个模型提供商
- 注入
ChatClient.Builder - 通过 HTTP 接口发起一次调用
- 确认能正常返回结果
6.3 正确学习顺序
当前阶段不要想着"一步学完 Spring AI"。
正确顺序是:
- 跑通调用
- 理解抽象
- 理解配置
- 理解响应
- 再进入进阶能力
7. 常见误区
误区 1:Spring AI 就是大模型
不是。
Spring AI 是框架,大模型是能力提供者。
误区 2:会调接口就等于会 Spring AI
不是。
Spring AI 的重点在于统一抽象、Spring 集成和工程化能力。
误区 3:一开始就该学 RAG / Agent
不对。
这些建立在基础调用链和核心抽象之上。
误区 4:切换 Provider 一定零成本
不完全对。
虽然抽象统一了,但不同模型的:
- 参数支持
- 输出风格
- 速度
- 成本
- 工具调用能力
都可能不同。
误区 5:只关注"怎么用",不关注"为什么这么设计"
这会导致你后面一旦出错就不会排查。
因为你只记住 API,没有理解它的职责边界。
8. 面试题
基础题
- Spring AI 是什么?
- Spring AI 解决了什么问题?
- Spring AI 和 OpenAI SDK 的区别是什么?
- Spring AI 在 AI 应用技术栈中处于什么位置?
理解题
- 为什么 Spring AI 要提供统一抽象?
ChatClient和ChatModel有什么区别?- 为什么企业使用 Spring AI 而不是在业务代码里直接写厂商 SDK?
表达题
- 如果你向一个不会 Spring AI 的同事解释它,你会怎么说?
- 如果模型从 Ollama 切到 OpenAI,哪些代码理论上可以不改?
9. 自测题
请你自己回答下面这些问题:
- Spring AI 是模型、平台、还是框架?
- Spring AI 和 OpenAI / Ollama 分别是什么关系?
ChatClient的职责是什么?ChatModel的职责是什么?- 为什么说 Spring AI 的核心价值之一是"统一抽象"?
- 为什么入门时应该先学最小聊天接口,而不是先学复杂 Agent?
- 你能否手写一个最小的
/ai/ask接口? - 如果接口调用失败,你第一步会检查什么?
10. 学完标志
学完这一节后,你应该能做到:
- 能清楚说出 Spring AI 是什么,不是什么
- 能说出 Spring AI 在 AI 技术栈中的位置
- 能解释
ChatClient和ChatModel的基本职责 - 能独立跑通一个最小聊天接口
- 能向别人讲清楚 Spring AI 为什么存在
- 能分清"模型能力"和"框架能力"的边界
第一阶段练习题
一、口头表达练习
请你用 3 句话 向面试官解释:
什么是 Spring AI?
要求:
- 不要只背定义
- 要体现它解决的工程问题
- 要体现它和模型厂商的关系
二、代码练习
把上面的接口改造成一个翻译接口:
text
GET /ai/translate?q=hello&lang=zh
要求:
- 使用
ChatClient - 返回翻译结果
- 自己设计 prompt 内容
三、思考题
如果把当前示例从 Ollama 切换到 OpenAI:
- 哪些内容应该改在配置层?
- 哪些业务代码理论上可以不改?
- 为什么这恰恰体现了 Spring AI 的设计价值?
本阶段学习方法建议
必须掌握
- Spring AI 的定位
- 核心调用链
- 最小可运行示例
知道即可
- RAG 细节
- MCP 细节
- 多模型高级能力
- 复杂 Advisor 链
初学者最容易学偏的地方
- 过早追求复杂能力
- 只会复制代码,不理解分层职责
- 只关注模型,不关注工程化
参考资料
第二阶段:核心概念
1. 学什么
这一阶段要把 Spring AI 最核心的 5 个概念真正串起来:
ChatModelChatClientPromptChatOptionsChatResponse
这一节的目标不是只记住"它们分别是什么",而是要看懂:
- 它们之间的调用关系
- 为什么要分层
- 为什么后续的 RAG、Tool Calling、Memory 都建立在这套抽象之上
必须吃透:
ChatClient和ChatModel的职责边界Prompt和ChatOptions的组成关系ChatResponse为什么比直接拿字符串更有工程价值
先知道,后深入:
ChatClientResponseresponseEntity()- Native Structured Output
- Streaming 下的结构化输出处理
2. 为什么重要
如果第一阶段是在脑子里搭出"Spring AI 是干什么的"这张地图,那第二阶段就是把地图上的主干道路修通。
后面你学:
- Tool Calling
- RAG
- Memory
- Advisor
- MCP
本质上都绕不开下面这条主线:
text
业务代码 -> ChatClient -> Prompt -> ChatModel -> Provider -> ChatResponse
你如果没把这一条链打通,后面就会出现典型问题:
- 会抄代码,但不会解释为什么这么写
- 会调用接口,但不会做分层设计
- 一旦模型切换、返回异常、token 超支,就不会定位问题
所以这阶段的重要性可以一句话概括:
它决定你后面是在"堆 AI 功能",还是在"做 AI 工程"。
3. 核心概念
先给你一个全局对照表。
| 概念 | 直觉类比 | 技术职责 | 你通常在业务里怎么用 |
|---|---|---|---|
ChatModel |
数据库驱动 / HTTP 底层客户端 | 对接具体模型提供商,实现真正的调用 | 通常不直接拼细节,而是作为底层能力被 ChatClient 使用 |
ChatClient |
JdbcClient / RestClient |
提供 fluent API,负责更友好的请求组织 | 业务代码最常直接面向它编程 |
Prompt |
一次完整请求报文 | 包含消息列表和可选模型参数 | 表达"这次到底问什么、怎么问" |
ChatOptions |
请求参数 | 控制模型、温度、token 等行为 | 做默认配置和请求级覆盖 |
ChatResponse |
完整响应对象 | 不仅有文本,还有 generation、usage、metadata | 做日志、成本、排错、结构化处理 |
3.1 ChatModel
定义:
ChatModel 是统一的聊天模型接口,屏蔽不同模型提供商之间的差异。
官方 API 核心形态可以概括为:
java
public interface ChatModel {
String call(String message);
ChatResponse call(Prompt prompt);
}
你要抓住一句话:
ChatModel 负责"真正发请求给模型"。
它更靠近底层,强调的是统一模型调用能力,而不是更高层的业务体验。
3.2 ChatClient
定义:
ChatClient 是建立在 ChatModel 之上的更高层 fluent API,用来更方便地构造 Prompt、绑定模板参数、挂接 Advisor、决定返回内容格式。
你要抓住一句话:
ChatClient 负责"让你更舒服地组织一次 AI 调用"。
它不是替代 ChatModel,而是建立在 ChatModel 之上的使用层抽象。
3.3 Prompt
定义:
Prompt 是一次模型请求的完整输入容器,本质上是:
- 多条
Message - 可选的
ChatOptions
你不要把 Prompt 简单理解成"一段字符串"。
在 Spring AI 里,一个 Prompt 更准确地说是:
"带角色的消息集合 + 本次请求参数"。
3.4 ChatOptions
定义:
ChatOptions 是传给模型的参数集合,用来控制本次调用行为。
常见便携参数包括:
modeltemperaturemaxTokenstopPtopKstopSequences
注意两点:
- Spring AI 提供的是可移植的通用选项接口
- 各 Provider 还会有自己特有的 options
所以它的设计不是"彻底抹平差异",而是:
先统一公共部分,再允许各家扩展。
3.5 ChatResponse
定义:
ChatResponse 是模型返回结果的统一封装。
它不只是一个字符串,还包含:
- 返回的文本结果
- 可能的多条
Generation - token usage 等 metadata
- 供应商返回的一些附加信息
所以:
content() 适合快速拿答案,ChatResponse 适合工程化处理。
3.6 容易混淆的概念对比
| 对比项 | ChatModel |
ChatClient |
|---|---|---|
| 所在层级 | 更底层 | 更上层 |
| 关注点 | 统一模型调用 | 组织调用体验 |
| 常见使用者 | 框架层、封装层 | 业务层 |
| 是否支持更丰富 fluent 组织 | 相对少 | 是 |
| 是否便于挂 Advisor / 模板 / 实体映射 | 不如 ChatClient 方便 |
更方便 |
| 对比项 | Prompt |
ChatOptions |
|---|---|---|
| 本质 | 请求整体 | 请求参数的一部分 |
| 包含什么 | Message 列表 + 可选 Options | 模型参数 |
| 解决什么问题 | "发什么内容" | "怎么发、用什么参数发" |
| 对比项 | content() |
chatResponse() |
|---|---|---|
| 返回值 | String |
ChatResponse |
| 优点 | 简单直接 | 信息完整 |
| 适用场景 | Demo、简单接口 | 生产日志、成本分析、排错 |
4. 原理解释
4.1 先讲直觉
可以把这 5 个核心对象理解成一次"标准化 AI 请求流水线":
- 你先决定要问什么,这形成
Prompt - 你决定这次怎么问,这些参数属于
ChatOptions - 你用
ChatClient把这次请求组织好 ChatClient把请求交给ChatModelChatModel转成各厂商自己的原生请求格式- 模型返回后,Spring AI 再统一包装成
ChatResponse
4.2 运行流程拆解
text
1. Spring Boot 根据 starter 和配置创建 ChatModel
2. Spring Boot 自动提供 ChatClient.Builder
3. 业务代码用 ChatClient 组织 system/user 消息
4. 如果用了模板变量,先渲染成最终 Prompt
5. 合并默认 ChatOptions 与本次运行时 ChatOptions
6. ChatModel 把 Prompt 转成厂商原生请求
7. Provider 返回原生响应
8. Spring AI 转成统一 ChatResponse
9. 业务代码选择 content() / chatResponse() / entity()
4.3 为什么 ChatClient 不直接等于 ChatModel
这是一个很关键的设计点。
如果只有 ChatModel,那么业务代码就要承担很多额外工作:
- 自己组织 Prompt
- 自己处理模板变量
- 自己处理返回值映射
- 自己扩展日志、Memory、RAG 等增强能力
Spring AI 没把这些职责硬塞进 ChatModel,而是新增了 ChatClient 这一层。
这样做的好处是:
ChatModel保持统一、稳定、底层ChatClient专注业务友好和增强能力
这就是典型的:
"底层接口保持小而稳,上层接口强调易用性和扩展性。"
4.4 为什么 Prompt 要设计成 Message 列表
因为现代 LLM 并不只是接收一段字符串,它更依赖"角色化消息结构"。
常见角色包括:
systemuserassistanttool
这样设计的好处是:
- 可以明确区分系统约束和用户输入
- 可以支持多轮对话
- 可以支持工具调用返回内容
- 可以和不同 Provider 的消息协议对齐
所以,Prompt 的本质不是"文本块",而是:
"结构化对话上下文"。
4.5 ChatOptions 为什么要分"启动默认值"和"运行时覆盖"
这是 Spring AI 非常工程化的一点。
官方文档里的流程是:
- 启动时给
ChatModel一组默认 options - 请求时
Prompt可以再带一组运行时 options - 二者合并,运行时覆盖默认值
这样设计的意义是:
- 全局统一默认模型和默认温度
- 个别请求还能按需覆盖
- 避免把所有差异都硬编码在配置文件里
这套机制非常适合企业应用,因为真实系统里经常会出现:
- 大多数请求用便宜模型
- 少数复杂请求切换更强模型
- 默认温度较低,但创意生成时临时调高
4.6 ChatResponse 为什么不能只当字符串看
很多初学者只盯着:
java
.call().content()
这当然能跑,但它会让你丢掉很多工程上非常重要的信息:
- 这次用了多少 token
- 响应元数据是什么
- 有几条 generation
- 是否需要做结构化解析
真实系统里,ChatResponse 的价值非常大,因为它直接关系到:
- 成本统计
- 性能分析
- 故障定位
- 质量评估
4.7 ChatClient 模板机制
ChatClient 支持把 user/system 内容写成模板,然后在运行时填参数。
这背后的设计思想是:
- 让 Prompt 更可复用
- 让输入和模板结构分离
- 让复杂 Prompt 不再全靠字符串拼接
官方文档说明,ChatClient 内部默认通过 PromptTemplate 和 StTemplateRenderer 来渲染模板。
所以推荐你形成习惯:
- 固定指令写模板
- 动态值走参数
- 不要手工拼接长字符串
5. 示例
下面这个例子一次性把第二阶段最关键的几个概念串起来:
ChatClientPrompt- 模板参数
ChatResponse- usage metadata
5.1 示例代码
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.stereotype.Service;
@Service
public class SpringAiTutorService {
private final ChatClient chatClient;
public SpringAiTutorService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String explainConcept(String concept) {
ChatResponse response = chatClient.prompt()
.system("你是一名 Java 和 Spring AI 导师,回答要准确、简洁、适合初学者。")
.user(user -> user
.text("请用 3 点解释 {concept},并补充一个常见误区。")
.param("concept", concept))
.call()
.chatResponse();
String content = response.getResult().getOutput().getText();
Long totalTokens = response.getMetadata().getUsage().getTotalTokens();
return "回答内容:\n" + content + "\n\n总 Token:" + totalTokens;
}
}
5.2 这个例子里每个对象在干什么
| 代码片段 | 它对应哪个概念 | 作用 |
|---|---|---|
builder.build() |
ChatClient |
创建业务调用入口 |
.system(...) + .user(...) |
Prompt 里的消息 |
构造本次请求内容 |
.param("concept", concept) |
模板变量 | 运行时填充 Prompt |
.call().chatResponse() |
ChatResponse |
获取完整响应对象 |
response.getMetadata().getUsage() |
metadata | 查看 token 消耗 |
5.3 如果要体现 ChatOptions 的运行时覆盖
不同 Provider 有自己的 Options 类型。
例如你用 OpenAI 时,常见写法会是:
java
.options(OpenAiChatOptions.builder()
.temperature(0.2)
.build())
这表示:
- 启动配置里可能已经设了默认温度
- 但这一次请求要临时覆盖成
0.2
这就是"默认配置 + 请求级覆盖"的体现。
6. 工程实践
6.1 企业里通常怎么落地这 5 个概念
真实项目里,通常不是在 Controller 里直接把所有 Prompt 都拼出来,而是:
- Controller 只接收请求参数
- Service 负责组织
ChatClient调用 - Prompt 模板集中管理
- 默认模型参数放配置文件
- 特殊请求再做运行时覆盖
- 关键接口记录
ChatResponsemetadata
推荐你从现在开始就按这个思路写代码。
6.2 一个真实工程场景
场景:你要做一个"接口文档智能解释器"。
用户输入一段接口定义,系统要返回:
- 这个接口是干什么的
- 参数含义
- 返回值解释
- 一个调用示例
这时候可以这样分层:
ChatClient:统一调用入口Prompt:system 里约束回答格式,user 里放接口定义ChatOptions:解释型任务把温度调低ChatResponse:记录 token 和返回 metadata
这就是为什么第二阶段这些概念不是"名词题",而是工程设计题。
6.3 当前阶段的操作建议
你现在练习时,建议固定按这套动作来:
- 每次都先想清楚 system 和 user 分别放什么
- 动态内容尽量用模板参数,不要字符串乱拼
- 默认先用
.chatResponse()看完整返回 - 确认没问题后,简单接口再简化成
.content() - 每次都问自己:这里应该写在配置层,还是写在请求层?
7. 常见误区
误区 1:ChatClient 和 ChatModel 只是两个名字不同的同一个东西
不是。
ChatModel 是统一模型接口,ChatClient 是更高层的业务友好 API。
误区 2:Prompt 就是一段字符串
不对。
在 Spring AI 里,Prompt 更准确地说是"消息集合 + 可选参数"。
误区 3:所有参数都应该写在配置文件里
不对。
配置文件适合默认值;请求级差异更适合运行时覆盖。
误区 4:只用 .content() 就够了
Demo 可以,工程里不够。
你会丢掉 usage、metadata、generation 等关键排错信息。
误区 5:system 和 user 放一起拼成一大段字符串也一样
不建议。
角色分离是现代 Prompt 结构的重要基础,混在一起会降低可维护性。
误区 6:模板参数只是语法糖
不只是。
它本质上是在把"Prompt 结构"和"运行时数据"分离,这是可维护性的关键。
8. 面试题
基础题
ChatModel和ChatClient的区别是什么?Prompt在 Spring AI 中由哪些部分组成?- 为什么
ChatResponse比直接返回字符串更有价值? ChatOptions解决的是什么问题?
理解题
- Spring AI 为什么要把
ChatClient设计在ChatModel之上? - 为什么 Prompt 要用多消息结构,而不是简单字符串?
- 默认配置和运行时配置合并的设计,有什么工程价值?
- 如果需要统计 token 消耗,你应该拿哪个对象上的什么信息?
进阶表达题
- 如果让你设计一个"稳定的 AI Service 层",你会如何组织
ChatClient、Prompt 和配置? - 如果业务要求"绝大多数请求用便宜模型,少量复杂请求切到强模型",Spring AI 的哪套机制最适合支持它?
9. 自测题
请你自己尝试回答:
- 用你自己的话解释
ChatModel、ChatClient、Prompt、ChatOptions、ChatResponse各自职责。 - 为什么说
Prompt不是简单字符串? .content()和.chatResponse()你会分别在什么场景下使用?- 为什么
ChatOptions需要支持"默认值 + 请求覆盖"? - 如果 system prompt、user prompt、temperature 都写死在 Controller 里,会带来什么问题?
- 你能否解释一次 Spring AI 请求从
ChatClient到ChatResponse的完整链路? - 如果模型输出质量忽高忽低,你会优先检查 Prompt、Options 还是 Provider?为什么?
10. 学完标志
学完这一阶段,你应该能做到:
- 能准确解释
ChatModel、ChatClient、Prompt、ChatOptions、ChatResponse - 能画出 Spring AI 一次聊天调用的完整链路
- 能说清楚为什么要把 system、user、options 分开组织
- 能在代码里使用模板参数构造 Prompt
- 能从
ChatResponse中读取文本和 usage - 能解释"默认配置 + 请求覆盖"的设计价值
- 能回答基础到中级面试里的核心概念题
第二阶段练习题
一、代码练习
把你第一阶段的 /ai/ask 接口升级成下面这个版本:
text
GET /ai/explain?concept=ChatClient
要求:
- 增加一个 system prompt,限定回答风格
- user prompt 使用模板变量
{concept} - 不要直接返回
.content(),改成先拿ChatResponse - 把"回答内容 + 总 token 数"一起返回
二、分析练习
请你口头回答下面两个问题:
- 为什么
ChatClient更适合业务层,而ChatModel更像底层抽象? - 为什么工程里不能把
ChatResponse永远简化成字符串?
三、设计练习
假设你要做两个 AI 接口:
- "解释代码"接口,要求稳定、准确
- "生成宣传文案"接口,要求更有创造性
请你思考:
- 两个接口的 Prompt 应该怎么区分?
- 两个接口的
ChatOptions应该怎么区分? - 哪些东西适合配成默认值,哪些适合请求时覆盖?
回复 继续,我下一阶段按同样的 Markdown 结构,带你进入:
第三阶段:核心机制
1. 学什么
这一阶段要搞清楚 Spring AI 是如何真正"跑起来"的。
重点包括:
- 为什么加一个 starter 再配几行 properties,模型就能用
starter + properties + autoconfiguration到底是怎么串起来的ChatModel、StreamingChatModel、ChatClient.Builder是怎么被创建出来的- 多 Provider 场景下应该怎么组织
- streaming 为什么不是"普通调用换个返回值"那么简单
这一节你要建立的是"框架执行机制视角",而不只是"业务怎么调"。
必须吃透:
- starter 的作用不只是"导依赖",更是"带入自动配置"
- properties 不只是配置文件,而是默认模型行为的来源
- 自动配置的核心是"条件装配 + 属性绑定 + 默认 Bean 暴露"
- streaming 走的是另一条模型调用分支
先知道,后深入:
- 各 Provider 自动配置源码细节
@ConditionalOnClass、@ConditionalOnMissingBean的全部组合- 具体 SDK HTTP 客户端实现差异
2. 为什么重要
这一阶段非常重要,因为真实开发里你会频繁遇到下面这些问题:
- 为什么我明明配了
api-key,却没有ChatModelBean? - 为什么换了 starter,配置前缀也要跟着改?
- 为什么
.call()能用,.stream()却报错或行为不符合预期? - 为什么项目里同时接了两个模型后,注入开始冲突?
- 为什么有的参数写在配置里有效,有的要请求时覆盖?
这些问题本质上都不是"Prompt 写得不对",而是:
你没有看懂 Spring AI 是如何借助 Spring Boot 自动装配完成模型接入的。
所以这一节的价值在于:
把"会调用"升级成"会搭、会配、会排、会解释"。
3. 核心概念
先给你一个总览表。
| 组件 | 它解决什么问题 | 发生在什么时候 |
|---|---|---|
starter |
把所需依赖和自动配置带进项目 | 启动前 / 构建期 |
properties |
提供模型默认配置 | 启动时绑定 |
| Auto Configuration | 按条件创建 Bean | 应用启动时 |
ChatModel |
提供统一模型调用能力 | 运行时调用 |
StreamingChatModel |
提供流式输出能力 | 运行时调用 |
ChatClient.Builder |
提供更高层 fluent API 构造能力 | 启动后可注入 |
3.1 starter
类比理解:
starter 就像"某类能力的总开关包"。
它通常做两件事:
- 带入这个 Provider 所需的依赖
- 带入 Spring Boot 自动配置
比如常见场景:
| Provider | 常见 starter | 常见配置前缀 |
|---|---|---|
| Ollama | spring-ai-starter-model-ollama |
spring.ai.ollama.* |
| OpenAI SDK | spring-ai-starter-model-openai-sdk |
spring.ai.openai-sdk.* |
注意:
starter 和 properties 前缀是配套的。
你换了 Provider,往往不仅是换依赖,也要换配置前缀。
3.2 properties
properties 负责承载默认模型配置,比如:
- base URL
- API Key
- model
- temperature
- max tokens
- timeout 或重试相关配置
你可以把它理解为:
"启动时给模型准备的默认作战参数"。
3.3 Auto Configuration
这是 Spring AI 真正 Spring 化的关键。
自动配置本质上做三件事:
- 判断当前类路径和配置是否满足某个 Provider 的装配条件
- 把配置文件绑定成配置对象
- 创建默认可注入的 Bean
最重要的设计思想是:
- 约定优于配置
- 条件装配
- 默认值可覆盖
3.4 ChatModel 与 StreamingChatModel
它们是两类不同的模型能力抽象:
| 抽象 | 用途 | 常见调用方式 |
|---|---|---|
ChatModel |
一次性返回完整结果 | .call() |
StreamingChatModel |
逐步返回结果片段 | .stream() |
要注意:
并不是所有 Provider、所有模型、所有场景对 streaming 的支持都完全一致。
所以 streaming 不是只把返回值从 String 变成 Flux<String> 这么简单,它依赖底层 Provider 能力和 Spring AI 的流式封装。
3.5 ChatClient.Builder
为什么注入的是 ChatClient.Builder,而不是直接只给你一个 ChatClient?
因为 Builder 更适合:
- 设置默认 system prompt
- 设置默认 advisors
- 设置默认 options
- 构造不同用途的
ChatClient
这体现的是:
把"底层模型能力"与"上层业务调用风格"解耦。
3.6 启动阶段与运行阶段对比
| 阶段 | 主要动作 | 关键对象 |
|---|---|---|
| 启动阶段 | 加载依赖、匹配自动配置、绑定属性、创建 Bean | starter、properties、Auto Configuration |
| 运行阶段 | 构造 Prompt、合并 options、调用模型、返回结果 | ChatClient、Prompt、ChatModel、ChatResponse |
4. 原理解释
4.1 先讲直觉
你可以把 Spring AI 的启动机制理解为:
"Spring Boot 先根据你项目里有什么依赖、配了什么参数,自动把模型客户端和相关 Bean 组好;业务代码只是拿来用。"
4.2 启动到可调用的完整链路
text
1. 你在 pom.xml / build.gradle 中引入某个 Spring AI starter
2. Spring Boot 启动时发现该 starter 带来的自动配置入口
3. 自动配置根据类路径和配置项判断是否生效
4. Spring 把 application.yml 中的属性绑定到对应配置对象
5. 自动配置创建 Provider 所需底层客户端和 ChatModel / StreamingChatModel
6. Spring 再暴露 ChatClient.Builder 等上层 Bean
7. 业务代码注入 ChatClient.Builder 并 build()
8. 运行时用 Prompt + Options 发起调用
9. 如果是普通调用,走 ChatModel
10. 如果是流式调用,走 StreamingChatModel
4.3 为什么 starter 能触发自动配置
因为 starter 不只是一个"依赖集合包",它通常还会把自动配置类带进 Spring Boot 的自动装配体系。
所以:
- 没有对应 starter,通常就没有对应自动配置
- 没有自动配置,很多 Bean 就不会自动创建
这也是为什么你常看到这样的现象:
- 代码没变
- 只换了依赖
- Bean 注入结果就变了
4.4 为什么 properties 能控制模型行为
因为 Spring Boot 会把配置文件内容绑定到配置对象上,然后自动配置再拿这些配置对象去创建底层模型客户端。
比如你配:
yaml
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: qwen2.5:7b
这并不是"业务代码读取 YAML 再自己处理",而是:
- Spring 先做属性绑定
- 自动配置读取绑定后的对象
- 用这些值构造默认
ChatModel
所以配置能改变模型行为,是因为:
配置参与了 Bean 创建,而不只是运行时字符串读取。
4.5 为什么 ChatClient.Builder 比直接暴露单个 ChatClient 更合理
如果框架只给你一个全局 ChatClient,很多业务差异就不好处理:
- 解释类接口和创意类接口默认 prompt 不同
- 有的接口要加 advisors,有的不要
- 有的接口走普通调用,有的走 stream
Builder 的好处是:
- 默认能力可以共用
- 局部能力可以按需 build 出不同客户端
所以这是一个典型的"默认统一 + 局部定制"的工程设计。
4.6 streaming 是怎么进调用链的
普通调用和流式调用的最大区别在于返回模型数据的时机不同。
普通调用:
text
请求发出 -> 等模型完整生成 -> 返回完整响应
流式调用:
text
请求发出 -> 模型边生成边返回片段 -> 应用逐段消费
在 Spring AI 里,你通常会这样写:
java
chatClient.prompt()
.user("解释一下 RAG")
.stream()
.content();
它背后意味着:
- 调用不再走一次性完整返回的那条路径
- 底层需要 Provider 支持流式响应
- Web 层通常要配合 SSE / WebFlux 等方式向前端输出
所以 streaming 的关键不只是 API,而是:
调用链、返回类型、Web 输出模型都发生了变化。
4.7 为什么 Spring AI 能做到"统一抽象 + Provider 扩展"
这是它设计里很值得学的一点。
它并没有强行要求所有模型都支持完全一样的能力,而是用了两层策略:
-
抽象公共能力
比如
ChatModel、Prompt、ChatOptions -
保留 Provider 扩展点
比如各家特有的 options、不同的流式支持、不同的 SDK 实现
这和很多 Spring 项目的哲学一致:
先抽象稳定共性,再通过扩展点承载差异。
5. 示例
这一节给你两个最小示例:
- 看懂 starter + properties + 自动装配的基本闭环
- 看懂 streaming 怎么进入调用链
5.1 示例一:最小自动装配闭环
依赖
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
</dependencies>
配置
yaml
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: qwen2.5:7b
temperature: 0.2
代码
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class AiExplainService {
private final ChatClient chatClient;
public AiExplainService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String explain(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
这个例子真正要你看懂的是:
- 你没有手写
ChatModelBean - 你没有手写
ChatClient.BuilderBean - 但它们都能注入
这就是自动配置在生效。
5.2 示例二:流式输出
如果你要把回答边生成边返回给前端,可以这样写:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class AiStreamController {
private final ChatClient chatClient;
public AiStreamController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping(value = "/ai/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String q) {
return chatClient.prompt()
.user(q)
.stream()
.content();
}
}
这个例子说明:
.call()对应完整响应路径.stream()对应流式响应路径- Web 层要能承接流式返回
6. 工程实践
6.1 企业里通常怎么组织配置
推荐做法:
- 把 Provider 的地址、Key、模型名、默认温度都放配置文件
- 把
ChatClient的使用集中在 service 层 - Controller 不要直接决定模型细节
- 特殊请求再做运行时 options 覆盖
- 对关键接口记录模型名、耗时、token、失败原因
6.2 多模型接入时怎么组织
初学者最容易犯的错,是一上来就在一个项目里把多个 Provider 混着注入,最后 Bean 冲突、职责混乱。
更好的做法是:
方案一:单 Provider 起步
最适合学习和大多数中小项目。
特点:
- 配置简单
- Bean 单一
- 排错容易
方案二:多 Provider,但明确分服务封装
例如:
CheapModelAiService负责低成本问答PremiumModelAiService负责复杂任务
而不是在 Controller 里写:
java
if (provider.equals("openai")) { ... } else if (provider.equals("ollama")) { ... }
因为那会把 Provider 差异直接泄漏到业务入口层。
6.3 streaming 的工程注意点
- 前端是否真的需要边生成边展示
- 后端是否准备好 SSE / WebFlux 输出模型
- 日志和错误处理是否能覆盖流式场景
- 是否需要对中断、超时、取消订阅做处理
很多项目不是不能用 streaming,而是没把它当成一条独立链路来设计。
6.4 当前阶段正确的练习顺序
- 先跑通单 Provider 的普通调用
- 看懂 starter 和 properties 的绑定关系
- 故意改错配置前缀,观察 Bean 是否还能注入
- 再尝试
.stream() - 最后再考虑多模型和更复杂封装
7. 常见误区
误区 1:starter 只是一个"方便导包"的依赖
不对。
它通常还携带自动配置能力,是 Bean 能自动出现的重要原因。
误区 2:properties 只是给代码读字符串用的
不对。
在 Spring Boot 体系下,properties 会参与属性绑定和 Bean 创建。
误区 3:有 ChatClient.Builder 就说明我手动写过相关 Bean
不一定。
很多时候它来自 Spring AI 的自动配置。
误区 4:.stream() 就是 .call() 的另一个名字
不对。
它背后要求不同的模型调用路径和 Web 输出方式。
误区 5:多 Provider 时直接在 Controller 里分支就行
不建议。
这会让业务入口层直接承担模型路由和 Provider 差异,后期很难维护。
误区 6:换了 starter 但配置前缀不改也能继续工作
通常不行。
starter 和配置前缀通常是成套设计的。
误区 7:版本不匹配只是"小问题"
不是。
Spring Boot 与 Spring AI 版本线不匹配时,很容易出现自动配置失效、类冲突、Bean 缺失等问题。
8. 面试题
基础题
- Spring AI 的 starter 在工程里起什么作用?
- 自动配置为什么能让你不手写
ChatModelBean? ChatClient.Builder为什么适合作为注入入口?.call()和.stream()的差别是什么?
理解题
- Spring AI 是如何把配置文件中的属性变成模型默认行为的?
- 为什么说 Spring AI 的核心机制离不开 Spring Boot 自动配置?
- 为什么 streaming 不只是返回类型不同,而是调用链不同?
- 如果项目里同时接两个 Provider,应该如何避免职责混乱和 Bean 冲突?
进阶题
- 为什么 Spring AI 能做到"统一抽象"和"Provider 扩展"同时成立?
- 如果一个接口要求稳定低温度输出,另一个要求高创造性输出,你会把差异放在配置层还是请求层?为什么?
9. 自测题
请你自己尝试回答:
- 只加 starter 和 properties,为什么模型相关 Bean 就能注入?
- 自动配置在这个过程中至少做了哪三件事?
ChatClient.Builder为什么不是多余的一层?- 为什么
StreamingChatModel可以看成是另一条能力链? - 如果
.call()正常、.stream()不正常,你会从哪些层面排查? - 多模型接入时,为什么不建议把 provider 路由逻辑写在 Controller?
- 如果配置文件里 model 改了,但接口输出风格没变,你会先检查什么?
10. 学完标志
学完这一阶段,你应该能做到:
- 能解释 starter、properties、autoconfiguration 在 Spring AI 中的配合关系
- 能说清模型相关 Bean 为什么会自动出现
- 能解释
ChatClient.Builder的存在价值 - 能区分普通调用链和流式调用链
- 能说明多 Provider 场景下的基本组织策略
- 能回答"为什么 Spring AI 看起来只配配置就能跑"的原理题
- 遇到 Bean 缺失、配置无效、streaming 异常时有基本排查方向
第三阶段练习题
一、配置练习
请你自己完成下面动作:
- 用 Ollama starter 跑通一个普通接口
- 把配置里的
temperature改成不同值,观察输出风格变化 - 故意把配置前缀改错一次,观察是否还能正常注入和调用
你的目标是亲自体会:
"自动配置不是魔法,它依赖正确的 starter 和正确的 properties。"
二、流式练习
实现一个接口:
text
GET /ai/stream?q=解释一下Spring AI的自动配置
要求:
- 使用
.stream().content() - 输出类型使用 SSE
- 自己描述它和
.call().content()在体验上的区别
三、设计练习
假设你的项目里要同时接入:
- 本地 Ollama,负责低成本开发调试
- 云端 OpenAI,负责线上高质量回答
请你思考:
- 这两种模型如何在工程里分层组织?
- 哪些配置应该放环境配置里?
- 哪些逻辑不应该直接出现在 Controller 里?
- 你会如何向面试官解释这种组织方式?
回复 继续,我下一阶段按同样的 Markdown 结构,带你进入:
第四阶段:底层原理
1. 学什么
这一阶段我们不再停留在"怎么用",而是专门回答:
- 为什么 Prompt 不是普通字符串,而是 role-based message
- 为什么 Spring AI 要引入
PromptTemplate和TemplateRenderer - 为什么 Advisor 链能承载 RAG、Memory、Tool Calling 这些增强能力
- 为什么
ChatResponse/ChatClientResponse里的 metadata 和 context 很重要 - Spring AI 的设计哲学到底是什么
这一节的目标是让你建立一种更稳定的理解方式:
以后看到新功能时,你能先判断它属于消息层、模板层、增强链层,还是模型层。
必须吃透:
- role-based message 的意义
- 模板渲染的职责边界
- Advisor 链的"请求前 + 响应后"双向处理模型
- metadata / context 对排错和 RAG 的价值
先知道,后深入:
- Recursive Advisors
- Modular RAG Architecture 细节
- 各 Provider 在 tool/message 协议层的特殊差异
2. 为什么重要
这一阶段重要,是因为很多初学者学到这里会进入一个误区:
"Spring AI 不就是把 Prompt 发给模型,再把结果拿回来吗?"
这句话只对了 30%。
真实的 AI 应用调用,通常还包括:
- 消息结构化
- 模板变量替换
- Memory 注入
- 检索结果注入
- 工具调用过程管理
- metadata 采集
- 追踪调用上下文
Spring AI 之所以能承载这些能力,不是因为它"功能多",而是因为它在底层抽象上提前留好了扩展点。
所以这一节的关键价值是:
让你理解 Spring AI 不是"功能堆砌",而是一套可扩展的分层设计。
3. 核心概念
3.1 Message 与 Role
类比理解:
如果普通字符串 Prompt 像"一段没有标注角色的聊天记录",
那么 role-based message 像"每句话都注明是谁说的、扮演什么角色"。
在 Spring AI 中,Prompt 本质上由多条 Message 组成,不同 message 可能对应不同 role。
常见 role 包括:
systemuserassistanttool
它解决的问题是:
- 区分系统规则和用户输入
- 表达多轮对话
- 表达工具调用结果
- 兼容现代模型 API 的结构化消息格式
3.2 PromptTemplate
PromptTemplate 的作用不是"让字符串替换更方便",而是:
把 Prompt 的结构和运行时数据分离。
你可以把它理解成:
- 模板 = 固定指令骨架
- 参数 = 每次变化的数据
这比手工拼接字符串更可维护,因为:
- 可复用
- 可测试
- 可读性更高
- 更适合长 Prompt 和团队协作
3.3 TemplateRenderer
Spring AI 使用 TemplateRenderer 来真正完成模板渲染。
官方文档说明默认实现是 StTemplateRenderer,基于 StringTemplate 引擎。
这说明 Spring AI 在模板层也做了抽象:
- 上层只关心"模板 + 参数"
- 底层可以替换具体渲染实现
所以设计思想是:
对上暴露统一模板接口,对下保留渲染实现可替换性。
3.4 Advisor 链
Advisor 是 Spring AI 里非常关键的扩展机制。
它不是简单的"工具类",而更像:
AI 调用链上的增强拦截器。
官方文档里说明,Advisor 可以:
- 在请求发给模型前检查和修改请求
- 决定是否继续往下调用
- 在响应回来后再处理结果
- 在链路里共享 context
因此它非常适合封装:
- Chat Memory
- RAG
- Tool Calling
- 安全过滤
- 推理增强
3.5 ChatResponse 与 ChatClientResponse
这两个对象容易混淆:
| 对象 | 核心作用 | 什么时候用 |
|---|---|---|
ChatResponse |
模型统一响应对象,含内容与 metadata | 普通调用、日志、token、排错 |
ChatClientResponse |
在 ChatResponse 基础上再带执行上下文 |
需要查看 Advisor 链附加数据时 |
官方文档特别提到,ChatClientResponse 能拿到 ChatClient execution context,例如 RAG 过程中检索到的相关文档。
这意味着:
普通聊天只看答案,复杂 AI 流程要看"答案是怎么来的"。
3.6 Spring AI 的设计哲学
这一阶段你要开始形成一句总总结:
Spring AI 的核心哲学是:把 AI 能力拆成可组合的稳定抽象,再用 Spring 风格把它们工程化。
它的几个设计关键词是:
- 统一抽象
- 条件装配
- 分层职责
- 可扩展增强链
- 保留 Provider 差异
- 面向工程而不是面向 Demo
4. 原理解释
4.1 为什么消息要按 role 组织
先讲直觉:
如果你把 system、user、tool 返回结果都拼成一个大字符串,那么模型只看见"混在一起的一坨文本"。
这样会带来几个问题:
- 系统约束不够明确
- 工具输出和用户问题边界不清晰
- 多轮对话难维护
- 不同模型协议难适配
而 role-based message 的设计,相当于告诉模型:
- 这条是系统规则
- 这条是用户问题
- 这条是工具返回结果
- 这条是历史回答
所以它的本质不是"格式好看",而是:
让上下文结构可计算、可组合、可移植。
4.2 PromptTemplate 的工作机制
它的运行流程可以拆成 4 步:
- 先定义一段带占位符的模板
- 运行时传入参数 map
TemplateRenderer根据模板语法做替换- 生成最终 message 文本,再放进
Prompt
可以画成这样:
text
模板文本 + 参数
↓
TemplateRenderer
↓
最终 message 文本
↓
Prompt
↓
ChatModel
这就是为什么模板层和模型层是分离的。
模型并不关心 {concept} 是怎么替换的,它只接收最终 Prompt。
4.3 Advisor 链为什么能挂 RAG、Memory、Tool Calling
这是底层设计最关键的一点。
官方文档描述的链路可以概括为:
- Spring AI 先根据用户的 Prompt 创建
ChatClientRequest - 再创建一个空的 advisor context
- Advisors 按顺序处理请求
- 每个 advisor 都可以修改请求,甚至阻断请求
- 最终框架内置的最后一个 advisor 才真正把请求发给模型
- 模型返回后,响应再沿着链路反向经过每个 advisor
- 最终形成
ChatClientResponse
这条链的价值非常大,因为它天然适合做"调用前增强"和"调用后处理"。
Memory 为什么适合放 Advisor
因为它本质上是在请求前补充历史上下文,或者在响应后保存本轮对话。
RAG 为什么适合放 Advisor
因为它本质上是在请求前做检索,把相关文档注入上下文。
Tool Calling 为什么也适合放 Advisor
因为它通常要在模型输出中识别工具调用意图,再触发工具执行,再把工具结果回填到上下文,属于典型的链式增强流程。
4.4 Advisor 顺序为什么重要
官方文档明确说明,Advisor 按 getOrder() 排序,数值越小优先级越高。
但要特别注意:
请求阶段和响应阶段的执行顺序是"栈式"的。
也就是说:
- 优先级高的 advisor 会先处理请求
- 但会后处理响应
这和 Servlet Filter / AOP 拦截链的思路很像。
所以如果你后面自己写 Advisor,一定要想清楚:
- 我需要先改请求还是后改响应
- 我需要拿到下游处理后的结果吗
- 我的顺序应该在 Memory 前还是 RAG 后
4.5 metadata 为什么重要
很多人刚接触时觉得 metadata 没那么重要,因为最直观的是文本内容。
但真实工程里,metadata 往往直接决定你能不能把系统维护下去。
比如:
- token 用量可以做成本核算
- rate limit 信息可以帮助限流与重试
- generation metadata 可以帮助调优模型行为
- provider response metadata 可以辅助故障定位
如果没有 metadata,你只能看到"回答差不多对不对";
有了 metadata,你才能进一步判断:
- 为什么这次贵
- 为什么这次慢
- 为什么这次被限流
- 为什么这次检索上下文没进来
4.6 ChatClientResponse 为什么是复杂流程的关键
普通流程里:
java
.call().content()
你只拿到了"结果文本"。
但复杂流程里,尤其是 RAG,你经常还想知道:
- 检索到了哪些文档
- 某个 advisor 是否改写了 prompt
- 上下文里放了什么中间状态
这就是 ChatClientResponse 的价值,它把:
ChatResponse- Advisor 执行上下文
放在一起返回。
这对调试复杂 AI 系统非常关键。
4.7 Spring AI 的设计哲学到底是什么
可以总结成 4 句话:
-
公共能力抽象化
把聊天、提示、向量检索等共性能力做成稳定接口
-
Provider 差异扩展化
不强行抹平一切差异,而是保留扩展点
-
增强能力链式化
用 Advisor 承载 Memory、RAG、Safety、Reasoning 等增强逻辑
-
Spring 工程化
用 starter、自动配置、Bean、Micrometer、Actuator 接入企业应用体系
所以 Spring AI 不是在做"某一种 AI 玩法",而是在做:
"让 AI 能力像 Spring 生态里的其他能力一样可组合、可治理、可维护。"
5. 示例
这一阶段给你两个最小示例:
- 一个模板渲染示例
- 一个 Advisor 链示意示例
5.1 示例一:PromptTemplate 最小示例
java
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class ConceptExplainService {
private final ChatClient chatClient;
public ConceptExplainService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public String explain(String concept, String level) {
return chatClient.prompt()
.system("你是一名 Spring AI 导师,回答必须清晰、准确。")
.user(user -> user
.text("请用{level}难度解释{concept},并给一个 Java 工程例子。")
.param("level", level)
.param("concept", concept))
.call()
.content();
}
}
这个例子里,ChatClient 内部会用模板渲染机制把 {level} 和 {concept} 替换成最终文本。
5.2 示例二:Advisor 链示意
java
ChatMemory chatMemory = ...;
VectorStore vectorStore = ...;
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.build();
String response = chatClient.prompt()
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "user-1001"))
.user("总结一下我们前面讨论过的 Spring AI 核心概念")
.call()
.content();
这个例子虽然比前面复杂,但它能非常直观地说明:
- Memory 不是你手动拼历史消息
- RAG 不是你手动把检索文本拼接到 prompt
- 它们都可以通过 Advisor 链接入调用过程
5.3 如果想拿执行上下文
在复杂流程里,可以考虑拿 chatClientResponse() 而不是只拿 content()。
这样你不仅能拿答案,还能拿到执行上下文,用于:
- 调试 RAG
- 查看中间状态
- 分析 Advisor 执行结果
6. 工程实践
6.1 企业里通常怎么用消息模型
推荐习惯:
- system 只放规则、身份、输出约束
- user 只放用户输入或任务请求
- tool / history 交给框架能力或 Advisor 管理
- 不要把所有内容都拼成一段大字符串
6.2 模板的工程实践
建议你在项目里这样做:
- 短 Prompt 可以直接写在代码里
- 长 Prompt 尽量抽成模板文件或集中模板定义
- 动态变量永远走参数,不要字符串拼接
- 模板需要版本化和可测试
这会直接影响:
- 维护成本
- 团队协作效率
- Prompt 调优效率
6.3 Advisor 的工程实践
推荐原则:
- 一个 Advisor 尽量只做一件事
- Memory、RAG、安全过滤尽量拆开
- 提前设计好 Advisor 顺序
- 复杂链路尽量保留 context,方便调试
如果你后面进入真实项目,这几条会非常重要。
6.4 一个真实工程场景
场景:做企业知识库问答助手。
一次调用背后可能会发生:
- 读取当前会话 ID
- 用 Memory Advisor 注入历史上下文
- 用 RAG Advisor 到向量库检索相关文档
- 把检索结果和问题一起送给模型
- 模型返回回答
- 记录 token、耗时、检索文档数量
- 保存本轮消息到 Memory
如果没有消息结构、模板机制、Advisor 链和 metadata,这样的流程会很难维护。
7. 常见误区
误区 1:Prompt 结构化只是"写法更优雅"
不只是。
它本质上是在为多轮对话、工具调用、模型协议兼容做准备。
误区 2:模板渲染只是为了少写字符串拼接
不只是。
它真正解决的是 Prompt 结构和动态数据解耦问题。
误区 3:Advisor 就是普通工具类
不对。
Advisor 运行在调用链上,能前置增强、后置处理、共享上下文。
误区 4:RAG 就是"查库后手工拼 prompt"
从原理上看可以这么做,但工程上更好的做法是把它放进可复用的增强链里。
误区 5:只要拿到回答文本,metadata 无所谓
不对。
没有 metadata,你很难做成本控制、性能分析和复杂排错。
误区 6:所有增强逻辑都应该写在一个大 Advisor 里
不建议。
这会让顺序、职责、调试都变得很混乱。
8. 面试题
基础题
- 为什么 Spring AI 的 Prompt 要按 role 组织 message?
PromptTemplate和普通字符串拼接相比有什么优势?- Advisor 在 Spring AI 中扮演什么角色?
ChatResponse和ChatClientResponse的区别是什么?
理解题
- 为什么 RAG、Memory、Tool Calling 适合通过 Advisor 实现?
- Advisor 顺序为什么重要?
- 为什么 metadata 对 AI 系统的工程化很关键?
TemplateRenderer的存在体现了怎样的设计思想?
进阶题
- 如果一个知识库问答系统回答不稳定,你会如何从消息结构、模板、Advisor 和 metadata 四层去排查?
- 你如何向面试官解释 Spring AI 的设计哲学,而不是只背 API 名称?
9. 自测题
请你自己尝试回答:
- 为什么现代 Prompt 更适合用 message 列表而不是单字符串?
- 模板机制到底解决了什么"工程问题"?
- Advisor 链和 Servlet Filter / AOP 拦截链有哪些相似之处?
- 为什么说
ChatClientResponse对复杂 AI 流程特别重要? - 如果你做一个带 RAG 的问答系统,哪些能力适合放在 Advisor 层?
- 如果 token 成本突然上升,你会从 metadata 里关注哪些信息?
- 你能否用自己的话总结 Spring AI 的设计哲学?
10. 学完标志
学完这一阶段,你应该能做到:
- 能解释 role-based message 为什么是现代 Prompt 的基础
- 能说清
PromptTemplate和TemplateRenderer的职责 - 能说明 Advisor 链为什么能挂接 Memory、RAG、Tool Calling
- 能理解
ChatResponse与ChatClientResponse的工程价值 - 能从设计层面解释 Spring AI 为什么这样分层
- 面对复杂 AI 调用链时,知道该从消息层、模板层、增强层、metadata 层分别看什么
第四阶段练习题
一、Prompt 结构练习
请你把一个"解释 Spring AI"的 Prompt 分成两种写法:
- 全部拼成一个大字符串
- 按
system + user分开写
然后回答:
- 哪种更容易维护?
- 哪种更容易扩展到多轮对话?
- 哪种更适合工具调用和 RAG?
二、模板练习
实现一个接口:
text
GET /ai/teach?topic=Prompt&level=beginner
要求:
- system 里定义导师角色
- user 使用模板变量
{topic}和{level} - 让模型输出"定义 + 类比 + 一个例子"
做完后,请你自己总结:
- 模板方式比字符串拼接到底好在哪里
三、架构练习
假设你要做一个"带历史上下文 + 知识库检索"的 AI 助手,请你自己画出一条调用链:
text
用户问题 -> Memory -> RAG -> 模型 -> 返回结果
然后回答:
- 哪些步骤适合做成 Advisor?
- 哪些信息适合放进 context?
- 哪些信息你希望最终能从
ChatClientResponse里看到?
回复 继续,我下一阶段按同样的 Markdown 结构,带你进入:
第五阶段:工程实践
1. 学什么
这一阶段进入真正"能上手做项目"的部分。
重点掌握 5 件事:
- OpenAI / Ollama 这两条最常见接入路径怎么落地
- Structured Output 怎么把模型输出变成 Java 对象
- Tool Calling 怎么把模型和外部能力连起来
- RAG 的最小工程闭环应该怎么搭
- 项目结构、配置分层、日志、监控应该怎么组织
这一节学完,你的目标不再只是"理解概念",而是要达到:
能搭出一个基础可用的 Spring AI 小项目。
必须吃透:
- Provider 接入和配置分层
entity()/ Structured Output 的基本落地方式- Tool Calling 的调用闭环
- RAG 的最小链路:切分 -> embedding -> 入库 -> 检索 -> 注入 prompt
先知道,后深入:
- 高级 Agent 编排
- 多阶段推理链
- 复杂 RAG 重排 / 查询重写 / reranker
- 复杂多工具协作
2. 为什么重要
前四个阶段帮你建立了:
- 地图
- 核心对象
- 运行机制
- 底层设计
但如果没有这一阶段,你很容易停留在:
- 会讲概念
- 会抄 Demo
- 不会搭真实项目
企业真正看重的是:
- 你能不能接通模型
- 能不能把结果映射成业务结构
- 能不能接知识库
- 能不能调工具
- 能不能做日志、限流、排错
所以第五阶段的意义是:
把"理解 Spring AI"转成"能交付一个最小 AI 功能模块"。
3. 核心概念
3.1 OpenAI 路线与 Ollama 路线
这是最常见的两条入门路线。
| 路线 | 特点 | 适合场景 |
|---|---|---|
| Ollama | 本地可跑、成本低、方便学习和开发调试 | 本地实验、内网、低成本开发 |
| OpenAI / 云端模型 | 质量更稳定、能力更强、线上常见 | 正式项目、生产环境、高质量生成 |
学习顺序建议:
- 先用 Ollama 跑通本地闭环
- 再学 OpenAI / 云端接入
- 最后比较模型质量、延迟、成本和适用场景
3.2 Structured Output
类比理解:
普通文本输出像"模型给你一段自然语言",
Structured Output 像"模型给你一份按 schema 组织的数据"。
它解决的问题是:
- 避免自己手工解析文本
- 让模型输出更适合业务处理
- 减少"回答看起来像 JSON,但其实不规范"的问题
Spring AI 提供两类常见方式:
- 直接
.entity(Class<T>) - 使用
StructuredOutputConverter
3.3 Tool Calling
Tool Calling 的本质不是"模型真的有工具权限",而是:
模型提出工具调用请求,应用负责执行工具,再把结果回传给模型。
这点非常重要,因为它关系到安全边界。
也就是说:
- 模型不会直接访问数据库/接口
- 真正执行动作的是你的应用
- Spring AI 负责把这套流程串起来
3.4 RAG
RAG 的本质是:
先从你的知识库检索相关内容,再把这些内容作为上下文提供给模型。
最小闭环通常包括:
- 原始数据准备
- 文档切分
- 生成 embedding
- 写入 Vector Store
- 查询时做 embedding 检索
- 把检索结果注入 Prompt
- 模型生成答案
3.5 工程组织
Spring AI 项目不是"先把所有 AI 功能塞进 Controller",而应该像正常 Spring 项目一样分层。
推荐角色分工:
| 层 | 职责 |
|---|---|
| Controller | 收参、返回结果 |
| Service | 组织 Prompt、ChatClient、Tool、RAG 调用 |
| Config | 模型 Bean、自定义 ChatClient、Tool 注册、Advisor 配置 |
| Data / Ingestion | 文档导入、切分、向量入库 |
| Observability | 日志、metrics、trace、成本统计 |
4. 原理解释
4.1 OpenAI / Ollama 两条接入路径的共同点
虽然 Provider 不同,但工程主线很像:
text
starter -> properties -> 自动配置 -> ChatClient -> Prompt -> Response
共同点:
- 都通过 Spring AI starter 接入
- 都通过配置文件指定模型和连接参数
- 都通过统一的
ChatClient/ChatModel调用
不同点:
- 认证方式不同
- 能力支持不同
- 默认模型行为不同
- 成本、延迟、部署方式不同
这再次说明:
Spring AI 做的是"统一主线",不是"抹平所有差异"。
4.2 Structured Output 的工作机制
它的核心思路是:
- 你告诉模型希望输出什么结构
- 模型按结构返回结果
- Spring AI 把返回内容映射到 Java 类型
最常见使用方式:
java
ActorFilms actorFilms = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorFilms.class);
它背后的价值是:
- 避免业务层自己写 JSON 解析
- 让 AI 输出更接近传统业务对象
- 更适合接口编排和持久化处理
但你要记住:
Structured Output 不是"100% 不会出错",只是大幅降低非结构化输出带来的混乱。
4.3 Tool Calling 的工作闭环
官方文档的工具调用流程可以概括为:
text
1. 应用把工具定义告诉模型
2. 模型决定是否调用工具,并给出工具名和参数
3. 应用根据工具名找到对应 ToolCallback
4. 应用执行工具
5. 工具结果被序列化后回传给模型
6. 模型基于工具结果生成最终回答
你一定要吃透一句话:
模型只会"请求调用工具",不会"直接执行工具"。
这也是为什么 Tool Calling 在工程里既强大又必须谨慎。
4.4 RAG 的最小闭环原理
不要把 RAG 理解成"把文档直接拼进去"。
一个更准确的最小工程闭环是:
text
原始文档
↓
切分成较小片段
↓
用 EmbeddingModel 生成向量
↓
写入 VectorStore
↓
用户提问
↓
将问题向量化并检索相似文档
↓
把相关文档放进 Prompt / Advisor
↓
模型回答
这里最常见的问题不是"代码不会写",而是:
- 文档没切好
- embedding 模型和存储维度不匹配
- 检索条数不合理
- 注入 prompt 的方式混乱
所以 RAG 本质上是"检索系统 + Prompt 组织"的组合。
4.5 为什么工程里要分"导入链路"和"问答链路"
很多初学者做 RAG 时,把"写入向量库"和"在线问答"混在一个接口里。
这是很常见的设计错误。
更合理的拆分是:
- 导入链路:读文档、切分、embedding、入库
- 问答链路:接问题、检索、组 Prompt、生成答案
原因很简单:
- 导入通常是批处理或后台任务
- 问答通常是在线低延迟请求
这两个链路的性能目标和失败处理方式完全不同。
4.6 为什么项目一开始就要考虑日志和监控
因为 AI 应用比普通 CRUD 更"不稳定":
- 输出不完全确定
- token 成本可波动
- Provider 可能限流
- 模型响应时间变化大
- 工具调用和 RAG 会增加额外链路
所以从第一版开始,你就应该考虑记录:
- 请求耗时
- 模型名称
- token 使用
- 检索文档数量
- Tool Calling 次数
- 失败原因
5. 示例
5.1 示例一:OpenAI 最小接入
依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai-sdk</artifactId>
</dependency>
配置
yaml
spring:
ai:
openai-sdk:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-5-mini
temperature: 0.2
调用
java
String answer = chatClient.prompt()
.user("用 3 点解释什么是 Spring AI")
.call()
.content();
这个例子说明:
- 云端模型接入和本地模型接入在业务调用层几乎一样
- 差异主要体现在 starter、配置和模型能力上
5.2 示例二:Structured Output
定义输出对象
java
public record ConceptSummary(
String definition,
String analogy,
String example
) {}
调用
java
ConceptSummary summary = chatClient.prompt()
.user("请按 definition、analogy、example 三个字段解释 Spring AI")
.call()
.entity(ConceptSummary.class);
这个例子很适合做:
- 智能表单填充
- 信息抽取
- 摘要结构化
- AI 结果进入业务流程
5.3 示例三:Tool Calling
定义工具
java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class DateTimeTools {
@Tool(description = "获取当前系统时间")
public String getCurrentTime() {
return LocalDateTime.now().toString();
}
}
调用
java
String result = chatClient.prompt()
.user("现在几点了?请先获取系统时间再回答。")
.tools(new DateTimeTools())
.call()
.content();
这里最重要的不是语法,而是你要理解:
- 工具描述会暴露给模型
- 模型决定要不要调用
- 真正执行工具的是应用
5.4 示例四:RAG 最小闭环示意
导入链路
java
List<Document> documents = List.of(
new Document("Spring AI provides abstractions for chat models, embeddings, vector stores, and tool calling.")
);
vectorStore.add(documents);
问答链路
java
String answer = chatClient.prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore))
.user("Spring AI 主要解决什么问题?")
.call()
.content();
这个例子只展示最小主线,但真实项目里你还会补:
- 文档切分
- metadata
- 检索过滤
- 文档来源标注
6. 工程实践
6.1 推荐的项目结构
一个基础 Spring AI 项目,可以先按下面结构组织:
text
com.example.ai
├── controller
├── service
├── config
├── tool
├── rag
├── ingestion
└── model
含义:
controller:HTTP 接口service:对话、结构化输出、业务编排config:ChatClient、自定义配置、Provider 配置tool:@Tool方法或 ToolCallbackrag:检索相关封装ingestion:文档导入、切分、向量入库model:结构化输出对象、请求响应对象
6.2 推荐的学习落地顺序
正确顺序:
- 普通聊天接口
- Structured Output
- Tool Calling
- 最小 RAG
- 日志与监控
- 再考虑会话记忆和复杂编排
不要一开始就:
- 多工具
- 多模型
- 多轮记忆
- 混合 RAG
- Agent 工作流
6.3 配置分层建议
推荐分三层:
第一层:环境配置
- API Key
- base URL
- 模型名
- 超时
第二层:应用默认配置
- 默认 system prompt
- 默认 temperature
- 默认 advisors
第三层:请求级覆盖
- 特殊 Prompt
- 特殊 model
- 特殊 tool 集合
- 特殊结构化输出需求
6.4 企业里通常怎么用 Tool Calling
企业最常见的不是"让模型做任意动作",而是给它有限、明确、可审计的工具集合。
例如:
- 查订单
- 查库存
- 查天气
- 生成工单
- 调用内部知识检索
最佳实践:
- 工具描述写清楚
- 参数 required 状态定义准确
- 对外部动作型工具做权限和幂等控制
- 工具返回值尽量结构化、可序列化
6.5 RAG 的最低可用实践
如果你第一次做 RAG,最推荐的顺序是:
- 先只导入少量高质量文档
- 验证检索结果是否靠谱
- 再把检索接入问答
- 再观察 hallucination 是否下降
- 再去调 chunk 大小、topK、过滤条件
不要一开始就导入海量杂乱文档。
否则你根本分不清是检索差、切分差、还是 Prompt 差。
6.6 日志与监控建议
起步阶段至少记录:
- 接口名
- 模型名
- 响应耗时
- token 数
- 是否命中工具调用
- 检索文档数
- 异常类型
如果进入生产环境,再进一步结合:
- Micrometer
- Actuator
- trace / span
- 成本报表
7. 常见误区
误区 1:只要能返回文本,就说明项目已经可用了
不对。
真实项目往往还需要结构化输出、日志、失败处理、权限控制、监控。
误区 2:Structured Output 就等于绝对可靠的 JSON
不对。
它能显著提升稳定性,但仍需要对异常输出和字段缺失做好兜底。
误区 3:Tool Calling 是模型自己去访问外部系统
不对。
模型只提出调用请求,真正执行的是你的应用。
误区 4:RAG 就是把文档塞给模型
过于简化。
真正的 RAG 至少包含 embedding、向量检索和上下文注入。
误区 5:导入向量库和在线问答可以随便混在一起
不建议。
它们的职责和性能目标不同,应该拆链路。
误区 6:项目一开始不用管日志监控,后面再补
风险很大。
AI 应用的问题比普通 CRUD 更难追踪,越晚补越难定位问题。
误区 7:本地 Ollama 跑通了,线上切 OpenAI 基本不用想别的
不完全对。
模型能力、响应风格、token 成本、工具支持、延迟都可能不同。
8. 面试题
基础题
- Spring AI 中 Structured Output 解决了什么问题?
- Tool Calling 的执行闭环是什么?
- RAG 的最小工程闭环包括哪些步骤?
- 为什么企业项目里常把模型接入、Tool、RAG 分层组织?
理解题
- 为什么模型并不会直接执行工具?
.entity(Class<T>)和直接拿文本再手工解析相比,有什么优缺点?- 为什么 RAG 项目里要把导入链路和问答链路拆开?
- 为什么本地 Ollama 与云端 OpenAI 虽然调用方式相似,但工程策略不一定相同?
进阶题
- 如果要做一个"订单查询助手",你会优先选择 Tool Calling、RAG 还是两者结合?为什么?
- 如果 AI 输出要进入正式业务流程,你会如何设计结构化输出、校验和兜底策略?
9. 自测题
请你自己尝试回答:
- 什么时候应该优先用 Structured Output?
- Tool Calling 的安全边界在哪里?
- 你能否画出一个最小 RAG 调用链?
- 为什么向量入库更适合做成单独导入流程?
- 如果 RAG 效果差,你会先检查文档质量、切分、embedding、检索还是 Prompt?为什么?
- 如果你现在要搭一个最小 Spring AI 项目,目录结构会怎么分?
- 如果要把 AI 输出写入数据库,为什么不建议直接依赖自然语言文本?
10. 学完标志
学完这一阶段,你应该能做到:
- 能独立接通本地模型或云端模型
- 能把 AI 输出映射成 Java 对象
- 能解释 Tool Calling 的完整执行闭环
- 能搭出一个最小 RAG 闭环
- 能按合理分层组织 Spring AI 项目
- 能说明日志、token、检索、工具调用为什么都要监控
- 已具备做一个基础 AI 功能模块的工程能力
第五阶段练习题
一、Structured Output 练习
实现一个接口:
text
GET /ai/summary?topic=Spring AI
要求:
- 输出结构为
definition / analogy / example - 不要只返回纯文本
- 使用
.entity(...)或 Structured Output Converter
然后思考:
- 如果模型漏了一个字段,你准备怎么处理?
- 为什么这种方式更适合进入业务流程?
二、Tool Calling 练习
实现一个最小工具:
- 获取当前时间
然后实现一个接口:
text
GET /ai/time
要求:
- 让模型通过工具获取当前时间再回答
- 解释"模型请求工具"和"应用执行工具"的区别
三、RAG 练习
做一个最小知识库问答 Demo:
- 准备 3 到 5 条关于 Spring AI 的知识文本
- 写入 Vector Store
- 做一个问答接口
- 用
QuestionAnswerAdvisor或类似方式接入检索
然后回答:
- 如果回答效果差,你第一步怎么排查?
- 如果检索到了无关文档,你会优先调哪里?
第六阶段:常见问题与排错
1. 学什么
这一阶段的目标不是继续增加新功能,而是建立一套真正可复用的排错方法。
重点包括:
- API Key、base URL、starter、版本不匹配怎么排
- 为什么
ChatModel/ChatClient.BuilderBean 注入失败 - 为什么模型输出不稳定
- 为什么 Structured Output、Tool Calling、RAG 不按预期工作
- 如何把"AI 应用排错"拆成固定层次
这一节学完后,你要达到的状态是:
不是只会说"好像出错了",而是能快速判断问题在哪一层。
必须吃透:
- 排错要按层分:依赖层、配置层、Bean 层、请求层、模型层、增强层
- 先排"有没有接通",再排"为什么效果差"
- 输出质量问题和系统接入问题不是一类问题
先知道,后深入:
- 高级 trace 体系
- 大规模线上限流与重试策略
- 多租户模型路由问题
2. 为什么重要
很多人学 AI 框架时有一个典型问题:
- Demo 能跑
- 一到自己项目里就开始报错
- 报错后不知道先查依赖、查配置、还是查 Prompt
Spring AI 的问题来源通常分成两大类:
第一类:系统没接通
例如:
- Bean 没创建
- API Key 错
- base URL 错
- starter 错
- 版本不匹配
第二类:系统接通了,但结果不对
例如:
- 回答很飘
- Structured Output 映射失败
- 工具没被调用
- RAG 检索结果不相关
这两类问题的排法完全不同。
如果你不先分类,就会陷入"所有地方一起猜"的低效状态。
所以这一阶段最重要的价值是:
把排错从"凭感觉乱试"变成"按层定位"。
3. 核心概念
3.1 六层排错模型
我建议你以后把 Spring AI 问题固定拆成下面六层:
| 层级 | 核心问题 | 常见现象 |
|---|---|---|
| 依赖层 | starter / 版本是否正确 | 类找不到、自动配置不生效 |
| 配置层 | properties 是否正确绑定 | 401、404、连接失败、模型不对 |
| Bean 层 | Bean 是否成功创建和注入 | NoSuchBeanDefinitionException |
| 请求层 | Prompt / Options / 参数是否合理 | 输出异常、字段丢失、风格不稳定 |
| 增强层 | Tool / Advisor / RAG / Memory 是否按预期工作 | 工具不触发、检索无效 |
| 模型层 | Provider 能力边界、限流、token、性能 | 429、超时、结果不稳定 |
3.2 "接通问题"与"效果问题"
这是最常混淆的一组概念。
| 问题类型 | 本质 | 例子 |
|---|---|---|
| 接通问题 | 系统没有正确跑起来 | Bean 不存在、401、连接失败 |
| 效果问题 | 系统跑起来了,但结果不好 | 回答不准、RAG 效果差、结构化输出不稳 |
你要养成一个习惯:
先确认链路通,再优化效果。
3.3 "框架问题"与"模型问题"
| 类型 | 本质 | 例子 |
|---|---|---|
| 框架问题 | Spring AI 接入、配置、调用链问题 | starter 错、Bean 缺失、Advisor 没挂上 |
| 模型问题 | 模型本身能力或限制问题 | 小模型理解差、工具能力弱、structured output 不稳定 |
很多排错失败,不是因为不会看日志,而是因为把框架问题和模型问题混在一起。
3.4 最重要的排错原则
- 先最小化问题
- 先去掉复杂能力
- 先验证最短路径
- 再逐层把复杂能力加回来
比如一个 RAG + Tool Calling + Structured Output 的接口出问题时,不要一上来全查。
应该先退化成:
text
纯聊天能不能通?
如果纯聊天都不通,就不要先怀疑 RAG。
4. 原理解释
4.1 排错总流程
建议你固定按下面顺序排:
text
1. 依赖对不对
2. 配置对不对
3. Bean 有没有
4. 最小聊天调用通不通
5. Prompt / Options 合不合理
6. Tool / RAG / Structured Output 单独是否正常
7. metadata / 日志 / token / trace 有没有异常
这个顺序非常重要,因为它遵循的是:
从基础设施到业务行为,从确定性问题到非确定性问题。
4.2 API Key / base URL / 模型名错误怎么排
这类问题一般发生在配置层。
常见现象:
401 Unauthorized403 Forbidden404 Not Found- 连接超时
- 调到错误模型
排查步骤:
- 确认使用的 starter 与 Provider 对应
- 确认 properties 前缀正确
- 确认环境变量是否真的注入
- 确认 base URL 是否指向正确服务
- 确认模型名是否是目标 Provider 真正支持的名字
典型例子:
- 用了
openai-sdkstarter,却还写spring.ai.openai.* - 本地 Ollama 没启动,但还在请求
http://localhost:11434 - OpenAI key 没注入环境变量
4.3 Bean 注入失败怎么排
如果你看到:
NoSuchBeanDefinitionException- 注入
ChatClient.Builder失败 - 注入
ChatModel失败
优先看三件事:
- starter 是否引入了正确模块
- Spring Boot 与 Spring AI 版本是否匹配
- 自动配置是否因为条件不满足而没生效
底层原理其实很简单:
- 没有正确依赖,自动配置类不会进来
- 没有正确配置,条件装配可能不会创建 Bean
- 版本不匹配,类路径和自动配置入口可能出问题
所以 Bean 问题大多数不是业务代码问题,而是:
依赖与配置问题。
4.4 输出不稳定怎么排
如果系统能返回结果,但风格忽左忽右、内容时好时坏,优先看:
- system prompt 是否稳定
- user prompt 是否过于含糊
- temperature 是否过高
- 是否切换了不同模型
- 是否把历史上下文、RAG 文档、工具结果混乱地拼在了一起
也就是说,这类问题大多不在"连通性",而在:
- Prompt 设计
- Options 设计
- 上下文组织
4.5 Structured Output 不稳定怎么排
典型现象:
- JSON 不完整
- 字段缺失
- 类型不匹配
.entity(...)转换失败
优先检查:
- 输出 schema 是否过于复杂
- Prompt 是否明确要求结构化字段
- 模型是否足够适合结构化输出
- 是否使用了合适的 Structured Output 方式
- 是否给业务层做了兜底校验
经验原则:
- 先从小对象开始
- 字段名尽量清晰
- 少做层级过深的嵌套
- 不要过早要求模型稳定输出复杂数组对象
4.6 Tool Calling 不触发怎么排
典型现象:
- 模型明明应该查工具,却直接瞎答
- 工具参数不对
- 工具方法根本没执行
排查顺序:
- 工具是否真的注册给了本次调用
- 工具描述是否足够清晰
- 模型是否具备较好的工具调用能力
- 用户 prompt 是否明确提示要使用工具
- 工具返回结果是否可被模型继续利用
注意:
"没调用工具"不一定是框架坏了,也可能是模型判断"不需要调用"。
4.7 RAG 效果差怎么排
RAG 问题最容易让人误判成"模型太笨"。
但大量情况下,真正问题在前面几步:
- 文档质量差
- 文档切分不合理
- embedding 模型不合适
- 向量维度不匹配
- topK 不合理
- 检索结果虽然有,但注入 prompt 的方式不对
所以排 RAG 时一定要分开看:
text
导入链路正常吗?
检索链路正常吗?
生成链路正常吗?
不要把它们混成一个整体。
4.8 性能和限流问题怎么排
典型问题:
- 响应很慢
- 429 限流
- token 成本突然上涨
- streaming 中途中断
优先看:
- metadata 中的 token usage
- 请求耗时
- rate limit 信息
- Prompt 长度
- RAG 注入文档数量
- Tool Calling 是否造成了额外链路开销
这类问题的本质是:
不是"答不答得对",而是"系统能不能稳定、可控地运行"。
4.9 一套可复用的排错方法论
以后你可以固定套下面这套模板:
第一步:判断是哪一类问题
- 接通问题
- 效果问题
- 性能问题
第二步:缩小问题范围
- 去掉 RAG
- 去掉 Tool Calling
- 去掉 Structured Output
- 退回纯聊天
第三步:检查最关键观测点
- Bean 是否存在
- 配置是否生效
- 最终 Prompt 长什么样
- metadata 和 token 怎么样
- 工具和检索是否真的执行
第四步:逐项恢复复杂能力
- 先加 Structured Output
- 再加 Tool
- 再加 RAG
- 最后加 Memory / 复杂 advisors
这就是一种"最小可复现 + 分层恢复"的排错思想。
5. 示例
5.1 示例一:Bean 注入失败时的最小排查
假设你有这样的代码:
java
@RestController
public class AiController {
private final ChatClient.Builder builder;
public AiController(ChatClient.Builder builder) {
this.builder = builder;
}
}
如果启动时报注入失败,最先检查:
- 是否引入了某个
spring-ai-starter-model-* - Spring Boot 版本和 Spring AI 版本是否匹配
application.yml是否使用了对应 Provider 的正确前缀
这时不要先改 Controller。
因为大概率不是 Controller 有问题,而是 Bean 根本没自动创建。
5.2 示例二:Structured Output 映射失败
java
public record UserIntent(String intent, String confidence) {}
UserIntent intent = chatClient.prompt()
.user("识别这句话的意图:帮我查一下订单状态")
.call()
.entity(UserIntent.class);
如果这里经常失败,你的排查方向应该是:
confidence是否应该是数值而不是字符串- 字段是否太抽象
- Prompt 是否明确要求固定字段输出
- 模型是否适合做结构化输出
5.3 示例三:RAG 效果差的排查顺序
不要上来就说"模型不行",先按这个顺序查:
- 检索出来的文档是不是相关
- 检索条数是否过多或过少
- 文档切分是否太大或太碎
- Prompt 是否真的把检索文档纳入上下文
- 最后再考虑换模型
这就是一个典型的"先查检索,再查生成"的例子。
6. 工程实践
6.1 推荐的排错顺序
真实项目里,建议固定按下面顺序排:
- 看异常类型
- 看配置和环境变量
- 看 Bean 注入和启动日志
- 跑最小聊天接口
- 再看 Structured Output / Tool / RAG
- 最后看性能、token、限流
6.2 建议保留的观测信息
每个核心接口至少保留:
- 请求 ID
- 会话 ID
- 模型名
- temperature
- token 用量
- 耗时
- 是否命中工具调用
- 检索到的文档数
- 异常摘要
这样你后面定位问题时,就不是"猜",而是"对照数据排"。
6.3 一个真实工程场景
场景:知识库助手回答总是不准。
错误做法:
- 直接换模型
- 直接加更多 Prompt
- 直接调高 topK
更好的做法:
- 先看检索文档是否相关
- 再看切分是否合理
- 再看 Prompt 是否明确要求"优先依据检索内容回答"
- 再看模型是否足够理解该任务
这说明排错时要先查"前置环节",而不是总盯着最后的模型输出。
6.4 初学者最值得养成的习惯
- 每做一个复杂能力,都先保留一个纯聊天对照接口
- 每做一个 RAG 能力,都保留"只看检索结果"的调试入口
- 每做一个 Tool Calling,都先单独测试工具方法本身
- 每做一个 Structured Output,都先从最简单对象开始
这会极大降低你后面排错的成本。
7. 常见误区
误区 1:只要结果不对,就先调 Prompt
不对。
如果系统根本没接通,调 Prompt 没意义。
误区 2:只要报 Bean 错误,就是 Spring 本身有问题
大多数时候不是。
更多是 starter、版本、配置条件没有满足。
误区 3:Structured Output 出错说明这个功能不能用
不对。
很多时候是 schema 设计过复杂,或者模型/Prompt 没对齐。
误区 4:Tool Calling 没触发说明 Spring AI 没有执行工具
不一定。
也可能是模型根本没有决定调用工具。
误区 5:RAG 回答差,一定是模型质量不够
不一定。
文档质量、切分策略、检索命中、上下文注入方式往往更关键。
误区 6:排错时所有能力一起看更快
恰恰相反。
越复杂越应该回到最小链路。
误区 7:日志以后再补
这是很危险的习惯。
没有日志和 metadata,很多 AI 问题根本没有证据可查。
8. 面试题
基础题
- Spring AI 项目中 Bean 注入失败通常先检查什么?
- 如何区分"接通问题"和"效果问题"?
- Structured Output 映射失败时,你会优先看哪些点?
- Tool Calling 不触发时,可能有哪些原因?
理解题
- 为什么排错要先退回最小聊天链路?
- RAG 效果差时,为什么不应该第一时间换模型?
- 如果输出不稳定,你会从 Prompt、Options、上下文、模型四个角度怎么排?
- 为什么日志、metadata、token usage 对排错特别重要?
进阶题
- 设计一套你自己的 Spring AI 排错 checklist,你会包含哪些层?
- 如果线上 AI 接口突然成本暴涨,你会如何定位是 Prompt 过长、RAG 注入过多,还是模型切换导致的?
9. 自测题
请你自己尝试回答:
- 你能否用"六层排错模型"解释 Spring AI 常见问题?
- 如果
ChatClient.Builder注入失败,你的前三步检查是什么? - 如果
.entity(...)经常失败,你会怎么缩小问题范围? - 如果工具没触发,你会如何区分是模型选择问题还是框架接入问题?
- 如果 RAG 回答偏离知识库,你会按什么顺序排?
- 如果响应很慢,你会优先看哪些观测指标?
- 你能否写出一套"从纯聊天到复杂链路逐步恢复"的排错步骤?
10. 学完标志
学完这一阶段,你应该能做到:
- 能按层次排查 Spring AI 常见问题
- 能区分"接通问题""效果问题""性能问题"
- 遇到 Bean、配置、模型、工具、RAG 相关问题时有固定排查路径
- 知道什么时候该退回最小链路
- 知道哪些日志和 metadata 必须保留
- 已具备基础到中级工程师水平的排错思路
第六阶段练习题
一、接通排查练习
请你故意制造下面任意一个问题:
- 写错 properties 前缀
- 写错模型名
- 不启动 Ollama
然后自己回答:
- 现象是什么?
- 它属于依赖层、配置层还是 Bean 层问题?
- 你是如何一步步确认问题位置的?
二、效果排查练习
做一个简单解释接口,然后分别尝试:
- 把 system prompt 写得非常模糊
- 把 temperature 调高
- 把 user prompt 写得过于含糊
然后比较输出差异,并总结:
- 哪些属于 Prompt 问题?
- 哪些属于 Options 问题?
- 哪些不是框架 bug,而是使用方式问题?
三、RAG / Tool 排查练习
请你任选一个方向:
方向 A:RAG
- 故意放入一条无关文档
- 测试回答效果
- 分析是检索问题还是生成问题
方向 B:Tool Calling
- 写一个"获取当前时间"的工具
- 分别测试"明确要求调用工具"和"模糊提问"两种 Prompt
- 观察工具触发情况
然后总结:
- 你如何判断是模型没有决定调用,还是工具没有正确接入?
第七阶段:面试与评估
1. 学什么
这一阶段的重点不再是"继续学新能力",而是把你前面学过的内容转化成:
- 能解释
- 能表达
- 能答题
- 能证明自己具备工程能力
重点包括:
- 面试官常问哪些 Spring AI 概念题、原理题、实战题
- 怎么回答"你做过 Spring AI 项目吗"
- 怎么把 Structured Output、Tool Calling、RAG 讲成工程实践,而不是名词堆砌
- 如何判断自己处于入门、初级还是中级水平
这一节的核心目标是:
把"我会用"升级成"我能让别人相信我会用"。
必须吃透:
- 面试回答不是背定义,而是讲"问题 - 方案 - 原理 - 权衡"
- 概念题和项目题的回答方式不同
- 中级面试的关键不是功能多,而是你能解释设计选择和排错路径
先知道,后深入:
- 系统设计级别的 AI 架构面试
- 线上容量规划与成本治理
- 复杂 Agent / 多模型路由设计
2. 为什么重要
很多人学完技术以后,会出现一种"隐性断层":
- 自己做的时候感觉懂
- 一到面试就说不清
- 面试官一追问为什么这样设计,就开始卡住
Spring AI 尤其容易这样,因为它涉及:
- 框架抽象
- 模型能力
- 工程实践
- RAG / Tool / Structured Output 等多种模式
如果你只是"记住 API",面试时很容易被追问垮:
- 为什么要用
ChatClient而不是直接 SDK? - Tool Calling 的安全边界是什么?
- RAG 项目里你怎么排错?
- Structured Output 为什么比直接解析文本更稳?
所以这一节的意义是:
帮你把知识点整理成"面试可输出的结构化表达"。
3. 核心概念
3.1 面试官通常在考什么
Spring AI 面试问题,通常不是只考"会不会写一个 Demo",而是会看这四类能力:
| 维度 | 面试官想知道什么 |
|---|---|
| 概念理解 | 你是否真的理解核心抽象 |
| 原理理解 | 你是否知道它为什么这么设计 |
| 工程能力 | 你是否能把它落到项目里 |
| 排错与权衡 | 你是否能处理真实问题,而不是只会 Happy Path |
3.2 概念题回答框架
回答概念题时,推荐你固定按下面顺序说:
- 它是什么
- 它解决什么问题
- 它和相近概念的区别
- 在工程里通常怎么用
例如回答 ChatClient:
- 它是 Spring AI 提供的上层 fluent API
- 用来更方便地组织 Prompt、模板参数、Advisor 和返回形式
- 它建立在
ChatModel之上,不是底层模型实现本身 - 业务层通常直接面向它编程
3.3 原理题回答框架
原理题不要只讲定义,要讲"设计动机"。
推荐结构:
- 先说现象
- 再说问题
- 再说 Spring AI 的设计方案
- 再说这种设计带来的好处和限制
例如回答"为什么 Spring AI 要做统一抽象":
- 因为不同模型厂商 SDK 差异很大
- 直接依赖厂商 SDK 会让业务代码耦合严重
- Spring AI 通过
ChatModel、EmbeddingModel等抽象统一公共能力 - 好处是业务层更稳定,缺点是不能完全抹平 Provider 差异
3.4 项目题回答框架
项目题最怕"讲功能清单"。
更好的回答结构是:
- 项目目标是什么
- 为什么选 Spring AI
- 核心架构怎么分层
- 关键能力怎么落地
- 遇到什么问题,怎么排
- 最终效果和权衡是什么
这比说"我们做了 RAG、Tool、Prompt"更像工程回答。
3.5 初级与中级的区别
| 层级 | 典型表现 |
|---|---|
| 入门 | 能跑通最小示例,知道核心概念名字 |
| 初级 | 能做普通聊天、Structured Output、最小 Tool / RAG |
| 中级 | 能解释原理、分层设计、做排错、讲清权衡与边界 |
所以中级并不是"你会的功能更多",而是:
你对机制、设计和工程问题的解释能力更强。
3.6 Spring AI 面试常考模块
你要重点准备下面这些模块:
- Spring AI 是什么,解决什么问题
ChatModel和ChatClient的区别- Prompt、Options、Response 的职责
- starter、properties、自动配置机制
- Structured Output 原理与适用场景
- Tool Calling 的执行闭环和安全边界
- RAG 的最小工程链路
- 常见问题与排错
- 为什么企业里要做日志、token、监控
4. 原理解释
4.1 面试官为什么喜欢追问 Spring AI 的"为什么"
因为 Spring AI 这个主题很容易让人只停留在表层:
- 会配依赖
- 会写接口
- 会调模型
但这不足以证明你真正掌握了它。
面试官真正想判断的是:
- 你是不是理解 Spring 风格抽象
- 你是不是知道统一接口背后的权衡
- 你是不是做过真实工程,而不是只复制了一个 Demo
所以当面试官追问"为什么"时,他不是在故意刁难你,而是在判断你有没有中级工程师的理解深度。
4.2 为什么概念题不能只背定义
因为定义太短,不足以证明你理解了使用场景和边界。
比如问:
"什么是 Tool Calling?"
如果你只答:
"Tool Calling 是调用工具的能力。"
这几乎没有信息量。
更好的回答应该是:
- Tool Calling 是让模型根据上下文决定是否请求调用外部工具的一种机制
- 模型不会直接执行工具,真正执行的是应用侧注册的工具逻辑
- 这样做能把模型推理能力和系统外部能力连接起来
- 但也必须控制权限、参数校验和幂等性
这就体现出你理解了:
- 概念
- 原理
- 边界
- 工程风险
4.3 为什么项目题最能拉开差距
因为概念可以背,项目细节很难背得像真的。
比如问你:
"你做过 RAG 吗?"
如果你只回答:
- 做过
- 就是把文档放进向量库再检索
这很弱。
如果你能回答:
- 我们把 RAG 拆成导入链路和问答链路
- 导入链路负责切分、embedding、入库
- 问答链路负责检索、Prompt 注入和回答生成
- 排错时我会先看检索结果是否相关,再看模型回答质量
这就非常像真实做过的人。
4.4 如何把一个功能讲成工程实践
无论是 Structured Output、Tool Calling 还是 RAG,你都可以固定套下面这套说法:
- 业务为什么需要它
- 技术上怎么实现
- 为什么不用更简单的方法
- 遇到什么问题
- 怎么排错和优化
例如 Structured Output:
- 业务需要把 AI 输出进入后续流程
- 纯文本难解析且不稳定
- 所以用
.entity(...)或转换器把输出映射成对象 - 实践中要防字段缺失和类型不匹配
- 需要做 schema 设计和业务校验兜底
这就是"工程表达"。
4.5 如何判断自己是不是达到中级
你可以用下面几个标准自检:
- 能否不用看文档,解释
ChatClient、Prompt、ChatOptions、ChatResponse - 能否讲清 starter、properties、自动配置主线
- 能否独立设计一个最小 Tool / RAG / Structured Output 模块
- 能否用层次化思路排错
- 能否把一个项目讲成"架构 + 权衡 + 问题 + 结果"
如果这些你都能做到,基本就已经接近中级水平了。
5. 示例
这一节的"示例"不是代码,而是面试表达示例。
5.1 示例一:回答"什么是 Spring AI"
一个较好的回答版本:
Spring AI 是一个用 Spring Boot 风格开发 AI 应用的框架。
它主要解决不同模型厂商接入方式不统一,以及 AI 应用缺乏工程化组织的问题。
在项目里我通常会用它来统一接聊天模型、结构化输出、工具调用和 RAG,而不是直接在业务代码里依赖某家模型 SDK。
这个回答好在:
- 说了是什么
- 说了解决什么问题
- 说了工程落点
5.2 示例二:回答"ChatClient 和 ChatModel 有什么区别"
一个较好的回答版本:
ChatModel更像 Spring AI 的底层统一模型接口,负责真正发起模型调用。
ChatClient是建立在它之上的上层 fluent API,更适合业务层组织 Prompt、模板参数、Advisor 和不同返回形式。所以实际项目里,底层能力统一依赖
ChatModel,但大多数业务代码会直接面向ChatClient编写。
5.3 示例三:回答"你做过 Spring AI 项目吗"
一个较好的项目型回答:
我做过一个知识库问答方向的 Spring AI Demo,并把它按工程方式拆成了模型接入、问答服务、RAG 导入链路和日志监控几个部分。
模型层我先用 Ollama 做本地开发,再预留 OpenAI 接入;问答链路上用
ChatClient统一组织 Prompt 和响应;知识库部分通过向量存储做最小 RAG;同时保留 token、耗时和检索文档数等观测信息。在这个过程中我比较关注分层和排错,比如 RAG 效果差时我会先看检索是否命中,再看 Prompt 和模型,而不是直接换模型。
这类回答比"我写过一个接口"强很多。
5.4 示例四:回答"RAG 怎么做、怎么排错"
一个较好的回答:
我会把 RAG 分成导入链路和问答链路。
导入链路负责文档切分、embedding 和向量入库;问答链路负责将用户问题向量化、检索相关文档,再把检索结果注入 Prompt 或 Advisor。
如果效果不好,我不会先怀疑模型,而是先看检索文档是否相关、切分是否合理、topK 和 Prompt 注入方式是否合适。
6. 工程实践
6.1 企业里通常怎么问 Spring AI
企业面试里,通常不会只问"API 会不会写",更常见的是:
- 你为什么选 Spring AI 而不是直接 SDK?
- 你做过哪些 AI 工程能力,而不只是聊天?
- 你怎么做 Structured Output / Tool Calling / RAG?
- 你怎么排错?
- 如果上线后成本高、效果差、工具乱调怎么办?
所以准备面试时,要优先准备"工程问题回答"。
6.2 推荐的答题顺序
当你被问一个技术点时,推荐顺序是:
- 定义
- 作用
- 原理
- 场景
- 边界 / 风险
例如回答 Tool Calling,就不要停在"可以调用工具",而要补上:
- 模型只发起调用请求
- 应用侧执行工具
- 要做权限和参数控制
6.3 一个真实面试场景
题目:
"如果让你做一个企业知识库助手,你会怎么设计?"
推荐回答骨架:
- 先定目标:是问答、摘要,还是流程辅助
- 模型接入:先用 Spring AI 统一抽象接 Provider
- 问答层:用
ChatClient组织 Prompt - 检索层:引入 embedding + vector store 做 RAG
- 工具层:对需要查实时数据的能力用 Tool Calling
- 监控层:记录 token、耗时、检索文档数、错误率
- 排错策略:把问题拆成接通、检索、生成、成本几层
这就比"我会用 Spring AI 做知识库问答"强得多。
6.4 企业最看重的不是"高级名词",而是稳定表达
很多候选人喜欢一上来讲:
- Agent
- MCP
- 多模型路由
- 推理链
但如果基础抽象和工程逻辑讲不稳,反而会减分。
更好的策略是:
- 先把主线讲清
- 再讲你掌握的进阶点
- 永远围绕"为什么这样设计"
7. 常见误区
误区 1:面试准备就是背定义
不够。
面试官更关心你是否理解设计动机、使用边界和工程问题。
误区 2:项目题只要说"做过"就行
远远不够。
你要能讲架构、链路、问题、排错和权衡。
误区 3:功能说得越多越厉害
不一定。
如果说不清原理和分层,反而会暴露理解浅。
误区 4:中级就是会更多高级名词
不对。
中级更看重的是解释能力、设计能力和排错能力。
误区 5:只准备 Happy Path
风险很大。
Spring AI 很容易被追问错误场景和边界问题。
误区 6:回答时只讲"怎么做",不讲"为什么这样做"
这会让回答显得很机械。
中级面试更看重设计因果关系。
8. 面试题
基础题
- Spring AI 是什么?它解决了什么问题?
ChatClient和ChatModel的区别是什么?Prompt、ChatOptions、ChatResponse各自负责什么?- 为什么 Spring AI 更适合企业应用,而不是直接在业务代码里依赖模型 SDK?
原理题
- Spring AI 为什么要做统一抽象,而不是直接透传各家 SDK?
- starter、properties、自动配置是怎么串起来的?
- 为什么 Prompt 要按 role 组织 message?
- Advisor 为什么适合承载 Memory、RAG、Tool Calling?
实战题
- 你会如何实现一个 Structured Output 接口?
- Tool Calling 的执行闭环是什么?它的安全边界在哪里?
- 你会如何设计一个最小 RAG 闭环?
- 如果一个 RAG 系统效果差,你会怎么排错?
项目题
- 你做过 Spring AI 项目吗?请介绍一下架构和关键能力。
- 如果让你做一个企业知识库助手,你会怎么设计?
- 如果线上模型成本突然升高、回答质量下降,你会怎么定位?
9. 自测题
请你自己尝试回答:
- 你能否用 1 分钟清楚解释什么是 Spring AI?
- 你能否用 3 分钟讲清一次 Spring AI 调用链?
- 你能否解释 Structured Output、Tool Calling、RAG 三者分别解决什么问题?
- 你能否把一个项目讲成"目标 -> 架构 -> 关键能力 -> 问题 -> 优化"?
- 你能否回答"为什么不用直接 SDK,而选 Spring AI"?
- 你能否说出自己目前最弱的是概念、原理、实践还是排错?
- 你能否判断自己现在更像入门、初级还是中级?
10. 学完标志
学完这一阶段,你应该能做到:
- 能稳定回答 Spring AI 的高频概念题和原理题
- 能把 Structured Output、Tool Calling、RAG 讲成工程实践
- 能把一个项目讲出分层、链路、问题和权衡
- 能区分入门、初级、中级的能力边界
- 已具备基础到中级面试的表达框架
第七阶段练习题
一、1 分钟表达练习
请你练习用 1 分钟回答:
text
什么是 Spring AI?它解决了什么问题?
要求:
- 不超过 3 句话
- 必须包含"框架定位"和"工程价值"
二、3 分钟项目表达练习
请你假设自己做过一个"Spring AI 知识库助手"项目,并按下面结构讲一遍:
- 项目目标
- 技术选型
- 核心架构
- 关键能力
- 一个实际问题与排查过程
三、模拟问答练习
请你自己回答下面 3 个问题:
- 为什么
ChatClient比直接 SDK 更适合业务层? - Tool Calling 的安全边界在哪里?
- 如果 RAG 效果差,你第一步先查什么?
然后对照检查:
- 你有没有讲"为什么"
- 你有没有讲"工程边界"
- 你有没有讲"排错思路"
回复 继续,我下一阶段按同样的 Markdown 结构,带你进入:
第八阶段:学习总结与知识闭环
1. 学什么
这一阶段不再新增核心功能,而是做三件更重要的事:
- 把整门技术的主干再收束一遍
- 把正确学习顺序再固定一遍
- 把"知识"真正转化成"能力"
这一节的目标是让你形成最终闭环:
能理解、能解释、能上手、能排错、能通过基础到中级面试。
这一阶段重点包括:
- Spring AI 的整体知识主干
- 正确学习顺序回顾
- 必须掌握与知道即可的再划分
- 企业里通常怎么真正使用它
- 下一步继续深入应该学什么
必须吃透:
- 学习主线一定是"聊天主线 -> 工程主线 -> 增强能力 -> 排错 -> 面试表达"
- 真正掌握不是"会调接口",而是"会分层、会取舍、会定位问题"
先知道,后深入:
- 多 Agent / 工作流编排
- MCP 的深度生态使用
- 复杂评测体系与 LLM-as-a-Judge
- 生产级多模型路由与成本治理
2. 为什么重要
很多人学技术时,前面学得很多,最后却没有形成能力闭环。
典型表现是:
- 看过很多概念
- 写过一些 Demo
- 真做项目还是发散
- 一到面试表达就碎片化
所以最后这一阶段真正解决的问题是:
问题 1:知识没有主干
学过很多点,但彼此之间没连起来。
问题 2:没有优先级
不知道哪些必须掌握,哪些只是了解即可。
问题 3:不会迁移
学过的知识没法转成项目能力和面试表达能力。
这一节的意义,就是把前面七个阶段压缩成一条真正能落地的主线。
3. 核心概念
3.1 Spring AI 的知识主干
到这里,你应该把 Spring AI 总结成下面这条主线:
text
模型接入
↓
ChatModel / ChatClient
↓
Prompt / ChatOptions / ChatResponse
↓
Structured Output / Tool Calling / RAG / Memory
↓
日志 / metadata / 排错 / 监控
↓
工程分层 / 面试表达 / 项目落地
这条主线里,前半部分是"调用模型",后半部分是"把模型变成工程能力"。
3.2 正确学习顺序再回顾
推荐你最终记住的顺序是:
- 先理解 Spring AI 的定位
- 再学
ChatModel、ChatClient、Prompt - 再学 starter、自动配置、properties
- 再学 Structured Output、Tool Calling、RAG
- 再学排错、监控和成本意识
- 最后训练项目表达和面试回答
这个顺序的本质是:
先学主干,再学增强;先学确定性,再学复杂性。
3.3 必须掌握 vs 知道即可
| 类型 | 内容 |
|---|---|
| 必须掌握 | ChatClient、ChatModel、Prompt、ChatOptions、ChatResponse |
| 必须掌握 | starter、properties、自动配置主线 |
| 必须掌握 | Structured Output、Tool Calling、RAG 的最小闭环 |
| 必须掌握 | 分层设计、日志、token、排错方法 |
| 必须掌握 | 面试中的概念解释、原理解释、项目表达 |
| 知道即可 | 多 Agent、高级工作流编排 |
| 知道即可 | 复杂 rerank、query rewrite、评测平台 |
| 知道即可 | 深层源码细节、所有 Provider 差异 |
| 知道即可 | 复杂 MCP 生态和生产级多模型路由 |
3.4 企业里通常怎么用它
企业里更常见的落地方式,不是"做一个会聊天的页面",而是把 Spring AI 接进已有业务流程。
典型用法包括:
- 智能问答
- 结构化信息抽取
- 内部知识库助手
- 工单/订单/库存查询助手
- AI 辅助表单生成与数据整理
- 研发辅助工具
所以企业真正看重的是:
- 可接入
- 可治理
- 可监控
- 可排错
- 可维护
3.5 学习本质
学 Spring AI 的本质,不是学"某家模型怎么调",而是学会:
- 如何用 Spring 风格组织 AI 能力
- 如何把不稳定的大模型能力纳入稳定工程体系
- 如何把 Prompt、Tool、RAG、监控、排错串成完整应用
4. 原理解释
4.1 为什么正确顺序能大幅降低学习成本
如果你先学复杂 Agent,再回头补 ChatClient、Prompt、自动配置,你会一直觉得知识点很多、很碎。
但如果顺序正确:
- 先学最小聊天链路
- 再学框架机制
- 再学增强能力
- 再学排错和表达
你会发现后面很多能力只是"主干上的插件",而不是全新体系。
4.2 为什么必须区分"必须掌握"和"知道即可"
因为技术学习最容易出现两个极端:
- 只会最浅的部分,做不了项目
- 一头扎进太深的高级主题,反而主干没掌握
所以正确策略是:
- 先把 80% 最常用、最有实战价值的内容吃透
- 再根据业务场景选修更深主题
这也是工程学习里很重要的"主干优先"原则。
4.3 什么叫真正形成知识闭环
真正闭环至少要满足下面五个条件:
- 你能解释核心概念
- 你能写最小示例
- 你能做一个基础项目
- 你能对常见问题排错
- 你能在面试中稳定表达
缺少其中任何一项,都不算真正掌握。
4.4 下一步继续深入应该学什么
当你把当前笔记吃透后,推荐按这个顺序继续深入:
-
生产级 RAG
包括 chunk 策略、metadata filter、rerank、query rewrite
-
观测与评测
包括 token 成本、trace、LLM-as-a-Judge、质量评测
-
MCP
包括 MCP Client / Server、工具与资源标准化接入
-
源码阅读
重点看
ChatClient、Prompt、Advisor、Provider 自动配置 -
生产治理
包括限流、降级、重试、多模型选择、成本控制
4.5 如何把它真正转化成自己的能力
最有效的方法不是继续看更多文章,而是做下面这三件事:
- 自己手写一个最小项目
- 自己做一次从接入到排错的完整练习
- 自己录音或口述回答高频面试题
因为只有把"理解"转成"输出",知识才真正稳定。
5. 示例
5.1 终局版 30 秒总结
你最终应该能这样总结 Spring AI:
Spring AI 是一个用 Spring Boot 方式开发 AI 应用的框架,它把聊天模型、结构化输出、工具调用、RAG 和可观测性这些能力整合进统一抽象里。
它的核心价值不只是"能调模型",而是帮助 Java 团队把 AI 能力工程化、可维护、可监控地接入业务。
真正掌握它的标志,不是会写一个 Demo,而是能分层设计、能排错、能讲清为什么这样设计。
5.2 一个完整学习闭环示例
text
看懂学习地图
↓
跑通最小聊天接口
↓
理解 ChatClient / Prompt / Response
↓
做 Structured Output
↓
做 Tool Calling
↓
做最小 RAG
↓
练习排错
↓
练习面试表达
↓
完成一个小项目
这个路径就是最值得重复复习的"闭环路径"。
5.3 一个真实工程场景
场景:你要做一个企业知识助手。
你真正会经历的是:
- 先接模型
- 再设计 Prompt 与返回结构
- 再决定是否需要 Tool 或 RAG
- 再加日志、token、监控
- 再处理错误场景
- 最后才是持续优化效果和成本
这正是这份学习路径想训练你的能力。
6. 工程实践
6.1 建议的最终学习落地动作
如果你想把这份笔记真正学完,建议你按下面顺序落地:
- 手写一个最小聊天接口
- 再手写一个 Structured Output 接口
- 再手写一个 Tool Calling 接口
- 再手写一个最小 RAG Demo
- 给这几个接口都补日志和基本排错信息
- 最后自己讲一遍项目架构
6.2 建议的复习方式
不要从头到尾机械重看。
更好的复习顺序是:
- 先看学习地图
- 再看第二、三阶段主干
- 再看第五、六阶段工程实践与排错
- 最后看第七阶段面试表达
这样复习效率更高。
6.3 你现在最该做的不是继续扩知识面
而是先用当前这份笔记完成一个最小项目。
因为对于大多数初学者来说,最大的短板不是"知道太少",而是"闭环太少"。
7. 常见误区
误区 1:学完很多概念就等于掌握了
不对。
真正掌握一定包含代码实践、排错和表达能力。
误区 2:继续看更多资料比做项目更重要
通常不是。
在主干已经明确之后,项目实践更能暴露真实短板。
误区 3:只有学高级主题才算进步
不一定。
把基础主干、工程分层和排错真正吃透,往往比追高级概念更有价值。
误区 4:面试准备和项目实践是两件分开的事
其实不是。
最好的面试素材往往就来自你真实做过、排过、优化过的项目。
误区 5:知识闭环就是把笔记看完
不是。
闭环的标志是"你能输出、能实现、能复盘、能改进"。
8. 面试题
- 如果让你用一句话概括 Spring AI 的核心价值,你会怎么说?
- Spring AI 的学习主干应该怎么排顺序?为什么?
- 为什么说真正掌握 Spring AI 不等于会写一个聊天接口?
- 企业里通常怎么把 Spring AI 接入实际业务?
- 你认为初级和中级 Spring AI 工程师的差别主要体现在哪里?
9. 自测题
- 你能否画出 Spring AI 的完整知识主干?
- 你能否说出哪些是必须掌握,哪些是知道即可?
- 你现在最薄弱的是接入、原理、工程实践、排错还是表达?
- 如果明天让你做一个最小项目,你会按什么顺序开始?
- 如果两周后要面试,你会优先复习哪几部分?
10. 学完标志
学完这一阶段,你应该能做到:
- 能把整门 Spring AI 的知识主干压缩成一条清晰主线
- 知道正确学习顺序和继续深入路径
- 能区分哪些必须掌握,哪些暂时知道即可
- 能把当前所学转成项目实践和面试表达
- 已形成完整的学习闭环
Spring AI 学习评估体系
1. 入门达标标准
满足下面大部分条件,说明你达到入门:
- 能解释 Spring AI 是什么,不是什么
- 能说出
ChatClient、ChatModel、Prompt、ChatResponse的基本职责 - 能跑通一个最小聊天接口
- 知道 starter、配置文件、模型接入的基本关系
- 知道 Structured Output、Tool Calling、RAG 分别是什么
2. 初级达标标准
满足下面大部分条件,说明你达到初级:
- 能独立接入一个本地或云端模型
- 能写出一个 Structured Output 接口
- 能做一个最小 Tool Calling 示例
- 能做一个最小 RAG 闭环
- 能按分层思路组织项目目录
- 能排查常见的配置错误、Bean 注入错误、Prompt 问题
- 能稳定回答基础面试题
3. 中级达标标准
满足下面大部分条件,说明你接近中级:
- 能解释 Spring AI 的核心抽象和设计动机
- 能讲清 starter、properties、自动配置主线
- 能说明 Structured Output、Tool Calling、RAG 的适用边界和设计权衡
- 能搭出一个带日志与基本监控的 AI 功能模块
- 能按层排查问题,而不是到处猜
- 能把项目讲成"目标 - 架构 - 能力 - 问题 - 优化 - 权衡"
- 能在面试里回答基础到中级问题
4. 一套自测题
基础自测
- Spring AI 的定位是什么?
ChatClient和ChatModel的区别是什么?Prompt为什么不是简单字符串?ChatResponse为什么比纯文本更有工程价值?
进阶自测
- starter、properties、自动配置是怎么串起来的?
- Structured Output 适合解决什么问题?
- Tool Calling 的执行闭环是什么?
- RAG 的最小链路包括哪些步骤?
工程自测
- 如果 Bean 注入失败,你先查什么?
- 如果 RAG 效果差,你先查什么?
- 如果输出不稳定,你先看 Prompt、Options 还是模型?为什么?
- 如果要做一个企业知识库助手,你会怎么分层?
5. 一套面试题
概念题
- Spring AI 是什么?解决什么问题?
ChatClient、ChatModel、Prompt、ChatOptions、ChatResponse各自职责是什么?- 为什么 Spring AI 比直接依赖厂商 SDK 更适合企业项目?
原理题
- Spring AI 为什么要做统一抽象?
- starter、properties、autoconfiguration 是如何协同工作的?
- 为什么 Prompt 要按 role 组织 message?
- Advisor 为什么适合承载 RAG 和 Memory?
实战题
- 你会如何实现一个 Structured Output 接口?
- Tool Calling 的安全边界在哪里?
- 你会如何做一个最小 RAG 闭环?
- 如果线上输出质量波动,你会如何排查?
项目题
- 请介绍一个你做过的 Spring AI 项目或 Demo
- 如果让你做一个企业知识库助手,你的技术方案是什么?
- 如果系统成本升高、响应变慢,你如何定位问题?
6. 一套实战任务
按顺序完成下面 4 个任务:
任务 1:最小聊天接口
实现:
text
GET /ai/ask?q=...
要求:
- 能正常调用模型
- 可切换本地 Ollama 或 OpenAI
任务 2:结构化输出接口
实现:
text
GET /ai/summary?topic=...
要求:
- 返回 Java 对象
- 至少包含
definition / analogy / example
任务 3:工具调用接口
实现:
text
GET /ai/time
要求:
- 通过工具返回当前时间
- 能解释工具执行闭环
任务 4:最小 RAG 问答
实现:
text
GET /ai/qa?q=...
要求:
- 预先导入知识文本到向量库
- 接口能基于检索结果回答
- 能观察检索效果
7. 一套项目任务
做一个完整小项目:
项目名称
Spring AI 企业知识助手
项目目标
- 回答 Spring AI / Java / 内部文档相关问题
- 对部分问题返回结构化结果
- 对实时查询类问题支持工具调用
最小功能要求
- 普通问答
- Structured Output
- 一个最小工具
- 一个最小 RAG
- 基本日志与 token 观测
建议模块
controllerserviceconfigtoolragingestionmodel
验收标准
- 能跑通
- 能解释架构
- 能演示工具调用
- 能演示 RAG
- 能说明一个你自己遇到过的排错问题
8. 对回答的评分标准
建议按 100 分评分:
| 维度 | 分值 | 评估点 |
|---|---|---|
| 概念理解 | 20 | 是否真的理解核心概念和边界 |
| 原理理解 | 20 | 是否能解释为什么这样设计 |
| 工程实践 | 20 | 是否能把能力落到代码和项目结构 |
| 排错能力 | 20 | 是否有分层排错思路 |
| 表达能力 | 20 | 是否能清楚、稳定地讲出来 |
可以按下面标准判断:
90-100:接近中级,能讲原理、做项目、会排错75-89:达到初级,主线清晰,能独立做基础功能60-74:达到入门,概念知道但实践和表达还不稳<60:说明主干还没有打牢,需要回到基础阶段补课
9. 如何根据错误结果反向补课
如果你在自测或面试中暴露出问题,可以按下面方式回补:
| 暴露问题 | 说明短板 | 回补阶段 |
|---|---|---|
| 说不清 Spring AI 是什么 | 地图和定位不清 | 第一阶段 |
分不清 ChatClient / ChatModel / Prompt |
核心抽象不牢 | 第二阶段 |
| 说不清 starter 和自动配置 | 框架机制不清 | 第三阶段 |
| 说不清 Advisor / metadata / 模板渲染 | 底层设计不清 | 第四阶段 |
| 不会做 Structured Output / Tool / RAG | 工程落地能力不足 | 第五阶段 |
| 遇到问题只会乱试 | 排错方法缺失 | 第六阶段 |
| 面试时回答发散、没有结构 | 表达框架不足 | 第七阶段 |
再进一步,你可以这样修复:
- 先回到对应阶段重读主干
- 立刻做一个最小代码练习
- 再用口头方式复述一遍
- 最后重新做同类自测题
这套"学 -> 写 -> 讲 -> 再测"的循环,是最快的补课方式。
到这里,这份 springai.md 已经完成了从:
- 学习地图
- 推荐路径
- 八个阶段教学
- 每阶段练习题
- 最终评估体系
的一整套闭环内容。