Spring AI ChatClient

发布时间:2026/10/11 9:08:31

Spring AI ChatClient Spring AI ChatClient 全解统一大模型调用客户端实战文档一、技术背景在大模型开发早期开发者对接不同厂商大模型会面临极高的接入成本接口标准不统一OpenAI、阿里百炼、DeepSeek、Ollama、通义千问每家 API 请求体、响应字段、鉴权规则完全独立一套业务代码只能绑定单一模型重复编码冗余同步对话、流式输出、参数配置、多模态逻辑每个厂商都要重新实现维护成本高切换成本巨大业务需要更换模型厂商时全量改写调用代码回归测试工作量极大底层细节繁琐每个模型需要单独处理超时、重试、令牌统计、异常捕获重复造轮子。Spring AI 官方推出ChatClient统一客户端核心目标就是抹平各大模型厂商 API 差异提供一套标准化、流式、可配置、链式调用的统一编程接口。无论本地 Ollama、云端 DeepSeek、阿里百炼、通义千问全部复用同一套调用逻辑仅修改配置即可无感切换模型。二、发展历程初代阶段分模型独立 ModelSpring AI 早期版本仅提供分模型专用ChatModelOllamaChatModel、DeepSeekChatModel 等注入不同 Bean 实现多模型切换但代码写法分散参数构建繁琐多模型共存场景代码臃肿。迭代阶段ChatClient 统一抽象推出Spring AI 1.0-M 系列正式发布ChatClient顶层统一封装基于建造者模式提供链式 API内置提示词模板、参数覆、流式、工具调用、多模态统一能力将所有模型能力收敛至同一套 API。成熟阶段多实例动态切换当前版本支持运行时动态构建多ChatClient实例无需重启服务即可切换模型内置全局默认客户端 动态临时客户端双模式适配复杂多模型共存业务成为 Spring AI 官方推荐标准调用方式。三、ChatClient 核心优缺点3.1 优点跨模型统一 API一套同步 / 流式 / 多模态代码兼容所有厂商切换模型仅修改配置业务逻辑无需改动。极简链式建造者编程无需手动组装 Prompt、Options链式调用可读性强参数灵活覆写支持全局默认配置 单次临时参数覆盖。内置全套通用能力原生支持提示词模板、记忆上下文、函数工具调用、令牌统计、超时重试、异常拦截不用自行封装工具类。多实例灵活管理支持全局默认ChatClient也可运行时动态创建独立客户端实现同一项目同时调用 Ollama、DeepSeek 多个模型。低学习成本屏蔽各厂商底层 JSON 请求细节开发者只关注业务提问内容不用处理底层 HTTP 通信、字段映射。天然适配单元测试无强制 Web 容器依赖搭配SpringBootTest可直接离线调试各类模型能力。3.2 缺点底层厂商特有高级能力访问繁琐厂商独有的扩展字段如 DeepSeek 深度思考 reasoning_content、阿里百炼专属绘图参数需要通过extraHeaders/extraBody透传不如原生 Model 直接扩展简洁。版本迭代较快Spring AI 尚处于里程碑版本少量 API 存在微调大型生产项目需锁定稳定版本。简单单一模型场景存在轻微封装损耗仅固定使用某一个云端模型时直接使用厂商原生 SDK 会少一层抽象极致性能场景原生 SDK 略占优势。四、适用业务场景4.1 优先选用 ChatClient 场景多模型动态切换业务平台支持用户自选大模型本地 Ollama / 云端 DeepSeek / 通义一套业务代码适配全部厂商企业标准化 AI 中台统一封装 AI 能力对外提供服务底层可按需切换成本 / 性能最优模型研发频繁调优提示词需要快速替换不同模型对比回答效果单元测试批量验证 Prompt兼顾私有化 云端双部署内网 Ollama 处理敏感数据云端模型处理高并发公网业务共用一套调用代码通用问答、知识库 RAG、简单 Agent 场景基础文本、流式对话需求不需要厂商独有高阶能力。4.2 不推荐使用场景重度依赖厂商专属独有能力高频使用厂商独有的深度思考、专属多模态、私有工具链扩展透传参数代码繁琐极致低延迟、超高 QPS 线上核心链路追求极致性能需要去掉中间抽象层直接使用厂商原生 SDK 直连 API固定单一模型且长期无替换计划项目永久只使用某一款商用模型无需兼容其他厂商。五、环境准备与通用配置5.1 Maven 依赖!-- Spring AI 核心统一依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter/artifactId version1.1.2/version /dependency !-- Ollama 本地模型适配示例1 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId version1.1.2/version /dependency !-- DeepSeek 云端模型适配示例2 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-deepseek/artifactId version1.1.2/version !-- 流式Flux响应式依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId scopetest/scope /dependency !-- 单元测试依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency5.2 application.yml 全局基础配置双模型示例OllamaDeepSeek# 全局默认选用Ollama本地模型 spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: qwen3:7b temperature: 0.3 num-ctx: 4096 deepseek: api-key: ${DEEPSEEK_API_KEY} base-url: https://api.deepseek.com/v1 chat: options: model: deepseek-chat temperature: 0.4六、入门实战全部基于 SpringBootTest6.1 测试公共说明SpringBootTest加载 Spring 上下文自动注入全局默认ChatClient同步对话一次性获取完整回答适合离线批量处理流式对话Flux 分段输出模拟打字机效果多模型动态切换运行时手动构建 DeepSeek 专用 ChatClient实现同一测试类同时调用本地 / 云端模型。实战 1基础同步 ChatClient 单元测试完整导入、零报错可运行import org.junit.jupiter.api.Test; import org.springframework.ai.chat.client.ChatClient; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; /** * ChatClient 同步对话测试 * 全局默认Ollama客户端一次性返回完整回答 */ SpringBootTest public class ChatClientSyncTest { // 注入全局默认ChatClientyml配置的Ollama Resource private ChatClient chatClient; Test void testSyncChat() { // 链式调用设置提问、全局参数、同步调用获取完整字符串 String response chatClient.prompt() .user(请简要介绍Spring AI ChatClient作用) .call() .content(); System.out.println(同步完整回答); System.out.println(response); } Test void testSyncWithCustomParam() { // 单次请求临时覆写模型参数不影响全局配置 String response chatClient.prompt() .options(opt - opt.temperature(0.1).maxTokens(1024)) .user(写一段严谨的接口设计规范) .call() .content(); System.out.println(自定义参数回答); System.out.println(response); } }实战 2流式输出 ChatClient 单元测试import org.junit.jupiter.api.Test; import org.springframework.ai.chat.client.ChatClient; import org.springframework.boot.test.context.SpringBootTest; import reactor.core.publisher.Flux; import javax.annotation.Resource; import java.util.StringJoiner; /** * ChatClient 流式分段输出测试 * 逐块返回内容适合交互式对话场景 */ SpringBootTest public class ChatClientStreamTest { Resource private ChatClient chatClient; Test void testStreamChat() { StringJoiner fullText new StringJoiner(); // 获取流式Flux数据流 FluxString flux chatClient.prompt() .user(详细讲解大模型同步与流式调用的区别) .stream() .content(); // 逐块打印拼接完整文本 flux.doOnNext(chunk - { System.out.print(chunk); fullText.add(chunk); }).blockLast(); // 阻塞等待流结束 System.out.println(\n流式拼接完整内容); System.out.println(fullText); } }实战 3运行时动态切换多模型Ollama ↔ DeepSeek核心能力不修改 yml、不重启上下文代码手动构建另一厂商 ChatClient实现多模型共存调用import org.junit.jupiter.api.Test; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.model.deepseek.DeepSeekChatModel; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; /** * 多模型动态切换测试 * 默认客户端Ollama本地 * 手动构建DeepSeek云端客户端同一方法切换模型 */ SpringBootTest public class ChatClientMultiModelTest { // 全局默认Ollama ChatClient Resource private ChatClient localChatClient; // 自动注入DeepSeek底层ChatModel用于构建独立客户端 Resource private DeepSeekChatModel deepSeekChatModel; Test void testMultiModelSwitch() { String question 什么是CQRS架构设计思想; // 1、使用本地Ollama模型回答 String localAnswer localChatClient.prompt() .user(question) .call() .content(); System.out.println(【本地Ollama回答】\n localAnswer); System.out.println(---------------------------------------); // 2、动态构建DeepSeek云端ChatClient切换模型 ChatModel cloudChatClient ChatClient.builder(deepSeekChatModel).build(); String cloudAnswer cloudChatClient.prompt() .user(question) .options(opt - opt.temperature(0.3)) .call() .content(); System.out.println(【云端DeepSeek回答】\n cloudAnswer); } }七、核心设计思想总结统一抽象为核心ChatClient 屏蔽各厂商 API 差异一套业务代码适配所有大模型大幅降低多模型项目维护成本链式建造者简化编码参数、提示词、流式、工具调用语义清晰可读性远优于传统 Prompt 组装双层客户端模式全局默认客户端满足绝大多数场景运行时动态构建客户端实现多模型灵活切换测试友好完全脱离 Web 容器依托 SpringBootTest 快速批量验证不同模型、不同提示词效果取舍思维通用 AI 业务首选 ChatClient重度依赖厂商私有高阶能力时再选用对应原生 ChatModel 直连。八、落地使用建议新项目统一使用ChatClient作为标准调用层禁止直接注入各厂商原生 ChatModel全局通用参数写进 yml单次业务特殊参数通过链式options临时覆写需要同时使用本地私有化 云端模型时采用「全局默认 动态构建」双客户端方案批量提示词验证、模型效果对比全部使用 SpringBootTest 单元测试无需启动服务若业务高频使用厂商独有扩展字段可封装统一工具方法透传 extraBody/extraHeaders减少重复代码。
延伸阅读

