# Spring AI RAG 知识库项目重点说明

# Spring AI RAG 知识库项目重点说明

## 项目概述

基于 Spring AI 1.0.0 + PostgreSQL + pgvector 构建的企业级 RAG(检索增强生成)知识库问答系统,支持本地模型和云端 DeepSeek API 动态切换。

## 核心技术架构

### 1. 技术栈

| 层级 | 技术 | 说明 |

|------|------|------|

| 后端框架 | Spring Boot 3.4.3 | Java 17+ |

| AI 框架 | Spring AI 1.0.0 | 统一的 AI 模型抽象 |

| 向量数据库 | PostgreSQL + pgvector | 存储文档向量,支持相似度检索 |

| 本地模型 | Ollama + qwen3.5:4b | 本地对话模型(CPU 推理) |

| Embedding | Ollama bge-m3 | 本地向量化模型(1024 维) |

| 云端模型 | DeepSeek deepseek-chat | 快速对话(需 API Key) |

| 前端 | React + TypeScript + Vite | 聊天界面 + 文件上传 |

### 2. RAG 流程

```

用户提问

向量检索 (VectorStore.similaritySearch)

↓  top-k=5 相关文档块

组装 Prompt (参考材料 + 问题)

LLM 生成回答 (ChatClient)

返回答案

```

## 重点配置文件

### application.yml

```yaml

spring:

ai:

ollama:

  base-url: http://localhost:11434

  chat:

    options:

      model: qwen3.5:4b        # 对话模型

      temperature: 0.3

  embedding:

    model: bge-m3:latest       # 向量化模型(1024 维)

vectorstore:

  pgvector:

    dimensions: 1024            # 向量维度必须与 Embedding 模型一致

    distance-type: COSINE_DISTANCE  # 余弦相似度

rag:

chunk:

size: 800      # 分块 token 数

overlap: 200   # 块间重叠 token 数

compression-enabled: true  # GZIP 压缩存储

max-file-size-mb: 100     # 文件大小限制(MB)

retrieval:

top-k: 5       # 检索返回最相关的 5 个块

memory:

max-messages: 20  # 对话记忆保留最近 20 条消息

```

## 核心服务说明

### ChatModelProvider(动态模型切换)

位于 `service/ChatModelProvider.java`

- 维护 `AtomicReference apiKey`

- 根据是否设置 API Key 动态切换对话模型

  • 无 API Key → Ollama 本地模型

  • 有 API Key → DeepSeek 云端 API

- Embedding 始终使用本地 Ollama bge-m3

```java

public ChatModel getChatModel() {

if (hasApiKey()) {

    return createDeepSeekChatModel();  // 动态创建 DeepSeek 模型

}

return ollamaChatModel;  // 使用预注入的 Ollama 模型

}

```

### DocumentIngestService(文档处理)

位于 `service/DocumentIngestService.java`

- 支持 13 种格式:pdf, doc, docx, ppt, pptx, xls, xlsx, txt, md, html, htm, rtf, csv

- 使用 Apache Tika 解析文档

- 使用 TokenTextSplitter 分块(默认 800 token, overlap 200)

- 每个文档块附加元数据:docId、category、source、importTime、fileSize

- GZIP 压缩存储:记录压缩前后长度,减少大文本存储开销

- 文件大小限制:默认 100MB,可通过 `rag.chunk.max-file-size-mb` 配置

### RagChatService(RAG 问答)

位于 `service/RagChatService.java`

- 向量相似度检索 → 组装 Prompt → ChatClient 调用 LLM

- 支持同步返回和流式输出(SSE)

- 使用 MessageChatMemoryAdvisor 实现多轮对话记忆

- 对话历史持久化到 PostgreSQL(spring-ai-starter-model-chat-memory-repository-jdbc)

- **元数据过滤**:支持按 category 分类检索,使用 FilterExpressionBuilder

## API 接口

| 接口 | 方法 | 说明 |

|------|------|------|

| `/api/documents/upload` | POST | 上传文档(multipart/form-data,file + category):warning: 限流:5次/分钟 |

| `/api/documents/upload/progress` | POST | 上传文档(带进度跟踪,返回 taskId):warning: 限流:5次/分钟 |

| `/api/upload/progress/{taskId}` | GET | 查询上传进度 |

