跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava

SpringAI 通过 Ollama 本地部署 Deepseek 模型构建对话机器人

SpringAI 结合 Ollama 本地部署 Deepseek 模型实现对话机器人。通过 Docker 运行 Open WebUI 测试模型,集成 Spring Boot 依赖调用本地 API。构建向量数据库 pgvector 支持 RAG 检索增强生成,导入文本与 PDF 文档至向量库。演示 Function Call 自定义函数注册与调用,以及多模态能力如图片识别。涵盖从环境搭建到功能集成的完整流程。

GopherDev发布于 2026/4/11更新于 2026/9/1069 浏览
SpringAI 通过 Ollama 本地部署 Deepseek 模型构建对话机器人

SpringAI 结合 Ollama 本地部署 Deepseek 模型实现对话机器人

Java 调用 Deepseek

本地没有安装 Ollama、Docker、Open WebUI,可先学习相关部署文档。

下载 Deepseek 模型

打开命令行窗口,拉取一下 Deepseek 模型:

ollama run deepseek-r1:7b

本地测试

运行 Open WebUI 容器,选择 Deepseek-r1 模型进行测试。

Java 调用模型

注释掉以前的 moonshot 依赖并删除相关代码,引入 ollama 依赖:

<!-- 引入 Ollama 依赖-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>

修改配置类 Init:

package com.yan.springai;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class Init {
    final OllamaChatModel model2;

    @Bean
    public ChatClient chatClient(ChatMemory chatMemory) {
        return ChatClient.builder(model2)
                .defaultSystem("假如你是特朗普,接下来的对话你必须以特朗普的语气来进行?")
                .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
                .build();
    }

    @Bean
    public ChatMemory chatMemory() {
        return new InMemoryChatMemory();
    }
}

修改配置文件 application.yml:

spring:
  ai:
    ollama:
      chat:
        options:
          model: deepseek-r1:7b
          base-url: http://localhost:11434

构建数据库

增强检索 RAG

Embedding 是一种将对象表示为数值向量的方法。使用 ollama 拉取 embedding 模型(如 all-minilm):

ollama pull all-minilm

向量数据库

使用 pgvector 扩展 PostgreSQL 数据库添加向量相似性搜索功能。

拉取 pgvector 镜像:

docker run -d --name pgvector -p 5433:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg16

Springboot 集成 pgvector

引入依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>

配置 application.yml:

spring:
  ai:
    vectorstore:
      pgvector:
        index-type: HNSW
        distance-type: COSINE_DISTANCE
        dimensions: 384
        batching-strategy: TOKEN_COUNT
        max-document-batch-size: 1000
        ollama:
          chat:
            options:
              model: deepseek-r1:7b
              embedding:
                enabled: true
                model: all-minilm
              base-url: http://localhost:11434
        datasource:
          url: jdbc:postgresql://localhost:5433/springai
          username: postgres
          password: postgres

连接数据库后执行建表语句:

create extension if not exists vector;
create extension if not exists hstore;
create extension if not exists "uuid-ossp";
create TABLE if not exists vector_store(
    id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
    content text,
    metadata json,
    embedding vector(384)
);
create index on vector_store using HNSW(embedding vector_cosine_ops);

在 resources 中放置 txt 文件,创建 VectorAPI 类:

package com.yan.springai.vector;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;

@RestController
@RequiredArgsConstructor
public class VectorAPI {
    final VectorStore store;

    @GetMapping("/vec/write")
    public String write() throws IOException {
        StringBuffer text = new StringBuffer();
        ClassLoader classLoader = getClass().getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("code.txt");
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                text.append(line);
            }
        }
        store.write(Arrays.stream(text.toString().split("。")).map(Document::new).toList());
        return "success";
    }
}

导入完毕后,修改 Init 代码启用 QuestionAnswerAdvisor:

package com.yan.springai;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class Init {
    final OllamaChatModel model2;
    final VectorStore vectorStore;

    @Bean
    public ChatClient chatClient(ChatMemory chatMemory) {
        return ChatClient.builder(model2)
                .defaultSystem("假如你是特朗普,接下来的对话你必须以特朗普的语气来进行?")
                .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory), new QuestionAnswerAdvisor(vectorStore))
                .build();
    }

    @Bean
    public ChatMemory chatMemory() {
        return new InMemoryChatMemory();
    }
}

ChatPDF

引入依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>

