SpringAI+Deepseek大模型应用开发 —对话机器人实战教程(附完整截图)
从零搭建 Spring AI 项目,实现 Java 调用本地 DeepSeek 大模型,并支持流式输出与角色设定。
前言
随着 AI 技术的快速发展,大模型已经逐渐成为企业开发的重要组成部分。
对于 Java 开发者来说,Spring 官方推出的 Spring AI 极大降低了接入大模型的门槛。
在本项目中,你将学习并完成:
✅ Spring AI 环境搭建
✅ Ollama 本地大模型部署
✅ DeepSeek-R1 模型接入
✅ Spring Boot 集成 AI 服务
✅ ChatClient 对话调用
✅ AI 流式输出(Streaming)
✅ 自定义 AI 人设(System Prompt)
✅ Lombok 简化开发
✅ AI 调用日志记录
✅ Vue3 前端项目部署
✅ AI 聊天页面实现
✅ 前后端接口联调
✅ 跨域问题解决
✅ AI 聊天会话管理
✅ 多轮对话功能实现
✅ 文件上传入口集成
✅ Spring AI + Ollama + DeepSeek 完整对话系统搭建
最终实现:
http://localhost:8080/ai/chat?prompt=你好
直接与本地大模型对话。
首先我们需要有jdk17、ollama、并且安装好deepseek-r1模型
通过ollama安装好模型(时间较长)
ollama run deepseek-r1:7b

安装好后在cmd中使用命令运行deepseek
(注意:这个界面不要关闭)
ollama run deepseek-r1:7b

一、创建 Spring Boot 项目
打开 IDEA,选择:
New Project → Spring Initializr
注意jdk最好一定要是17
创建项目

二、添加项目依赖
在依赖选择界面中添加:
1. Spring Web
用于提供 REST 接口能力。

2. MySQL Driver
后续存储聊天记录时使用。

3. Ollama
Spring AI 官方提供的 Ollama Starter。

最终依赖:
Spring Web
MySQL Driver
Ollama
点击:
Create
完成项目创建。
四、配置 Spring AI
打开:
src/main/resources/application.yaml
添加配置:
spring:
application:
name: heima-springai
ai:
ollama:
base-url: http://127.0.0.1:11434
chat:
model: deepseek-r1:7b
为什么用
application.yaml后缀?YAML 格式比传统
.properties更结构化、易读,天然支持层级嵌套(如spring.ai.ollama),配置 Spring AI 这类多层级配置时更简洁清晰,也更符合现代 Spring Boot 项目的主流规范。为什么要把
localhost改成127.0.0.1?有些电脑默认把
localhost解析为 IPv6 地址::1,但 Ollama 默认只监听 IPv4 的127.0.0.1:11434,导致 Spring 连接超时。直接写死127.0.0.1,可强制使用 IPv4 回环地址,绕开系统 IPv6 解析异常,保证能稳定连接到本地 Ollama 服务。
五、创建 ChatClient 配置
Spring AI 推荐通过 ChatClient 调用大模型。
创建:
config/CommonConfiguration.java
代码如下:
package com.itheima.heimaspringai.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommonConfiguration {
@Bean
public ChatClient chatClient(OllamaChatModel model) {
return ChatClient
.builder(model)
.build();
}
}
配置完成效果

CommonConfiguration 配置类
该配置类用于向 Spring 容器中注册
ChatClient对象,方便后续统一调用 AI 大模型服务。代码说明:
@Bean:将ChatClient注册为 Spring Bean。OllamaChatModel model:自动注入 Spring AI 提供的 Ollama 模型实例。ChatClient.builder(model):基于指定的大模型创建聊天客户端。build():完成构建并交由 Spring 容器管
六、创建聊天接口
创建:
controller/ChatController.java
代码:
package com.itheima.heimaspringai.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
public class ChatController {
private final ChatClient chatClient;
@RequestMapping("/chat")
public String chat(String prompt){
return chatClient
.prompt()
.user(prompt)
.call()
.content();
}
}
创建 Controller

