Spring AI + DeepSeek,10分钟快速构建本地化AI对话系统!

1、环境准备

在开始之前,请确保你的环境满足以下要求:

  • 操作系统: Windows 11
  • Java版本: JDK 17+(请注意Spring Boot 3.4.3的兼容性)
  • 依赖管理: Maven 3.8.3+
2、环境搭建(docker,redis, ollama 和deepseek)
1. 安装docker

本地环境使用Docker进行部署,可以大大节省环境配置的工作量,同时减少组件对系统性能的影响。在不开发时,关闭Docker,还能避免各种干扰。对于还不熟悉Docker的同学,建议尽快学习掌握这一强大工具。

由于很多教程都是基于Linux系统的,这里我们详细讲解一下在Windows本地环境下的搭建步骤。首先,访问docker官网,根据你的系统选择合适的版本进行下载和安装。安装完成后,进入PowerShell,输入指令docker ps,如果能看到相关输出,说明Docker安装成功。

img

安装后,进入PowerShell,输入指令 docker ps ,看到这个就OK了

img
img

2.Redis容器部署

说明:此命令从Docker Hub拉取指定版本的Redis镜像,为后续的容器部署做好准备。

docker pull redis:7.4.2

在本地创建文件夹C:\docker\redis\confC:\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版本模型下载到本地,以便后续进行调用和测试。

3、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模型集成以及代码简化等功能支持。同时,通过插件配置和仓库配置,确保项目的构建和依赖管理能够顺利进行。微信搜索公众号:Java后端编程,回复:java 领取资料 。

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")
publicclass OllamaChatController {

    privatefinal ChatClient chatClient;
    privatefinal 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
publicclass ChatRedisMemory implements ChatMemory {

    privatestaticfinal String KEY_PREFIX = "chat:history:";
    privatefinal 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()));
            }elseif(MessageType.ASSISTANT.getValue().equals(chat.getType())){
                listOut.add(new AssistantMessage(chat.getText()));
            }elseif(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
publicclass 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);
    }
}


4、测试与验证
环境启动

确保docker部署的redis和ollama服务都正常运行,通过 docker ps查看

img
如果没有的话, 可以 通过docker start redis, docker start ollama来启动

Spring 服务启动后,我们看看效果吧

我们Spring 项目中,开放了一个接口

http://127.0.0.1:8083/ai/v1/ollama/redis/chat

接下来我们问几个问题, 这些问题一环套一环, 看看他回答的怎么样吧.

  • 你是谁
  • 列举3个中国文学家
  • 他们的出生地在哪
  • 这些地方都有什么特产

不过,由于部署的版本较低,回答速度可能较慢,且可能出现中英文混杂的情况。在生产环境中,建议部署高算力的版本以获得更好的性能。

img

img

img

img

另外,我们看看redis中的存储

img

补充

本文通过完整的Docker部署、Spring AI集成和Redis持久化方案,详细展示了如何低成本构建本地化AI服务。这一方案不仅支持连续对话等复杂场景,还通过容器化技术实现了环境隔离。

对于开发者而言,掌握这一技术栈将显著提升私有化AI应用的开发效率,为各种需要数据安全和隐私保护的应用场景提供有力支持。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

2024最新版优快云大礼包:《AGI大模型学习资源包》免费分享**

一、2025最新大模型学习路线

一个明确的学习路线可以帮助新人了解从哪里开始,按照什么顺序学习,以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛,没有明确的学习路线可能会导致新人感到迷茫,不知道应该专注于哪些内容。

我们把学习路线分成L1到L4四个阶段,一步步带你从入门到进阶,从理论到实战。

L1级别:AI大模型时代的华丽登场

L1阶段:我们会去了解大模型的基础知识,以及大模型在各个行业的应用和分析;学习理解大模型的核心原理,关键技术,以及大模型应用场景;通过理论原理结合多个项目实战,从提示工程基础到提示工程进阶,掌握Prompt提示工程。

L2级别:AI大模型RAG应用开发工程

L2阶段是我们的AI大模型RAG应用开发工程,我们会去学习RAG检索增强生成:包括Naive RAG、Advanced-RAG以及RAG性能评估,还有GraphRAG在内的多个RAG热门项目的分析。

L3级别:大模型Agent应用架构进阶实践

L3阶段:大模型Agent应用架构进阶实现,我们会去学习LangChain、 LIamaIndex框架,也会学习到AutoGPT、 MetaGPT等多Agent系统,打造我们自己的Agent智能体;同时还可以学习到包括Coze、Dify在内的可视化工具的使用。

L4级别:大模型微调与私有化部署

L4阶段:大模型的微调和私有化部署,我们会更加深入的探讨Transformer架构,学习大模型的微调技术,利用DeepSpeed、Lamam Factory等工具快速进行模型微调;并通过Ollama、vLLM等推理部署框架,实现模型的快速部署。

整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握;而L3 L4更多的是通过项目实战来掌握大模型的应用开发,针对以上大模型的学习路线我们也整理了对应的学习视频教程,和配套的学习资料。

二、大模型经典PDF书籍

书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础(书籍含电子版PDF)

三、大模型视频教程

对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识

四、大模型项目实战

学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

五、大模型面试题

面试不仅是技术的较量,更需要充分的准备。

在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。


因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

2024最新版优快云大礼包:《AGI大模型学习资源包》免费分享

### SpringAI DeepSeek 中实现流式返回 在处理长时间运行的任务或大量数据传输时,采用流式返回可以显著提升用户体验和资源利用率。对于SpringAI DeepSeek框架而言,通过使用`Server-Sent Events (SSE)` 或 `WebSocket`协议能够实现实时的数据推送。 当利用HTTP协议下的服务器发送事件(SSE),客户端建立连接后保持打开状态直到一方关闭它,在此期间服务端可随时向客户端推送上新消息[^1]。 下面是一个基于Spring WebFlux的简单例子来展示如何配置并应用SSE: ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class StreamController { @GetMapping("/stream") public Flux<String> stream() { return Flux.interval(Duration.ofSeconds(1)) .map(sequence -> "Event at: " + LocalDateTime.now()); } } ``` 这段代码定义了一个RESTful API `/stream` ,每当有新的请求到达时会每隔一秒产生一个新的时间戳字符串作为响应的一部分持续不断地发给调用者直至连接被终止。 为了使上述功能正常工作还需要确保项目依赖中包含了Webflux模块的支持: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> ``` 此外,如果希望更灵活地控制流的行为比如暂停/恢复数据流动,则可能需要引入Reactor库中的更多高级特性如`Sink.Many<T>`接口用于手动触发元素发射等操作。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值