| `/api/upload/progress/{taskId}/stream` | GET | SSE 流式进度推送 |

| `/api/documents/list` | GET | 获取文档列表(可选参数:category) |

| `/api/documents/stats` | GET | 获取文档统计(文档数、块数、总大小) |

| `/api/documents/{docId}` | DELETE | 删除单个文档 |

| `/api/documents/category/{category}` | DELETE | 删除指定分类下的所有文档 |

| `/api/documents/expired` | DELETE | 删除过期文档(参数:retentionDays,默认30) |

| `/api/documents/all` | DELETE | 删除所有文档 |

| `/api/chat` | POST | 同步问答(JSON:question + conversationId + category):warning: 限流:30次/分钟 |

| `/api/chat/stream` | GET | 流式输出(SSE,参数:question + conversationId + category):warning: 限流:30次/分钟 |

| `/api/chat/memory/{conversationId}` | DELETE | 清空指定对话历史 |

| `/api/chat/memory/all` | DELETE | 清空所有对话历史 |

| `/api/chat/memory/stats` | GET | 获取对话统计(会话数、消息数) |

| `/api/config/apikey` | POST | 设置 DeepSeek API Key |

| `/api/config/status` | GET | 获取当前模式(ollama / deepseek) |

### API 限流说明

系统基于客户端 IP 实施限流保护:

| 接口类型 | 限流速率 | 说明 |

|----------|----------|------|

| 聊天接口 | 30 次/分钟 | `/api/chat`、`/api/chat/stream` |

| 上传接口 | 5 次/分钟 | `/api/documents/upload`、`/api/documents/upload/progress` |

超出限流时返回 HTTP 429 状态码和错误消息。

## 数据库配置

### 环境要求

- **PostgreSQL 15+**(需编译安装 pgvector v0.8.2+)

- **pgvector 扩展**(用于向量存储和相似度检索)

### 数据库初始化步骤

```sql

-- 1. 创建数据库

CREATE DATABASE rag_db;

-- 2. 创建用户(可选,也可使用 postgres 超级用户)

CREATE USER rag_user WITH PASSWORD ‘rag_pass’;

GRANT ALL PRIVILEGES ON DATABASE rag_db TO rag_user;

-- 3. 启用 pgvector 扩展

\c rag_db

CREATE EXTENSION IF NOT EXISTS vector;

-- 4. 授权 public schema 权限

GRANT ALL ON SCHEMA public TO rag_user;

```

### 数据库连接配置

application.yml 中的数据源配置:

```yaml

spring:

datasource:

url: jdbc:postgresql://localhost:5432/rag_db

username: rag_user

password: rag_pass

driver-class-name: org.postgresql.Driver

```

### pgvector 向量存储配置

```yaml

spring:

ai:

vectorstore:

  pgvector:

    initialize-schema: true    # 启动时自动创建 vector_store 表

    dimensions: 1024            # 向量维度(BGE-M3 模型输出 1024 维)

    distance-type: COSINE_DISTANCE  # 余弦相似度

```

### 自动创建的数据库表

系统启动时由 Spring AI 自动创建以下表:

#### 1. `vector_store`(文档向量存储)

由 `PgVectorStore` 自动创建,存储文档分块后的向量:

| 列名 | 类型 | 说明 |

|------|------|------|

| `id` | VARCHAR(36) | 主键(UUID) |

| `content` | TEXT | 文档块文本内容 |

| `metadata` | JSONB | 元数据(docId、category、source、importTime) |

| `embedding` | VECTOR(1024) | 文档块的向量表示 |

#### 2. `chat_memory`(对话历史存储)

由 `JdbcChatMemoryRepository` 自动创建,存储多轮对话历史:

| 列名 | 类型 | 说明 |

|------|------|------|

| `conversation_id` | VARCHAR(255) | 对话会话 ID |

| `message_id` | VARCHAR(255) | 消息 ID(UUID) |

| `role` | VARCHAR(50) | 角色(user / assistant / system) |

| `content` | TEXT | 消息内容 |

| `created_at` | TIMESTAMP | 创建时间 |

### 验证数据库是否正常

```bash

# 连接数据库

psql -U rag_user -d rag_db

# 查看表

\dt

# 查看 vector_store 表结构

\d vector_store

# 查看 chat_memory 表结构

\d chat_memory

```