ChatController 控制器说明
该控制器用于对外提供 AI 对话接口,通过注入
ChatClient与大模型进行交互,并将模型返回结果直接响应给前端。代码解析:
@RestController:标识为 Spring Boot REST 控制器,返回 JSON 或字符串数据。@RequestMapping("/ai"):统一设置接口访问前缀为/ai。@RequiredArgsConstructor:Lombok 自动生成构造方法,实现ChatClient的依赖注入。private final ChatClient chatClient:注入 Spring AI 提供的聊天客户端对象。chat()方法:接收用户输入的prompt,调用大模型生成回复并返回结果。
prompt():创建对话请求。user(prompt):设置用户问题。call():发送请求到大模型。content():获取模型返回的文本内容。
七、解决 Lombok 报错
如果:
@RequiredArgsConstructor
出现红色报错。
需要在 pom.xml 添加:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
</dependency>
刷新 Maven。
Lombok配置截图

@RequiredArgsConstructor、@Data、@Builder等 Lombok 注解用于自动生成构造方法、getter/setter 和其他样板代码。引入 Lombok 依赖后,Spring Boot 能识别这些注解,消除 IDE 报错,提高开发效率,同时保持代码简洁。
八、启动项目测试
启动项目:
游览器访问:
http://localhost:8080/ai/chat?prompt=你是谁?
返回:
您好!我是由中国的深度求索(DeepSeek)公司开发的智能助手 DeepSeek-R1。
测试结果

九、实现流式输出
普通模式需要等待模型全部生成完成。
Spring AI 支持流式返回。
导入:
import reactor.core.publisher.Flux;
修改 Controller:
@RequestMapping(
value = "/chat",
produces = "text/html;charset=UTF-8"
)
public Flux<String> chat(String prompt){
return chatClient
.prompt(prompt)
.stream()
.content();
}
流式输出代码

流式输出解释(ChatController.java)
在
ChatController中,使用了 Spring WebFlux 的Flux<String>来实现流式响应:核心点说明
- 流式输出 (
.stream())
- 调用
chatClient.prompt(prompt).stream()会以流的方式返回 AI 生成的内容。- 优点:前端可以实时接收到生成结果,而不是等待整个回答完成。
- 适合长文本或对话机器人场景。
- 返回类型
Flux<String>
Flux是 Spring WebFlux 提供的响应式类型,可以支持多次异步数据推送。- 每生成一段文本,就通过
Flux发送给前端,实现“边生成边显示”。为什么要设置
producesproduces = "text/html;charset=UTF-8"
- 明确告诉浏览器返回内容的类型和编码,防止中文乱码。
- 对于流式输出,Spring 会以 Server-Sent Events (SSE) 或分块响应的方式发送文本流,前端可以逐段解析和渲染。
流式输出效果

模型边生成边返回
十、自定义 AI 人设
很多场景下需要让 AI 扮演固定角色。
例如:
-
客服
-
面试官
-
英语老师
-
编程导师
修改CommonConfiguration:
@Bean
public ChatClient chatClient(OllamaChatModel model){
return ChatClient
.builder(model)
.defaultSystem("""
你是一个热血、可爱的智能助手,
你的名字叫小团团,
请以小团团身份回答问题。
""")
.build();
}
配置系统提示词

自定义 AI 人设
为了让 AI 具备固定的身份、语气和行为风格,可以在创建
ChatClient时通过defaultSystem()设置系统提示词(System Prompt)。
defaultSystem()本质上是为大模型设置全局系统提示词(System Prompt),能够统一控制 AI 的身份、语气和行为规则,是实现个性化 AI 助手的重要方式。
测试效果
访问:
http://localhost:8080/ai/chat?prompt=你是谁?
返回:

AI 已按照指定身份回答问题
十一、项目结构
最终项目结构如下:
src
└─main
├─java
│ └─com.itheima.heimaspringai
│ ├─config
│ │ └─CommonConfiguration.java
│ ├─controller
│ │ └─ChatController.java
│ └─HeimaSpringAiApplication.java
│
└─resources
└─application.yaml
核心流程解析
整个调用链如下:
浏览器
↓
ChatController
↓
ChatClient
↓
Spring AI
↓
Ollama
↓
DeepSeek-R1
关键代码:
chatClient
.prompt()
.user(prompt)
.call()
.content();
流式版本:
chatClient
.prompt(prompt)
.stream()
.content();
十三、 开启会话日志,观察 Spring AI 请求过程
在完成 AI 人设配置后,为了方便调试和排查问题,我们可以开启 Spring AI 的会话日志功能。这样能够清晰地看到用户输入、提示词构建以及模型返回结果。
配置日志级别
在 application.yaml 中添加如下配置:
logging:
level:
org.springframework.ai.chat.client.advisor: debug
com.itheima.heimaspringai: debug
配置说明
-
org.springframework.ai.chat.client.advisor-
开启 Spring AI Advisor 组件日志。
-
可以查看 Prompt 构建、请求发送、响应返回等详细过程。
-
-
com.itheima.heimaspringai-
开启当前项目包下的日志输出。
-
方便观察业务代码执行情况。
-
开启后,每次调用大模型时,控制台都会打印完整的会话信息。
十四、添加日志拦截器
为了记录 AI 对话内容,在 ChatClient 配置时增加日志 Advisor:
@Bean
public ChatClient chatClient(OllamaChatModel model){
return ChatClient
.builder(model)
.defaultSystem("你是一个热血、可爱的智能助手,你的名字叫小团团,请以小团团的身份和语气回答问题")
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
代码说明
.defaultAdvisors(new SimpleLoggerAdvisor())
用于给 ChatClient 添加日志拦截器。
其作用包括:
-
记录用户输入内容
-
记录系统提示词(System Prompt)
-
记录模型返回结果
-
方便调试 Prompt 效果
-
便于排查 AI 回答异常问题
十五、查看会话日志输出
项目启动后访问接口:
http://localhost:8080/ai/chat?prompt=你好
控制台会输出类似信息:
{
"messageType":"ASSISTANT",
"text":"你好呀!我是小团团,一个可爱又活泼的智能助手。有什么我可以为你做的吗?😊"
}

十六、 会话日志的作用
在实际开发中,会话日志非常重要,主要用于:
调试 Prompt
查看最终发送给大模型的提示词内容。
验证 AI 人设
确认系统角色是否成功生效。
排查回答异常
当模型输出不符合预期时,可以通过日志快速定位问题。
优化提示词工程
观察不同 Prompt 对回答质量的影响,从而持续优化 AI 效果。
十七. 对接前端项目,实现 AI 应用中心
前面已经完成了 Spring AI + Ollama + DeepSeek 的后端对话功能,接下来开始接入前端项目,实现完整的 AI 应用页面。
项目效果
首页提供多个 AI 应用入口:
-
AI 聊天
-
哄哄模拟器
-
智能客服
-
ChatPDF
用户点击对应功能即可进入具体业务页面。
十八. 部署并启动前端项目
前端项目采用 Vue3 + Vite 开发。
项目结构如下:
spring-ai-nginx
├── conf
├── contrib
├── docs
├── html
├── logs
├── temp
└── nginx.exe
启动前端项目(直接点击nginx.exe就可以)

启动成功后访问:
http://localhost:5173
即可看到 AI 应用中心首页。

十九. 进入 AI 聊天模块
点击首页中的:
AI聊天
进入聊天页面。
二十. 解决CORS问题
跨域配置(CORS)
作用:
- 解决前后端分离开发时的跨域访问问题。
- 允许前端(如
localhost:5173)访问后端接口(如localhost:8080)。 - 开放指定请求方式(GET、POST、DELETE、OPTIONS)和请求头。
- 避免浏览器出现 CORS 跨域拦截 或 Failed to fetch 错误。
简而言之:
该配置用于允许前端项目正常调用 Spring Boot 后端接口,实现前后端联调。
由于前端运行在:
http://localhost:5173
后端运行在:
http://localhost:8080
属于不同端口,因此需要解决跨域问题。
示例配置:
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173")
.allowedMethods("*")
.allowedHeaders("*");
}
};
}
}
这样前端即可正常调用后端接口。

