在前面“Chat Models”小节内容中我们在与各个模型进行对话时使用的是对应模型的ChatModel对象例如DeepSeekChatModel、OllamaChatModel。然后通过chatModel.call(...)、chatModel.stream(...)直接调用模型这其中我们需要手动构造Prompt、处理流响应例如将返回封装成对象、从模型响应中获取回复内容、拼装调用链RAG需要自己构建流程等操作。以上使用ChatModel与模型进行对话方式中如果在项目中使用大模型涉及到记忆上下文、Prompt模版化、RAG开发、返回内容映射为实体等操作时单纯的ChatModel代码量很多维护成本高为了简化这个流程Spring AI 中提供了ChatClient对象该对象可以看做一个更高级的“客户端 API”建立在ChatModel之上可以用链式的方式快速搭配 Prompt、系统设定、变量替换、上下文记忆等并支持文本/JSON/实体对象等多种形式的输出。ChatModel和Chat Client对象对比如下特别注意ChatClient 目前只支持 Chat对话模型不包括 Embedding、Image、Audio 等多模态模型要使用 Embedding、Image 或 Audio 等模型需要直接使用 Spring AI 提供的对应 API比如 EmbeddingModel、ImageModel、AudioModel。使用ChatClient下面以使用DeepSeek为例来演示Spring AI中如何使用ChatClient。涉及ChatClient创建、设置提示词、流式回复、回复映射到对象操作。1) 创建SpringBoot项目命名为“SpringAIChatClient2) 配置项目pom.xml?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.5.0/version relativePath/ !-- lookup parent from repository -- /parent groupIdcom.example/groupId artifactIdSpringAIChatClient/artifactId version0.0.1-SNAPSHOT/version nameSpringAIChatClient/name descriptionSpringAIChatClient/description properties java.version17/java.version /properties !-- 导入 Spring AI BOM用于统一管理 Spring AI 依赖的版本 引用每个 Spring AI 模块时不用再写 version只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -- dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0-SNAPSHOT/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-deepseek/artifactId /dependency /dependencies !-- 声明仓库 用于获取 Spring AI 以及相关预发布版本-- repositories repository idspring-snapshots/id nameSpring Snapshots/name urlhttps://repo.spring.io/snapshot/url releases enabledfalse/enabled /releases /repository repository nameCentral Portal Snapshots/name idcentral-portal-snapshots/id urlhttps://central.sonatype.com/repository/maven-snapshots//url releases enabledfalse/enabled /releases snapshots enabledtrue/enabled /snapshots /repository /repositories /projectpackage com.example.springaichatclient.controller; import com.example.springaichatclient.pojo.Student; import jakarta.servlet.http.HttpServletResponse; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.core.ParameterizedTypeReference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import java.util.List; import java.util.Map; RestController RequestMapping(/ai) public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { // 在 controller 构造函数中直接建立 ChatClient // 并设置默认 system 提示词 this.chatClient builder .defaultSystem(你是一个聊天助手名字叫小智。) .build(); } //使用文本响应用户问题、设置Prompt提示词 GetMapping(/chat) public MapString, String chatText(RequestParam(message) String message) { String reply chatClient.prompt(如果用户让你讲故事只能讲解神话故事不能讲其他的。) .user(message) .call() .content(); return Map.of(reply, reply); } GetMapping(/response) public ChatResponse chatResponse(RequestParam(message) String message) { return chatClient.prompt() .user(message) .call() .chatResponse(); } GetMapping(/getOneStudent) public Student getOneEntity() { return chatClient.prompt() .user(生成1个Student对象输出单个JSON对象字段idLongnameStringageInteger) .call() .entity(Student.class); } GetMapping(/getStudentList) public ListStudent getStudentList() { return chatClient.prompt() .user(生成3个Student对象JSON数组格式字段idLongnameStringageInteger) .call() .entity(new ParameterizedTypeReferenceListStudent() {}); } GetMapping(path /chatStream) public FluxString chatStream(RequestParam(message) String message, HttpServletResponse response) { // 避免返回乱码 response.setCharacterEncoding(UTF-8); return chatClient.prompt() .user(message) .stream()// 开启流式响应 .content(); } }以上代码中需要注意defaultSystem(...)是设置全局系统提示词prompt(...)是设置当前对话提示词user(...)为用户输入消息内容。**6) 启动项目并测试**启动项目后浏览器输入如下内容进行测试# http://localhost:8080/ai/chat?message给我讲个故事 { reply: 好的我很高兴为您讲一个神话故事。今天我要讲的是中国上古神话《夸父追日》\n\n很久很久以前在北方的大荒之中住着一个名叫夸父的巨人。他是后土神的孙子信神的儿子。夸父身高如山力大无穷耳朵上挂着两条黄蛇手里也握着两条黄蛇。\n\n那时候太阳每天从东边升起西边落下夸父觉得太阳跑得太快了大地上的时间太短暂。于是他决定要追上太阳让它慢下来。\n\n夸父迈开巨大的步伐开始追逐太阳。他跑啊跑啊跨过一座座高山越过一条条大河。太阳在天上跑夸父在地上追。眼看就要在禺谷追上太阳了夸父却感到口渴难忍。\n\n他俯下身来一口气喝干了黄河的水又喝干了渭河的水还是觉得口渴。于是他向北跑去想要喝大泽的水。可是还没跑到大泽夸父就因为极度干渴而倒下了。\n\n临死前夸父扔出了他的手杖。那手杖化作了一片桃林结满了鲜美的桃子为后来路过的人解渴。\n\n这个故事展现了古人征服自然的雄心壮志也告诉我们做事要量力而行。您觉得这个神话故事怎么样 } # http://localhost:8080/ai/response?message你是谁 { ... ... results: [ { metadata: { finishReason: STOP, contentFilters: [], empty: true }, output: { messageType: ASSISTANT, metadata: { finishReason: STOP, index: 0, id: f68527d6-6a26-4bbb-89c0-ca849e17b900, role: ASSISTANT, messageType: ASSISTANT }, toolCalls: [], media: [], prefix: null, reasoningContent: null, text: 你好我是小智一个智能聊天助手随时为你提供帮助和解答问题。无论是日常疑问、学习辅导还是闲聊放松我都可以陪你聊聊有什么我可以帮你的吗 } } ] } # http://localhost:8080/ai/getOneStudent { id: 1, name: 张三, age: 20 } # http://localhost:8080/ai/getStudentList [ { id: 1, name: 张三, age: 20 }, { id: 2, name: 李四, age: 21 }, { id: 3, name: 王五, age: 22 } ]一个项目使用多个聊天模型在“SpringAIChatClient”项目中如果此刻我们需要只用智普AI进行图片的识别那么就需要在项目pom.xml中引入智普AI相关依赖然后再在“resources/application.properties”中配置智普AI相关的URL/ApiKey/Model等信息这样就不能再使用DeepSeek模型因为默认Spring AI只配置一个ChatClient.Builder。如果在一个项目中需要使用多个模型我们可以通过如下方式手动管理多个ChatClient实例步骤如下1. 在SpringBoot项目中引入多个模型的依赖包2. 关闭ChatClient.Builder 自动创建改为手动管理ChatClient创建在resources/application.properties配置文件中配置“spring.ai.chat.client.enabledfalse”该配置项默认为trueSpring Boot 会自动创建一个 ChatClient.Builder Bean 可以直接注入使用手动管理 ChatClient 的创建需要设置为false即不再使用ChatClient.Builder Bean 注入3. 自定义配置类实现多模型ChatClient创建创建项目并配置多模型2) 配置项目pom.xml在项目的pom.xml中引入了deepseek和zhipuai相关的依赖包。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.5.0/version relativePath/ !-- lookup parent from repository -- /parent groupIdcom.example/groupId artifactIdSpringAIMutiModelClient/artifactId version0.0.1-SNAPSHOT/version nameSpringAIMutiModelClient/name descriptionSpringAIMutiModelClient/description properties java.version17/java.version /properties !-- 导入 Spring AI BOM用于统一管理 Spring AI 依赖的版本 引用每个 Spring AI 模块时不用再写 version只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -- dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0-SNAPSHOT/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 引入 DeepSeek 模型 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-deepseek/artifactId /dependency !-- 引入 智普AI 模型-- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-zhipuai/artifactId /dependency /dependencies !-- 声明仓库 用于获取 Spring AI 以及相关预发布版本-- repositories repository idspring-snapshots/id nameSpring Snapshots/name urlhttps://repo.spring.io/snapshot/url releases enabledfalse/enabled /releases /repository repository nameCentral Portal Snapshots/name idcentral-portal-snapshots/id urlhttps://central.sonatype.com/repository/maven-snapshots//url releases enabledfalse/enabled /releases snapshots enabledtrue/enabled /snapshots /repository /repositories /project多模型聊天示例1) 创建controller包并创建MultiModelController.java文件在该文件中引入配置文件中配置的两个ChatClient可以在不同的controller方法中使用对应模型的ChatClient来进行对话。package com.example.springaimutimodelclient.controller; import com.example.springaimutimodelclient.pojo.IdCardInfo; import org.springframework.ai.chat.client.ChatClient; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.io.IOException; RestController RequestMapping(/ai) public class MultiModelController { private final ChatClient deepseekClient; private final ChatClient zhipuaiClient; public MultiModelController( Qualifier(deepseekClient) ChatClient deepseekClient, Qualifier(zhipuaiClient) ChatClient zhipuaiClient) { this.deepseekClient deepseekClient; this.zhipuaiClient zhipuaiClient; } GetMapping(/deepseek) public String chatWithDeepseek(RequestParam(message) String message) { return deepseekClient.prompt() .user(message) .call() .content(); } GetMapping(/zhipuai) public String chatWithZhipuai(RequestParam(message) String message) { return zhipuaiClient.prompt() .user(message) .call() .content(); } }图片内容识别示例1) 准备图片将图片“img.png”和“身份证.jpg”上传至项目“SpringAIMutiModelClient”的resources资源目录下。2) 创建pojo包并创建IdCardInfo.java类由于后续与大模型对话记性身份证识别时需要自动映射为实体对象这里创建IdCardInfo.java类package com.example.springaimutimodelclient.pojo; public class IdCardInfo { private String name; private String sex; private String nation; private String birth; private String address; private String idNo; public String getName() { return name; } public void setName(String name) { this.name name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex sex; } public String getNation() { return nation; } public void setNation(String nation) { this.nation nation; } public String getBirth() { return birth; } public void setBirth(String birth) { this.birth birth; } public String getAddress() { return address; } public void setAddress(String address) { this.address address; } public String getIdNo() { return idNo; } public void setIdNo(String idNo) { this.idNo idNo; } Override public String toString() { return IdCardInfo{ name name \ , sex sex \ , nation nation \ , birth birth \ , address address \ , idNo idNo \ }; } }3) 创建controller包并创建 MultiModelController.java文件package com.example.springaimutimodelclient.controller; import com.example.springaimutimodelclient.pojo.IdCardInfo; import org.springframework.ai.chat.client.ChatClient; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.io.IOException; RestController RequestMapping(/ai) public class MultiModelController { private final ChatClient deepseekClient; private final ChatClient zhipuaiClient; public MultiModelController( Qualifier(deepseekClient) ChatClient deepseekClient, Qualifier(zhipuaiClient) ChatClient zhipuaiClient) { this.deepseekClient deepseekClient; this.zhipuaiClient zhipuaiClient; } GetMapping(/deepseek) public String chatWithDeepseek(RequestParam(message) String message) { return deepseekClient.prompt() .user(message) .call() .content(); } GetMapping(/zhipuai) public String chatWithZhipuai(RequestParam(message) String message) { return zhipuaiClient.prompt() .user(message) .call() .content(); } }4) 启动项目并测试启动项目后浏览器输入如下内容进行测试