### ChatMemory 持久化配置

```yaml

spring:

ai:

chat:

  memory:

    repository:

      jdbc:

        initialize-schema: always  # 启动时自动初始化对话记忆表

```

## 文档管理功能

### 核心组件

| 组件 | 文件 | 说明 |

|------|------|------|

| DocumentRepository | `repository/DocumentRepository.java` | 文档元数据管理,支持查询、删除、统计 |

| DocumentManagementService | `service/DocumentManagementService.java` | 业务逻辑层,提供文档管理方法 |

| DocumentManagementController | `controller/DocumentManagementController.java` | REST API 接口 |

| DocumentCleanupScheduler | `scheduler/DocumentCleanupScheduler.java` | 定时任务,自动清理过期文档 |

### 功能特性

1. **文档列表查询** - 按 docId 分组展示,包含文件名、分类、块数、大小、导入时间

2. **文档删除** - 支持单文档删除、按分类删除、删除过期文档、清空所有文档

3. **统计信息** - 文档数量、块数量、总存储空间大小

4. **自动清理** - 可配置的定时任务,自动清理超过保留天数的文档

### 配置说明

```yaml

rag:

document:

default-retention-days: 30  # 默认保留30天

cleanup-enabled: false       # 默认关闭自动清理

cleanup-cron: "0 0 2 \* \* ?" # 每天凌晨2点执行

```

### 前端界面

点击右上角「:file_folder: Documents」按钮展开文档管理面板:

- 显示文档统计信息(文档数、块数、总大小)

- 表格展示所有文档列表

- 支持单个删除、批量清理过期文档、清空所有文档

## 数据库表

系统自动创建以下表(由 Spring AI JdbcChatMemoryRepository 和 PgVectorStore 管理):

- `chat_memory` - 对话历史存储

- `vector_store` - 文档向量存储

## 项目文件结构

```

src/main/java/com/example/rag/

├── RagApplication.java # 启动类(启用 @EnableScheduling

├── config/

│ ├── ChatMemoryConfig.java # 对话记忆配置(MessageWindowChatMemory)

│ ├── EmbeddingConfig.java # Embedding 模型配置

│ ├── OllamaChatConfig.java # Ollama 对话模型配置

│ ├── DeepSeekChatConfig.java # DeepSeek 对话模型配置

│ ├── AsyncConfig.java # 异步线程池配置

│ ├── RateLimitConfig.java # API 限流规则配置

│ └── WebMvcConfig.java # Web MVC 配置(拦截器、CORS)

├── controller/

│ ├── ChatController.java # 同步问答 API

│ ├── StreamController.java # 流式输出 API(SSE)

│ ├── DocumentController.java # 文档上传 API

│ ├── DocumentManagementController.java # 文档管理 API(列表、删除、统计)

│ ├── ConfigController.java # 配置 API(API Key、状态)

│ ├── UploadProgressController.java # 上传进度 API(查询、SSE推送)

│ └── ChatMemoryController.java # 对话记忆 API(清理、统计)

├── service/

│ ├── DocumentIngestService.java # 文档导入服务(多格式解析、分块、向量化)

│ ├── RagChatService.java # RAG 问答服务(检索 + 问答)

│ ├── ChatModelProvider.java # 动态模型切换(Ollama/DeepSeek)

│ ├── DocumentManagementService.java # 文档管理服务(删除、统计、清理)

│ ├── UploadProgressService.java # 上传进度管理(状态跟踪、SSE推送)

│ └── ChatMemoryService.java # 对话记忆服务(清理、统计)

├── repository/

│ └── DocumentRepository.java # 文档元数据访问层

├── scheduler/

│ └── DocumentCleanupScheduler.java # 定时清理任务

├── interceptor/

│ └── RateLimitInterceptor.java # API 限流拦截器

├── dto/

│ ├── ChatRequest.java

│ └── ChatResponse.java

└── exception/

└── GlobalExceptionHandler.java        # 全局异常处理(含验证错误处理)

```

## 功能特性总结

| 功能模块 | 特性 | 说明 |

|----------|------|------|

| 文档处理 | 多格式支持 | PDF、Word、PPT、Excel、TXT、MD 等 13 种格式 |

| | 智能分块 | TokenTextSplitter,可配置 chunkSize/overlap |