- @Configuration
表示这是一个 Spring 配置类,会被 Spring 容器识别并加载。- implements WebMvcConfigurer
实现WebMvcConfigurer接口,可以自定义 Spring MVC 的行为,比如跨域、拦截器、格式化器等。- addCorsMappings(CorsRegistry registry)
这是配置跨域访问(CORS)的核心方法:
addMapping("/**"):表示对所有接口路径都允许跨域。allowedOrigins("*"):允许任意域名访问(开发阶段常用,生产可指定域名)。allowedMethods("GET","POST","DELETE","OPTIONS"):允许的 HTTP 方法。allowedHeaders("*"):允许所有请求头。- 整体作用
- 解决前端跨域问题,例如前端
localhost:5173请求后端localhost:8080接口时,浏览器不会再报 CORS 错误。- 保证前端可以正常发送 AJAX 或 fetch 请求调用后端接口。
二十一. 前后端聊天接口打通
当前端发送:
你好,你是谁?
请求流程如下:
Vue页面
↓
Axios请求
↓
Spring Boot
↓
Spring AI
↓
Ollama
↓
DeepSeek-R1
↓
返回结果
后端收到消息后:
chatClient
.prompt(prompt)
.call()
.content();
调用 DeepSeek 模型生成回复。

整体架构:
┌──────────────┐
│ Vue3前端 │
└──────┬───────┘
│ HTTP
▼
┌──────────────┐
│ Spring Boot │
└──────┬───────┘
│
▼
┌──────────────┐
│ Spring AI │
└──────┬───────┘
│
▼
┌──────────────┐
│ Ollama │
└──────┬───────┘
│
▼
┌──────────────┐
│ DeepSeek-R1 │
└──────────────┘
二十二、会话记忆功能
在 Spring AI 中实现对话上下文记忆,核心是通过 ChatMemory 存储对话历史,并配合 MessageChatMemoryAdvisor 实现多轮对话的上下文关联,以下是关键实现步骤:
1. 注册 ChatMemory 对象
ChatMemory 是 Spring AI 提供的对话历史存储接口,我们使用 MessageWindowChatMemory 实现,它默认保留最近 10 条对话消息,防止上下文过长;通过 MessageChatMemoryAdvisor 将会话记忆功能注入 ChatClient,让每次对话自动带上上下文历史:
2. 配置带会话记忆的 ChatClient
通过 MessageChatMemoryAdvisor 将会话记忆功能注入 ChatClient,让每次对话自动带上上下文历史:

3. 接口层绑定会话 ID
在 Controller 中,通过前端传递的 chatId 区分不同用户的对话会话,确保每个会话的上下文独立存储:

4、验证是否实现会话记忆

这部分代码其实是在实现 “会话历史管理”,核心目标是让 AI 支持多会话、多轮对话以及历史记录查询。
博客中建议按照下面的结构来写,逻辑会非常清晰。
二十三、会话历史功能实现
为了实现类似 ChatGPT 的多会话能力,需要解决三个问题:
-
管理会话ID
-
保存会话ID
-
查询历史会话
整体流程如下:
用户发起聊天
│
▼
携带 chatId
│
▼
保存 chatId
│
▼
调用 AI
│
▼
绑定 chatId 到 Memory
│
▼
保存上下文消息
│
▼
查询历史会话
一、管理会话ID
功能说明
会话ID(chatId)用于区分不同聊天窗口。
例如:
chat-001
├─ 你好
├─ 你是谁
└─ 今天天气怎么样
chat-002
├─ Java是什么
├─ SpringBoot是什么
└─ 如何学习Java
AI会根据不同的 chatId 维护不同的上下文。
ChatHistoryRepository接口

