# mtl-rag
**Repository Path**: mutongli/mtl-rag
## Basic Information
- **Project Name**: mtl-rag
- **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-10-15
- **Last Updated**: 2026-10-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# mtl-rag
**端到端 RAG 系统:多格式文档解析 · 混合检索 · 流式生成**
[中文](#中文) · [English](#english)
---
## 中文
### 简介
`mtl-rag` 是一个可开箱即用的端到端检索增强生成(RAG)系统:把本地文档(PDF / DOCX / Markdown / TXT / HTML / CSV / XLSX)解析、分块、向量化并写入 Qdrant,再以「混合检索 + 重排」召回上下文,交由在线 LLM 流式作答,回答末尾自动附带与入模片段严格对应的参考资料。
系统同时提供三种对外形态:命令行(CLI)、HTTP API(FastAPI)与 Web UI(Streamlit),三者共用同一套核心管道。
### 特性
- **多格式解析**:PDF(含表格)/ DOCX / Markdown / TXT / HTML / CSV / XLSX;目录可递归批量导入,单文件失败不影响整体。
- **两类知识库**:
- 非结构化库:支持五种分块策略(`fixed` / `recursive` / `semantic` / `parent_child` / `sentence_window`)。
- 结构化库:字段级 `embed` / `show` / `filter` 控制、自定义 Jinja2 序列化模板、主键确定性去重;`filter` 字段自动建 payload 索引。
- **文档内嵌图片**:默认本地 OCR(RapidOCR)文字化入库,可切换在线多模态 VLM 或关闭,原图落盘供 UI 展示。
- **混合检索**:dense 向量 + BM25 sparse 双路召回 + RRF 跨变体融合;支持同父去重、阈值过滤与空结果兜底、可选 MMR 多样性。
- **检索增强(均可配置开关)**:LLM 查询改写、Multi-Query 多变体并行检索。
- **可插拔重排**:`noop`(关闭)/ `bge-local`(本地 fastembed ONNX,默认)/ `jina-api`(在线兼容 Jina 协议)。
- **流式生成**:LLM 逐段流式输出;空检索时直接返回固定拒答话术,不调用 LLM、不附参考资料。
- **幂等重入库**:uuid5 确定性 point id + 入库前按 `source` 删除旧点,重复导入同一文件不会产生重复数据。
- **三种对外形态**:CLI / HTTP API(含 SSE 流式端点 + Swagger 文档)/ Streamlit Web UI。
### 架构
```
文档 ──► 解析(document) ──► 分块(chunking) ──► Embedding + 入库(ingestion/embedding/vectorstore)
│
▼
Qdrant ◄── 检索(retrieval:dense/hybrid + RRF + rerank + 改写)
│
▼
RAG 编排(rag) ──► LLM 流式生成
│
▼
CLI / HTTP API / Web UI
```
### 目录结构
```
mtl_rag/
├── core/ # 配置(pydantic-settings)与日志
├── document/ # 各格式解析器 + 内嵌图片处理(OCR / VLM)
├── chunking/ # 五种可插拔分块策略
├── embedding/ # OpenAI 兼容 Embedding + fastembed sparse 向量
├── vectorstore/ # Qdrant 双模式连接(server / local)与 collection 管理
├── ingestion/ # 非结构化 / 结构化入库管道 + 上传编排(API 与 Web 共用)
├── retrieval/ # 检索管道、RRF 融合、查询改写、可插拔 reranker
├── rag/ # RAG 编排引擎 + Prompt 模板(token 预算管理)
├── api/ # FastAPI 应用(kb / ingest / ask / health)
├── web/ # Streamlit Web UI(问答 / 知识库 / 导入 三页)
├── cli/ # click 命令入口
├── llm.py # 同步 SSE 流式 LLM 客户端
├── registry.py # 知识库注册表(SQLite WAL)
└── exceptions.py
prompts/ # default_rag.j2 提示模板
tests/ # 单元测试(本地嵌入式 Qdrant + Stub,不触网)
docs/ # 需求台账 + 任务文档
```
### 环境要求
- Python ≥ 3.11(开发验证于 3.12)
- 可选:Qdrant 服务(未部署时自动回退到本地嵌入式模式)
- 联网可用的 Embedding / LLM API(OpenAI 兼容协议)
### 安装
```bash
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
# 国内建议用清华镜像
pip install -e ".[dev]" -i https://pypi.tuna.tsinghua.edu.cn/simple
```
> **Windows 平台约束**:`pyproject.toml` 已将 `onnxruntime` 按平台锁定。Win32 限 `<1.20`——本机 `msvcp140.dll` 若为旧版(如 14.24),`onnxruntime>=1.20` 会在导入时崩溃;Linux/macOS 的 manylinux wheel 自带原生依赖,不设此限制(要求 glibc ≥ 2.28)。
### 配置
复制模板并填入真实值:
```bash
cp .env.example .env
```
所有字段均可被同名环境变量覆盖,字段名大小写不敏感。关键项:
| 分组 | 配置项 | 说明 |
|------|--------|------|
| Qdrant | `QDRANT_URL` / `QDRANT_API_KEY` | 服务模式地址与密钥 |
| Qdrant | `QDRANT_LOCAL_PATH` | 本地嵌入式模式存储路径(未部署服务时使用) |
| Embedding | `EMBEDDING_PROVIDER` / `EMBEDDING_API_KEY` / `EMBEDDING_MODEL` / `EMBEDDING_DIM` | 必填;`DIM` 必须与模型输出维度一致(如 `text-embedding-3-small=1536`) |
| LLM | `LLM_PROVIDER` / `LLM_API_KEY` / `LLM_MODEL` | 必填;`LLM_BASE_URL` 用于兼容 endpoint(Ollama 伪装 / 国产云),直连官方留空 |
| LLM | `LLM_MAX_CONTEXT_TOKENS` / `LLM_OUTPUT_RESERVE_TOKENS` / `LLM_TEMPERATURE` | token 预算与生成温度 |
| Rerank | `RERANK_ENABLED` / `RERANK_PROVIDER` | 默认开、`bge-local`;可切 `jina-api` |
| 分块 | `CHUNK_STRATEGY` / `CHUNK_SIZE_TOKENS` / `CHUNK_OVERLAP_TOKENS` | 策略与切分粒度 |
| 图片 | `IMAGE_PROCESSING` / `IMAGE_STORE_PATH` / `IMAGE_MAX_PER_DOC` | `ocr`(默认)/ `vlm` / `off` |
| 检索 | `RETRIEVAL_TOP_K` / `RETRIEVAL_TOP_N_AFTER_RERANK` / `SIMILARITY_THRESHOLD` / `USE_MMR` | 召回与入模片段数 |
| 检索增强 | `RETRIEVAL_HYBRID` / `RRF_K` / `QUERY_REWRITE_ENABLED` / `MULTI_QUERY_ENABLED` | 均为开关 |
| 应用 | `APP_HOST` / `APP_PORT` | `mtl-rag serve` 默认监听地址 |
### 使用
#### CLI
```bash
# 知识库管理
mtl-rag kb list # 列出全部知识库及 chunk 数
mtl-rag kb create docs --type unstructured # 创建非结构化库
mtl-rag kb create items --type structured --schema schema.json # 创建结构化库
mtl-rag kb info docs # 查看类型 / schema / chunk 数
mtl-rag kb delete docs --yes # 删除(含 Qdrant collection)
# 文档导入(文件或目录,目录默认递归)
mtl-rag ingest ./data --kb docs
mtl-rag ingest ./data --kb docs --no-recursive
# 流式问答
mtl-rag ask "这份报告的核心结论是什么?" --kb docs
mtl-rag ask "..." --kb docs --full # 一次性输出完整回答
mtl-rag ask --kb docs --interactive # 交互模式,循环提问
mtl-rag ask "..." --kb docs --top-k 20 --top-n 5
# 启动 HTTP API
mtl-rag serve --host 0.0.0.0 --port 8000
```
`mtl-rag kb create --schema` 支持两种书写形态:`{"primary": ..., "serialize_template": ..., "fields": {...}}`,或扁平结构(主键写在字段配置的 `primary: true`)。
#### HTTP API
启动后访问 `http://localhost:8000/docs` 查看 Swagger。
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查(不带版本前缀) |
| GET | `/api/v1/knowledge-bases` | 列出知识库 |
| POST | `/api/v1/knowledge-bases` | 创建知识库(重名 → 409) |
| GET | `/api/v1/knowledge-bases/{kb_name}` | 知识库详情(不存在 → 404) |
| DELETE | `/api/v1/knowledge-bases/{kb_name}` | 删除知识库(204) |
| POST | `/api/v1/ingest` | 导入文档(multipart:`kb_name` + `files`) |
| POST | `/api/v1/ask` | 非流式问答,返回回答 + `sources` |
| POST | `/api/v1/ask/stream` | SSE 流式问答 |
SSE 线格式:逐段 `data: {"content": "..."}`,末尾 `data: {"done": true}`;中途出错则以 `data: {"error": "..."}` 事件承载。
```bash
# 创建知识库
curl -X POST http://localhost:8000/api/v1/knowledge-bases \
-H "Content-Type: application/json" \
-d '{"name": "docs", "type": "unstructured"}'
# 导入文档
curl -X POST http://localhost:8000/api/v1/ingest \
-F "kb_name=docs" -F "files=@./data/report.pdf"
# 流式问答
curl -N -X POST http://localhost:8000/api/v1/ask/stream \
-H "Content-Type: application/json" \
-d '{"query": "核心结论是什么?", "knowledge_base": "docs"}'
```
#### Web UI
```bash
streamlit run mtl_rag/web/app.py
```
三个页面:**问答**(流式回答 + 参考资料展开)、**知识库**(创建 / 删除)、**导入**(多文件上传 + 结果表)。
### 测试
```bash
# Windows
.\.venv\Scripts\python.exe -m pytest tests -q
# macOS / Linux
python -m pytest tests -q
```
测试使用本地嵌入式 Qdrant 与确定性 Stub Embedding,并 stub 掉 LLM 调用,全程不触网;HTTP API 用 `TestClient`、Web 用 Streamlit `AppTest` 驱动。
### 跨平台部署注意
- **Docker / slim 镜像**:`rapidocr` 依赖的 `opencv-python` 需要系统库,需先安装 `libgl1` 与 `libglib2.0-0`。
- **模型缓存**:`fastembed` 首次运行需从 HuggingFace 下载 ONNX 模型,国内部署请配置 HF 镜像或提前预热缓存。
- **VC++ 运行库**:Windows 下 `onnxruntime` 版本受 `msvcp140.dll` 版本制约(见上文「Windows 平台约束」)。
### 文档索引
| 文档 | 说明 |
|------|------|
| [docs/requirements/需求.md](docs/requirements/需求.md) | 需求台账 |
| [docs/tasks/rag-system/00-总任务计划.md](docs/tasks/rag-system/00-总任务计划.md) | 任务总计划与依赖关系 |
| [docs/tasks/rag-system/](docs/tasks/rag-system/) | 各子任务方案与验收记录(task-01 … task-08) |
| [.env.example](.env.example) | 全量配置项模板 |
---
## English
### Overview
`mtl-rag` is an out-of-the-box end-to-end Retrieval-Augmented Generation system. It parses local documents (PDF / DOCX / Markdown / TXT / HTML / CSV / XLSX), chunks and embeds them into Qdrant, retrieves context via hybrid search with reranking, and streams the answer from an online LLM — appending a reference list that strictly maps to the chunks fed into the prompt.
Three interfaces share the same core pipeline: a CLI, an HTTP API (FastAPI) and a Web UI (Streamlit).
### Features
- **Multi-format parsing**: PDF (incl. tables) / DOCX / Markdown / TXT / HTML / CSV / XLSX; recursive directory import where a single failure does not abort the batch.
- **Two knowledge-base types**:
- Unstructured: five pluggable chunking strategies (`fixed` / `recursive` / `semantic` / `parent_child` / `sentence_window`).
- Structured: field-level `embed` / `show` / `filter` flags, custom Jinja2 serialization templates, deterministic primary-key deduplication; `filter` fields get payload indexes.
- **Embedded images**: local OCR (RapidOCR) by default, switchable to an online VLM or disabled; original images are persisted for UI display.
- **Hybrid retrieval**: dense vectors + BM25 sparse recall with RRF fusion; same-parent dedup, score thresholding with empty-result fallback, optional MMR diversity.
- **Retrieval enhancements (toggleable)**: LLM query rewriting and multi-query parallel retrieval.
- **Pluggable reranking**: `noop` / `bge-local` (local fastembed ONNX, default) / `jina-api`.
- **Streaming generation**: token-by-token output; on empty retrieval a fixed refusal is returned directly, without calling the LLM.
- **Idempotent re-ingestion**: deterministic uuid5 point ids plus delete-by-`source` before insert.
- **Three interfaces**: CLI / HTTP API (SSE streaming + Swagger docs) / Streamlit Web UI.
### Architecture
```
docs ──► parse(document) ──► chunk(chunking) ──► embed + ingest(ingestion/embedding/vectorstore)
│
▼
Qdrant ◄── retrieve(retrieval: dense/hybrid + RRF + rerank + rewriting)
│
▼
RAG orchestration(rag) ──► streamed LLM answer
│
▼
CLI / HTTP API / Web UI
```
### Project layout
```
mtl_rag/
├── core/ # settings (pydantic-settings) and logging
├── document/ # per-format parsers + embedded image handling (OCR / VLM)
├── chunking/ # five pluggable chunking strategies
├── embedding/ # OpenAI-compatible embeddings + fastembed sparse vectors
├── vectorstore/ # Qdrant dual-mode connection (server / local) and collection management
├── ingestion/ # unstructured / structured ingestion pipelines + shared upload orchestration
├── retrieval/ # retrieval pipeline, RRF fusion, query rewriting, pluggable rerankers
├── rag/ # RAG engine + prompt templates (token-budget aware)
├── api/ # FastAPI app (kb / ingest / ask / health)
├── web/ # Streamlit UI (chat / knowledge base / import)
├── cli/ # click command entrypoint
├── llm.py # synchronous SSE streaming LLM client
├── registry.py # knowledge-base registry (SQLite WAL)
└── exceptions.py
prompts/ # default_rag.j2 prompt template
tests/ # unit tests (local embedded Qdrant + stubs, offline)
docs/ # requirement ledger + task documents
```
### Requirements
- Python ≥ 3.11 (verified on 3.12)
- Optional: a Qdrant server (falls back to embedded local mode when unavailable)
- Network access to Embedding / LLM APIs (OpenAI-compatible)
### Installation
```bash
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -e ".[dev]"
```
> **Windows constraint**: `pyproject.toml` pins `onnxruntime` per platform. On Win32 it is capped at `<1.20` — with an older `msvcp140.dll` (e.g. 14.24), `onnxruntime>=1.20` crashes on import. Linux/macOS manylinux wheels bundle their native dependencies and are not restricted this way (glibc ≥ 2.28 required).
### Configuration
```bash
cp .env.example .env
```
Every field can be overridden by an environment variable of the same (case-insensitive) name. Key options:
| Group | Key | Description |
|-------|-----|-------------|
| Qdrant | `QDRANT_URL` / `QDRANT_API_KEY` | Server-mode endpoint and key |
| Qdrant | `QDRANT_LOCAL_PATH` | Embedded local storage path (when no server is deployed) |
| Embedding | `EMBEDDING_PROVIDER` / `EMBEDDING_API_KEY` / `EMBEDDING_MODEL` / `EMBEDDING_DIM` | Required; `DIM` must match the model output (e.g. `text-embedding-3-small=1536`) |
| LLM | `LLM_PROVIDER` / `LLM_API_KEY` / `LLM_MODEL` | Required; `LLM_BASE_URL` for compatible endpoints, leave empty for official APIs |
| LLM | `LLM_MAX_CONTEXT_TOKENS` / `LLM_OUTPUT_RESERVE_TOKENS` / `LLM_TEMPERATURE` | Token budget and generation temperature |
| Rerank | `RERANK_ENABLED` / `RERANK_PROVIDER` | On by default, `bge-local`; can switch to `jina-api` |
| Chunking | `CHUNK_STRATEGY` / `CHUNK_SIZE_TOKENS` / `CHUNK_OVERLAP_TOKENS` | Strategy and granularity |
| Images | `IMAGE_PROCESSING` / `IMAGE_STORE_PATH` / `IMAGE_MAX_PER_DOC` | `ocr` (default) / `vlm` / `off` |
| Retrieval | `RETRIEVAL_TOP_K` / `RETRIEVAL_TOP_N_AFTER_RERANK` / `SIMILARITY_THRESHOLD` / `USE_MMR` | Recall and prompt-fragment counts |
| Enhancements | `RETRIEVAL_HYBRID` / `RRF_K` / `QUERY_REWRITE_ENABLED` / `MULTI_QUERY_ENABLED` | All toggles |
| App | `APP_HOST` / `APP_PORT` | `mtl-rag serve` bind address |
### Usage
#### CLI
```bash
# Knowledge bases
mtl-rag kb list
mtl-rag kb create docs --type unstructured
mtl-rag kb create items --type structured --schema schema.json
mtl-rag kb info docs
mtl-rag kb delete docs --yes
# Ingest (file or directory; directories recurse by default)
mtl-rag ingest ./data --kb docs
mtl-rag ingest ./data --kb docs --no-recursive
# Streaming Q&A
mtl-rag ask "What are the key takeaways?" --kb docs
mtl-rag ask "..." --kb docs --full
mtl-rag ask --kb docs --interactive
mtl-rag ask "..." --kb docs --top-k 20 --top-n 5
# HTTP API
mtl-rag serve --host 0.0.0.0 --port 8000
```
`--schema` accepts either `{"primary": ..., "serialize_template": ..., "fields": {...}}` or a flat object with `primary: true` on the key field.
#### HTTP API
Swagger is available at `http://localhost:8000/docs`.
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check (no version prefix) |
| GET | `/api/v1/knowledge-bases` | List knowledge bases |
| POST | `/api/v1/knowledge-bases` | Create (duplicate name → 409) |
| GET | `/api/v1/knowledge-bases/{kb_name}` | Details (missing → 404) |
| DELETE | `/api/v1/knowledge-bases/{kb_name}` | Delete (204) |
| POST | `/api/v1/ingest` | Ingest (multipart: `kb_name` + `files`) |
| POST | `/api/v1/ask` | Non-streaming answer with `sources` |
| POST | `/api/v1/ask/stream` | SSE streaming answer |
SSE wire format: `data: {"content": "..."}` per segment, terminated by `data: {"done": true}`; mid-stream errors are delivered as `data: {"error": "..."}`.
#### Web UI
```bash
streamlit run mtl_rag/web/app.py
```
Three pages: **Chat** (streamed answers with expandable references), **Knowledge base** (create / delete), **Import** (multi-file upload with a result table).
### Tests
```bash
python -m pytest tests -q
```
Tests run fully offline using an embedded local Qdrant, deterministic stub embeddings and a stubbed LLM; the HTTP API is driven via `TestClient` and the Web UI via Streamlit `AppTest`.
### Cross-platform deployment
- **Docker / slim images**: `opencv-python` (a `rapidocr` dependency) needs system libraries — install `libgl1` and `libglib2.0-0`.
- **Model cache**: `fastembed` downloads ONNX models from HuggingFace on first run; configure an HF mirror or pre-warm the cache.
- **VC++ runtime**: on Windows the `onnxruntime` version is constrained by `msvcp140.dll` (see the "Windows constraint" note above).
### Documentation
| Document | Description |
|----------|-------------|
| [docs/requirements/需求.md](docs/requirements/需求.md) | Requirement ledger |
| [docs/tasks/rag-system/00-总任务计划.md](docs/tasks/rag-system/00-总任务计划.md) | Task plan and dependencies |
| [docs/tasks/rag-system/](docs/tasks/rag-system/) | Per-task specs and acceptance records (task-01 … task-08) |
| [.env.example](.env.example) | Full configuration template |
---
## License
未指定 / Not specified.