| | GZIP 压缩 | 可选压缩存储,减少存储空间 |

| | 文件大小限制 | 默认 100MB,可配置 |

| 向量检索 | 相似度检索 | 基于 pgvector 的余弦相似度 |

| | 分类过滤 | 支持按 category 检索 |

| | Top-K 配置 | 默认返回前 5 个相关块 |

| 对话能力 | 多轮对话 | MessageWindowChatMemory,保留最近 20 条消息 |

| | 同步/流式 | 支持同步返回和 SSE 流式输出 |

| | 模型切换 | 本地 Ollama / 云端 DeepSeek API |

| 文档管理 | 文档列表 | 按 docId 分组展示 |

| | 删除功能 | 单文档、分类、过期、全部删除 |

| | 统计信息 | 文档数、块数、存储空间 |

| | 自动清理 | 可配置定时任务,清理过期文档 |

## 快速开始

### 1. 环境准备

```bash

# 确保已安装

# - PostgreSQL 15+ + pgvector v0.8.2+

# - Ollama + qwen3.5:4b + bge-m3:latest

# 创建数据库

createdb rag_db

psql rag_db -c “CREATE EXTENSION IF NOT EXISTS vector;”

```

### 2. 启动服务

```bash

# 后端(端口 8080)

cd f:\traesolocode\rag

mvn spring-boot:run

# 前端(端口 3000)

cd f:\traesolocode\rag\rag-frontend

npm install

npm run dev

```

### 3. 访问应用

打开浏览器访问:http://localhost:3000

### 4. 配置 DeepSeek API(推荐)

为获得更好的响应速度,建议配置 DeepSeek API:

1. 点击右上角「:gear: Config」按钮

2. 输入 DeepSeek API Key(从 platform.deepseek.com 获取)

3. 点击「Save」

4. 状态标签变为绿色「DeepSeek API」表示切换成功

### 5. 使用流程

1. **上传文档** - 点击文件选择按钮,选择文档(支持 PDF、Word、PPT、TXT 等)

2. **选择分类** - 上传时可选择文档分类(HR/Finance/Tech/Legal/Default)

3. **提问** - 在输入框中输入问题,AI 会基于已上传文档内容进行回答

4. **文档管理** - 点击「:file_folder: Documents」按钮查看和管理已上传文档

## 前端界面说明

### 顶部控制栏

- **状态标签**:显示当前模式(紫色 = Ollama Local,绿色 = DeepSeek API)

- **:file_folder: Documents**:文档管理面板(点击展开/收起)

- **:gear: Config**:模型配置面板(点击展开/收起)

### 文档管理面板

- **统计信息**:文档数、块数、总存储空间

- **清理按钮**:清理过期文档、删除所有文档

- **文档列表**:文件名、分类、块数、大小、导入时间、删除操作

### 聊天区域

- **文件上传**:支持拖拽或点击选择,显示实时上传进度

- **上传进度条**:显示上传状态、进度百分比、当前处理步骤、块数统计

- **分类过滤**:提问时可选择只检索特定分类的文档

- **消息列表**:用户消息(蓝色)、AI 回答(白色)

- **对话清理**:「:wastebasket: Clear Chat」按钮清空当前对话历史

## 常见问题排查

| 问题 | 原因 | 解决方案 |

|------|------|----------|

| 检索结果不准确 | 分块参数不合适 | 调整 `rag.chunk.size`(增大或减小) |

| 向量维度不匹配 | Embedding 模型维度与配置不一致 | 确认 `spring.ai.vectorstore.pgvector.dimensions: 1024` |

| pgvector 表未创建 | 数据库连接或权限问题 | 确认 `initialize-schema: true` 且用户有 public schema 权限 |

| DeepSeek API 调用失败 | API Key 无效或额度不足 | 检查 `platform.deepseek.com` 账户状态 |

| 对话无法记住上下文 | conversationId 不一致 | 确认前端每次请求使用相同的 conversationId |

| 文件上传失败(文件过大) | 超过文件大小限制 | 调整 `rag.chunk.max-file-size-mb` |

| 分类过滤不生效 | category 未正确传入 | 确认上传时设置了 category,检索时传入相同 category |

| 对话历史过长导致超时 | 历史消息过多 | 调小 `rag.memory.max-messages` |