public interface ChatHistoryRepository {
void save(String type, String chatId);
List<String> getChatIds(String type);
}
作用
定义会话管理规范:
save()
保存会话ID
getChatIds()
获取某业务下的所有会话ID
例如:
chat
service
pdf
不同业务可以拥有自己的会话列表。
二、保存会话ID
功能说明
当用户第一次进入聊天页面时,需要记录当前会话。
例如:
chat
├─ 1001
├─ 1002
└─ 1003
后续用户可以看到自己的历史会话。
InMemoryChatHistoryRepository实现

数据结构
private final Map<String, List<String>> chatHistory
= new HashMap<>();
结构如下:
{
"chat":[
"1001",
"1002",
"1003"
],
"pdf":[
"2001",
"2002"
]
}
save方法
@Override
public void save(String type, String chatId) {
List<String> charIds =
chatHistory.computeIfAbsent(
type,
k -> new ArrayList<>());
if(charIds.contains(chatId)){
return;
}
charIds.add(chatId);
}
代码解析
1. 获取会话列表
chatHistory.computeIfAbsent(
type,
k -> new ArrayList<>()
);
等价于:
if(!chatHistory.containsKey(type)){
chatHistory.put(type,new ArrayList<>());
}
List<String> chatIds =
chatHistory.get(type);
作用:
如果业务类型不存在,则自动创建集合。
2. 去重
if(charIds.contains(chatId)){
return;
}
避免重复保存。
例如:
1001
1001
1001
最终只保留一条记录。
3. 保存会话
charIds.add(chatId);
保存当前会话ID。
ChatController中调用

chatHistoryRepository.save("chat",chatId);
执行流程
用户发送消息
│
▼
chatController
│
▼
save(chatId)
│
▼
保存到Map
│
▼
调用AI
绑定会话上下文
.advisors(
a->a.param(
ChatMemory.CONVERSATION_ID,
chatId
)
)
作用
告诉 Spring AI:
当前聊天属于哪个会话
例如:
chatId = 1001
则后续所有消息:
用户:你好
AI:你好
用户:你是谁
AI:我是AI助手
都会保存到:
1001
对应的上下文中。
三、查询历史会话
会话保存后,需要提供查询接口。
查询会话ID列表

@GetMapping("/{type}")
public List<String> getChatIds(
@PathVariable("type")
String type){
return chatHistoryRepository
.getChatIds(type);
}
接口
GET /ai/history/chat
返回:
[
"1001",
"1002",
"1003"
]
getChatIds实现
建议放图:

@Override
public List<String> getChatIds(String type) {
return chatHistory.getOrDefault(
type,
List.of()
);
}
作用
如果不存在该业务类型:
chat
pdf
service
直接返回空集合:
[]
避免空指针异常。
查询具体会话内容

@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(
@PathVariable("type") String type,
@PathVariable("chatId") String chatId) {
List<Message> messages =
chatMemory.get(chatId);
if(messages == null){
return List.of();
}
return messages.stream()
.map(MessageVO::new)
.toList();
}
查询流程
chatId
│
▼
ChatMemory
│
▼
获取Message集合
│
▼
转换为MessageVO
│
▼
返回前端
MessageVO作用

@Data
public class MessageVO {
private String role;
private String content;
}
转换角色
switch(message.getMessageType())
Spring AI返回:
USER
ASSISTANT
转换成:
{
"role":"user",
"content":"你好"
}
{
"role":"assistant",
"content":"你好,请问有什么可以帮助您?"
}
方便前端渲染聊天记录。
┌────────────────────┐
│ 用户发送消息 │
│ prompt + chatId │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ ChatController.chat │
│ (聊天入口) │
└─────────┬──────────┘
│
├─────────────────────────────┐
│ │
▼ ▼
【1. 保存会话ID】 【2. AI对话并保存聊天记录】
chatHistoryRepository.save() chatClient.prompt()
│ │
▼ ▼
InMemoryChatHistoryRepository advisors()
│ 设置会话ID
▼ │
Map<String,List<String>> ▼
│ ChatMemory
│ │
▼ ▼
保存 chatId 保存用户消息和AI回复
(type=chat)
chat
├── chat001
├── chat002
└── chat003
更多推荐






所有评论(0)