SpringAI + Ollama + DeepSeek + RAG 搭建 AI 航线规划系统

本文基于 SpringAI 1.0.0-M6(里程碑版本)编写,正式版 API 可能有调整。核心架构思路(Ollama + PgVector + RAG)不变。

一、部署 Ollama

1. 编写 docker-compose.yml 文件

yaml 复制代码
services:
  ollama:
    volumes:
      - ./models:/root/.ollama  # 模型下载位置
    container_name: ollama
    pull_policy: always
    tty: true
    restart: unless-stopped
    image: ollama/ollama:latest
    ports:
      - "11434:11434"  # Ollama API 端口

2. 部署 Ollama

复制代码
docker-compose up -d

3. 验证部署

arduino 复制代码
docker ps
# ollama容器为Up状态说明部署成功

4. 部署 DeepSeek 大模型

arduino 复制代码
docker exec -it ollama ollama run deepseek-r1:1.5b

5. 部署 Embedding 模型 all-minilm

python 复制代码
docker exec -it ollama ollama pull all-minilm

6. 验证模型

bash 复制代码
docker exec -it ollama ollama list
# deepseek和all-minilm都在说明运行正常

二、部署 PgVector 向量数据库

1. 拉取镜像

bash 复制代码
docker pull ankane/pgvector

2. 创建并运行容器

css 复制代码
docker run -d \
  --name pgvector-container \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  ankane/pgvector

3. 验证容器

arduino 复制代码
docker ps
# pgvector-container为Up状态说明运行成功

4. 创建数据库和向量表

使用 DBeaver 连接 PG,创建 drone_ai 数据库,执行:

sql 复制代码
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
​
CREATE TABLE public.vector_store (
    id uuid DEFAULT uuid_generate_v4() NOT NULL,
    "content" text NULL,
    metadata json NULL,
    embedding public.vector(384) NULL,
    CONSTRAINT vector_store_pkey PRIMARY KEY (id)
);
​
CREATE INDEX vector_store_embedding_idx ON public.vector_store
    USING hnsw (embedding vector_cosine_ops);

向量字段 embedding 纬度设置为 384,对应 all-minilm 模型支持的纬度。

三、编写 SpringAI 代码

1. Maven 依赖

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.9</version>
</parent>
​
<properties>
    <java.version>17</java.version>
    <spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
​
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
​
<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>

2. application.yaml 配置

yaml 复制代码
spring:
  application:
    name: ai-chat
  ai:
    ollama:
      chat:
        options:
          model: deepseek-r1:1.5b
      base-url: http://localhost:11434
      embedding:
        enabled: true
        model: all-minilm
  datasource:
    url: jdbc:postgresql://localhost:5432/drone_ai
    username: postgres
    password: postgres
  vectorstore:
    pgvector:
      dimensions: 384
      index-type: hnsw
      distance-type: cosine_distance

3. AIConfig --- 初始化 ChatClient

java 复制代码
@Configuration
@RequiredArgsConstructor
public class AIConfig {
​
    final OllamaChatModel ollamaChatModel;
    final VectorStore vectorStore;
​
    @Bean
    public ChatClient chatClient() {
        return ChatClient.builder(ollamaChatModel)
                .defaultSystem("你需要根据知识库内的数据回答问题")
                .defaultAdvisors(
                    new MessageChatMemoryAdvisor(chatMemory()),
                    new QuestionAnswerAdvisor(vectorStore))
                .build();
    }
​
    @Bean
    public ChatMemory chatMemory() {
        return new InMemoryChatMemory();
    }
}

4. VectorService --- 知识库初始化