更多相关文章

2026/10/11 9:08:21

IDEA集成Git实战:从核心概念到团队协作开发全流程

1. 从“单打独斗”到“团队协作”:为什么我们需要Git如果你还在用U盘拷贝代码,或者把项目文件夹命名为“最终版”、“最终版2”、“最终版再也不改了”,那么是时候了解一下Git了。这不仅仅是一个工具,更是一种工作方式的升级。想象…

2026/10/10 19:22:48

初中生背单词总忘?3个高效记忆方法与智能化工具提效指南

【摘要】本文针对初中生单词记不住、易遗忘、不会用等常见痛点,梳理语境关联、艾宾浩斯循环巩固、音形义联动3个高效记忆方法,并结合天学网智能化工具的应用数据,说明如何提升单词记忆效率和使用能力,为初中英语词汇学习提供可落地…

2026/10/6 21:46:48

2026年智习室选型指南:核心痛点、技术方案与3个避坑要点

【摘要】本文围绕2026年智习室选型,梳理行业核心痛点,对比通用型与垂直类技术方案的差异,并结合天学网英语智习室的实测数据,总结3个可落地的避坑要点,帮助学校和机构降低选型决策成本。一、智习室行业核心痛点梳理行业…

2026/10/11 9:07:52

单调栈解接雨水:从边界思维到完整代码实现

