Spring AI 快速入门:5 分钟搭建 Java 大模型对话应用
1. 为什么 Java 开发者现在都在聊 Spring AI如果你是一个写了几年 Spring Boot 的 Java 开发者最近大概率会有一种微妙的感觉身边做业务系统的同事开始问“大模型怎么接进我们的项目”招聘 JD 里冒出了“有 AI 应用开发经验优先”技术群里隔三差五就有人甩出一段 Spring AI 的代码问“这个 ChatClient 怎么配”。这不是错觉而是 Java 生态正在经历的一次真实迁移——大模型能力正在从“独立的小工具”变成“业务系统里的一个普通依赖”。Spring AI 就是在这个背景下出现的。它做的事情说白了就是把调用大模型这件事抽象成 Spring 生态里你早就熟悉的那套东西一个 starter 依赖、一份 application.yml 配置、一个可以Autowired注入的客户端对象。你不需要去学 Python不需要折腾 LangChain 那一套概念用你写JdbcTemplate、RestTemplate的经验就能把对话、流式输出、提示词模板、向量检索这些能力接进现有的 Spring Boot 工程里。这篇内容面向的是有 Java 和 Spring Boot 基础、但还没真正动手接过 AI 能力的开发者。我会带你从零搭一个能跑起来的最小 AI 应用把依赖怎么选、配置怎么写、代码怎么组织、坑在哪里全部讲清楚。所谓“5 分钟”指的是核心代码量确实很少但我会把每一步背后的原因和容易翻车的地方都补上让你不只是复制粘贴能跑而是真的理解自己在做什么。整篇内容会围绕一个可运行的最小工程展开代码可以直接抄配置可以照着改遇到问题也能在排查章节里找到对应思路。2. 动手前的整体设计与技术选型2.1 这个最小应用到底要做什么先把目标定清楚避免一上来就陷入“要不要做 RAG、要不要接向量库”的纠结。我们要做的是一个最小可用的对话应用提供一个 HTTP 接口接收用户输入的一句话调用大模型把模型的回复返回给调用方。就这么简单。为什么从这个点切入因为它是所有 AI 应用的原子单元。你后面要做的知识库问答、智能客服、文档摘要、代码助手本质上都是在这个“输入-模型-输出”的链路上加东西加检索、加记忆、加工具调用、加多轮编排。如果这个最小链路你都没跑通直接上复杂架构出问题时你根本不知道是模型的问题、网络的问题还是框架配置的问题。先把最短路径打通再逐步加料这是我在实际项目里反复验证过的顺序。这个应用适合谁参考如果你是要在现有 Spring Boot 后台里加一个“AI 助手”入口或者想快速验证某个模型平台能不能用或者只是想在本地跑通一次完整的调用链路建立手感那这个结构就是为你准备的。它不追求生产级的健壮性但结构是干净的可以直接作为后续扩展的骨架。2.2 为什么选 Spring AI 而不是自己写 HTTP 调用有人会问调用大模型不就是发个 HTTP 请求吗我自己用RestTemplate或WebClient拼 JSON 不就行了为什么要引入一个框架这个问题问得好我一开始也是这么想的。自己拼 JSON 确实能跑通但当你真正开始做业务时会发现你要处理的琐事远比想象的多不同模型平台的请求体格式不一样、流式返回的 SSE 解析要自己写、提示词模板要自己管理、多轮对话的消息历史要自己维护、重试和超时要自己封装、切换模型供应商时要改一堆代码。这些活儿单看每一件都不难但堆在一起就是持续的维护成本。Spring AI 的价值就在于把这些共性能力抽象掉了。它定义了一套统一的ChatModel、ChatClient接口底层对接不同平台时你只需要换依赖和配置业务代码基本不动。这跟你当年从手写 JDBC 转向 MyBatis 或 JPA 是同一个逻辑不是手写不行而是抽象之后你才能把精力放在业务本身。而且它天然融入了 Spring Boot 的自动配置体系依赖一加、配置一写客户端对象就自动装配好了这种“熟悉感”对 Java 开发者来说学习成本极低。2.3 模型平台的选择与依赖引入Spring AI 支持多种模型平台选哪个取决于你手头有什么。如果你已经有某家平台的 API Key直接用那家就行如果还没有建议先用国内可访问、注册门槛低的平台跑通流程比如通义千问DashScope、智谱、DeepSeek 等这些在 Spring AI 生态里都有对应的 starter 支持。这里有个关键点必须说清楚Spring AI 的版本和 starter 的 artifactId 在不同版本间有过调整网上很多老教程的依赖坐标已经不能用了。以当前主流版本为例核心依赖通常包括两部分一个是 Spring AI 的核心 starter一个是具体模型平台的 starter。下面是一个典型的 Maven 依赖结构具体版本号请以你使用的 Spring AI 版本为准dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/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-openai/artifactId /dependency /dependencies用 BOM 统一管理版本是个好习惯能避免核心包和模型包版本不一致导致的诡异问题。我踩过一次坑核心包和模型包版本差了一个小版本启动时报了个跟序列化相关的错排查了半天才发现是版本没对齐。所以强烈建议用 BOM 锁版本。注意不同模型平台的 starter artifactId 命名规则不完全一样有的叫spring-ai-starter-model-xxx有的在早期版本里叫spring-ai-xxx-spring-boot-starter。引入前务必对照你所用版本的官方文档确认不要直接抄旧博客里的坐标。3. 核心配置与代码实现细节3.1 application.yml 里到底要配什么依赖加完之后接下来是配置。这一步是新手最容易卡住的地方因为配置项的名字和结构在不同版本里也有变化。核心要配的无非三样接口地址base-url、API Key、模型名称。以 OpenAI 兼容协议的平台为例配置大概长这样spring: ai: openai: base-url: https://你的平台接口地址/v1 api-key: ${AI_API_KEY} chat: options: model: 你的模型名称 temperature: 0.7这里有几个细节值得展开。第一api-key我用了环境变量占位符${AI_API_KEY}而不是把密钥硬编码在文件里。这是基本的安全习惯密钥进了 Git 仓库就等于泄露了尤其是团队协作时。第二base-url一定要看清楚平台文档给的是带/v1还是不带很多平台的 OpenAI 兼容接口路径是/v1/chat/completions如果你 base-url 配错了会直接报 404而且报错信息往往不直观容易误以为是 Key 的问题。第三temperature控制输出的随机性0 到 2 之间值越低越确定、越高越发散。做事实性问答建议调低到 0.2 左右做创意文案可以调到 1.0 以上。提示如果你用的是国内平台注意有些平台的模型名称是区分大小写和版本的比如带日期后缀的版本号。配错模型名通常会返回“模型不存在”之类的错误别慌先去平台控制台确认准确的模型标识。3.2 用 ChatClient 写业务代码配置就绪后业务代码其实非常短。Spring AI 提供了两个层次的 API底层的ChatModel和高层的ChatClient。日常开发我建议直接用ChatClient它的链式调用更符合直觉也更容易做提示词模板和参数覆盖。RestController RequestMapping(/ai) public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是一个简洁专业的技术助手回答控制在三句话以内。) .build(); } GetMapping(/chat) public String chat(RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }这段代码里有几个设计点值得说。ChatClient.Builder是 Spring AI 自动配置注入的你不需要自己 new。defaultSystem设置的是系统提示词相当于给模型定了一个“人设”和输出约束这个位置非常关键——很多人抱怨模型回答啰嗦、跑题八成是系统提示词没写好。.call()是同步阻塞调用.content()取出纯文本结果。如果你要流式输出把.call()换成.stream()返回类型改成FluxString配合前端的 SSE 就能实现打字机效果。3.3 提示词模板的正确用法实际业务里用户输入很少是孤零零一句话往往要拼进一个固定模板。Spring AI 提供了PromptTemplate但更常用的方式是在ChatClient里直接用占位符String answer chatClient.prompt() .user(u - u.text(请用一句话解释这个概念{topic}) .param(topic, 依赖注入)) .call() .content();用占位符而不是字符串拼接好处是模板和参数分离便于统一管理和复用也避免了用户输入里带特殊字符时把模板结构搞乱。我见过有人直接请解释 userInput拼接结果用户输入里带了换行和指令性文字直接把系统提示词覆盖了这就是典型的提示词注入风险。用参数化模板能在一定程度上缓解这个问题当然真正的防护还需要在业务层做输入校验。4. 完整实操流程与关键环节4.1 从零到跑通的完整步骤把前面的内容串起来一个完整的搭建流程是这样的。第一步用你习惯的方式创建一个 Spring Boot 工程Spring Boot 版本建议 3.2 以上因为 Spring AI 对 JDK 和 Spring Boot 版本有要求JDK 至少 17。第二步按 2.3 节引入 BOM 和对应模型平台的 starter。第三步在 application.yml 里写好 base-url、api-key、model 三项配置。第四步写一个 Controller注入ChatClient.Builder并构建客户端。第五步启动应用用浏览器或 curl 访问接口验证。验证命令可以这样写curl http://localhost:8080/ai/chat?message什么是Spring Boot如果返回了一段合理的文本恭喜你最小链路通了。如果报错先别急着改代码按下一节的排查思路走一遍绝大多数问题都出在配置和网络这两块。4.2 参数选择背后的计算逻辑很多人配temperature、max-tokens这些参数时是凭感觉填的其实它们有明确的作用。temperature影响的是模型输出概率分布的平滑程度值越高低概率词被选中的机会越大输出越多样值越低越倾向于选高概率词输出越稳定。做客服问答、数据抽取这类任务我一般设 0.1 到 0.3做头脑风暴、文案生成设 0.8 到 1.2。max-tokens限制的是单次回复的最大长度它直接关系到成本和响应时间。你要根据业务场景估算一句中文大约对应 1 到 2 个 token不同模型的分词器不一样如果你希望回复不超过 200 字那max-tokens设 400 左右比较稳妥。设太小会导致回复被截断设太大则可能让模型“刹不住车”输出冗长内容还多花钱。这个值没有万能答案建议先设一个保守值观察实际输出后再调整。4.3 流式输出的实现要点同步调用在体验上有个硬伤用户要等模型把整段话生成完才能看到结果长回复时等待感很强。流式输出能显著改善这一点。实现上把返回类型改成FluxStringGetMapping(value /chat/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chatStream(RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); }produces TEXT_EVENT_STREAM_VALUE这个声明很关键它告诉 Spring 用 SSE 协议返回浏览器端用EventSource就能接收。这里有个容易忽略的点流式接口在网关或反向代理后面时要确保代理没有开启响应缓冲否则流式效果会被“攒”成一次性返回看起来跟同步没区别。这个坑我在实际部署时踩过排查了半天才发现是代理配置的问题。5. 常见问题与排查技巧实录5.1 启动和调用阶段的典型报错新手在这一步遇到的问题高度集中我整理成一张速查表方便你对照排查现象可能原因排查方向启动报找不到 ChatModel Beanstarter 没引入或版本不匹配检查依赖坐标和 BOM 版本调用返回 401API Key 错误或未生效确认环境变量是否注入成功调用返回 404base-url 路径不对对照平台文档确认是否带 /v1返回“模型不存在”模型名称写错去平台控制台核对准确标识请求超时网络不通或超时设置过短检查网络连通性和超时配置中文乱码编码未指定确认请求和响应都用 UTF-8这张表覆盖了我自己和身边同事遇到过的绝大多数问题。你会发现真正跟“AI”相关的错误其实很少大部分都是配置和网络问题。这也是我一直强调先把最小链路跑通的原因——链路越短变量越少排查越容易。5.2 几个容易忽视的坑第一个坑是超时设置。大模型生成回复的时间比普通接口长得多尤其是长文本默认的超时时间往往不够。建议把连接超时和读取超时都调大读取超时至少设 60 秒流式场景还要更长。第二个坑是并发和限流。很多平台的免费额度有 QPS 限制本地测试时单线程没问题一上并发就报限流错误。生产环境一定要做限流和重试重试要带退避策略别一失败就疯狂重试把额度打满。第三个坑是提示词里的敏感信息。有些人图省事把数据库连接串、内部接口地址写进系统提示词这些内容会随请求发到模型平台。记住一个原则发给模型的内容等同于发给了第三方任何不该外传的信息都不要放进去。第四个坑是把模型输出直接当可信数据用。模型会“一本正经地胡说”如果你拿它的输出去执行数据库操作或调用其他接口一定要做校验和兜底不能无条件信任。5.3 我个人的实操心得说几个文档里不会写、但实际很有用的经验。第一先用最简单的 curl 验证平台接口本身通不通再去调 Spring AI 的代码。这样能把“平台问题”和“框架问题”分开排查效率翻倍。第二把系统提示词当成代码来管理单独放一个文件或配置项别硬编码在 Java 字符串里改起来方便也便于做 A/B 测试。第三日志要打全把请求的模型名、耗时、token 消耗都记下来出问题时这些是唯一的线索也是后续做成本优化的依据。第四关于模型切换Spring AI 的抽象让换平台变得相对容易但不同平台的提示词“脾气”不一样同一段提示词在 A 平台效果好换到 B 平台可能就变差。所以切换平台时别只测通不通一定要用你的真实业务样例跑一遍效果对比。第五成本意识要早建立token 是要花钱的开发阶段就养成看用量、控长度的习惯别等账单出来才后悔。6. 从最小应用继续往下走跑通这个最小应用之后你手里就有了一块可以往上搭积木的地基。接下来常见的扩展方向有几个加多轮对话记忆让模型记住上下文接向量数据库做 RAG让模型基于你的私有文档回答加工具调用Function Calling让模型能查数据库、调接口做提示词模板的集中管理支撑多个业务场景。每一个方向都是在今天这个“输入-模型-输出”链路上加东西而不是推倒重来。这也是我建议从最小应用起步的原因你对这条链路的每一环都心里有数后面加任何东西你都知道它插在哪里、为什么插在那里。我个人在实际项目里的体会是AI 应用开发的难点从来不在“调通模型”而在“把模型稳定、可控、低成本地嵌进业务流程”而这件事恰恰是 Spring 生态最擅长的地方。