摘要:ChatClient 怎么用、多模型怎么配、上下文怎么记、怎么让 AI 调你的代码——Spring AI 2.0 官方能力一条线串起来,看完就能动手


上一篇文章我写 Spring AI 2.0 入门,有人可能问:

“跑通了,但只会 chatClient.prompt().call() 这一招。多模型怎么配?对话怎么记住上下文?让 AI 调我的业务方法怎么搞?能不能串起来讲一遍?”

能。

这篇基于 Spring AI 官方文档(spring.io/projects/spring-ai,当前版本 2.0.0)重新梳理,把 ChatClient、多模型、记忆、Function Calling 串成一条线。看完你就能从"会 demo"到"能干活"。


先对齐认知:Spring AI 到底能干什么

官方文档的说法:Spring AI 提供跨模型提供商的 Portable API(可移植 API),同步、流式都支持。

大白话:不管你接 OpenAI 还是 Ollama,代码写法一模一样。它还内置了:

| 官方能力

|

是干什么的

Chat Completion

|

对话补全,最常用的聊天/生成

| |

Embedding

|

文本向量化,做相似度检索

| |

Text to Image

|

文生图

| |

Audio Transcription

|

语音转文字

| |

Text to Speech

|

文字转语音

| |

Moderation

|

内容审核

| |

Structured Outputs

|

AI 输出直接映射成 POJO

| |

Function Calling

|

让 AI 调用你写的 Java 方法

| |

Vector Database

|

向量库,RAG 的地基

| |

Chat Memory

|

对话记忆,多轮上下文

| |

MCP

|

让 AI 通过标准协议用外部工具

| |

Observability

|

观测、追踪、指标

|

模型提供商原生支持 OpenAI、Anthropic、Amazon Bedrock、Mistral、Ollama 等。DeepSeek、通义千问这类国内模型没有官方 starter,但大多有 OpenAI 兼容接口,配 base-url 就能接。


准备工作

三样东西:

1. JDK 17+

<span leaf="">java -version</span><span leaf=""><br></span>

2. Spring Boot 3.4+(Spring AI 2.0 官方基于 3.4.2 做依赖管理)

3. 一个模型

  • 想省事:注册 OpenAI,拿 API Key

  • 想免费:装 Ollama 本地跑(后面有示例)


第一步:建项目,依赖用 BOM 管

打开 start.spring.io,选 Spring Web + Spring AI 的 starter。

关键点:Spring AI 发布节奏比 Spring Boot 快,官方推荐用 BOM 统一管版本。 不然依赖版本对不上,启动报错查半天。

pom.xml

<span leaf="">&lt;!-- 1. BOM 统一管理 Spring AI 版本 --&gt;</span><span leaf=""><br></span><span leaf="">&lt;dependencyManagement&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &lt;dependencies&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &lt;dependency&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;artifactId&gt;spring-ai-bom&lt;/artifactId&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;version&gt;2.0.0&lt;/version&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;type&gt;pom&lt;/type&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;scope&gt;import&lt;/scope&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &lt;/dependency&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &lt;/dependencies&gt;</span><span leaf=""><br></span><span leaf="">&lt;/dependencyManagement&gt;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&lt;!-- 2. OpenAI starter,版本交给 BOM --&gt;</span><span leaf=""><br></span><span leaf="">&lt;dependency&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &lt;artifactId&gt;spring-ai-starter-model-openai&lt;/artifactId&gt;</span><span leaf=""><br></span><span leaf="">&lt;/dependency&gt;</span><span leaf=""><br></span>

用 Ollama 就把 starter 换成 spring-ai-starter-model-ollama


第二步:配置 application.yml

<span leaf="">spring:</span><span leaf=""><br></span><span leaf="">&nbsp; application:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; name: spring-ai-demo</span><span leaf=""><br></span><span leaf="">&nbsp; ai:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; openai:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; api-key: ${OPENAI_API_KEY} &nbsp; &nbsp; &nbsp;# Key 走环境变量,别写死在代码里</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; chat:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; options:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; model: gpt-4o-mini</span><span leaf=""><br></span>

国内网络走中转的话加 base-url:

<span leaf="">spring:</span><span leaf=""><br></span><span leaf="">&nbsp; ai:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; openai:</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; base-url: https://你的中转地址/v1</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; api-key: ${OPENAI_API_KEY}</span><span leaf=""><br></span>

第三步:ChatClient——一切从这开始

ChatClient 是 Spring AI 2.0 的门面,官方定位是"Fluent API,惯用法类似 WebClient"。

ChatController.java

