# rag_test **Repository Path**: li_hy/rag_test ## Basic Information - **Project Name**: rag_test - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-17 - **Last Updated**: 2026-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RAG 文本分片 & 多模态向量导入工具 基于 Python Flask 的 Web 应用,支持上传 txt / pdf / docx / pptx 文档与图片 / 视频媒体,选择分片策略、预览分片结果,并将分片向量化后存入向量数据库;检索页支持 Query 改写(本地模型 / 云端千问可切换)、**稠密向量 + BM25 稀疏的 RRF 混合检索 + Rerank 交叉编码器精排** 的三段式检索主干,并可在页面上勾选不同「提升召回率的方法」做单条检索或多方案对比,右侧实时展示召回过程链路(各阶段开关、耗时与候选数据)。回答经 LangChain 调用通义千问生成,并在结果中直接展示命中的图片 / 视频。 ## 功能 - **文档上传**: - `.txt` 文本(UTF-8 / GBK / GB2312 自动识别) - `.pdf` 文档(基于 PyMuPDF 提取文本,按页拼接;支持页数/空页统计) - `.docx` Word 文档(基于 python-docx 提取正文段落与表格单元格文本,统计段落数/表格数;不支持旧版 .doc 二进制格式) - `.pptx` PowerPoint 演示文稿(基于 python-pptx 按幻灯片提取文本框、表格与备注,统计幻灯片数/空幻灯片数;不支持旧版 .ppt 二进制格式) - 文档最大 16MB;加密 PDF、纯扫描件、空文档会提示无法提取 - **媒体上传与文本提取**(`/api/upload_media`): - 支持图片 `.jpg` / `.jpeg` / `.png` / `.bmp`,视频 `.mp4` / `.mpeg` / `.avi` / `.mov` / `.mpg` / `.webm` / `.flv` / `.mkv` - 媒体文件最大 100MB;视频单独限制 10MB,超过会拒绝上传 - 上传成功后**立即**调用 Qwen-VL 模型提取文本(图片一次调用同时返回标题和内容;视频使用 opencv 抽取 10 帧,逐帧调用 Qwen-VL 描述后拼接) - 提取文本在前端展示并供后续向量导入使用;提取失败不影响文件保存 - **分片策略**(仅针对文本文档): - **固定大小分片**:按字符数切分,支持重叠窗口 - **段落分片**:按空行切分,保持段落语义完整 - **递归字符分片**:按分隔符优先级递归切分(段落 → 句子 → 词语) - **父子分片**(`chunk_type=parent_child`):先切父块(保留较大上下文),再在父块内切子块用于向量召回;父块正文写入旁文件(`chroma_data/parents/`),检索命中子块后可按 `parent_id` 回溯父块作为返回/回答上下文 - **结构化分块与标题路径注入**:按文档标题层级切分并把「标题路径」拼接到分片正文,增强长文档定位;表格做自然语言行展开的结构化解析 - **文档入库 OCR**:扫描件 PDF 与文档内嵌图片可调用 Qwen-VL 做 OCR(`ENABLE_DOC_OCR` 控制,默认开启) - **分片预览**:展示每个分片的编号、字符范围和内容 - **向量导入**: - 文本文档:按分片逐条生成文本向量存入 ChromaDB - 媒体文件:一文件一向量,同时存储**视觉向量**和**文本向量**两条记录(双路存储),ID 格式 `xxx_visual_0` / `xxx_text_0`,通过 `vector_type` 元数据区分 - 文本/图片/视频统一使用云端多模态 embedding 模型 `tongyi-embedding-vision-flash-2026-03-06`,跨模态向量处于同一语义空间 - **Query 改写**(检索前自动执行): - 支持两种改写模型,在检索页面下拉切换:**本地模型**(OpenAI 兼容接口 `http://127.0.0.1:12880`)/ **云端千问**(DashScope,复用 `DASHSCOPE_API_KEY`) - 按通用改写规则(同义替换、补全省略、去除冗余、关键词重述、拆分问题)产出 3-5 个改写 query,逐个给出 0-1 置信度打分 - 支持多轮对话:前端传入历史查询数组,改写时结合上下文消解指代 - 自动选择置信度最高的改写 query 进入检索与回答流程;页面展示全部候选、置信度进度条、改写规则,并高亮实际选用项 - 改写过程写入独立 JSONL 日志 - **检索主干 + RAG 问答**(`/search` 页面):三段式,实测本数据集文档级 Recall@5 由纯向量 68.8% 提升至 97.3%: - **输入与参数**:输入一句话,选择已有知识库,设置输出条数(3-10)与相关度阈值(0-1);召回与返回解耦,粗排内部统一取大召回池(`CHROMA_RECALL_POOL`,默认 50),输出条数只决定最终返回量 - **第一段 稠密向量召回**:以原始 query 做余弦相似度检索(同一来源的 visual + text 双路向量均可入候选);开启查询侧增强时追加 HyDE / Step-Back / 术语变体为额外路 - **第二段 BM25 稀疏检索 + RRF 混合融合**:jieba 分词 + rank-bm25 做稀疏路,与稠密路按带权倒数排名融合 `score = w_dense/(k+rank_dense) + w_sparse/(k+rank_sparse)`(`HYBRID_RRF_K` 默认 60,权重默认 1.0/1.0);同一 source 取最高融合分作代表。RRF 量级与 cosine 不同,不对 RRF 分做 0.4/0.6 加权 - **第三段 Rerank 精排**:取 RRF 候选池前 `RERANK_CANDIDATE_POOL`(默认 20)条交交叉编码器(默认 `qwen3.7-text-rerank`,DashScope TextReRank)重排收敛到输出条数;`min_score` 作用于精排分。精排失败回退混合顺序并在结果 `rerank.status` 标注 - **命中的图片/视频**在结果中直接渲染预览(图片缩略图、视频播放器);最后用置信度最高的改写 query 经 LangChain 调用通义千问(qwen-plus),仅基于检索资料生成回答并列出参考条目 - **可选择的召回增强方法 & 多方案对比**(`/search` 页面): - 页面将提升召回率的方法以复选框分组暴露:召回链路阶段(纯向量基线 / 混合无精排 / 线上主干,三选一互斥)、查询侧增强(HyDE、Step-Back、术语/同义词扩展、查询意图路由)、结果侧增强(父子块展开) - “检索并生成回答”按当前勾选取一条链路并问答;“对比召回效果”用同一 query 串行跑完所有勾选方法(只检索不问答),以标签页对比各方案的候选池、命中数、最高分、总耗时/精排耗时与命中条目 - 后端根据开关映射为请求参数(`enable_sparse` / `enable_rerank` / `enable_hyde` / `enable_step_back` / `enable_term_expansion` / `use_routing` / `parent_mode` / `generate_answer`),默认值与线上主干一致;所有默认值与线上行为保持向后兼容 - 响应附带 `pipeline` 字段(本次开关 `mode`、参数 `params`、指标 `metrics`、各阶段 `stages` 含 enabled/耗时/说明、总耗时 `total_latency_ms`),驱动右侧“召回过程”面板展示链路漏斗与每条命中的稠密/BM25/精排名次 - **数据库状态**:实时查看指定 Collection 的已存储分片数量,并列出全部知识库 ## 项目结构 ``` rag_test/ ├── app.py # Flask 后端(路由 + API,串联改写→路由→扩展→混合检索→精排→问答) ├── chunking.py # 文本分片模块(固定/段落/递归 + 父子分块 + 标题路径注入) ├── document_loader.py # 文档解析(TXT 解码 / PDF / DOCX / PPTX、表格结构化、扫描件/内嵌图 OCR) ├── llm_answer.py # RAG 回答(LangChain LCEL 调用通义千问) ├── query_rewrite.py # Query 改写(本地模型 / 云端千问,置信度打分 + 日志) ├── query_expansion.py # HyDE / Step-Back 查询扩展(功能项 11,额外召回路) ├── query_router.py # 查询意图路由(功能项 12,按类型调权重/召回池/扩展开关) ├── term_expander.py # 领域术语/同义词受控扩展(功能项 13,查询侧变体) ├── sparse_retriever.py # BM25 稀疏检索(jieba 分词,按知识库维护倒排索引) ├── reranker.py # Rerank 交叉编码器精排(DashScope TextReRank + 调用日志) ├── vector_db.py # 向量库操作(ChromaDB + 云端多模态 embedding + search_hybrid RRF 混合 + 父块旁文件) ├── media_text_extractor.py # 媒体文本提取(Qwen-VL 图片 OCR/描述、视频抽帧描述) ├── llm_logger.py # 大模型调用统一 JSONL 日志工具 ├── think_logger.py # 对话与思考过程 Markdown 日志(自动分片) ├── requirements.txt # Python 依赖 ├── data/ │ └── terminology.json # 领域术语/同义词词典(供 term_expander 使用) ├── eval/ # 检索召回率评测(评测集 + run_eval.py + 网格调参 + reports/) ├── templates/ │ ├── index.html # 上传分片与导入页面 │ └── search.html # 检索页面(左右两栏:查询与方法选择 / 召回过程与对比) ├── static/ │ ├── css/style.css # 样式 │ └── js/ │ ├── app.js # 上传分片页面前端逻辑 │ └── search.js # 检索页前端逻辑(方法选择/对比、召回过程链路、媒体预览) ├── doc/ # 方案设计文档 ├── sample.txt # 示例文本 ├── uploads/ # 媒体文件上传保存目录(运行后自动生成) ├── log/ # 大模型调用 JSONL 日志目录(含 rerank_ 精排日志) ├── think/ # 对话与思考过程 Markdown 日志目录(运行后自动生成) ├── ide_think/ # IDE 对话与思考过程记录目录 └── chroma_data/ # 向量数据持久化目录(含 parents/ 父块旁文件) ``` ## 快速开始 ### 1. 安装 Python 依赖 要求 Python 3.10+。建议使用 conda 环境。 ```bash cd D:\ai\work_space\rag_test # 切换 conda 环境(切换不成功请先创建或选择其他可用环境后重试) conda activate big_model # 安装依赖(使用 python -m pip 防止多环境错配) python -m pip install -r requirements.txt ``` 主要依赖说明: - `flask` Web 框架;`chromadb` 向量数据库 - `pymupdf` PDF 解析;`python-docx` / `python-pptx` Office 文档解析 - `langchain` + `langchain-openai` RAG 回答链 - `dashscope` 云端千问 / Qwen-VL / 多模态 embedding / TextReRank 精排调用 - `jieba` + `rank-bm25` BM25 稀疏检索(中文分词) - `opencv-python` 视频抽帧(视频处理必需) ### 2. 关于向量数据库:ChromaDB 本项目使用 **ChromaDB** 作为向量数据库,已包含在 `requirements.txt` 中,pip 安装即可,**无需额外安装服务**。 - 开源(Apache 2.0),GitHub Star 数在向量数据库中名列前茅 - Python 原生,`pip install chromadb` 一键安装 - 支持本地持久化,无需独立部署服务器 - 内置 HNSW 索引,支持余弦/点积/L2 距离 - 对 RAG 场景非常友好,LangChain / LlamaIndex 都原生集成 ### 3. 配置 DashScope API Key 文本/图片/视频的 embedding 生成、Qwen-VL 媒体文本提取、云端 Query 改写、RAG 回答生成都依赖阿里云 DashScope 服务,必须配置 API Key: ```bash # Windows PowerShell $env:DASHSCOPE_API_KEY = "你的_api_key" # Windows CMD set DASHSCOPE_API_KEY=你的_api_key ``` > 项目原本地 embedding 服务(`http://127.0.0.1:8080/embedding`)相关代码已注释保留,当前统一改用云端多模态 embedding 模型 `tongyi-embedding-vision-flash-2026-03-06`,文本与图片/视频向量处于同一语义空间,支持跨模态检索。如需切回本地 embedding,可在 [vector_db.py](file:///d:/ai/work_space/rag_test/vector_db.py) 中恢复注释代码。 ### 4. 准备 Query 改写模型 检索前会先进行 Query 改写,在检索页面可通过下拉框选择改写模型: - **本地模型(默认)**:需自行启动一个 OpenAI 兼容的 chat/completions 服务,监听 **12880 端口**,例如 vLLM、llama.cpp server、LM Studio、Ollama 兼容模式等: ```bash # 以 OpenAI 兼容格式调用为例 curl http://127.0.0.1:12880/v1/chat/completions \ -X POST \ -H "Content-Type: application/json" \ -d '{"model": "local-model", "messages": [{"role": "user", "content": "你好"}]}' ``` - **云端千问**:无需额外服务,确保已配置 `DASHSCOPE_API_KEY` 环境变量即可,在页面下拉框选择「云端千问(DashScope)」。 ### 5. 启动 RAG Web 应用 ```bash python app.py ``` 浏览器打开 **http://127.0.0.1:5000** ### 6. 使用流程 **导入知识库(首页):** 文本文档流程: 1. 点击上传区域选择 .txt / .pdf / .docx / .pptx 文件(或用 sample.txt 测试) 2. 选择分片类型,调整参数 3. 点击「开始分片」预览结果 4. 点击「导入到向量数据库」完成向量化存储 媒体文件流程: 1. 点击上传区域选择图片或视频文件 2. 上传成功后后端立即调用 Qwen-VL 提取文本并返回,页面展示标题和内容 3. 在 Collection 名称输入框指定目标知识库 4. 点击「导入到向量数据库」:后端生成视觉向量和文本向量两条记录存入 ChromaDB **检索问答与召回对比(/search 页面):** 1. 输入查询句子,选择已导入的知识库 2. 设置输出条目数量、相关度阈值,并选择 Query 改写模型(本地 / 云端千问) 3. 在「提升召回率的方法」区勾选要启用的手段(链路阶段三选一;HyDE / Step-Back / 术语扩展 / 意图路由 / 父子块可叠加),可用“仅线上主干 / 三档链路对比 / 全部方法”快捷预设 4. 点击「检索并生成回答」:左下展示 Query 改写候选与置信度、千问回答、参考条目;右侧「召回过程」面板展示本次链路(各阶段开关/耗时/说明)与命中条目的稠密/BM25/精排名次;命中图片/视频时直接在条目内渲染预览 5. 点击「对比召回效果」:用同一 query 串行跑完所有勾选方法(不问答),在右侧以标签页对比各方案的候选池、命中数、最高分与耗时 ## 配置项(环境变量) | 变量 | 默认值 | 说明 | |------|--------|------| | `PORT` | `5000` | Flask 服务端口 | | `CHROMA_PERSIST_DIR` | `./chroma_data` | 向量数据存储目录 | | `COLLECTION_NAME` | `rag_documents` | 默认 Collection 名称(前端可覆盖) | | `MULTIMODAL_EMBEDDING_MODEL` | `tongyi-embedding-vision-flash-2026-03-06` | 云端多模态 embedding 模型(文本/图片/视频统一) | | `VLM_MODEL_NAME` | `qwen-vl-max` | 媒体文本提取使用的 Qwen-VL 模型 | | `QUERY_REWRITE_API_URL` | `http://127.0.0.1:12880/v1/chat/completions` | 本地 Query 改写模型地址(OpenAI 兼容接口) | | `QUERY_REWRITE_MODEL` | `local-model` | 本地 Query 改写模型名称 | | `QUERY_REWRITE_CLOUD_MODEL` | `qwen-plus` | 云端千问改写模型名称 | | `DASHSCOPE_API_KEY` | 无(必填) | 阿里云百炼/DashScope API Key,用于 embedding 生成、Qwen-VL 媒体文本提取、云端 Query 改写与 RAG 回答生成 | | `QWEN_MODEL` | `qwen-plus` | 千问回答模型名称(如 qwen-turbo / qwen-max) | | `QWEN_BASE_URL` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | DashScope OpenAI 兼容端点(回答生成与云端改写共用) | | `LLM_LOG_DIR` | `./log` | 大模型调用 JSONL 日志目录 | | `THINK_DIR` | `./think` | 对话与思考过程 Markdown 日志目录 | | `CHROMA_RECALL_POOL` | `50` | 粗排阶段内部召回量(与前端输出条数解耦) | | `CHROMA_HNSW_SEARCH_EF` / `CHROMA_HNSW_CONSTRUCTION_EF` / `CHROMA_HNSW_M` | `200` / `200` / `32` | HNSW 索引参数(**仅新建 Collection 时生效**,已有库 modify 不生效) | | `HYBRID_RRF_K` | `60` | RRF 混合融合常数 k | | `HYBRID_W_DENSE` / `HYBRID_W_SPARSE` | `1.0` / `1.0` | 稠密路 / 稀疏路融合权重(意图路由开启时按类型覆盖) | | `RERANK_MODEL` | `qwen3.7-text-rerank` | Rerank 精排模型(DashScope TextReRank) | | `RERANK_CANDIDATE_POOL` | `20` | 送入精排的 RRF 候选池大小 | | `RERANK_TIMEOUT` | `10` | 精排调用超时(秒),超时回退混合顺序 | | `ENABLE_QUERY_EXPANSION` | `0` | HyDE / Step-Back 查询扩展默认开关(请求可传 `enable_hyde`/`enable_step_back` 覆盖) | | `ENABLE_QUERY_ROUTING` | `0` | 查询意图路由默认开关(请求可传 `use_routing` 覆盖) | | `ENABLE_ROUTING_LLM` | `0` | 意图路由规则无命中时是否回退一次 LLM 分类 | | `ENABLE_TERM_EXPANSION` | `0` | 术语/同义词受控扩展默认开关(请求可传 `enable_term_expansion` 覆盖) | | `TERM_EXPANSION_MAX` | `5` | 单次查询最多生成的术语变体子查询数 | | `ENABLE_DOC_OCR` | `1` | 文档入库时是否对扫描页/内嵌图调用 Qwen-VL OCR | > 检索主干默认即“单路稠密 + BM25 混合 RRF + Rerank 精排”;前端可通过 `enable_sparse`/`enable_rerank` 等参数临时降级特定阶段(仅影响本次请求,不改环境变量)。 ### 大模型调用日志(log/ 目录) 所有大模型 / API 调用统一通过 [llm_logger.py](file:///d:/ai/work_space/rag_test/llm_logger.py) 记录为 JSONL,按天保存,写入失败不影响主流程。日志含业务问答内容,已在 `.gitignore` 中忽略。 **RAG 回答日志** `llm_YYYY-MM-DD.jsonl`: - `timestamp` 调用时间、`model` 模型名、`status`(`success` / `error` / `skipped_no_context`)、`latency_ms` 耗时 - `question` 用户问题、`context` 实际送入模型的完整上下文(含编号和来源) - `retrieved` 检索条目明细(排名、相关度、来源、分片信息、正文) - `answer` 模型回答、`error` 错误信息(成功时为空) **Query 改写日志** `query_rewrite_YYYY-MM-DD.jsonl`: - `timestamp` 调用时间、`provider`(`local` / `cloud`)、`model` 模型名、`api_url` 实际请求地址、`status`(`success` / `error`)、`latency_ms` 耗时 - `original_query` 原始 query、`rewrites` 改写候选列表(每项含 `query`、`confidence` 置信度、`rule` 使用的改写规则) - `selected_query` 置信度最高、被选中进入检索的 query、`selected_confidence` 对应置信度、`error` 错误信息(成功时为空) 改写失败(如本地改写服务未启动、云端缺少 API Key)时该次检索请求会直接返回错误并记录 `error` 日志,不使用原始 query 降级检索。(意图路由与 HyDE/Step-Back 扩展的调用也写入本日志,用 `mode` 字段区分,如 `mode="query_routing"`) **精排日志** `rerank_YYYY-MM-DD.jsonl`: - `timestamp`、`model`(如 `qwen3.7-text-rerank`)、`status`(`success` / `error`)、`latency_ms` 耗时 - `query` 精排用查询、`input_count` / `output_count` 输入输出条数、`error` 错误信息 **媒体文本提取日志** `vlm_extract_YYYY-MM-DD.jsonl`: - `timestamp` 调用时间、`model` 模型名(Qwen-VL 或 qwen-plus)、`status`、`latency_ms` - `input` 文件路径或帧路径、`prompt` 提示词 - `output` 提取结果(图片返回 `{title, text}`,视频帧返回描述文本)、`error` **向量嵌入日志** `embedding_YYYY-MM-DD.jsonl`: - `timestamp`、`model`(`tongyi-embedding-vision-flash-2026-03-06`)、`status`、`latency_ms` - `input_type`(`text` / `image` / `video`)、`input`(文本前 500 字或文件路径) - `output_dim` 向量维度(仅记录维度,不记录完整向量)、`error` ### 对话与思考过程日志(think/ 目录) 每次检索问答流程会通过 [think_logger.py](file:///d:/ai/work_space/rag_test/think_logger.py) 以 Markdown 格式记录到 `think/YYYY-MM-DD.md`,单文件超过 10MB 时自动拆分为 `YYYY-MM-DD_2.md`、`_3.md` ... 记录内容包括: - 时间戳、用户原始查询、历史查询、检索参数(知识库、top_k、阈值、改写模型) - 查询改写候选表(候选、置信度、是否选用) - 检索结果(命中条目、分数、内容摘要、元数据、媒体 URL) - 大模型回答 ### Collection 名称 在「分片结果预览」中可填写 Collection 名称,导入时分片向量会写入指定 Collection: - 规则:3-63 字符,仅允许字母、数字、下划线、连字符和点,首尾必须为字母或数字,不允许连续的点(ChromaDB 限制) - 名称留空时使用环境变量 `COLLECTION_NAME` 或默认值 `rag_documents` - 文本文档导入时 Collection 不存在会自动创建;媒体导入同理 - 检索时 Collection 不存在会返回错误(不自动创建);状态面板会实时显示当前输入名称的分片数(未创建时标注「未创建」) ## 其他开源向量数据库参考 | 数据库 | 特点 | 适用场景 | |--------|------|---------| | **ChromaDB** | Python 原生、轻量、零部署 | 开发/原型/RAG 快速上手 | | Milvus | 分布式、高性能、大规模 | 生产环境亿级向量 | | Qdrant | Rust 编写、高性能、过滤强 | 需要复杂元数据过滤 | | Weaviate | 内置模块化、GraphQL | 全功能搜索平台 | | FAISS | Meta 开源、底层库 | 嵌入式向量检索(非完整数据库) | 如果你需要更高性能或分布式部署,可以考虑 Milvus 或 Qdrant: **Milvus 安装(Docker):** ```bash # 下载 standalone 配置 wget https://github.com/milvus-io/milvus/releases/download/v2.4.10/milvus-standalone-docker-compose.yml -O docker-compose.yml docker compose up -d # pip install pymilvus 即可连接 ``` **Qdrant 安装(Docker):** ```bash docker run -p 6333:6333 -v qdrant_storage:/qdrant/storage qdrant/qdrant # pip install qdrant-client 即可连接 ```