typescript 复制代码
@Service
@Slf4j
@RequiredArgsConstructor
public class VectorServiceImpl implements IVectorService {
​
    final VectorStore vectorStore;
​
    @PostConstruct
    public void init() {
        try {
            vectorStore.write(buildDroneDocument());
            vectorStore.write(buildObstacleDocument());
            log.info("知识库初始化完成");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
​
    @Override
    public List<Document> buildDroneDocument() throws IOException {
        log.info("开始构建无人机知识库");
        List<Document> documents = new ArrayList<>();
        String droneJson = FileUtils.readFileToString(new File("/data/ai/无人机.json"));
        List<DroneEntity> droneEntities = JSON.parseArray(droneJson, DroneEntity.class);
        Map<String, Object> metadata = Map.of("type", "无人机");
        for (DroneEntity entity : droneEntities) {
            documents.add(new Document(entity.toString(), metadata));
        }
        log.info("无人机知识库构建完成");
        return documents;
    }
​
    @Override
    public List<Document> buildObstacleDocument() throws IOException {
        log.info("开始构建障碍物知识库");
        List<Document> documents = new ArrayList<>();
        String obstacleJson = FileUtils.readFileToString(new File("/data/ai/障碍物数据.json"));
        JSONObject jsonObject = JSON.parseObject(obstacleJson);
        JSONArray jsonArray = jsonObject.getJSONArray("features");
        Map<String, Object> metadata = Map.of("type", "障碍物");
        for (int i = 0; i < jsonArray.size(); i++) {
            jsonObject = jsonArray.getJSONObject(i);
            ObstacleEntity entity = new ObstacleEntity();
            entity.setObstacleId("Obstacle_" + jsonObject.getString("id"));
            entity.setGeometry(jsonObject.getString("geometry"));
            entity.setType(jsonObject.getJSONObject("properties").getString("type"));
            entity.setHeight(jsonObject.getJSONObject("properties").getInteger("height"));
            documents.add(new Document(entity.toString(), metadata));
        }
        log.info("障碍物知识库构建完成");
        return documents;
    }
}

5. AIService --- 问答逻辑

less 复制代码
@Service
@Slf4j
@RequiredArgsConstructor
public class AIServiceImpl {
​
    private final ChatClient chatClient;
​
    /**
     * 向AI提问,基于RAG知识库回答
     * @param question 自然语言描述的问题
     * @return AI的回答
     */
    public String askAI(String question) {
        return chatClient.prompt().user(question).call().content();
    }
}

6. ChatAPIController --- HTTP 接口

less 复制代码
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatAPIController {
​
    private final AIServiceImpl aiService;
​
    @GetMapping("/ask")
    public String plan(@RequestParam("question") String question) {
        return aiService.askAI(question);
    }
}

四、架构总结

scss 复制代码
用户问题 → ChatClient → RAG检索(PgVector) → DeepSeek生成回答
                ↓
        QuestionAnswerAdvisor
                ↓
          VectorStore(PgVector)
                ↓
      无人机知识库 + 障碍物知识库
  • Ollama:本地部署大模型,无需云API
  • DeepSeek R1:推理能力强,适合复杂航线规划
  • PgVector:PostgreSQL向量扩展,存储和检索知识库
  • SpringAI:统一AI调用接口,支持RAG开箱即用
  • all-minilm:轻量级Embedding模型,384维向量

关于作者

独立开发者,主业 Java 后端。一个人用 SpringBoot + AI 交付过企业级管理平台和微信小程序,业余接外包。

相关推荐
大勇前进39 分钟前
SQL Server 分页查询多种写法对比:哪种性能最高?
后端
妙码生花42 分钟前
从 PHP 到 AI + Golang,程序员自救转型手记(五十六):附件管理、增加根据文件后缀生成 SVG 文件图标的接口
前端·后端·go
前端Hardy1 小时前
Vue 终于杀进终端界!这个开源项目让 CLI 开发像写网页一样简单
前端·javascript·后端
老孙讲技术1 小时前
周界枪响了、电梯脸却进了物业群:一个 callback,怎么把 alarm 和 faceAnalysis 拆成两条值班线?
后端·物联网
大勇前进1 小时前
千万级大表 SQL Server 查询慢?一套可落地的性能调优实战路径
后端
前端开发张小七1 小时前
Java 学习笔记 · 第三课:多线程与并发编程(线程、同步、死锁、Lock、乐观锁与悲观锁)
java·后端·程序员
站大爷IP1 小时前
Python 的 defaultdict 把我坑惨了,原来缺失键会自动创建,但 `__missing__` 的副作用让我调试到崩溃
后端
用户名不能为空被占用1 小时前
记一次数据权限改造,看 NestJS 装饰器与守卫的组合应用
后端·nestjs
摇滚侠1 小时前
SpringBoot 官网 阅读笔记 启用生产就绪功能 端点
spring boot·笔记·后端