最近后台收到不少同学问“SpringAI 项目到底怎么上手”“系统提示词怎么配”“能不能直接接 DeepSeek”,今天就把我这段时间折腾 SpringAI 的入门经验整理出来。这篇内容不是照着官方文档念一遍,而是把新手最容易卡住的点、最该提前知道的知识串起来,从环境搭建到第一个对话接口,再到提示词配置、智能审核这类实战场景,一次性把 SpringAI 大模型应用开发的基础脉络理清楚。
适合刚接触 SpringAI 的 Java 后端、想在企业项目里快速接入大模型能力的开发同学。哪怕你没写过 AI 应用也没关系,只要会 Spring Boot 的基础用法,照着下面的步骤走,基本当天就能跑通一个能对话、能调用工具的小项目。
1. SpringAI 到底是个啥:先解决“为什么”的问题
1.1 一图流理解 SpringAI 的定位
很多新手第一次听到 SpringAI,下意识会以为它是一个大模型,其实不是。SpringAI 是 Spring 官方推出的 AI 应用开发框架,定位很明确:把大模型接入 Spring Boot 项目的“最后一公里”问题解决了。
打个比方:大模型本身像一台发电机,能产生电力,但你需要把电接到家里、装上开关、接入各种电器才能用。SpringAI 就是那套电路系统,让你不用自己造发电机,也不用自己拉电线,只需要插上插头就能用电。
你不需要管 OpenAI、DeepSeek、通义千问这些模型的 HTTP 接口差异,SpringAI 帮你把底层调用封装成了统一的 ChatClient、ChatModel 这些接口。业务代码里只需要面向 Spring 的抽象编程,换模型服务商的时候改一行配置就行。
1.2 SpringAI 与其他 AI 框架的对比
市面上的 AI 开发框架不少,新手容易挑花眼。我用实际体验给大家捋一捋主流方案的差别。
| 方案 | 使用门槛 | 和 Spring 生态的关系 | 典型场景 |
|---|---|---|---|
| SpringAI | 低,熟悉 Spring Boot 即可上手 | 原生融合,Bean 管理、配置体系一致 | Java 应用内嵌 AI 能力 |
| LangChain4j | 中,需要理解自己的抽象概念 | 支持 Spring Boot 集成,但相对独立 | Java 项目里的 AI Agent 开发 |
| LangChain | 中高,Python 生态 | 与 Java 技术栈关系弱,需要通过 HTTP 调用 | Python 数据分析、AI 应用原型 |
| 直接调云厂商 SDK | 低,但重复工作量大 | 与 Spring 无关,需要自己封装 | 简单的接口透传 |
这里强调一点:如果你是纯 Java 后端,团队没有专门做 AI 算法的人,SpringAI 是最省心的选择。它的依赖注入、自动配置、配置项管理和 Spring Boot 完全是同一套思路,学习成本基本集中在大模型概念本身,而不是框架用法上。
1.3 为什么要用 SpringAI:核心优势解读
我对比完发现,SpringAI 的核心价值在于三件事。
第一,统一抽象。不管今天是接 OpenAI 兼容接口,还是接国产大模型,代码层面都是 ChatModel。业务里写一次,后面换模型只是改 yml 里的 model 名称和 base-url。
第二,和 Spring 生态无缝衔接。你用 SpringAI 写的工具函数可以像普通 Bean 一样管理,配上 @Description 注解就能被模型自动识别调用。做 Web 项目时,Controller、Service、持久层那套开发习惯完全不用变。
第三,内置了提示词模板、输出解析、向量数据库集成、工具调用这些 AI 应用的高频能力。这些能力如果自己从头写,至少要额外写大几百行代码,还要处理各种异常边界。
2. 环境准备与项目初始化:把地基打牢
2.1 工具链选型:JDK、Maven、IDE 怎么配
SpringAI 对 Java 版本有要求,我用的是 JDK 17,这是目前 Spring Boot 3.x 的基准版本,建议直接用它,别用 JDK 8 然后到处踩依赖冲突的坑。Maven 用 3.6.3 以上,IDE 这块我用 IDEA,社区版就够。
另外要注意,SpringAI 目前版本更新速度比较快,1.0 之前的版本 API 变化很大。如果你现在新建项目,直接上手 1.0.x 或者查看官方文档推荐的稳定版本。
2.2 创建一个带 SpringAI 依赖的最小工程
最简单的方式是在 Spring Initializr 上生成项目骨架,Group、Artifact 按自己的习惯填,依赖先只勾选一个 Spring Web,然后手动在 pom.xml 里加 SpringAI 依赖。
我平时习惯用 Maven,核心依赖有两种加法,取决于你要接什么模型。以接 DeepSeek 这类 OpenAI 兼容模型为例:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> <version>1.0.0</version> </dependency>如果你接的是通义、智谱、Kimi 等国内厂商,SpringAI 同样提供了对应的 starter。先看到这里,记住一件最要紧的事:版本必须和你的 Spring Boot 版本匹配,否则启动时会出现各种各样的 NoSuchMethodError、ClassNotFound。
2.3 配置文件的写法:api-key 和模型名放哪
在 application.yml 里,最核心的配置是 api-key、base-url 和模型名称。以 DeepSeek 为例:
spring: application: name: springai-demo ai: openai: api-key: ${DEEPSEEK_API_KEY} base-url: https://api.deepseek.com chat: options: model: deepseek-chat这一段看起来简单,但有新手容易踩一个大坑:SpringAI 里关于模型供应商的配置前缀并不统一。接 OpenAI 官方的 key 时,前缀是 spring.ai.openai;接 DeepSeek 时,因为兼容 OpenAI 协议,很多人直接套用 openai 前缀,但其实客户端会自动把 base-url 拼到请求路径上,如果 base-url 末尾多了一个斜杠,或者漏了带 v1 的路径,就会一直报 404。
我的建议是,环境变量只存敏感信息,比如 api-key 用 ${DEEPSEEK_API_KEY} 引用,模型名、base-url 这些可以写在 yml 里方便调试。另外,不要把 key 硬编码到代码里,这既是安全习惯,也方便不同环境切换。
2.4 验证环境是否联通:先写一个失败得快速的测试
配置完成后,不要急着写业务,先做一个连通性验证。我的习惯是写一个 ApplicationRunner,在启动时自动发一条最简单的话给模型:
@Component public class StartupProbe implements ApplicationRunner { private final ChatModel chatModel; public StartupProbe(ChatModel chatModel) { this.chatModel = chatModel; } @Override public void run(ApplicationArguments args) { String response = chatModel.call("你好"); System.out.println("AI 回复: " + response); } }如果启动后控制台打印出正常的 AI 回复,说明网络、key、模型名都没问题。这一步能把环境问题和业务代码问题隔离开,排错范围缩小很多。
3. 第一个对话 Demo:把最基础的链路跑通
3.1 ChatClient:你实际打交道最多的对象
在 SpringAI 里,ChatModel 是底层能力的入口,但日常开发我更推荐直接用 ChatClient。它提供了更流畅的链式调用 API,代码可读性高,也更容易维护。
配置一个 ChatClient Bean,在配置类里写:
@Configuration public class ChatConfig { @Bean public ChatClient chatClient(ChatModel chatModel) { return ChatClient.builder(chatModel).build(); } }如果你需要支持不同的模型,比如一个走 DeepSeek 做对话,一个走多模态模型做图片识别,那就生成两个 ChatClient Bean,搭配 @Qualifier 使用。新手阶段先别搞那么复杂,一个 ChatClient 足够。
3.2 写一个最简单的对话接口
有了 Bean,接下来就是创建一个 Controller,用来暴露 HTTP 接口:
@RestController @RequestMapping("/ai") public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/chat") public String chat(@RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } }启动项目后,浏览器访问 http://localhost:8080/ai/chat?message=你好,就能看到模型返回的文本。这一条链路虽然不长,但里面有几个概念你无论如何都要搞明白:Prompt、Message、ChatResponse。这三个对象是你后面写复杂逻辑的地基。
3.3 从同步接口到流式输出,别忽略体验细节
同样的接口,如果改成流式输出,响应体验会好很多,尤其当模型吐字比较长的时候。加上 spring-boot-starter-webflux 之后,接口可以返回 Flux:
@GetMapping(value = "/chat/stream", produces = "text/event-stream") public Flux<String> chatStream(@RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); }前端用 EventSource 或者 fetch 流式读取,就能实现打字机效果。注意,Spring MVC 默认是阻塞模型,如果你的项目里本来没有 WebFlux,建议单开一个 Controller 或者单独配置返回值类型,别改全局配置,否则可能影响现有接口。
4. 核心概念拆解:Prompt、Message、ChatResponse
4.1 Prompt 不是简单字符串,而是一组指令的集合
很多新手把 prompt 当成“用户输入的文本”,写代码时直接一个 String 传进去。这够用,但理解不全面。
SpringAI 里,Prompt 是一个封装对象,它可以包含多个 Message,还可以附带模型生成参数,比如 temperature、maxTokens、topP 这些。这意味着,你每次调模型,不只是在“问问题”,而是在构造一次完整的模型调用上下文。
把 prompt 当作一个上下文包来理解,后续做复杂业务会轻松很多:你要注意用户说了什么,也要注意系统设定了什么规则,还要注意这次调用允许多少创意度。
4.2 理解 Message 的角色:System、User、Assistant
Message 在 SpringAI 里主要有三种类型,对应大模型 API 中常见的消息角色。
系统消息(SystemMessage)是最容易被新手忽略的。它负责定义模型的角色和回复规矩,比如“你是一个专业的电商审核员”“回答必须使用中文”“不要输出多余解释”。这一步对应的是大家经常搜的“SpringAI 系统提示词怎么配置”,本质上就是把一段固定的规则文本放到 SystemMessage 里,每次调用之前自动带上。
用户消息(UserMessage)就是你要让模型处理的具体内容,可以是一段文本,也可以是图片等多模态内容。
助手消息(AssistantMessage)一般用于多轮对话时把之前的回复作为上下文传给模型,构造对话记忆时会用到。
用 ChatClient 配置系统提示词非常直观:
String response = chatClient.prompt() .system("你是一个严谨的电商评论审核员,只输出 PASS 或 REJECT,不要多余解释。") .user("商品质量很好,但快递太慢了") .call() .content();4.3 ChatResponse 里到底有什么
ChatResponse 是模型调用结果的封装。新手经常只调 .content() 拿文本,忽略了它内部的丰富信息。
通过 ChatResponse 可以拿到生成结果、Token 用量、结束原因等元数据。这在做成本统计、日志审计时非常重要。一个实际用的多的场景是统计消耗,尤其是在接付费模型的时候,不同模型的 token 单价不一样,把每次调用的 token 数记录到数据库,月底对账就有依据了。
ChatResponse response = chatClient.prompt() .user("讲个冷笑话") .call(); String content = response.getResult().getOutput().getContent(); Generation generation = response.getResult();4.4 参数调优:temperature 是最直观的旋钮
模型调用参数里,新手最先需要认识的就是 temperature。它控制输出的随机性:值越低,输出越确定、越保守;值越高,输出越发散、有创造性。
我自己的经验是,做审核、分类、结构化抽取这类任务,temperature 设置成 0 或者 0.1 比较稳;做文案创作、头脑风暴,可以调到 0.7 以上。SpringAI 中通过 options 设置:
String response = chatClient.prompt() .user("写一句夏季饮品宣传语") .options(ChatOptions.builder() .temperature(0.8) .maxTokens(200) .build()) .call() .content();这里有一个新手容易掉进去的误区:以为 temperature 越高,模型回答质量越高。其实不是,temperature 只影响随机性,不影响模型的知识水平和逻辑上限。输出质量的关键还是提示词写得好不好、模型选得对不对。
5. 实战一:用 SpringAI 做智能审核/内容分析
5.1 场景设定:从评论区审核开始
网络热词里“springai 智能审核”出现频率不低,这正好是 SpringAI 非常适合的落地场景。我这里用一个评论审核的例子来演示,毕竟内容审核是很多业务系统的刚需。
需求背景:一个社区产品每天有大量用户评论,需要判断评论是正常、广告、辱骂还是其他违规内容。传统方式维护敏感词表太死板,语义绕过容易被漏掉,用大模型做语义理解会灵活很多。
5.2 用提示词模板来管理审核规则
直接通过 .system() 写死规则也能用,但规则多了之后,代码会变得很难维护。更好的方案是使用 SpringAI 的提示词模板机制,把规则外部化。
在 resources 下建一个 prompts 目录,放一个审核模板:
你是一个内容安全审核员。 审核规则: 1. 判断内容属于 normal、advertisement、abuse、other_violation 中的一类 2. 只输出分类名称,不要额外解释 用户评论: {userComment}代码里读取模板并填充变量:
public String audit(String comment) { String prompt = """ 你是一个内容安全审核员。 审核规则: 1. 判断内容属于 normal、advertisement、abuse、other_violation 中的一类 2. 只输出分类名称,不要额外解释 用户评论: %s """.formatted(comment); return chatClient.prompt() .system("请严格遵守上面的审核规则") .user(prompt) .call() .content(); }这样看起来已经能用,但生产环境绝对不能直接把模型输出当字符串去匹配,因为模型偶尔会多输出一个标点、换行,或者把“normal”写成“Normal”,后面接一个 JSON 解析会稳定很多。
5.3 结构化输出:让模型返回 JSON 而不是大白话
只要涉及后续的逻辑处理,结构化的输出几乎必配。把这个需求改成返回 JSON:
public AuditResult audit(String comment) { String prompt = """ 你是一个内容安全审核员。 请对以下用户评论进行分类和简要说明,以 JSON 格式返回,字段如下: { "category": "normal 或 advertisement 或 abuse 或 other_violation", "reason": "判断理由" } 用户评论: %s """.formatted(comment); return chatClient.prompt() .user(prompt) .call() .entity(AuditResult.class); }对应的实体:
public record AuditResult(String category, String reason) { }用 .entity() 是 SpringAI 提供的高效方式,框架会帮你把模型返回的 JSON 映射到 Java 对象。这一步很关键,因为它让 AI 能力真正融入了 Java 的业务代码,后续存库、告警、统计都能直接用对象字段。
我做这类审核接口时还有两个小经验。一是别把审核结果直接当终审结论,更稳的做法是高风险内容再走一轮人工抽检;二是大模型审核接口要设计超时和降级,模型服务抖动不能影响主链路,可以设置调用失败后走敏感词兜底。
6. 实战二:进阶知识——工具调用与 RAG 入门
6.1 工具调用:让模型能查数据库、调接口
大模型的训练数据是固定的,所以它天生不知道你系统里的订单状态、库存数量。工具调用(Function Calling)是解决这个问题的主流方案:模型在需要额外信息时,会生成一个结构化的函数调用请求,你的代码执行后再把结果回传给模型。
SpringAI 的 @Description 注解在这里非常关键。模型靠它理解这个工具是干什么的、参数是什么含义。写一个查询订单的示例:
@Component public class OrderQueryTools { @Description("根据订单号查询订单状态") public OrderStatus queryOrder(String orderId) { // 实际这里会注入 OrderService 查数据库 return new OrderStatus(orderId, "SHIPPED"); } }然后在 ChatClient 上挂载这个工具:
String response = chatClient.prompt() .user("订单 20251212 现在什么状态") .tools(new OrderQueryTools()) .call() .content();这里需要特别提醒新手:工具调用不是模型直接执行方法,而是模型“决定要不要调用、传入什么参数”,真正的方法执行还是发生在你的应用里。所以工具方法里务必做好参数校验,不能盲目相信模型生成的参数。
6.2 RAG:给模型外挂一本“内部知识库”
RAG,检索增强生成,是当前落地大模型最实用的技术之一。它的核心思路是:先把知识文档拆分成片段,做向量化存入向量数据库;用户提问时,先从库里检索最相关的片段,再把这些片段和问题一起丢给模型生成答案。
这样模型就能基于你的私有知识库回答问题,而且不需要微调模型本身。
SpringAI 提供了非常友好的向量数据库抽象,以 PostgreSQL 的 pgvector 为例,加入依赖:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-pgvector</artifactId> </dependency>然后把文档加载、拆分成 Embedding,存入 VectorStore,查询时再走向量检索。完整的代码展开会很长,但需要记住的关键链路只有三条:文档加载、向量化入库、语义检索。
如果你刚入门,建议先用官方示例把 RAG 跑通,再考虑结合自己的业务文档。这个领域踩坑点通常不在 SpringAI 本身,而在数据质量管理,你自己造的文档如果都是重复信息,检索效果就非常差。
7. 常见问题与避坑实录
7.1 启动失败:依赖冲突和配置前缀
这里把容易遇到的问题整理成一个速查表,方便大家对照排查。
| 现象 | 原因 | 解决办法 |
|---|---|---|
| 启动报 NoClassDefFoundError | SpringAI 版本与 Spring Boot 版本不匹配 | 查看官方文档的版本兼容矩阵 |
| 接口调用报 401 | api-key 没配置或配置错 | 检查环境变量 DI和 yml 引用是否一致 |
| 接口调用报 404 | base-url 路径不对,末尾缺少 v1 或多了斜杠 | 去掉末尾斜杠,确认供应商的 API 路径 |
| 返回内容出现中文乱码 | 响应编码问题 | 检查应用编码和接口 produces 设置 |
7.2 提示词反复试都不生效?问题可能不在提示词
有段时间我调一个分类功能,系统提示词怎么强调都没用,模型总是输出多余内容。后来排查下来是参数问题,temperature 设置成了 0.9,导致即使规则明确,输出也偏发散。把 temperature 调低后,效果立刻就稳定了。
所以出现这种问题时,先按顺序排查三件事:模型参数有没有调高、系统提示词有没有真的传进去、模型本身是不是能力不够。很多时候,不是提示词不行,而是模型服务的版本默认参数和你预期不一致。
7.3 成本控制:一次调用烧了多少 token
大模型应用上线后,最容易被忽视的是 token 成本。我自己写过一个小工具,在 Service 层包装了一次 ChatModel 调用,打印出每次调用的 prompt tokens 和 completion tokens,并记录到日志。
public ChatResponse chatWithLog(String message) { ChatResponse response = chatClient.prompt() .user(message) .call(); // 在实际项目里,在这里记录 token 用量并存储起来 return response; }这个习惯能帮你尽早建立成本意识。我见过不少团队上线 AI 功能后才问“这个月怎么烧了几万”,基本都是因为没提前做 token 管控。建议从开发第一天就把 token 统计埋进去。
7.4 模型回复不稳定:加一层后处理校验
大模型再强,也难免偶尔“嘴瓢”。如果你的业务对准确性要求高,建议在模型输出后增加一层代码校验。比如审核场景,模型返回 JSON 后,先校验 category 字段是否是合法枚举值,不合法就直接走默认策略,而不是让脏数据往下游流动。
这一层校验看起来简单,但真的能挡住不少线上问题。
7.5 调试工具建议:聊聊 SpringAI 的调试方式
排查问题的时候,建议打开 SpringAI 的请求日志。调试阶段可以把日志级别调低:
logging: level: org.springframework.ai: DEBUG这样能在控制台里看到发给模型的请求体、返回体,比在代码里到处打断点高效很多。等排查完再调回 INFO,否则生产环境的日志量会把你淹没。
8. SpringAI 后续还可以扩展的方向
这里再聊一些我个人的体会。SpringAI 只是 AI 应用开发的起点,不是终点。入门之后,比较值得投入的方向有三个:一个是针对业务场景的提示词工程优化,把规则沉淀成可复用的模板;另一个是工具调用与现有业务系统的深度整合,让模型真正能完成业务动作而不是只停留在对话;再一个是评估体系的建设,因为大模型应用的难点从“能不能跑通”变成“效果稳不稳定”之后,你要有办法量化输出质量。
在团队已经有了基础能力之后,越早建立模板管理和评估机制,后面做复杂应用越省力气。很多项目的失败不是模型选错,而是没有一套可持续优化的流程。
我自己的做法是,每次业务方反馈效果不佳,都会把输入、输出、当时用的模型参数一起存下来,形成一份真实的回归测试集。这样一来,调整提示词或者切换模型时,拿这套集子一跑,效果有没有变好一目了然。这比凭感觉迭代要靠谱得多。
最后再分享一个小经验:学习 SpringAI 时,不要一开始就追新版本特性。把 ChatClient、Prompt、Message、工具调用这几个核心概念弄扎实,后面看任何复杂的 AI 应用架构都能快速理解。毕竟框架会迭代,但大模型应用的核心链路,万变不离其宗。