<span leaf="">@RestController</span><span leaf=""><br></span><span leaf="">public class ChatController {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; private final ChatClient chatClient;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; // Spring 自动注入 Builder,build 一下就有</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatController(ChatClient.Builder builder) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; this.chatClient = builder.build();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; // GET /chat?msg=你好</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; @GetMapping("/chat")</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public String chat(@RequestParam String msg) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; return chatClient.prompt()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .user(msg)</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .call()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .content();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

10 行代码,一个 AI 接口。 没有 HttpClient、没有 JSON 解析、没有重试逻辑。

ChatClient 的几个核心返回方式,官方文档里列得很清楚:

| 调用

|

返回

|

适用场景

call().content()

String

|

直接要文本

| | call().entity(Class) |

对象

|

结构化输出

| | call().entity(ParameterizedTypeReference) |

泛型对象

|

返回 List 等集合

| | call().chatResponse() |

ChatResponse

|

要 token 数等元数据

| | stream().content() |

Flux

|

流式打字机效果

|


进阶一:结构化输出——AI 返回直接变对象

让 AI 提取新闻的标题、地点、日期:

News.java

<span leaf="">public record News(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; String title,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; String location,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; String date</span><span leaf=""><br></span><span leaf="">) {}</span><span leaf=""><br></span>

Controller

<span leaf="">@GetMapping("/extract")</span><span leaf=""><br></span><span leaf="">public News extract(@RequestParam String text) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; return chatClient.prompt()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .user("从新闻中提取:标题、地点、日期。\n" + text)</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .call()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .entity(News.class); &nbsp; &nbsp;// 一行转对象</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

要返回集合类型(比如一批新闻),用 ParameterizedTypeReference

<span leaf="">@GetMapping("/extractAll")</span><span leaf=""><br></span><span leaf="">public List&lt;News&gt; extractAll(@RequestParam String text) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; return chatClient.prompt()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .user("提取所有新闻,返回列表。\n" + text)</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .call()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .entity(new ParameterizedTypeReference&lt;List&lt;News&gt;&gt;() {});</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

.entity() 这行,官方 Structured Outputs 能力帮你把模型输出映射成 POJO,不用手写 JSON 解析。


进阶二:多模型配置——不同任务用不同模型

官方文档专门有一节 Working with Multiple Chat Models,覆盖这几个真实场景:

场景 1:按任务分模型。 简单问答走便宜快的模型,复杂推理走贵的模型。

<span leaf="">@Configuration</span><span leaf=""><br></span><span leaf="">public class ChatModelsConfig {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; @Bean</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatClient fastChatClient(ChatClient.Builder builder) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; // 便宜模型:gpt-4o-mini</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; return builder.build();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; @Bean</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatClient smartChatClient(ChatClient.Builder builder) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; // 贵模型:gpt-4o</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; return builder</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .defaultOptions(ChatOptions.builder()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .model("gpt-4o")</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build())</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

Controller 里注入两个 Bean,按业务选:

<span leaf="">@RestController</span><span leaf=""><br></span><span leaf="">public class ChatController {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; private final ChatClient fast;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; private final ChatClient smart;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatController(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Qualifier("fastChatClient") ChatClient fast,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @Qualifier("smartChatClient") ChatClient smart) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; this.fast = fast;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; this.smart = smart;</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

场景 2:兜底(fallback)。 主模型挂了自动切备用模型,官方示例里有 FallbackModelAccessor

场景 3:A/B 测试。 两个模型对比效果,再决定上线哪个。

场景 4:用户自己选。 前端下拉框选模型,后端用对应的 ChatClient 处理。

场景 5:组合专业模型。 翻译用一个模型、总结用一个模型,各干各的强项。

核心思想:一个项目可以有多个 ChatClient Bean,按场景注入。


进阶三:Chat Memory——让 AI 记住上下文

默认每次调用都"失忆"。要记住多轮对话,官方提供 Chat Memory 机制,用 advisor 挂上去:

<span leaf="">@RestController</span><span leaf=""><br></span><span leaf="">public class ChatController {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; private final ChatClient chatClient;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatController(ChatClient.Builder builder) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; this.chatClient = builder</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .defaultSystem("你是一个耐心的 Java 老师")</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .defaultAdvisors(new MessageChatMemoryAdvisor(</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 最多记 10 条历史消息</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MessageWindowChatMemory.builder()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .maxMessages(10)</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build()))</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; @GetMapping("/chat")</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public String chat(@RequestParam String msg,</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;@RequestParam String userId) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; return chatClient.prompt()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .user(msg)</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 会话 ID:同一 userId 共享上下文</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .advisors(a -&gt; a.param(ChatMemory.CONVERSATION_ID, userId))</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .call()</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .content();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

几个要点(都核对过官方文档):

  • 会话 ID 的 key 是 ChatMemory.CONVERSATION_ID

    ——旧版叫 CHAT_MEMORY_CONVERSATION_ID_KEY,2.0 简化了

  • 记忆窗口

    用 MessageWindowChatMemory.builder().maxMessages(10) 控制,别让上下文无限膨胀

  • 记忆存储

    官方支持 6 种:In-Memory、JDBC(MySQL/PostgreSQL 等)、Cassandra、Neo4j、MongoDB、Redis。生产环境用 JDBC 或 Redis,别用默认的 In-Memory(重启就丢)


进阶四:Function Calling——让 AI 调用你的 Java 方法

这是把 AI 从"聊天机器人"变成"能干活的应用"的关键。

场景: 用户问"上海明天天气怎么样",AI 自己不知道天气,但它可以调用你的 getWeather() 方法。

官方 2.0 的写法: 注册工具时用 ToolCallback API(官方升级说明里明确:从 FunctionCallback 迁移到 ToolCallback)。

<span leaf="">@Component</span><span leaf=""><br></span><span leaf="">public class WeatherTools {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; @Tool(description = "查询指定城市指定日期的天气")</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public String getWeather(String city, String date) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; // 这里调用真实的天气服务</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; return city + " " + date + ":晴,26°C";</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

ChatClient 挂上工具:

<span leaf="">// 把工具类注册进去</span><span leaf=""><br></span><span leaf="">this.chatClient = builder</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; .defaultTools(new ObjectProvider&lt;WeatherTools&gt;() {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 简化示意:直接 new 或从容器拿</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; }.getIfAvailable())</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; .build();</span><span leaf=""><br></span>

更常见的是直接注入工具 Bean:

<span leaf="">@RestController</span><span leaf=""><br></span><span leaf="">public class ChatController {</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; private final ChatClient chatClient;</span><span leaf=""><br></span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; public ChatController(ChatClient.Builder builder, WeatherTools weatherTools) {</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; this.chatClient = builder</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .defaultTools(weatherTools) &nbsp; // 注册工具</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .build();</span><span leaf=""><br></span><span leaf="">&nbsp; &nbsp; }</span><span leaf=""><br></span><span leaf="">}</span><span leaf=""><br></span>

流程是这样的:

<span leaf="">用户:上海明天天气怎么样?</span><span leaf=""><br></span><span leaf="">&nbsp; ↓</span><span leaf=""><br></span><span leaf="">模型:我需要调用 getWeather(city=上海, date=明天)</span><span leaf=""><br></span><span leaf="">&nbsp; ↓</span><span leaf=""><br></span><span leaf="">Spring AI 调你的 Java 方法,拿到结果</span><span leaf=""><br></span><span leaf="">&nbsp; ↓</span><span leaf=""><br></span><span leaf="">模型:根据结果组织回答:"上海明天晴,26°C"</span><span leaf=""><br></span>

你写的是普通 Java 方法,AI 帮你调度它。 查数据库、调内部 API、算个价格,都行。


生产方向:官方还给了这几张牌

可观测性(Observability): 官方内置,跟 Micrometer 打通。请求耗时、token 消耗、错误原因都能上监控面板。

MCP(Model Context Protocol): 官方文档有专门章节。让 AI 通过标准协议调用外部工具和数据源,可以理解成"AI 世界的 USB 接口"。

RAG(检索增强生成): 官方支持向量库 + ETL 框架。让 AI 基于你的私有文档回答,而不是瞎编。

这三块每一块都够单独写一篇,先知道有这些能力,用到了再深入。


踩坑记录

坑 1:版本对不上,启动报错

Spring AI 依赖不指定版本,或者跟 Spring Boot 版本不匹配,启动直接红。

用 BOM 管版本,别一个个手写版本号。

坑 2:流式输出报错

<span leaf=""># 报错:streaming not supported</span><span leaf=""><br></span>

官方实现说明:流式(stream)只在响应式栈(WebFlux)下支持,同步调用走 Servlet 栈。想用流式,项目要用 WebFlux。

坑 3:ChatClient 注入失败

No qualifying bean of type ChatClient

确认:pom 里有 starter、BOM 版本对、Spring Boot >= 3.4。

坑 4:401 认证失败

<span leaf="">export OPENAI_API_KEY=sk-xxxx</span><span leaf=""><br></span><span leaf="">mvn spring-boot:run</span><span leaf=""><br></span>

Key 用环境变量传,确认启动时真的传了。

坑 5:国内访问 OpenAI 超时

配 base-url 走中转,或换 DeepSeek / 通义千问(OpenAI 兼容接口)/ Ollama。


总结

| 需求

|

官方方案

接大模型

|

ChatClient,一行调用

| |

返回结构化数据

| .entity(Class) | |

流式打字机

| stream()

(需 WebFlux)

| |

不同任务不同模型

|

多个 ChatClient Bean

| |

多轮对话记忆

|

ChatMemory + advisor

| |

让 AI 调你的方法

|

ToolCallback / @Tool

| |

生产观测

|

Observability

| |

接外部工具

|

MCP

|

Spring AI 2.0 的核心价值:一套 API 接所有模型,官方把结构化输出、记忆、工具调用、可观测性全给你备齐了。 你只需要写业务代码,剩下的框架管。