基于文档的检索增强生成(RAG)问答系统,支持中英文混合文档,并通过自我批评机制在输出前自动检测并修正幻觉内容。
- 混合检索 — 语义向量(Dense)+ 关键词(BM25)双路检索,经 RRF 融合排序,人名、专有名词与语义查询均能精准命中
- 中英文支持 — 使用多语言 embedding 模型
paraphrase-multilingual-MiniLM-L12-v2,支持中文、英文及混合文档 - 中文 PDF 修复 — 自动消除 PDF 解析时产生的字符级换行("你\n有\n什\n么" → "你有什么"),保证 embedding 质量
- 本地向量化 — embedding 完全在本地运行,无需外部向量化 API
- ChromaDB 持久化 — 向量数据落盘,重启后无需重新入库,文档不变则无需重跑
ingest.py - LCEL 生成链 — 全部 LangChain 管道使用 pipe(
|)语法构建,严格约束 LLM 只基于上下文作答 - 自我批评校验 — 第二次 LLM 调用,逐条核查初始答案与原文的一致性,自动修正不符内容
- Rich 终端界面 — 彩色来源表格、答案面板、批评说明,交互体验清晰
bash setup.sh # 创建 conda 环境 'trustrag' 并安装全部依赖
conda activate trustrag
cp .env.example .env # 编辑 .env,填入 OPENAI_API_KEY=sk-...cp 你的文档.pdf data/
python src/ingest.pypython src/main.py输入问题后按 Enter,输入 q 退出。
注意: embedding 模型(约 480MB)在首次运行时从 HuggingFace Hub 自动下载,后续使用本地缓存,无需重复下载。
trustrag/
├── setup.sh # 一键创建 conda 环境并安装依赖
├── requirements.txt # 精确锁定的依赖版本
├── .env.example # API Key 配置模板
├── data/ # 放置 PDF 文档
├── chroma_db/ # ChromaDB 持久化目录(ingest.py 自动创建)
└── src/
├── ingest.py # PDF 解析 → 文本清理 → 切块 → 向量化 → 入库
├── retriever.py # Dense + BM25 混合检索,RRF 融合排序
├── generator.py # LCEL 生成链:上下文 + 问题 → gpt-4o-mini → 初始答案
├── self_critique.py # LCEL 批评链:验证答案与原文一致性,自动修正
└── main.py # 交互式命令行入口(Rich TUI,完整流水线)
用户输入问题
│
▼
┌─────────────────────────────────┐
│ 混合检索器 │
│ Dense(语义向量) + BM25(关键词)│ top-k 文档块
│ ↓ RRF 融合排序 ↓ │ ──────────────┐
└─────────────────────────────────┘ │
▼
┌──────────────────┐
│ 生成器 │ 初始答案
│ gpt-4o-mini │ ──────────┐
│ (严格接地) │ │
└──────────────────┘ │
▼
┌──────────────────┐
│ 自我批评校验 │
│ gpt-4o-mini │
│ (逐条核查修正) │
└──────────────────┘
│
▼
验证后答案 + 批评说明
| 检索方式 | 适合场景 | 示例 |
|---|---|---|
| Dense 语义检索 | 概念理解、近义词匹配 | "TrustRAG 如何减少幻觉?" |
| BM25 关键词检索 | 人名、机构名、专有术语 | "RRF 是什么算法?" |
| RRF 融合 | 两路结果按排名合并,天然量纲统一 | 所有查询均受益 |
请输入问题: TrustRAG 使用了哪些检索方法?
┌──────────────── 检索到的来源文档 ─────────────────────────────────┐
│ 序号 │ 文件名 │ 页码 │ 相关性 │ 摘录 │
│ 1 │ demo_document.pdf │ 1 │ 0.033 │ TrustRAG uses hybrid │
│ 2 │ demo_document.pdf │ 1 │ 0.031 │ Reciprocal Rank Fus… │
└───────────────────────────────────────────────────────────────────┘
┌──────────────── 验证后的最终答案 ─────────────────────────────────┐
│ TrustRAG 使用了混合检索策略,包含以下两路: │
│ 1. Dense 语义检索:使用多语言 embedding 模型将查询和文档映射到 │
│ 向量空间,通过余弦相似度匹配【来源 1】 │
│ 2. BM25 关键词检索:基于词频统计精确匹配人名、专有名词等【来源 2】 │
│ 两路结果通过 Reciprocal Rank Fusion(RRF)融合排序后返回 top-5。 │
└───────────────────────────────────────────────────────────────────┘
┌──────────────── 自我批评 — ✓ 未发现问题 ──────────────────────────┐
│ 未发现问题,答案完全基于上下文。 │
└───────────────────────────────────────────────────────────────────┘
| 参数 | 文件 | 默认值 | 说明 |
|---|---|---|---|
| 文本块大小 | src/ingest.py |
500 字符 | 增大可保留更多上下文,减小可提升精度 |
| 块重叠长度 | src/ingest.py |
50 字符 | 防止切块截断语义 |
| 检索候选数 | src/retriever.py |
每路 12 条 | Dense 和 BM25 各取 12 条后融合 |
| 最终返回数 | src/retriever.py |
5 条 | RRF 融合后取 top-5 送入 LLM |
| LLM 模型 | src/generator.py |
gpt-4o-mini | 可替换为其他 OpenAI 模型 |
| Embedding 模型 | src/ingest.py |
paraphrase-multilingual-MiniLM-L12-v2 | 支持50+语言 |
Q:为什么每次启动 main.py 都会加载模型?
A:ChromaDB 存储的是文档向量,但用户输入的查询每次都需要实时向量化才能做相似度搜索,因此 embedding 模型必须在内存中。模型文件从本地缓存加载(不重新下载),通常 1 秒内完成,且在整个会话期间只加载一次。
Q:什么时候需要重新运行 ingest.py?
A:只有在以下情况下才需要重新入库:新增/删除/修改了 data/ 目录中的 PDF 文件。如果只是重新提问,无需重新入库。
Q:支持哪些文档格式?
A:目前支持 PDF 格式。如需支持 Word、TXT 等格式,可在 src/ingest.py 中替换对应的 LangChain Document Loader。