1. 这个"trap"到底在装什么水1.1 题目速览:接雨水到底在算什么接雨水(Trapping Rain Water)是 LeetCode 第 42 题,也是单调栈这个数据结构最经典的出场场景之一。题目本身很短:给你一个非负整数数组height&a…

2026/10/11 9:07:52

Python 安装

Python 安装 1. Linux 系统 1.1 使用系统自带 Python(推荐) 大多数 Linux 发行版预装了 Python 3,只需让 python 命令指向 python3: sudo apt update sudo apt install python-is-python3 -y验证安装: python --v…

2026/10/11 9:07:52

C语言学生成绩管理系统实现:链表、文件读写与scanf陷阱全解析

不瞒你说,这学期的《程序设计基础》第二次作业把我折腾得不轻。题目其实是个老熟人——用C语言做一个学生成绩管理系统,支持成绩录入、修改、删除、查询、统计、排序,还要能把数据存进文件里。听起来不就是控制台版的“花名册”嘛&#xff0c…

2026/10/11 9:07:52

langchain agent调用mcp报401?把endpoint改到TaoToken的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 9:02:52

镀锌桥架采购常见问题解答 新明电气 大厂直供 降低采购成本

镀锌桥架作为电缆敷设体系中的基础支撑构件,凭借热镀锌工艺带来的防锈防腐能力与较高的经济性,长期占据工业与基建项目线缆配套市场的重要位置。然而在实际采购过程中,不少项目采购人员由于对产品工艺、规格体系、供货周期缺乏系统了解&#…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