# rag-knowledge-base **Repository Path**: dream-long/rag-knowledge-base ## Basic Information - **Project Name**: rag-knowledge-base - **Description**: 企业知识库问答系统(设计 + 学习方案) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-02 - **Last Updated**: 2026-06-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 企业知识库智能问答系统(RAG) > 基于 **Spring AI 1.0 + RAG 架构**的文档问答系统 —— 上传企业文档(PDF/Word/Markdown),AI 基于文档内容回答问题,并**标注答案来源**。 让 AI 能回答"它原本不知道"的问题:把企业文档切分、向量化入库,提问时**先检索相关内容、再让 LLM 看着资料作答**,对抗大模型的"知识截止"和"幻觉"两大硬伤。 **架构亮点:本地 Embedding 向量化(省钱)+ 云端 DeepSeek 生成(保质量)的混合部署。** --- ## ✨ 功能特性 - 📄 **多格式文档** —— PDF / Word / Markdown 上传,Apache Tika 统一解析 - 🔍 **语义检索** —— 向量相似度匹配("请假"能匹配"休假申请",非关键词) - 🎯 **检索调优** —— Top-K + 相似度阈值过滤,挡掉无关噪音 - 📌 **来源标注** —— 答案末尾标"来源:xxx.pdf",可溯源可信 - 💬 **多轮对话** —— 会话隔离 + 上下文记忆,支持追问("那夜班几点") - 🖥️ **Web 界面** —— 文档上传 + 对话问答 + Markdown 渲染 --- ## 🛠️ 技术栈 | 类别 | 技术 | 说明 | |---|---|---| | 语言 / 框架 | Java 21、Spring Boot 3.5.14 | | | AI 框架 | Spring AI 1.0.0 | | | 向量库 | **PGVector**(PostgreSQL 插件) | 门槛低,SQL 友好 | | Embedding | **Ollama `bge-m3`**(本地,1024 维) | 免费、离线、中文好 | | 生成模型 | **DeepSeek**(`deepseek-chat`) | 便宜、中文好 | | 文档解析 | Spring AI `TikaDocumentReader` | 一个 reader 吃多种格式 | | 前端 | 原生 HTML + `marked.js` | Markdown 渲染 | --- ## 🏗️ 架构:RAG 两阶段 + 混合部署 ``` ═══════ 阶段一:索引(上传文档时)═══════ [PDF/Word/MD] → Tika 解析 → TokenTextSplitter 切分 → bge-m3 本地向量化(1024维) → 存入 PGVector (文本块 + 向量 + 文件名 metadata) ═══════ 阶段二:检索 + 生成(提问时)═══════ [用户提问] → bge-m3 向量化 → PGVector 相似度检索(topK + 阈值) → 拼成 prompt(指令 + 资料 + 问题)→ DeepSeek 生成 + 标注来源 ``` **混合架构**:向量化用本地 Ollama(海量文档切块向量化不花钱),生成用云端 DeepSeek(保证回答质量)。通过 `spring.ai.model.embedding=ollama` + `spring.ai.model.chat=openai` 显式指定,避免多 provider 的 Bean 冲突。 --- ## 💡 核心技术决策(踩坑实录) > 这部分是项目最有价值的地方 —— 真实 RAG 工程会遇到的坑和解法。 ### 1. Embedding 选型:为什么换 `bge-m3`? 最初用 `nomic-embed-text`,发现**中文语义检索完全失效**:不同的中文查询("怎么请假" vs "休假流程")返回**完全相同的向量**。实验定位到 nomic 的分词器基本不认中文(当未知字符处理)。换成多语言模型 **`bge-m3`(1024 维)** 后,中文语义检索恢复正常。 > 教训:**Embedding 模型的语言支持决定 RAG 的检索质量上限**。 ### 2. 检索调优:固定阈值的困境 加相似度阈值(0.6)能挡掉无关问题("今天天气"→0.46 被挡),但发现**不同文档的分数分布不同**:具体类文档(值班制度)命中 0.7+,抽象类文档(交易说明)仅 0.5。固定阈值对后者过严。 解法:① 阈值降到 0.5(平衡);② **"召回宽、来源标注严"** —— 召回用低阈值(给 LLM 足够上下文),来源标注只标 score 更高的块(避免标进低相关文档)。 > 进阶方向:低阈值召回 + LLM 兜底判断、Rerank 重排序、问题改写。 ### 3. 多轮对话:追问的指代难题 追问"那夜班几点"时,整句向量被指代词稀释,检索为空 → 直接返回"资料里没有"。解法:**非首轮检索为空时,不直接返回,带着对话历史问 LLM**,让它从上文找答案。 ### 4. 数据质量 = RAG 上限 测试发现 `hello world` 这种噪音数据会被检索到(与"今天天气"相似度接近阈值)。印证了 **garbage in, garbage out** —— 知识库的数据质量直接决定问答质量。 --- ## 📁 项目结构 ``` src/main/java/com/hs/rag/knowledgebase/ ├── RagKnowledgeBaseApplication.java └── controller/ └── DocumentIndexController.java # 索引 / 检索 / 问答 / 上传 / 多轮 src/main/resources/ ├── application.yml # Ollama + DeepSeek + PGVector 配置 └── static/index.html # Web 前端(上传 + 问答) ``` --- ## 🌐 API 端点 | 端点 | 方法 | 说明 | |---|---|---| | `/api/rag/documents` | POST | 上传文档(multipart),解析→切分→向量化→入库 | | `/api/rag/search` | GET | 检索调试(返回相关块 + 相似度分数,无阈值) | | `/api/rag/ask` | GET | 单轮问答(检索 + 生成 + 来源) | | `/api/rag/chat` | POST | 多轮问答(会话隔离,带上下文) | --- ## 🚀 快速开始 ### 前置环境 **① PGVector(Docker)** ```bash docker run -d --name rag-pgvector --restart unless-stopped \ -p 5432:5432 \ -e POSTGRES_PASSWORD=yourpassword \ -e POSTGRES_DB=ragdb \ -v ragpg-data:/var/lib/postgresql/data \ pgvector/pgvector:pg16 ``` **② Ollama + Embedding 模型** ```bash # 安装 Ollama 后 ollama pull bge-m3 # 验证:ollama list 能看到 bge-m3,默认监听 localhost:11434 ``` **③ 环境变量** ```bash export DEEPSEEK_API_KEY=sk-xxxxx export PG_PASSWORD=yourpassword ``` > ⚠️ Windows 中文环境注意:IDEA 运行配置 VM options 加 `-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8`,否则发给 DeepSeek 的中文会乱码。 ### 运行 ```bash mvn clean package -DskipTests java -jar target/rag-knowledge-base-0.0.1-SNAPSHOT.jar # 访问 http://localhost:8080 ``` --- ## ⚙️ 关键配置 ```yaml spring: ai: model: chat: openai # 生成 → DeepSeek embedding: ollama # 向量化 → Ollama(解决多 provider Bean 冲突) ollama: base-url: http://localhost:11434 embedding: options: model: bge-m3 openai: # DeepSeek 走 OpenAI 协议 api-key: ${DEEPSEEK_API_KEY} base-url: https://api.deepseek.com chat: options: model: deepseek-chat vectorstore: pgvector: dimensions: 1024 # 必须与 bge-m3 维度一致 initialize-schema: true # 自动建 vector_store 表 datasource: url: jdbc:postgresql://localhost:5432/ragdb username: postgres password: ${PG_PASSWORD} ``` > 📌 **维度必须匹配**:`bge-m3` 是 1024 维,配错存不进去。换 embedding 模型时记得同步改 `dimensions` 并重建表。 --- ## 🔗 系列项目 本项目是「Java 工程师 AI 应用开发转型」系列的第三个项目: 1. [mysql-mcp-server](../mysql-mcp-server) —— MCP Server(数据库工具 + SQL 安全护栏) 2. [mysql-mcp-agent](../mysql-mcp-agent) —— ReAct Agent(自然语言查库) 3. **rag-knowledge-base** —— RAG 企业知识库(本项目)