Spring AI + DeepSeek 入门实战:先跑通第一个对话接口
很多 Java 开发者听过 Spring AI,但真正跑过 Demo 的不多。本文用 Spring Boot + Spring AI + DeepSeek,从 0 跑通第一个 AI 对话接口,包含依赖配置、Key 配置、接口代码、调用测试和常见坑。
摘要
很多 Java 开发者都听过 Spring AI,但真正动手跑过 Demo 的并不多。这篇文章不空聊概念,直接用 Spring Boot + Spring AI + DeepSeek 从 0 跑通一个最简单的 AI 对话接口,包含项目创建、依赖配置、API Key 配置、Controller 编写、接口测试和常见坑处理。
前言
最近我准备系统研究一下 Spring AI。
说实话,一开始看到 Spring AI、RAG、Agent、Tools、向量库这些词的时候,很容易有点懵。
但我后来想了一下,学这种新东西,第一步最好别上来就看太多概念。
最稳的方式其实很简单:
先跑起来。
你先把第一个 Demo 跑通,再回过头去理解它为什么这么写,会舒服很多。
这篇文章就不讲太多高大上的东西了,目标只有一个:
用 Spring Boot + Spring AI + DeepSeek 跑通第一个对话接口。
为什么这里用 DeepSeek?
主要是因为国内很多开发者接 OpenAI 不太方便,而 DeepSeek 对国内用户更友好一些。Spring AI 官方也提供了 DeepSeek Chat 的接入支持,配置项里可以直接使用 spring.ai.deepseek.api-key,模型可以使用 deepseek-chat 或 deepseek-reasoner。
这篇跑通以后,后面再继续学:
- Prompt 怎么写
- 结构化输出怎么做
- Tools 怎么接
- RAG 怎么玩
就不会那么虚了。
一、这篇文章最终要实现什么
我们这篇不做复杂功能,只做一个最小闭环。
最终效果是:
用户访问接口:
http://localhost:8080/ai/chat?message=用大白话介绍一下Spring AI
后端调用 DeepSeek 模型,然后返回一段回答。
类似这样:
Spring AI 可以理解成 Spring 生态里接入大模型能力的一套工具。对于 Java 开发者来说,它可以让你用熟悉的 Spring Boot 方式,把 AI 对话、提示词、工具调用等能力接进项目里。
这就够了。
二、准备工作
开始之前,先准备这些东西。
1. JDK
建议使用:
JDK 17+
Spring Boot 3.x 本身就要求 Java 17 起步,所以这里直接用 JDK 17 比较稳。
如果你的机器上有多个 jdk 版本的话那就切换到对应的 jdk 版本

查看当前所使用的 jdk 版本

2. Maven
本篇使用 Maven 示例。
你可以先确认一下本地 Maven 是否正常:
mvn -v