编写代码读取 PDF:

package com.yan.springai.Pdf;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.reader.ExtractedTextFormatter;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class Pdf {
    final VectorStore store;

    @GetMapping("/pdf/read")
    public String getDocsFromPdf() {
        PagePdfDocumentReader pdfReader = new PagePdfDocumentReader("classpath:/baogao.pdf",
                PdfDocumentReaderConfig.builder()
                        .withPageTopMargin(0)
                        .withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
                                .withNumberOfTopTextLinesToDelete(0)
                                .build())
                        .withPagesPerDocument(1)
                        .build());
        store.write(pdfReader.read());
        return "success";
    }
}

Function Call 调用自定义函数

部分模型(如 Deepseek)可能不支持 Function Call,建议参考 Moonshot、OpenAI 等支持情况。

创建逻辑函数:

package com.yan.springai.func;
import java.util.function.Function;

public class OaService implements Function<OaService.Request, OaService.Response> {
    public Response apply(Request request) {
        System.err.printf("%s is token off%n", request.who);
        return new Response(10);
    }

    public record Request(String who) {}
    public record Response(int days) {}
}

注册 Function 到 Spring 容器:

package com.yan.springai.func;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FunctionRegistry {
    @Bean
    public FunctionCallback askForLeaveCallBack() {
        return FunctionCallback.builder()
                .function("askForLeave", new OaService())
                .description("当有人请假时,返回请假天数")
                .build();
    }
}

调用函数:

package com.yan.springai.func;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class FuncAPI {
    final ChatClient chatClient;

    @GetMapping("/ai/func")
    public String funcCall(@RequestParam(value = "message") String message) {
        return chatClient.prompt(message)
                .functions("askForLeave")
                .call().content();
    }
}

多模态能力

多模态大语言模型能够理解和生成多种类型数据,包括文本、图片、音频和视频等。

Deepseek 等模型可能不支持多模态,可下载 llava 模型:

ollama run llava

在 resources 传入图片,编写 ImageAPI 类:

package com.yan.springai.model;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.Media;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaModel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequiredArgsConstructor
public class ImageAPI {
    final OllamaChatModel model;

    @GetMapping("/ai/chatWithPic")
    public String chatWithPic() {
        ClassPathResource imageData = new ClassPathResource("/cat.png");
        Message userMessage = new UserMessage("请用中文描述一下这张图片是什么东西?", List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageData)));
        return model.call(new Prompt(List.of(userMessage), ChatOptions.builder()
                .model(OllamaModel.LLAVA.getName()).build()))
                .getResult().getOutput().getText();
    }
}

目录

  1. SpringAI 结合 Ollama 本地部署 Deepseek 模型实现对话机器人
  2. Java 调用 Deepseek
  3. 下载 Deepseek 模型
  4. 本地测试
  5. Java 调用模型
  6. 构建数据库
  7. 增强检索 RAG
  8. 向量数据库
  9. Springboot 集成 pgvector
  10. ChatPDF
  11. Function Call 调用自定义函数
  12. 多模态能力

更多推荐文章

查看全部
  • 01-前端AI应用:浏览器中的机器学习模型
  • Windows 下部署 OpenClaw 并接入飞书机器人实战
  • 2025年AIGC市场:规模、趋势与挑战
  • Linux 基础指令介绍(上)
  • VSCode 集成 GitHub Copilot 使用指南
  • 前端无障碍性:让所有人都能使用你的网站
  • llama.cpp 量化大模型内网部署与运行实战
  • WebP格式处理一站式解决方案:让Photoshop完美支持现代图像格式
  • Python 逆向工程:从.pyc 字节码还原.py 源码
  • C++ 入门基础:历史渊源、应用场景与学习建议
  • Visual C++运行库一键安装方案:解决 DLL 缺失错误
  • Python 浏览器自动化:Playwright 结合 AI 的最佳实践
  • OpenClaw 部署报错:Cannot read properties of undefined
  • 2022 CSP-S 提高组策略游戏真题及题解
  • 宇树 G1 机器人导航仿真:地图构建与配置指南
  • 大语言模型训练优化指南
  • Python 中的“==”与 is:本质区别与实战优化
  • C++ 继承详解:面向对象代码复用的核心机制
  • MaoXian Web Clipper 离线网页剪辑工具详解
  • Flutter 三方库 shelf_web_socket 在 OpenHarmony 的适配指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online