1275 lines
39 KiB
Markdown
1275 lines
39 KiB
Markdown
# Design Document
|
||
|
||
## Overview
|
||
|
||
本设计文档描述了基于Ollama本地大模型和ChromaDB向量数据库的RAG知识库系统。系统采用纯Java实现,所有功能集成在Spring Boot应用中,通过HTTP直接调用Ollama API。核心功能包括文档管理、向量检索、RAG增强生成和智能问答。系统支持监听指定文件夹自动加载文档,部署时只需一个JAR包。
|
||
|
||
### 技术栈
|
||
|
||
- **后端框架**: Spring Boot 2.x
|
||
- **向量数据库**: ChromaDB (通过HTTP API调用)
|
||
- **嵌入模型**: nomic-embed-text (768维向量)
|
||
- **生成模型**: deepseek-r1:32b (32B参数)
|
||
- **文档解析**: Apache PDFBox (PDF), Apache POI (Word)
|
||
- **HTTP客户端**: OkHttp / RestTemplate
|
||
- **前端**: Vue.js (现有系统)
|
||
|
||
### 部署架构
|
||
|
||
```
|
||
[Vue.js前端]
|
||
↓ HTTP
|
||
[Spring Boot JAR包:8080]
|
||
├── 知识库管理模块
|
||
├── RAG检索模块
|
||
├── AI生成模块
|
||
└── 文件监听模块
|
||
↓ HTTP ↓ 文件系统
|
||
[Ollama:11434] [ChromaDB数据: D:\wwwroot\RAG\data\chroma_db]
|
||
[文档文件: D:\wwwroot\RAG\uploads]
|
||
```
|
||
|
||
## Architecture
|
||
|
||
### 系统分层架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Presentation Layer │
|
||
│ - Vue.js前端界面 │
|
||
│ - 知识库管理页面 │
|
||
│ - 报告展示页面 │
|
||
│ - 智能问答界面 │
|
||
└──────────────────────┬──────────────────────────────────────┘
|
||
│ REST API
|
||
┌──────────────────────▼──────────────────────────────────────┐
|
||
│ Spring Boot Application Layer │
|
||
│ Controllers: │
|
||
│ - PsyKnowledgeController (知识库管理) │
|
||
│ - PsyAIAnalysisController (AI分析) │
|
||
│ - PsyAIChatController (智能问答) │
|
||
└──────────────────────┬──────────────────────────────────────┘
|
||
│
|
||
┌──────────────────────▼──────────────────────────────────────┐
|
||
│ Service Layer │
|
||
│ - KnowledgeService (文档管理) │
|
||
│ - EmbeddingService (向量化) │
|
||
│ - RetrievalService (RAG检索) │
|
||
│ - GenerationService (AI生成) │
|
||
│ - OllamaClient (Ollama API调用) │
|
||
│ - ChromaDBClient (ChromaDB HTTP API调用) │
|
||
│ - FileWatcherService (文件夹监听) │
|
||
└──────────────────────┬──────────────────────────────────────┘
|
||
│
|
||
┌──────────────────────▼──────────────────────────────────────┐
|
||
│ Utility Layer │
|
||
│ - DocumentParser (文档解析: PDF, Word, TXT) │
|
||
│ - TextSplitter (文本分块) │
|
||
│ - PromptBuilder (提示词构建) │
|
||
│ - VectorUtils (向量计算工具) │
|
||
└──────────────┬────────────────────┬─────────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────┐ ┌──────────────────┐
|
||
│ Ollama API │ │ ChromaDB │
|
||
│ (Port 11434) │ │ HTTP API │
|
||
│ - embed │ │ (Port 8000) │
|
||
│ - generate │ │ │
|
||
└──────────────────┘ └──────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────────────────────────┐
|
||
│ File System │
|
||
│ D:\wwwroot\RAG\ │
|
||
│ ├── data\chroma_db\ (向量数据) │
|
||
│ ├── uploads\ (文档文件) │
|
||
│ └── logs\ (日志文件) │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
### 数据流
|
||
|
||
#### 文档上传流程
|
||
```
|
||
用户上传文档 → Spring Boot Controller接收
|
||
→ DocumentParser解析文档 → TextSplitter分块
|
||
→ OllamaClient调用nomic-embed-text向量化
|
||
→ ChromaDBClient存储向量 → 保存原文件到uploads目录
|
||
→ 返回成功响应
|
||
```
|
||
|
||
#### 文件夹自动加载流程
|
||
```
|
||
管理员放置文档到 D:\wwwroot\RAG\uploads
|
||
→ FileWatcherService检测到新文件
|
||
→ 自动触发文档解析流程 → 分块 → 向量化 → 存储
|
||
→ 记录处理日志
|
||
```
|
||
|
||
#### RAG报告生成流程
|
||
```
|
||
用户请求报告 → Spring Boot Controller接收
|
||
→ GenerationService构建查询(测评数据+用户档案)
|
||
→ EmbeddingService向量化查询
|
||
→ RetrievalService从ChromaDB语义检索(Top-5)
|
||
→ 过滤低相似度结果(threshold=0.7)
|
||
→ PromptBuilder构建提示词(检索知识+用户数据)
|
||
→ OllamaClient调用deepseek-r1生成报告
|
||
→ 存储报告到MySQL → 返回报告+引用来源
|
||
→ 前端展示报告
|
||
```
|
||
|
||
#### 智能问答流程
|
||
```
|
||
用户提问 → Spring Boot Controller接收
|
||
→ EmbeddingService向量化问题
|
||
→ RetrievalService从ChromaDB语义检索
|
||
→ PromptBuilder构建提示词
|
||
→ OllamaClient调用deepseek-r1生成回答
|
||
→ 返回回答+引用来源 → 前端展示
|
||
```
|
||
|
||
## Components and Interfaces
|
||
|
||
### 1. Service Layer (Spring Boot Services)
|
||
|
||
#### 1.1 KnowledgeService (知识库管理服务)
|
||
|
||
**职责**: 管理文档的上传、存储、检索和删除
|
||
|
||
**接口**:
|
||
```java
|
||
@Service
|
||
public class KnowledgeService {
|
||
|
||
/**
|
||
* 上传并处理文档
|
||
* @param file 上传的文件
|
||
* @param category 文档分类
|
||
* @param metadata 元数据
|
||
* @return 文档信息
|
||
*/
|
||
public DocumentInfo uploadDocument(MultipartFile file, String category, Map<String, Object> metadata);
|
||
|
||
/**
|
||
* 获取文档列表
|
||
* @param category 分类过滤(可选)
|
||
* @param page 页码
|
||
* @param size 每页大小
|
||
* @return 文档列表
|
||
*/
|
||
public PageResult<DocumentInfo> listDocuments(String category, int page, int size);
|
||
|
||
/**
|
||
* 获取文档详情
|
||
* @param docId 文档ID
|
||
* @return 文档详情
|
||
*/
|
||
public DocumentInfo getDocument(String docId);
|
||
|
||
/**
|
||
* 删除文档
|
||
* @param docId 文档ID
|
||
* @return 是否成功
|
||
*/
|
||
public boolean deleteDocument(String docId);
|
||
|
||
/**
|
||
* 搜索文档
|
||
* @param keyword 关键词
|
||
* @return 文档列表
|
||
*/
|
||
public List<DocumentInfo> searchDocuments(String keyword);
|
||
|
||
/**
|
||
* 重建索引
|
||
* @return 重建结果
|
||
*/
|
||
public RebuildResult rebuildIndex();
|
||
}
|
||
```
|
||
|
||
#### 1.2 EmbeddingService (向量化服务)
|
||
|
||
**职责**: 使用nomic-embed-text模型生成文本向量
|
||
|
||
**接口**:
|
||
```java
|
||
@Service
|
||
public class EmbeddingService {
|
||
|
||
private final OllamaClient ollamaClient;
|
||
private static final String MODEL_NAME = "nomic-embed-text";
|
||
|
||
/**
|
||
* 将单个文本转换为768维向量
|
||
* @param text 文本内容
|
||
* @return 768维向量数组
|
||
*/
|
||
public float[] embedText(String text);
|
||
|
||
/**
|
||
* 批量向量化
|
||
* @param texts 文本列表
|
||
* @return 向量列表
|
||
*/
|
||
public List<float[]> embedBatch(List<String> texts);
|
||
}
|
||
```
|
||
|
||
#### 1.3 RetrievalService (RAG检索服务)
|
||
|
||
**职责**: 从ChromaDB检索相关文档片段
|
||
|
||
**接口**:
|
||
```java
|
||
@Service
|
||
public class RetrievalService {
|
||
|
||
private final ChromaDBClient chromaDBClient;
|
||
private final EmbeddingService embeddingService;
|
||
private static final String COLLECTION_NAME = "psychology_knowledge";
|
||
|
||
/**
|
||
* 语义检索相关文档
|
||
* @param query 查询文本
|
||
* @param topK 返回结果数量
|
||
* @param threshold 相似度阈值
|
||
* @return 检索结果列表
|
||
*/
|
||
public List<RetrievalResult> retrieve(String query, int topK, double threshold);
|
||
|
||
/**
|
||
* 带分类过滤的检索
|
||
* @param query 查询文本
|
||
* @param category 分类
|
||
* @param topK 返回结果数量
|
||
* @return 检索结果列表
|
||
*/
|
||
public List<RetrievalResult> retrieveWithFilter(String query, String category, int topK);
|
||
}
|
||
```
|
||
|
||
#### 1.4 GenerationService (AI生成服务)
|
||
|
||
**职责**: 调用deepseek-r1模型生成报告和回答
|
||
|
||
**接口**:
|
||
```java
|
||
@Service
|
||
public class GenerationService {
|
||
|
||
private final OllamaClient ollamaClient;
|
||
private final RetrievalService retrievalService;
|
||
private static final String MODEL_NAME = "deepseek-r1:32b";
|
||
|
||
/**
|
||
* 生成综合分析报告
|
||
* @param assessmentData 测评数据
|
||
* @param userProfile 用户档案
|
||
* @return 生成结果(报告内容+来源引用)
|
||
*/
|
||
public GenerationResult generateReport(Map<String, Object> assessmentData, Map<String, Object> userProfile);
|
||
|
||
/**
|
||
* 回答问题
|
||
* @param question 问题
|
||
* @param context 上下文(可选)
|
||
* @return 生成结果(回答+来源引用)
|
||
*/
|
||
public GenerationResult answerQuestion(String question, String context);
|
||
|
||
/**
|
||
* 生成矫治建议
|
||
* @param userProfile 用户档案
|
||
* @param assessmentData 测评数据
|
||
* @return 生成结果(建议+来源引用)
|
||
*/
|
||
public GenerationResult generateSuggestions(Map<String, Object> userProfile, Map<String, Object> assessmentData);
|
||
}
|
||
```
|
||
|
||
#### 1.5 OllamaClient (Ollama API客户端)
|
||
|
||
**职责**: 封装Ollama HTTP API调用
|
||
|
||
**接口**:
|
||
```java
|
||
@Component
|
||
public class OllamaClient {
|
||
|
||
private final RestTemplate restTemplate;
|
||
private final String baseUrl = "http://localhost:11434";
|
||
|
||
/**
|
||
* 调用嵌入API
|
||
* @param model 模型名称
|
||
* @param text 文本内容
|
||
* @return 向量数组
|
||
*/
|
||
public float[] embed(String model, String text);
|
||
|
||
/**
|
||
* 调用生成API
|
||
* @param model 模型名称
|
||
* @param prompt 提示词
|
||
* @param options 生成选项
|
||
* @return 生成的文本
|
||
*/
|
||
public String generate(String model, String prompt, Map<String, Object> options);
|
||
|
||
/**
|
||
* 获取可用模型列表
|
||
* @return 模型列表
|
||
*/
|
||
public List<ModelInfo> listModels();
|
||
|
||
/**
|
||
* 检查连接状态
|
||
* @return 是否连接成功
|
||
*/
|
||
public boolean checkConnection();
|
||
}
|
||
```
|
||
|
||
#### 1.6 ChromaDBClient (ChromaDB HTTP API客户端)
|
||
|
||
**职责**: 封装ChromaDB HTTP API调用
|
||
|
||
**接口**:
|
||
```java
|
||
@Component
|
||
public class ChromaDBClient {
|
||
|
||
private final RestTemplate restTemplate;
|
||
private final String baseUrl = "http://localhost:8000";
|
||
|
||
/**
|
||
* 添加文档向量
|
||
* @param collectionName 集合名称
|
||
* @param documents 文档列表
|
||
* @param embeddings 向量列表
|
||
* @param metadatas 元数据列表
|
||
* @param ids ID列表
|
||
*/
|
||
public void addDocuments(String collectionName, List<String> documents,
|
||
List<float[]> embeddings, List<Map<String, Object>> metadatas, List<String> ids);
|
||
|
||
/**
|
||
* 查询相似文档
|
||
* @param collectionName 集合名称
|
||
* @param queryEmbedding 查询向量
|
||
* @param topK 返回数量
|
||
* @param filter 过滤条件
|
||
* @return 查询结果
|
||
*/
|
||
public QueryResult query(String collectionName, float[] queryEmbedding, int topK, Map<String, Object> filter);
|
||
|
||
/**
|
||
* 删除文档
|
||
* @param collectionName 集合名称
|
||
* @param ids ID列表
|
||
*/
|
||
public void deleteDocuments(String collectionName, List<String> ids);
|
||
|
||
/**
|
||
* 获取集合信息
|
||
* @param collectionName 集合名称
|
||
* @return 集合信息
|
||
*/
|
||
public CollectionInfo getCollection(String collectionName);
|
||
}
|
||
```
|
||
|
||
#### 1.7 FileWatcherService (文件夹监听服务)
|
||
|
||
**职责**: 监听uploads文件夹,自动处理新文档
|
||
|
||
**接口**:
|
||
```java
|
||
@Service
|
||
public class FileWatcherService {
|
||
|
||
private final KnowledgeService knowledgeService;
|
||
private final String watchPath = "D:\\wwwroot\\RAG\\uploads";
|
||
|
||
/**
|
||
* 启动文件监听
|
||
*/
|
||
@PostConstruct
|
||
public void startWatching();
|
||
|
||
/**
|
||
* 处理新文件
|
||
* @param file 新文件
|
||
*/
|
||
private void processNewFile(File file);
|
||
|
||
/**
|
||
* 停止文件监听
|
||
*/
|
||
@PreDestroy
|
||
public void stopWatching();
|
||
}
|
||
```
|
||
|
||
### 2. Utility Components
|
||
|
||
#### 2.1 DocumentParser (文档解析器)
|
||
|
||
**职责**: 解析不同格式的文档
|
||
|
||
**接口**:
|
||
```java
|
||
@Component
|
||
public class DocumentParser {
|
||
|
||
/**
|
||
* 解析PDF文档
|
||
* @param filePath 文件路径
|
||
* @return 文本内容
|
||
*/
|
||
public String parsePdf(String filePath);
|
||
|
||
/**
|
||
* 解析Word文档
|
||
* @param filePath 文件路径
|
||
* @return 文本内容
|
||
*/
|
||
public String parseDocx(String filePath);
|
||
|
||
/**
|
||
* 解析TXT文档
|
||
* @param filePath 文件路径
|
||
* @return 文本内容
|
||
*/
|
||
public String parseTxt(String filePath);
|
||
|
||
/**
|
||
* 自动识别格式并解析
|
||
* @param filePath 文件路径
|
||
* @return 文本内容
|
||
*/
|
||
public String parse(String filePath);
|
||
}
|
||
```
|
||
|
||
#### 2.2 TextSplitter (文本分块器)
|
||
|
||
**职责**: 将长文本分割成适合向量化的块
|
||
|
||
**接口**:
|
||
```java
|
||
@Component
|
||
public class TextSplitter {
|
||
|
||
private int chunkSize = 800;
|
||
private int chunkOverlap = 200;
|
||
|
||
/**
|
||
* 按字符数分块
|
||
* @param text 文本内容
|
||
* @return 文本块列表
|
||
*/
|
||
public List<String> split(String text);
|
||
|
||
/**
|
||
* 按句子分块(保持语义完整)
|
||
* @param text 文本内容
|
||
* @return 文本块列表
|
||
*/
|
||
public List<String> splitBySentences(String text);
|
||
}
|
||
```
|
||
|
||
#### 2.3 PromptBuilder (提示词构建器)
|
||
|
||
**职责**: 构建发送给大模型的提示词
|
||
|
||
**接口**:
|
||
```java
|
||
@Component
|
||
public class PromptBuilder {
|
||
|
||
/**
|
||
* 构建报告生成提示词
|
||
* @param retrievedDocs 检索到的文档
|
||
* @param assessmentData 测评数据
|
||
* @param userProfile 用户档案
|
||
* @return 提示词
|
||
*/
|
||
public String buildReportPrompt(List<RetrievalResult> retrievedDocs,
|
||
Map<String, Object> assessmentData,
|
||
Map<String, Object> userProfile);
|
||
|
||
/**
|
||
* 构建问答提示词
|
||
* @param question 问题
|
||
* @param retrievedDocs 检索到的文档
|
||
* @return 提示词
|
||
*/
|
||
public String buildQAPrompt(String question, List<RetrievalResult> retrievedDocs);
|
||
|
||
/**
|
||
* 构建建议生成提示词
|
||
* @param userProfile 用户档案
|
||
* @param assessmentData 测评数据
|
||
* @param retrievedDocs 检索到的文档
|
||
* @return 提示词
|
||
*/
|
||
public String buildSuggestionPrompt(Map<String, Object> userProfile,
|
||
Map<String, Object> assessmentData,
|
||
List<RetrievalResult> retrievedDocs);
|
||
}
|
||
```
|
||
|
||
### 3. Controller Layer
|
||
|
||
#### 3.1 PsyKnowledgeController (知识库管理控制器)
|
||
|
||
**职责**: 提供知识库管理的REST API
|
||
|
||
**接口**:
|
||
```java
|
||
@RestController
|
||
@RequestMapping("/psychology/knowledge")
|
||
public class PsyKnowledgeController {
|
||
|
||
@PostMapping("/upload")
|
||
public AjaxResult uploadDocument(@RequestParam("file") MultipartFile file,
|
||
@RequestParam("category") String category);
|
||
|
||
@GetMapping("/list")
|
||
public TableDataInfo listDocuments(@RequestParam(required = false) String category,
|
||
@RequestParam(defaultValue = "1") int pageNum,
|
||
@RequestParam(defaultValue = "10") int pageSize);
|
||
|
||
@GetMapping("/{id}")
|
||
public AjaxResult getDocument(@PathVariable String id);
|
||
|
||
@DeleteMapping("/{id}")
|
||
public AjaxResult deleteDocument(@PathVariable String id);
|
||
|
||
@PostMapping("/search")
|
||
public AjaxResult searchDocuments(@RequestParam String keyword);
|
||
|
||
@PostMapping("/rebuild")
|
||
public AjaxResult rebuildIndex();
|
||
}
|
||
```
|
||
|
||
#### 3.2 PsyAIAnalysisController (AI分析控制器)
|
||
|
||
**职责**: 提供AI分析功能的REST API
|
||
|
||
**接口**:
|
||
```java
|
||
@RestController
|
||
@RequestMapping("/psychology/ai")
|
||
public class PsyAIAnalysisController {
|
||
|
||
@PostMapping("/generate-report")
|
||
public AjaxResult generateReport(@RequestBody AIReportRequest request);
|
||
|
||
@PostMapping("/chat")
|
||
public AjaxResult chat(@RequestBody AIChatRequest request);
|
||
|
||
@GetMapping("/system/status")
|
||
public AjaxResult getSystemStatus();
|
||
}
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### 1. ChromaDB Collection Schema
|
||
|
||
ChromaDB通过HTTP API访问,数据格式如下:
|
||
|
||
```json
|
||
// Collection: psychology_knowledge
|
||
{
|
||
"name": "psychology_knowledge",
|
||
"metadata": {
|
||
"description": "监狱心理学知识库",
|
||
"embedding_model": "nomic-embed-text",
|
||
"dimension": 768
|
||
}
|
||
}
|
||
|
||
// Document Schema
|
||
{
|
||
"id": "doc_uuid_chunk_index",
|
||
"document": "文档内容文本...",
|
||
"embedding": [0.1, 0.2, ...],
|
||
"metadata": {
|
||
"doc_id": "doc_uuid",
|
||
"filename": "心理学教材.pdf",
|
||
"category": "人格心理学",
|
||
"chunk_index": 0,
|
||
"total_chunks": 10,
|
||
"upload_time": "2025-01-01 10:00:00",
|
||
"file_size": 1024000,
|
||
"page_number": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. Java Domain Models
|
||
|
||
#### 2.1 文档信息
|
||
```java
|
||
public class DocumentInfo {
|
||
private String docId; // 文档唯一ID
|
||
private String filename; // 文件名
|
||
private String category; // 分类
|
||
private Integer totalChunks; // 总分块数
|
||
private Long fileSize; // 文件大小(字节)
|
||
private Date uploadTime; // 上传时间
|
||
private Map<String, Object> metadata; // 其他元数据
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.2 检索结果
|
||
```java
|
||
public class RetrievalResult {
|
||
private String docId; // 文档ID
|
||
private String filename; // 文件名
|
||
private Integer chunkIndex; // 分块索引
|
||
private Double similarity; // 相似度分数
|
||
private String content; // 文档片段内容
|
||
private Map<String, Object> metadata; // 元数据
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.3 生成结果
|
||
```java
|
||
public class GenerationResult {
|
||
private String content; // 生成的内容(报告/回答)
|
||
private List<SourceReference> sources; // 来源引用
|
||
private Double generationTime; // 生成耗时(秒)
|
||
// getters and setters
|
||
}
|
||
|
||
public class SourceReference {
|
||
private String docId; // 文档ID
|
||
private String filename; // 文件名
|
||
private Integer chunkIndex; // 分块索引
|
||
private Double similarity; // 相似度
|
||
private String content; // 引用片段
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.4 AI报告请求
|
||
```java
|
||
public class AIReportRequest {
|
||
private Map<String, Object> assessmentData; // 测评数据
|
||
private Map<String, Object> userProfile; // 用户档案
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.5 AI问答请求
|
||
```java
|
||
public class AIChatRequest {
|
||
private String question; // 问题
|
||
private String context; // 上下文(可选)
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.6 系统状态响应
|
||
```java
|
||
public class SystemStatusResponse {
|
||
private Boolean ollamaConnected; // Ollama连接状态
|
||
private Boolean chromaDBConnected; // ChromaDB连接状态
|
||
private List<String> availableModels; // 可用模型列表
|
||
private KnowledgeStats knowledgeStats; // 知识库统计
|
||
// getters and setters
|
||
}
|
||
|
||
public class KnowledgeStats {
|
||
private Integer totalDocuments; // 文档总数
|
||
private Integer totalChunks; // 向量总数
|
||
private Long storageSize; // 存储空间(字节)
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 2.7 重建结果
|
||
```java
|
||
public class RebuildResult {
|
||
private Boolean success; // 是否成功
|
||
private String message; // 消息
|
||
private Integer processedFiles; // 处理的文件数
|
||
private Integer totalChunks; // 生成的向量数
|
||
private Double elapsedTime; // 耗时(秒)
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
### 3. Ollama API Models
|
||
|
||
#### 3.1 嵌入请求
|
||
```java
|
||
public class OllamaEmbedRequest {
|
||
private String model; // 模型名称
|
||
private String prompt; // 文本内容
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 3.2 嵌入响应
|
||
```java
|
||
public class OllamaEmbedResponse {
|
||
private float[] embedding; // 向量数组
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 3.3 生成请求
|
||
```java
|
||
public class OllamaGenerateRequest {
|
||
private String model; // 模型名称
|
||
private String prompt; // 提示词
|
||
private Boolean stream; // 是否流式输出
|
||
private Map<String, Object> options; // 生成选项
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 3.4 生成响应
|
||
```java
|
||
public class OllamaGenerateResponse {
|
||
private String response; // 生成的文本
|
||
private Boolean done; // 是否完成
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
### 4. ChromaDB API Models
|
||
|
||
#### 4.1 添加文档请求
|
||
```java
|
||
public class ChromaAddRequest {
|
||
private List<String> ids; // ID列表
|
||
private List<String> documents; // 文档列表
|
||
private List<float[]> embeddings; // 向量列表
|
||
private List<Map<String, Object>> metadatas; // 元数据列表
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 4.2 查询请求
|
||
```java
|
||
public class ChromaQueryRequest {
|
||
private float[] queryEmbeddings; // 查询向量
|
||
private Integer nResults; // 返回数量
|
||
private Map<String, Object> where; // 过滤条件
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
#### 4.3 查询响应
|
||
```java
|
||
public class ChromaQueryResponse {
|
||
private List<String> ids; // ID列表
|
||
private List<String> documents; // 文档列表
|
||
private List<Double> distances; // 距离列表
|
||
private List<Map<String, Object>> metadatas; // 元数据列表
|
||
// getters and setters
|
||
}
|
||
```
|
||
|
||
## Data Models
|
||
|
||
### ChromaDB存储路径
|
||
```
|
||
D:\wwwroot\RAG\
|
||
├── data\
|
||
│ └── chroma_db\ # ChromaDB数据目录
|
||
│ ├── chroma.sqlite3 # 元数据数据库
|
||
│ └── [向量数据文件]
|
||
├── uploads\ # 上传文件临时存储
|
||
│ └── [原始文档文件]
|
||
└── logs\ # 日志文件
|
||
└── ai_service.log
|
||
```
|
||
|
||
## Correctness Properties
|
||
|
||
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||
|
||
|
||
### Property Reflection
|
||
|
||
在编写具体属性之前,我们需要识别并消除冗余的属性:
|
||
|
||
**识别的冗余情况**:
|
||
1. 属性3.1和4.1都测试向量维度为768,可以合并为一个通用的"向量化输出维度"属性
|
||
2. 属性3.2和4.2都测试Top-K检索返回不超过5个结果,可以合并
|
||
3. 属性3.6和4.5都测试返回格式包含内容和来源,可以合并为通用的"响应格式完整性"属性
|
||
4. 属性1.1、1.2、1.3都测试文档解析,可以合并为一个"文档解析正确性"属性
|
||
5. 属性1.6和8.5都测试数据持久化,可以合并为"数据持久化round-trip"属性
|
||
|
||
**合并后的核心属性集**:
|
||
- 文档处理: 解析、分块、向量化、存储
|
||
- 检索功能: Top-K检索、相似度过滤、分类过滤
|
||
- 生成功能: 报告生成、问答、响应格式
|
||
- 管理功能: CRUD操作、索引重建
|
||
- 系统功能: 错误处理、状态查询、数据持久化
|
||
|
||
### Core Properties
|
||
|
||
**Property 1: 文档解析正确性**
|
||
*For any* 支持格式的文档(PDF、Word、TXT),解析后提取的文本内容应该与文档中的实际文本内容一致
|
||
**Validates: Requirements 1.1, 1.2, 1.3**
|
||
|
||
**Property 2: 文档分块大小约束**
|
||
*For any* 文档,分块后除最后一块外,每个块的字符数应该在500到1000之间
|
||
**Validates: Requirements 1.4**
|
||
|
||
**Property 3: 向量维度一致性**
|
||
*For any* 文本输入(文档块、查询、问题),使用nomic-embed-text生成的向量维度应该恰好为768
|
||
**Validates: Requirements 1.5, 3.1, 4.1**
|
||
|
||
**Property 4: 数据持久化round-trip**
|
||
*For any* 文档及其向量和元数据,存储到ChromaDB后应该能检索到完全相同的数据;AI服务重启后数据应该保持完整
|
||
**Validates: Requirements 1.6, 8.5**
|
||
|
||
**Property 5: 上传失败状态不变性**
|
||
*For any* 导致上传失败的无效输入,系统应该返回错误信息且ChromaDB中的文档数量保持不变
|
||
**Validates: Requirements 1.7**
|
||
|
||
**Property 6: 文档列表完整性**
|
||
*For any* 知识库状态,上传N个新文档后,文档列表的长度应该增加N
|
||
**Validates: Requirements 2.1**
|
||
|
||
**Property 7: 文档删除有效性**
|
||
*For any* 已存在的文档,删除后该文档ID不应该出现在文档列表中,且无法通过该ID检索到文档详情
|
||
**Validates: Requirements 2.3**
|
||
|
||
**Property 8: 分类过滤正确性**
|
||
*For any* 分类查询,返回的所有文档的category字段都应该等于查询的分类值
|
||
**Validates: Requirements 2.4**
|
||
|
||
**Property 9: 文件名搜索匹配性**
|
||
*For any* 搜索关键词,返回的所有文档的文件名都应该包含该关键词(不区分大小写)
|
||
**Validates: Requirements 2.5**
|
||
|
||
**Property 10: Top-K检索数量约束**
|
||
*For any* 查询向量,从ChromaDB检索时返回的结果数量应该不超过指定的top_k值(默认5)
|
||
**Validates: Requirements 3.2, 4.2**
|
||
|
||
**Property 11: 相似度阈值过滤**
|
||
*For any* 检索结果,所有返回的文档片段的相似度分数都应该大于或等于0.7
|
||
**Validates: Requirements 3.3**
|
||
|
||
**Property 12: 上下文包含必要信息**
|
||
*For any* 报告生成请求,构建的上下文字符串应该同时包含检索到的知识片段、测评数据和用户档案信息
|
||
**Validates: Requirements 3.4**
|
||
|
||
**Property 13: 生成响应非空性**
|
||
*For any* 有效的上下文输入,AI模型生成的报告或回答内容应该是非空字符串
|
||
**Validates: Requirements 3.5, 4.4**
|
||
|
||
**Property 14: 响应格式完整性**
|
||
*For any* 成功的生成请求(报告或问答),响应应该同时包含生成的内容字段和来源引用列表字段
|
||
**Validates: Requirements 3.6, 4.5**
|
||
|
||
**Property 15: 错误响应包含信息**
|
||
*For any* 导致失败的请求,响应应该包含非空的错误消息字段
|
||
**Validates: Requirements 3.7**
|
||
|
||
**Property 16: 提示词包含查询和知识**
|
||
*For any* 问答请求,构建的提示词应该同时包含用户问题和检索到的知识片段
|
||
**Validates: Requirements 4.3**
|
||
|
||
**Property 17: 系统状态响应完整性**
|
||
*For any* 系统状态查询,响应应该包含Ollama连接状态、ChromaDB状态和可用模型列表这三个字段
|
||
**Validates: Requirements 5.1**
|
||
|
||
**Property 18: 连接测试返回明确状态**
|
||
*For any* Ollama连接测试,响应应该明确指示连接成功或失败(布尔值或状态码)
|
||
**Validates: Requirements 5.2**
|
||
|
||
**Property 19: 统计数据准确性**
|
||
*For any* 知识库状态,统计查询返回的文档总数应该等于文档列表的长度
|
||
**Validates: Requirements 5.3**
|
||
|
||
**Property 20: 错误日志记录完整性**
|
||
*For any* 捕获的异常,日志文件应该包含该异常的时间戳和堆栈跟踪信息
|
||
**Validates: Requirements 5.4**
|
||
|
||
**Property 21: HTTP请求包含必要数据**
|
||
*For any* Spring Boot发起的报告生成请求,HTTP请求体应该包含assessmentData和userProfile字段
|
||
**Validates: Requirements 6.1**
|
||
|
||
**Property 22: 报告持久化round-trip**
|
||
*For any* AI服务返回的报告,存储到MySQL后应该能通过报告ID查询到相同的报告内容
|
||
**Validates: Requirements 6.2**
|
||
|
||
**Property 23: 错误处理日志记录**
|
||
*For any* AI服务返回的错误响应,Spring Boot应该在日志中记录该错误
|
||
**Validates: Requirements 6.4**
|
||
|
||
**Property 24: 问答请求包含问题**
|
||
*For any* Spring Boot发起的问答请求,HTTP请求体应该包含question字段
|
||
**Validates: Requirements 6.5**
|
||
|
||
**Property 25: API响应JSON格式有效性**
|
||
*For any* AI服务的HTTP响应,响应体应该是有效的JSON格式
|
||
**Validates: Requirements 8.2**
|
||
|
||
**Property 26: 本地存储路径正确性**
|
||
*For any* 向量存储操作,数据文件应该出现在配置的ChromaDB目录路径(D:\wwwroot\RAG\data\chroma_db)下
|
||
**Validates: Requirements 9.3**
|
||
|
||
**Property 27: 文件本地存储验证**
|
||
*For any* 上传的文档,原始文件应该存在于本地uploads目录中
|
||
**Validates: Requirements 9.5**
|
||
|
||
**Property 28: 索引重建后数据一致性**
|
||
*For any* 重建索引操作,重建完成后ChromaDB中的文档数量应该等于uploads目录中的文档文件数量
|
||
**Validates: Requirements 10.1, 10.2, 10.3**
|
||
|
||
**Property 29: 重建进度范围有效性**
|
||
*For any* 重建过程中的进度查询,返回的进度值应该在0到100之间(包含边界)
|
||
**Validates: Requirements 10.4**
|
||
|
||
**Property 30: 重建完成响应完整性**
|
||
*For any* 成功完成的重建操作,响应应该包含成功状态标志和统计信息(文档数、向量数)
|
||
**Validates: Requirements 10.5**
|
||
|
||
## Error Handling
|
||
|
||
### 错误分类
|
||
|
||
1. **输入验证错误**
|
||
- 无效文件格式
|
||
- 文件大小超限
|
||
- 缺少必需参数
|
||
- 响应: HTTP 400, 明确的错误消息
|
||
|
||
2. **资源不存在错误**
|
||
- 文档ID不存在
|
||
- 分类不存在
|
||
- 响应: HTTP 404, 资源标识信息
|
||
|
||
3. **外部服务错误**
|
||
- Ollama API不可用
|
||
- ChromaDB连接失败
|
||
- 响应: HTTP 503, 服务名称和错误详情
|
||
|
||
4. **处理超时错误**
|
||
- 文档解析超时
|
||
- AI生成超时
|
||
- 响应: HTTP 504, 超时操作描述
|
||
|
||
5. **系统内部错误**
|
||
- 未预期的异常
|
||
- 文件系统错误
|
||
- 响应: HTTP 500, 通用错误消息(不暴露内部细节)
|
||
|
||
### 错误处理策略
|
||
|
||
```python
|
||
# 统一错误响应格式
|
||
{
|
||
"code": 400, # HTTP状态码
|
||
"message": "文件格式不支持", # 用户友好消息
|
||
"error_type": "INVALID_FILE_FORMAT", # 错误类型代码
|
||
"details": { # 可选的详细信息
|
||
"supported_formats": ["pdf", "docx", "txt"]
|
||
},
|
||
"timestamp": "2025-01-01T10:00:00Z"
|
||
}
|
||
```
|
||
|
||
### 日志记录
|
||
|
||
- **INFO级别**: 正常操作(文档上传、报告生成)
|
||
- **WARNING级别**: 可恢复错误(相似度过滤、重试成功)
|
||
- **ERROR级别**: 需要关注的错误(API调用失败、数据损坏)
|
||
- **CRITICAL级别**: 系统级故障(ChromaDB不可用、Ollama崩溃)
|
||
|
||
日志格式:
|
||
```
|
||
[2025-01-01 10:00:00] [ERROR] [knowledge_service.py:123] 文档上传失败: doc_id=abc123, error=FileNotFoundError
|
||
Traceback: ...
|
||
```
|
||
|
||
## Testing Strategy
|
||
|
||
### 单元测试 (Unit Tests)
|
||
|
||
使用pytest框架,测试各个组件的独立功能:
|
||
|
||
**测试范围**:
|
||
- DocumentParser: 测试各种格式文档的解析
|
||
- TextSplitter: 测试分块算法的边界情况
|
||
- EmbeddingService: 测试向量化API调用(使用mock)
|
||
- RetrievalService: 测试检索逻辑(使用内存ChromaDB)
|
||
- PromptBuilder: 测试提示词模板构建
|
||
|
||
**示例测试**:
|
||
```python
|
||
def test_text_splitter_chunk_size():
|
||
"""测试分块大小约束"""
|
||
splitter = TextSplitter(chunk_size=800, chunk_overlap=200)
|
||
text = "a" * 5000
|
||
chunks = splitter.split(text)
|
||
|
||
for i, chunk in enumerate(chunks[:-1]): # 除最后一块
|
||
assert 500 <= len(chunk) <= 1000
|
||
```
|
||
|
||
### 属性测试 (Property-Based Tests)
|
||
|
||
使用Hypothesis库进行属性测试,验证correctness properties:
|
||
|
||
**配置**: 每个属性测试运行至少100次迭代
|
||
|
||
**测试库**: Hypothesis (Python)
|
||
|
||
**示例属性测试**:
|
||
```python
|
||
from hypothesis import given, strategies as st
|
||
|
||
@given(st.text(min_size=1000, max_size=10000))
|
||
def test_property_2_chunk_size_constraint(text):
|
||
"""
|
||
Feature: local-rag-knowledge-base, Property 2: 文档分块大小约束
|
||
"""
|
||
splitter = TextSplitter(chunk_size=800, chunk_overlap=200)
|
||
chunks = splitter.split(text)
|
||
|
||
# 除最后一块外,每块应在500-1000字符之间
|
||
for chunk in chunks[:-1]:
|
||
assert 500 <= len(chunk) <= 1000
|
||
|
||
@given(st.text(min_size=10, max_size=1000))
|
||
def test_property_3_vector_dimension(text):
|
||
"""
|
||
Feature: local-rag-knowledge-base, Property 3: 向量维度一致性
|
||
"""
|
||
embedding_service = EmbeddingService()
|
||
vector = embedding_service.embed_text(text)
|
||
|
||
assert len(vector) == 768
|
||
```
|
||
|
||
### 集成测试 (Integration Tests)
|
||
|
||
测试组件之间的交互:
|
||
|
||
**测试场景**:
|
||
1. 完整的文档上传流程(解析→分块→向量化→存储)
|
||
2. 端到端的报告生成流程(查询→检索→生成→返回)
|
||
3. Spring Boot与Flask AI服务的HTTP通信
|
||
4. ChromaDB数据持久化和恢复
|
||
|
||
**测试环境**:
|
||
- 使用测试专用的ChromaDB实例
|
||
- 使用本地Ollama服务(需要预先启动)
|
||
- 使用测试数据库(MySQL)
|
||
|
||
### 测试数据
|
||
|
||
**文档测试数据**:
|
||
- 准备10-20个心理学相关的测试文档(PDF、Word、TXT)
|
||
- 包含不同长度(1页、10页、100页)
|
||
- 包含特殊字符、多语言内容
|
||
|
||
**测评数据测试数据**:
|
||
- 模拟MMPI、SCL-90等量表的答题数据
|
||
- 包含边界情况(全选A、随机选择)
|
||
|
||
### 性能测试
|
||
|
||
**基准指标**:
|
||
- 文档上传处理时间: <5秒/MB
|
||
- 向量检索时间: <1秒
|
||
- 报告生成时间: <30秒
|
||
- 并发请求支持: 10个并发用户
|
||
|
||
**测试工具**: pytest-benchmark, locust
|
||
|
||
### 测试覆盖率目标
|
||
|
||
- 代码覆盖率: >80%
|
||
- 属性覆盖率: 100%(所有correctness properties都有对应测试)
|
||
- API覆盖率: 100%(所有API端点都有测试)
|
||
|
||
## Deployment Considerations
|
||
|
||
### 环境要求
|
||
|
||
**硬件要求**:
|
||
- CPU: 8核以上(推荐16核)
|
||
- 内存: 32GB以上(deepseek-r1:32b需要约20GB)
|
||
- 存储: 100GB以上(模型+知识库+日志)
|
||
- GPU: 可选,可加速推理
|
||
|
||
**软件要求**:
|
||
- Java 8+ (JDK)
|
||
- Ollama (已安装nomic-embed-text和deepseek-r1:32b)
|
||
- ChromaDB (通过Docker或直接安装)
|
||
- MySQL 5.7+
|
||
|
||
### 部署步骤
|
||
|
||
1. **创建目录结构**
|
||
```cmd
|
||
mkdir D:\wwwroot\RAG\data\chroma_db
|
||
mkdir D:\wwwroot\RAG\uploads
|
||
mkdir D:\wwwroot\RAG\logs
|
||
```
|
||
|
||
2. **启动Ollama服务**
|
||
```cmd
|
||
ollama serve
|
||
```
|
||
|
||
3. **启动ChromaDB服务**
|
||
```cmd
|
||
# 方式1: 使用Docker
|
||
docker run -p 8000:8000 chromadb/chroma
|
||
|
||
# 方式2: 使用Python安装
|
||
pip install chromadb
|
||
chroma run --path D:\wwwroot\RAG\data\chroma_db --port 8000
|
||
```
|
||
|
||
4. **配置Spring Boot**
|
||
```yaml
|
||
# application.yml
|
||
rag:
|
||
ollama:
|
||
url: http://localhost:11434
|
||
embed-model: nomic-embed-text
|
||
generate-model: deepseek-r1:32b
|
||
chromadb:
|
||
url: http://localhost:8000
|
||
collection: psychology_knowledge
|
||
storage:
|
||
upload-path: D:/wwwroot/RAG/uploads
|
||
log-path: D:/wwwroot/RAG/logs
|
||
file-watcher:
|
||
enabled: true
|
||
watch-path: D:/wwwroot/RAG/uploads
|
||
```
|
||
|
||
5. **打包Spring Boot应用**
|
||
```cmd
|
||
mvn clean package -DskipTests
|
||
```
|
||
|
||
6. **启动Spring Boot应用**
|
||
```cmd
|
||
java -jar ry-xinli-admin/target/xinli-admin.jar
|
||
```
|
||
|
||
### 一键部署方案
|
||
|
||
**部署包结构**:
|
||
```
|
||
xinli-rag-deploy/
|
||
├── xinli-admin.jar # Spring Boot应用
|
||
├── start.bat # 启动脚本
|
||
├── stop.bat # 停止脚本
|
||
├── config/
|
||
│ └── application.yml # 配置文件
|
||
├── data/ # 数据目录(自动创建)
|
||
├── uploads/ # 文档上传目录(自动创建)
|
||
└── logs/ # 日志目录(自动创建)
|
||
```
|
||
|
||
**start.bat 内容**:
|
||
```batch
|
||
@echo off
|
||
echo 正在启动心理评估系统...
|
||
|
||
REM 检查Ollama是否运行
|
||
curl -s http://localhost:11434/api/tags >nul 2>&1
|
||
if errorlevel 1 (
|
||
echo [错误] Ollama服务未启动,请先启动Ollama
|
||
pause
|
||
exit /b 1
|
||
)
|
||
|
||
REM 检查ChromaDB是否运行
|
||
curl -s http://localhost:8000/api/v1/heartbeat >nul 2>&1
|
||
if errorlevel 1 (
|
||
echo [警告] ChromaDB服务未启动,正在启动...
|
||
start /b chroma run --path D:\wwwroot\RAG\data\chroma_db --port 8000
|
||
timeout /t 5
|
||
)
|
||
|
||
REM 启动Spring Boot应用
|
||
echo 正在启动Spring Boot应用...
|
||
java -jar xinli-admin.jar
|
||
|
||
pause
|
||
```
|
||
|
||
### 监控和维护
|
||
|
||
**监控指标**:
|
||
- Spring Boot健康状态(/actuator/health)
|
||
- Ollama API响应时间
|
||
- ChromaDB存储空间使用率
|
||
- 请求成功率和错误率
|
||
- 文件监听服务状态
|
||
|
||
**日志管理**:
|
||
- 使用Logback按天切分日志
|
||
- 保留最近30天的日志
|
||
- 自动压缩归档旧日志
|
||
- 日志级别可配置(INFO/DEBUG/ERROR)
|
||
|
||
**备份策略**:
|
||
- 每天自动备份ChromaDB数据目录
|
||
- 每周备份uploads目录
|
||
- 保留最近7天的备份
|
||
- 支持手动触发备份
|
||
|
||
### 安全考虑
|
||
|
||
1. **访问控制**:
|
||
- Spring Security权限控制
|
||
- 知识库管理需要管理员权限
|
||
- API接口需要登录认证
|
||
|
||
2. **文件验证**:
|
||
- 上传文件格式白名单(PDF、Word、TXT)
|
||
- 文件大小限制(默认50MB)
|
||
- 文件内容安全扫描
|
||
|
||
3. **输入清理**:
|
||
- 防止SQL注入
|
||
- 防止XSS攻击
|
||
- 参数验证和清理
|
||
|
||
4. **日志脱敏**:
|
||
- 日志中不记录敏感个人信息
|
||
- 自动脱敏身份证号、手机号等
|
||
|
||
5. **数据安全**:
|
||
- 所有数据本地存储
|
||
- 不依赖外网服务
|
||
- 可选文档加密存储
|
||
|
||
## Future Enhancements
|
||
|
||
1. **知识库版本管理**: 支持文档的版本控制和回滚
|
||
2. **多模型支持**: 支持切换不同的嵌入模型和生成模型
|
||
3. **增量更新**: 支持文档的增量更新而非完全重新处理
|
||
4. **缓存机制**: 缓存常见查询的检索结果
|
||
5. **批量处理**: 支持批量上传和处理文档
|
||
6. **知识图谱**: 构建心理学概念的知识图谱
|
||
7. **多语言支持**: 支持英文等其他语言的文档
|
||
8. **细粒度权限**: 不同用户角色访问不同的知识库分类
|