3. Spring Boot
这里使用 Spring Boot 3.x。
我下面示例用的是:
Spring Boot 3.5.0
Spring AI 1.0.0
Spring AI 官方文档里也提到,Spring AI 1.0.0 支持 Spring Boot 3.4.x 和 3.5.x。
4. DeepSeek API Key
你需要先去 DeepSeek 平台申请一个 API Key。
拿到之后,后面会配置成环境变量:
DEEPSEEK_API_KEY
三、创建一个 Spring Boot 项目
你可以用 Spring Initializr 创建项目,也可以自己手动建。
如果用 Spring Initializr,建议先选这些:
- Spring Boot 3.x
- Java 17
- Maven
- Spring Web
项目名称可以叫:
spring-ai-deepseek-demo
包名示例:
com.itlay.demo
先不加数据库、不加 Redis、不加复杂依赖。
越简单越好。
四、配置 pom.xml
下面是一个完整的 pom.xml 示例。
小伙伴们可以直接参考。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/>
</parent>
<groupId>com.itlay</groupId>
<artifactId>spring-ai-deepseek-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-ai-deepseek-demo</name>
<description>Spring AI DeepSeek Demo</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Web 接口依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI DeepSeek 支持 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
<!-- 测试依赖,可选 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这里重点看两个地方。
第一个是 Spring AI BOM:
<artifactId>spring-ai-bom</artifactId>
它主要是用来统一 Spring AI 相关依赖版本。
第二个是 DeepSeek starter:
<artifactId>spring-ai-starter-model-deepseek</artifactId>
这个就是我们本篇接入 DeepSeek 的核心依赖。Spring AI 官方文档里 DeepSeek Chat 也是通过 DeepSeek 相关配置接入的。
五、配置 application.yml
接下来配置 application.yml。
server:
port: 8080
spring:
ai:
deepseek:
api-key: ${DEEPSEEK_API_KEY}
chat:
options:
model: deepseek-chat
这里先保持最小配置。
重点有两个:
api-key: ${DEEPSEEK_API_KEY}
表示从环境变量里读取 DeepSeek API Key。
model: deepseek-chat
表示使用 DeepSeek 的 deepseek-chat 模型。
Spring AI DeepSeek 文档中也说明,spring.ai.deepseek.chat.options.model 可以使用 deepseek-chat 或 deepseek-reasoner,默认模型为 deepseek-chat。
六、配置 DeepSeek API Key
这里建议不要直接把 Key 写死在配置文件里。
本地开发可以先临时配置环境变量。
Mac / Linux
export DEEPSEEK_API_KEY=你的DeepSeekKey
然后在同一个终端里启动项目:
mvn spring-boot:run
Windows PowerShell
$env:DEEPSEEK_API_KEY="你的DeepSeekKey"
然后启动项目:
mvn spring-boot:run
如果你只是本地测试,临时写在 application.yml 里也能跑。
比如:
spring:
ai:
deepseek:
api-key: sk-xxxxxx
Tips:但这种方式不建议提交到 Git,API Key 属于敏感信息,别直接放到公开仓库里。
七、创建启动类
新建启动类:
package com.itlay.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiDeepseekDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDeepseekDemoApplication.class, args);
}
}
这部分跟普通 Spring Boot 项目没区别。
八、写第一个对话接口
接下来写最关键的 Controller。
新建:
com.itlay.demo.controller.ChatController
代码如下:
package com.itlay.demo.controller;
import org.springframework.ai.chat.client.ChatClient;
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;
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
}
这段代码很短,但已经完成了一个完整的 AI 对话接口。
我们拆开看一下。
1. 注入 ChatClient.Builder
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
ChatClient 是 Spring AI 里比较常用的对话客户端,官方文档也给了通过 ChatClient.Builder 构建客户端的示例。
你可以先把它理解成:
用来帮我们向模型发起对话请求的客户端。
2. 构造 Prompt
this.chatClient.prompt()
这里表示开始构造一次对话请求。
3. 传入用户问题
.user(message)
这里把接口接收到的 message 传给模型。
4. 发起调用
.call()
这里才是真正发起请求。
5. 获取文本结果
.content()
这里拿到模型返回的文本内容。
整个链路就是:

九、启动项目测试
启动项目:
mvn spring-boot:run
控制台正常启动后,浏览器访问:
http://localhost:8080/ai/chat?message=用大白话介绍一下Spring AI
也可以用 curl:
curl "http://localhost:8080/ai/chat?message=用大白话介绍一下Spring AI"
如果一切正常,你会看到类似这样的回答:
Spring AI 可以理解成 Spring 生态里接入大模型能力的一套工具。对于 Java 开发者来说,它可以让你用熟悉的 Spring Boot 方式,把 AI 对话、提示词、工具调用等能力接进项目里。
到这一步,第一个 Spring AI + DeepSeek 对话接口就跑通了。
这一步特别重要。
因为很多人学新东西,卡的不是后面多高级的部分,而是第一步就没跑起来。
十、加一个 System Prompt,让回答更稳定一点
刚才那个接口已经能用了。
但它的问题是,模型回答风格不太受控。
比如同样的问题,它可能有时候回答很长,有时候回答很散。
我们可以加一个 system 提示词,告诉模型它应该怎么回答。
新增一个接口:
@GetMapping("/chat2")
public String chat2(@RequestParam String message) {
return this.chatClient.prompt()
.system("你是一个面向 Java 开发者的 Spring AI 入门助手。回答要求:简洁、清楚、少说空话,尽量用大白话解释。")
.user(message)
.call()
.content();
}
完整 Controller 变成这样:
package com.itlay.demo.controller;
import org.springframework.ai.chat.client.ChatClient;
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;
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
@GetMapping("/chat2")
public String chat2(@RequestParam String message) {
return this.chatClient.prompt()
.system("你是一个面向 Java 开发者的 Spring AI 入门助手。回答要求:简洁、清楚、少说空话,尽量用大白话解释。")
.user(message)
.call()
.content();
}
}
访问:
http://localhost:8080/ai/chat2?message=Spring AI适合什么人学
这个接口相比第一个接口,多了一个系统提示词。
你可以先简单理解成:
system用来约束模型角色和回答风格。user是用户真正提出的问题。
这一步虽然简单,但已经开始接近实际项目里的 Prompt 设计了。
十一、再写一个 POST 接口,更像真实项目
GET 接口适合演示。
但真实项目里,一般更常用 POST 请求。
比如前端传一个 JSON:
{
"message": "请用三句话解释一下 Spring AI"
}
我们先创建一个请求对象。
ChatRequest.java
package com.itlay.demo.dto;
public class ChatRequest {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
然后 Controller 里加一个 POST 接口。
@PostMapping("/chat")
public String chatPost(@RequestBody ChatRequest request) {
return this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(request.getMessage())
.call()
.content();
}
完整 Controller:
package com.itlay.demo.controller;
import com.itlay.demo.dto.ChatRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
@GetMapping("/chat2")
public String chat2(@RequestParam String message) {
return this.chatClient.prompt()
.system("你是一个面向 Java 开发者的 Spring AI 入门助手。回答要求:简洁、清楚、少说空话,尽量用大白话解释。")
.user(message)
.call()
.content();
}
@PostMapping("/chat")
public String chatPost(@RequestBody ChatRequest request) {
return this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(request.getMessage())
.call()
.content();
}
}
这个版本就更像真实项目里会用的接口了。
十二、给接口返回一个统一结构
上面的接口直接返回字符串,演示可以。
但真实项目里,我们一般不会直接返回一段文本,而是会包一层统一响应。
比如:
{
"success": true,
"data": "模型回答内容"
}
先建一个通用返回类。
ApiResponse.java
package com.itlay.demo.dto;
public class ApiResponse<T> {
private boolean success;
private T data;
private String message;
public ApiResponse() {
}
public ApiResponse(boolean success, T data, String message) {
this.success = success;
this.data = data;
this.message = message;
}
public static <T> ApiResponse<T> ok(T data) {
return new ApiResponse<>(true, data, "success");
}
public static <T> ApiResponse<T> fail(String message) {
return new ApiResponse<>(false, null, message);
}
public boolean isSuccess() {
return success;
}
public T getData() {
return data;
}
public String getMessage() {
return message;
}
public void setSuccess(boolean success) {
this.success = success;
}
public void setData(T data) {
this.data = data;
}
public void setMessage(String message) {
this.message = message;
}
}
然后把 POST 接口改一下:
@PostMapping("/chat-result")
public ApiResponse<String> chatResult(@RequestBody ChatRequest request) {
String content = this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(request.getMessage())
.call()
.content();
return ApiResponse.ok(content);
}
这样返回格式就更像一个正常后端接口了。
返回示例:
{
"success": true,
"data": "Spring AI 更像是 Spring 生态里的统一接入层,可以让你用更熟悉的 Spring Boot 方式接入不同模型,而不是自己手写 HTTP 调用。",
"message": "success"
}
十三、简单加一下参数校验
上面的代码还有个问题:
如果用户传空字符串,接口还是会去调用模型。
这就没必要了。
我们简单加一个校验。
@PostMapping("/chat-result")
public ApiResponse<String> chatResult(@RequestBody ChatRequest request) {
if (request == null || request.getMessage() == null || request.getMessage().trim().isEmpty()) {
return ApiResponse.fail("message 不能为空");
}
String content = this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(request.getMessage())
.call()
.content();
return ApiResponse.ok(content);
}
这点很小,但挺重要。
因为真实接口里,别让无效请求白白消耗模型调用。
后面如果再深入,还可以继续加:
- 字符长度限制
- 频率限制
- 用户鉴权
- 调用日志
- 异常处理
但第一篇先不用展开太多。
十四、加一个简单异常处理
AI 接口调用并不是百分百成功。
可能会遇到:
- API Key 错误
- 网络超时
- 模型不可用
- 请求频率限制
- 服务端异常
所以我们简单包一层异常处理。
@PostMapping("/chat-result")
public ApiResponse<String> chatResult(@RequestBody ChatRequest request) {
if (request == null || request.getMessage() == null || request.getMessage().trim().isEmpty()) {
return ApiResponse.fail("message 不能为空");
}
try {
String content = this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(request.getMessage())
.call()
.content();
return ApiResponse.ok(content);
} catch (Exception e) {
return ApiResponse.fail("AI 服务调用失败:" + e.getMessage());
}
}
这里为了演示简单,直接在 Controller 里 catch 了。
如果是正式项目,我更建议放到:
- service 层
- 全局异常处理器
- 调用日志里
别让 Controller 越写越厚。
十五、稍微整理一下代码结构
如果你只是演示,上面写法够了。
但如果你想让代码更像项目一点,可以拆成 Service。
AiChatService.java
package com.itlay.demo.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class AiChatService {
private final ChatClient chatClient;
public AiChatService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
public String chat(String message) {
return this.chatClient.prompt()
.system("你是一个 Java 后端技术助手,回答尽量简洁、清楚。")
.user(message)
.call()
.content();
}
}
ChatController.java
package com.itlay.demo.controller;
import com.itlay.demo.dto.ApiResponse;
import com.itlay.demo.dto.ChatRequest;
import com.itlay.demo.service.AiChatService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/ai")
public class ChatController {
private final AiChatService aiChatService;
public ChatController(AiChatService aiChatService) {
this.aiChatService = aiChatService;
}
@PostMapping("/chat-result")
public ApiResponse<String> chatResult(@RequestBody ChatRequest request) {
if (request == null || request.getMessage() == null || request.getMessage().trim().isEmpty()) {
return ApiResponse.fail("message 不能为空");
}
try {
String content = aiChatService.chat(request.getMessage());
return ApiResponse.ok(content);
} catch (Exception e) {
return ApiResponse.fail("AI 服务调用失败:" + e.getMessage());
}
}
}
这样结构会清楚一些:
controller:负责接收请求
service:负责调用 AI
dto:负责请求和响应对象
虽然只是一个小 Demo,但这个拆法更适合后面继续扩展。
十六、项目目录结构参考
最后目录大概是这样:
spring-ai-deepseek-demo
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── itlay
│ └── demo
│ ├── SpringAiDeepseekDemoApplication.java
│ ├── controller
│ │ └── ChatController.java
│ ├── dto
│ │ ├── ApiResponse.java
│ │ └── ChatRequest.java
│ └── service
│ └── AiChatService.java
└── resources
└── application.yml
这套结构不复杂,但已经比“所有代码写在 Controller 里”要舒服一点。
十七、第一次最容易踩的几个坑
1. API Key 没配成功
最常见。
如果报鉴权失败,先检查:
echo $DEEPSEEK_API_KEY
看看环境变量到底有没有生效。
如果你是 IDEA 启动项目,还要注意:
终端里配置的环境变量,不一定自动带到 IDEA 的运行配置里。
这种情况可以在 IDEA 的 Run Configuration 里手动配置环境变量。
2. 配置前缀写错
DeepSeek 原生接法用的是:
spring:
ai:
deepseek:
api-key: xxx
不要写成:
spring:
ai:
openai:
api-key: xxx
这是两套不同配置。
当然,DeepSeek 本身也提供 OpenAI 兼容接口,但这篇我们走的是 Spring AI 原生 DeepSeek 接法。
3. 模型名写错
本篇使用的是:
model: deepseek-chat
别一上来乱填模型名。
Spring AI DeepSeek 文档里也明确提到,模型 ID 可以使用 deepseek-chat 或 deepseek-reasoner。
4. 网络问题
如果接口一直超时,别只盯着代码看。
先确认:
- 当前机器能不能访问 DeepSeek API
- 公司网络有没有限制
- 本地代理有没有影响
- API Key 是否有效
很多时候,新手第一次接模型服务,卡住的不是代码,而是网络和权限。
5. 版本不匹配
Spring AI 和 Spring Boot 版本最好不要乱搭。
我这篇示例用的是:
Spring Boot 3.5.0
Spring AI 1.0.0
JDK 17
你如果换版本,依赖名、配置项、API 都可能有差异。
新手第一篇建议先按一套版本跑通,再考虑升级。
十八、先不要搞太多东西
这篇我们只做了一件事:
跑通第一个 Spring AI + DeepSeek 对话接口。
不要一上来就搞:
- RAG
- 向量库
- Agent
- 多轮记忆
- Tools
- 流式输出
这些都可以做,但不是第一步。
第一步最重要的是建立信心:
原来 Spring Boot 接 AI 模型,也就是这么一步步配起来。
你跑通了这个 Demo,后面再看 Prompt、结构化输出、Tools,就不会那么虚。
总结
本篇文章我们用 Spring Boot + Spring AI + DeepSeek 跑通了一个最小对话接口。
主要做了这些事:
- 创建 Spring Boot 项目
- 引入 Spring AI DeepSeek 依赖
- 配置 DeepSeek API Key
- 使用
ChatClient调用模型 - 写了 GET 和 POST 两种接口
- 做了简单的统一返回和参数校验
- 补了一些常见坑
其实整体看下来,你会发现第一步并没有想象中那么复杂。
对 Java 开发者来说,Spring AI 的好处就在于:
它让你可以继续用熟悉的 Spring Boot 方式,把 AI 能力接进项目里。
这篇先把门打开,大家加油:)
更多推荐



所有评论(0)