
实战:Spring AI + Ollama + DeepSeek:本地化AI对话系统实战(含Docker部署与Redis持久化)
在大模型技术迅速普及的当下,如何在本地环境中以低成本部署高性能AI服务,成为了广大开发者关注的焦点。本文将结合Spring AI、Ollama和DeepSeek-R1模型,通过Docker容器化部署和Redis持久化存储,手把手教你构建一个支持连续对话的AI应用。这一方案特别适合需要私有化部署、数据安全可控的场景,如企业内部知识库、教育问答系统等。
概述
在大模型技术迅速普及的当下,如何在本地环境中以低成本部署高性能AI服务,成为了广大开发者关注的焦点。本文将结合Spring AI、Ollama和DeepSeek-R1模型,通过Docker容器化部署和Redis持久化存储,手把手教你构建一个支持连续对话的AI应用。这一方案特别适合需要私有化部署、数据安全可控的场景,如企业内部知识库、教育问答系统等。
这是一篇全家桶式教程,主要内容包括:
- 环境搭建:从安装Docker开始,到通过Docker安装Redis、Ollama,并部署DeepSeek模型,一步步带你搞定环境配置。
- Spring AI集成:详细讲解如何将Ollama和DeepSeek集成到Spring AI中,并实现连续对话功能。
- 效果验证:通过实际测试,展示系统的运行效果,让你直观感受这一方案的强大之处。
环境准备
在开始之前,请确保你的环境满足以下要求:
- 操作系统:Windows 11
- Java版本:JDK 17+(请注意Spring Boot 3.4.3的兼容性)
- 依赖管理:Maven 3.8.3+
环境搭建(docker,redis, ollama 和deepseek)
1. 安装docker
本地环境使用Docker进行部署,可以大大节省环境配置的工作量,同时减少组件对系统性能的影响。在不开发时,关闭Docker,还能避免各种干扰。对于还不熟悉Docker的同学,建议尽快学习掌握这一强大工具。
由于很多教程都是基于Linux系统的,这里我们详细讲解一下在Windows本地环境下的搭建步骤。首先,访问docker官网,根据你的系统选择合适的版本进行下载和安装。安装完成后,进入PowerShell,输入指令docker ps
,如果能看到相关输出,说明Docker安装成功。
安装后,进入PowerShell,输入指令 docker ps
,看到这个就OK了
2.Redis容器部署
说明:此命令从Docker Hub拉取指定版本的Redis镜像,为后续的容器部署做好准备。
docker pull redis:7.4.2
在本地创建文件夹C:\docker\redis\conf
和C:\docker\redis\data
,并在conf文件夹下创建文件redis.conf
,内容如下:
bind 0.0.0.0
port 6379
requirepass 123123
dir /data
appendonly yes
bind 0.0.0.0
:允许外部访问requirepass 123123
:设置访问密码,你可以根据需要自行设定,虽然是本地环境,但养成良好的安全习惯很重要appendonly yes
:开启AOF持久化
接下来,进行容器部署:
docker run -d \
-p 6579:6379 \
-v C:/docker/redis/data:/data \
-v C:/docker/redis/conf:/usr/local/etc/redis \
--name redis \
redis:7.4.2 redis-server /usr/local/etc/redis/redis.conf
说明:通过上述命令,我们基于刚才拉取的镜像创建并启动了一个Redis容器,同时将本地的配置文件和数据目录挂载到容器中,方便进行持久化存储和配置管理。
3. 安装ollama
下载ollama
docker pull ollama/ollama:0.6.2
创建本地文件夹C:\docker\ollama
,然后运行以下命令:
docker run -d \
-v C:\docker\ollama:/root/.ollama \
-p 11434:11434 \
--name ollama \
ollama/ollama:0.6.2
- 功能说明:映射本地模型存储目录
/root/.ollama
,开放11434端口供API调用。你可以通过Deepseek模型版本查看了解更多相关信息。
4. 安装Deepseep
模型拉取,由于我没有显卡,因此最多也就拉取7b模型,大家也可以根据自己的需求选择合适的模型。
docker exec -it ollama ollama pull deepseek-r1:7b
说明:此命令在已启动的Ollama容器中执行模型拉取操作,将DeepSeek-R1的7b版本模型下载到本地,以便后续进行调用和测试。
Spring AI 集成与代码实现
1. maven的核心依赖
<!-- 全局属性管理 -->
<properties>
<java.version>23</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 自定义依赖版本 -->
<spring-boot.version>3.4.3</spring-boot.version>
<spring.ai.version>1.0.0-M6</spring.ai.version>
<maven.compiler.version>3.11.0</maven.compiler.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<!-- <version>1.0.0-SNAPSHOT</version>-->
<version>1.0.0-M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 构建配置 -->
<build>
<plugins>
<!-- 编译器插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<release>${java.version}</release>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- Spring Boot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<!-- 仓库配置 -->
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
说明:上述Maven配置文件中,我们引入了Spring Boot、Spring Data Redis、Spring AI以及Lombok等依赖,为项目提供了Web开发、Redis数据操作、AI模型集成以及代码简化等功能支持。同时,通过插件配置和仓库配置,确保项目的构建和依赖管理能够顺利进行。
2. 核心配置(application.yml
)
server:
port: 8083
spring:
application:
name: Ollama-AI
data:
redis:
host: 127.0.0.1
port: 6579
password: 123123
database: 0
ai:
ollama:
base-url: http://127.0.0.1:11434
chat:
model: deepseek-r1:7b
说明:在application.yml文件中,我们配置了服务器端口、Spring应用名称、Redis连接信息以及AI相关配置。其中,Redis的主机、端口、密码等参数需要与实际部署的Redis容器相匹配,而AI部分则指定了Ollama的基地址和使用的模型版本,确保系统能够正确连接和调用相应的AI服务。
3. 实现连续对话
3.1 控制器层(OllamaChatController.java
)
@Slf4j
@RestController
@RequestMapping("/ai/v1")
public class OllamaChatController {
private final ChatClient chatClient;
private final ChatMemory chatMemory;
public OllamaChatController(ChatClient.Builder builder, ChatMemory chatMemory) {
this.chatClient = builder
.defaultSystem("只回答问题,不进行解释")
.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
.build();
this.chatMemory = chatMemory;
}
@GetMapping("/ollama/redis/chat")
public String chat(@RequestParam String userId, @RequestParam String input) {
log.info("/ollama/redis/chat input: [{}]", input);
String text = chatClient.prompt()
.user(input)
.advisors(spec -> spec.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, userId)
.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
.call()
.content();
return text.split("</think>")[1].trim();
}
}
说明:该控制器类定义了一个RESTful接口,用于接收用户的输入并返回AI模型的回复。通过依赖注入获取ChatClient和ChatMemory实例,构建聊天客户端,并在chat方法中处理用户的输入,调用AI模型进行对话,并将结果返回给前端。
3.2 Redis持久化(ChatRedisMemory.java
)
@Slf4j
@Component
public class ChatRedisMemory implements ChatMemory {
private static final String KEY_PREFIX = "chat:history:";
private final RedisTemplate<String, Object> redisTemplate;
public ChatRedisMemory(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void add(String conversationId, List<Message> messages) {
String key = KEY_PREFIX + conversationId;
List<ChatEntity> listIn = new ArrayList<>();
for(Message msg: messages){
String[] strs = msg.getText().split("</think>");
String text = strs.length==2?strs[1]:strs[0];
ChatEntity ent = new ChatEntity();
ent.setChatId(conversationId);
ent.setType(msg.getMessageType().getValue());
ent.setText(text);
listIn.add(ent);
}
redisTemplate.opsForList().rightPushAll(key,listIn.toArray());
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
}
@Override
public List<Message> get(String conversationId, int lastN) {
String key = KEY_PREFIX + conversationId;
Long size = redisTemplate.opsForList().size(key);
if (size == null || size == 0){
return Collections.emptyList();
}
int start = Math.max(0, (int) (size - lastN));
List<Object> listTmp = redisTemplate.opsForList().range(key, start, -1);
List<Message> listOut = new ArrayList<>();
ObjectMapper objectMapper = new ObjectMapper();
for(Object obj: listTmp){
ChatEntity chat = objectMapper.convertValue(obj, ChatEntity.class);
// log.info("MessageType.USER [{}], chat.getType [{}]",MessageType.USER, chat.getType());
if(MessageType.USER.getValue().equals(chat.getType())){
listOut.add(new UserMessage(chat.getText()));
}else if(MessageType.ASSISTANT.getValue().equals(chat.getType())){
listOut.add(new AssistantMessage(chat.getText()));
}else if(MessageType.SYSTEM.getValue().equals(chat.getType())){
listOut.add(new SystemMessage(chat.getText()));
}
}
return listOut;
}
@Override
public void clear(String conversationId) {
redisTemplate.delete(KEY_PREFIX + conversationId);
}
}
说明:此组件实现了ChatMemory接口,利用Redis进行对话历史记录的持久化存储。通过RedisTemplate操作Redis列表,实现了对话记录的添加、获取和清除功能,确保对话上下文能够在多次请求之间保持连贯,从而支持连续对话。
3.3 配置类与序列化(RedisConfig.java
)
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//生成整个 RedisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
说明:该配置类用于创建RedisTemplate Bean,通过设置不同的序列化方式,确保在与Redis进行数据交互时,键和值能够正确地进行序列化和反序列化操作,从而保证数据的完整性和可读性。
3.4. 实体类(ChatEntity.java
)
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ChatEntity implements Serializable {
String chatId;
String type;
String text;
}
说明:这是一个简单的Java实体类,用于表示对话中的每一条消息记录。包含对话ID、消息类型和消息文本三个属性,通过Lombok的注解自动生成构造方法、getter和setter方法,简化了代码编写。
3.5. 启动类(OllamaChatDemoApplication.java
)
@EnableCaching
@SpringBootApplication
public class OllamaChatDemoApplication {
public static void main(String[] args) {
SpringApplication.run(OllamaChatDemoApplication.class, args);
}
}
测试与验证
环境启动
确保docker部署的redis和ollama服务都正常运行,通过 docker ps
查看
如果没有的话, 可以 通过docker start redis
, docker start ollama
来启动
Spring 服务启动后,我们看看效果吧
我们Spring 项目中,开放了一个接口
http://127.0.0.1:8083/ai/v1/ollama/redis/chat
接下来我们问几个问题, 这些问题一环套一环, 看看他回答的怎么样吧.
- 你是谁
- 列举3个中国文学家
- 他们的出生地在哪
- 这些地方都有什么特产
不过,由于部署的版本较低,回答速度可能较慢,且可能出现中英文混杂的情况。在生产环境中,建议部署高算力的版本以获得更好的性能。
另外,我们看看redis中的存储
补充点内容
本文通过完整的Docker部署、Spring AI集成和Redis持久化方案,详细展示了如何低成本构建本地化AI服务。这一方案不仅支持连续对话等复杂场景,还通过容器化技术实现了环境隔离。对于开发者而言,掌握这一技术栈将显著提升私有化AI应用的开发效率,为各种需要数据安全和隐私保护的应用场景提供有力支持。
更多推荐
所有评论(0)