Releases: leonyangdev/UltimateRAG
Releases · leonyangdev/UltimateRAG
v0.4.0
Stage 4: GraphRAG & Fine-tuning
🎯 概述
Stage 4 是 UltimateRAG 的最高阶段,实现了知识图谱增强的 RAG (GraphRAG) 和领域微调能力。
核心能力
| 能力 | 描述 |
|---|---|
| 知识图谱 | 自动从文档中抽取实体和关系,构建知识图谱 |
| 图检索 | 基于图遍历的智能检索,发现隐性关联 |
| 全局摘要 | 基于社区检测生成全局性摘要 |
| Embedding 微调 | 使用私有数据微调 Embedding 模型 |
| LLM 微调数据 | 自动生成高质量的微调训练数据 |
| 本地 LLM 微调 | 🆕 使用 LoRA 在本地设备上微调开源大模型 |
🚀 快速开始
1. 安装依赖
pip install -r requirements.txt对于 Neo4j 支持(可选):
pip install neo4j2. 配置环境
在 .env 文件中添加:
# 基础配置(继承自 Stage 1-3)
OPENAI_API_KEY=your_api_key
MODEL_NAME=gpt-4o
# GraphRAG 配置
GRAPH_STORE_TYPE=memory # 或 neo4j
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your_password
# 微调配置
EMBEDDING_FINETUNE_MODEL=BAAI/bge-base-zh-v1.53. 使用 GraphRAG
from src.stage_4.main import run_graph_rag_demo
# 运行演示
run_graph_rag_demo()或者手动使用:
from src.stage_4.graph_rag import GraphRAGChain
from src.stage_1.document_loader import DocumentLoader
# 加载文档
loader = DocumentLoader()
documents = loader.load_directory("./data/documents")
# 初始化 GraphRAG
graph_rag = GraphRAGChain(documents)
# 构建知识图谱(首次使用)
graph_rag.build_knowledge_graph()
# 提问
result = graph_rag.ask("分析文档中提到的公司之间的关系")
print(result.answer)📖 模块详解
1. GraphRAG 模块
实体抽取 (EntityExtractor)
从文本中识别和提取命名实体:
from src.stage_4.graph_rag import EntityExtractor
extractor = EntityExtractor()
entities = extractor.extract("华为公司在深圳成立,任正非是创始人。")
# 输出:
# [
# Entity(name="华为公司", type="Organization", ...),
# Entity(name="深圳", type="Location", ...),
# Entity(name="任正非", type="Person", ...)
# ]支持的实体类型:
Person- 人物Organization- 组织/公司Location- 地点Event- 事件Concept- 概念/术语Product- 产品Time- 时间
关系抽取 (RelationExtractor)
提取实体之间的关系:
from src.stage_4.graph_rag import RelationExtractor
extractor = RelationExtractor()
relations = extractor.extract(
text="华为公司在深圳成立,任正非是创始人。",
entities=entities
)
# 输出:
# [
# Relation(source="任正非", target="华为公司", type="founded", ...),
# Relation(source="华为公司", target="深圳", type="located_in", ...)
# ]知识图谱 (KnowledgeGraph)
管理实体和关系的图结构:
from src.stage_4.graph_rag import KnowledgeGraph
kg = KnowledgeGraph()
# 添加实体
kg.add_entity(entity)
# 添加关系
kg.add_relation(relation)
# 查询实体的邻居
neighbors = kg.get_neighbors("华为公司", hops=2)
# 查找路径
path = kg.find_path("任正非", "深圳")
# 获取子图
subgraph = kg.get_subgraph(["华为公司", "任正非"])图检索器 (GraphRetriever)
基于图结构进行智能检索:
from src.stage_4.graph_rag import GraphRetriever
retriever = GraphRetriever(knowledge_graph)
# 检索相关实体和上下文
results = retriever.retrieve(
query="华为的创始人是谁?",
top_k=5
)2. 微调模块
Embedding 微调
使用私有数据微调 Embedding 模型:
from src.stage_4.fine_tuning import EmbeddingFineTuner
fine_tuner = EmbeddingFineTuner(
base_model="BAAI/bge-base-zh-v1.5",
output_dir="./models/my_embedding"
)
# 生成训练数据
training_data = fine_tuner.generate_training_data(documents)
# 训练
fine_tuner.train(training_data, epochs=3)LLM 微调数据准备
自动生成微调训练数据:
from src.stage_4.fine_tuning import LLMFineTuner
finetuner = LLMFineTuner()
# 生成 QA 对
qa_pairs = finetuner.generate_qa_pairs(documents)
# 导出为不同格式
finetuner.export_jsonl(qa_pairs, "train.jsonl") # OpenAI 格式
finetuner.export_alpaca(qa_pairs, "train_alpaca.json") # Alpaca 格式本地 LLM 微调 (LoRA) 🆕
使用 LoRA 技术在本地设备上微调开源大模型:
from src.stage_4.fine_tuning import quick_finetune
# 一键微调(使用默认配置)
result = quick_finetune(
data_path="./data/finetune/train_alpaca.json",
model="Qwen/Qwen2.5-0.5B-Instruct", # 推荐小模型
epochs=3
)自定义配置:
from src.stage_4.fine_tuning import LocalLLMFineTuner, LocalFineTuneConfig
config = LocalFineTuneConfig(
base_model="Qwen/Qwen2.5-1.5B-Instruct",
output_dir="./models/my_finetuned_model",
lora_rank=8,
epochs=3,
device="cpu", # 支持 cpu / cuda / mps
)
finetuner = LocalLLMFineTuner(config)
finetuner.run_full_pipeline("./data/finetune/train_alpaca.json")
# 使用微调后的模型
response = finetuner.chat("小米公司是什么时候成立的?")📖 详细文档请参考 LocalFineTune.md
🔧 配置说明
Stage4Config 参数
@dataclass
class Stage4Config(Stage3Config):
# GraphRAG 配置
graph_store_type: str = "memory" # memory / neo4j
entity_types: List[str] = ... # 支持的实体类型
relation_types: List[str] = ... # 支持的关系类型
max_entities_per_chunk: int = 20 # 每个文档块最大实体数
max_relations_per_chunk: int = 30 # 每个文档块最大关系数
graph_traversal_depth: int = 2 # 图遍历深度
# Neo4j 配置
neo4j_uri: str = "bolt://localhost:7687"
neo4j_username: str = "neo4j"
neo4j_password: str = ""
# Embedding 微调配置
embedding_finetune_model: str = "BAAI/bge-base-zh-v1.5"
embedding_finetune_epochs: int = 3
embedding_finetune_batch_size: int = 32
# LLM 微调数据配置
qa_pairs_per_doc: int = 5
qa_difficulty_levels: List[str] = ["easy", "medium", "hard"]📊 架构图
┌─────────────────────────────────────────────────────────────────┐
│ UltimateRAG Stage 4 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ GraphRAG 模块 │ │ 微调模块 │ │
│ ├──────────────────────┤ ├──────────────────────┤ │
│ │ • EntityExtractor │ │ • EmbeddingFineTuner │ │
│ │ • RelationExtractor │ │ • LLMFineTuner │ │
│ │ • KnowledgeGraph │ │ • TrainingDataGen │ │
│ │ • GraphStore │ │ • LocalLLMFineTuner │ 🆕 │
│ │ • GraphRetriever │ │ (LoRA 微调) │ │
│ │ • GraphRAGChain │ │ │ │
│ └──────────┬───────────┘ └───────────┬──────────┘ │
│ │ │ │
│ └────────────┬───────────────┘ │
│ │ │
│ ┌────────────▼───────────────┐ │
│ │ UltimateRAGChain │ │
│ │ (整合 Stage 1-4) │ │
│ └────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
│
│ 继承
▼
┌─────────────────────────────────────────────────────────────────┐
│ Stage 1-3 组件 │
│ VectorStore | HybridRetriever | Reranker | AgenticRAG | ... │
└─────────────────────────────────────────────────────────────────┘
🎓 典型使用场景
场景 1: 企业关系分析
# 分析合同文档中的公司关系
result = graph_rag.ask("找出 A 公司和 B 公司之间所有的合作关系")
# 返回:
# - 详细的关系描述
# - 关系路径可视化
# - 相关证据文档场景 2: 人物关系网络
# 分析新闻中的人物关系
result = graph_rag.ask("张三和李四是什么关系?他们之间有什么交集?")场景 3: 全局概括
# 对大量文档进行全局性总结
result = graph_rag.ask("总结过去三年公司在 AI 领域的战略布局")场景 4: 领域适配
# 微调 Embedding 模型适配医疗领域
fine_tuner.train(medical_documents)
# 使用微调后的模型
graph_rag.set_embedding_model("./models/medical_embedding")⚠️ 注意事项
-
性能考虑
- 实体/关系抽取会增加 LLM 调用,建议批量处理
- 大规模图谱建议使用 Neo4j
- Embedding 微调需要 GPU 效果更好
-
成本控制
- 设置合理的
max_entities_per_chunk - 使用缓存避免重复抽取
- 考虑使用本地 LLM 进行抽取
- 设置合理的
-
数据质量
- 抽取质量依赖于 LLM 能力
- 建议人工审核关键实体/关系
- 定期清理冗余实体
-
本地 LLM 微调 🆕
- CPU 训练速度较慢,建议使用小模型(0.5B-1.5B)
- 16GB 内存推荐使用 Qwen2.5-0.5B 或 1.5B
- LoRA rank 越小内存需求越低(推荐 4-8)
- 详见 LocalFineTune.md
📈 与前序阶段对比
| 维度 | Stage 3 | Stage 4 |
|---|---|---|
| 检索方式 | 向量 + BM25 | 向量 + BM25 + 图遍历 |
| 上下文理解 | 局部文档 | 全局关联 |
| 问题类型 | 单文档问答 | 跨文档关系推理 |
| 定制能力 | 通用模型 | 领域微调 |
| 复杂度 | 中等 | 高 |
🔗 相关资源
v0.3.0
Phase 3: 架构进化 (Modular & Agentic RAG)
📚 本阶段学习收获
Tip
Phase 3 实现了让 RAG 系统具备"思考"能力的核心组件,系统不再是简单的"检索-生成"直线流程,而是一个能够动态决策的智能网络。
🎯 完成的功能
1. 智能路由器 (router.py)
- 使用 LLM 结构化输出进行问题分类
- 支持 5 种路由类型:知识库、Web 搜索、计算器、代码执行、直接回答
- 包含置信度评估和回退策略
- 提供基于关键词的快速路由备选方案
2. 自反思 RAG (self_rag.py)
- 检索相关性评估(Relevance Evaluation)
- 答案质量评估(Quality Evaluation)
- 自动迭代优化(最多 N 轮)
- 查询优化与重检索
3. 工具集成 (tools/)
- Web 搜索工具:使用 DuckDuckGo 免费搜索
- 计算器工具:安全的数学表达式求值
- 代码执行器:受限沙箱中的 Python 执行
- 统计计算器:数据统计分析
4. 父子索引检索器 (parent_child_retriever.py)
- 大块(Parent)存储完整上下文
- 小块(Child)用于精准匹配
- 支持上下文窗口扩展
- 内存映射存储
5. 上下文压缩 (context_compressor.py)
- LLM 驱动的相关句子提取
- 基于关键词的快速压缩
- 压缩比统计与优化
💡 技术要点
1. LangChain 结构化输出
使用 Pydantic 模型定义输出格式,LLM 返回结构化数据:
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
class RouteDecision(BaseModel):
route_type: RouteType = Field(description="路由类型")
confidence: float = Field(description="置信度", ge=0.0, le=1.0)
reasoning: str = Field(description="决策原因")
llm = ChatOpenAI(model="gpt-4o")
structured_llm = llm.with_structured_output(RouteDecision)
result = structured_llm.invoke(prompt) # 返回 RouteDecision 实例2. Self-RAG 工作流程
用户问题 → 检索文档 → 相关性评估 → 生成答案 → 质量评估
↑ ↓
└─────── 质量不足时重新检索 ←────────┘
3. 父子索引原理
原始文档
↓
切分为大的父块(2000 字符)
↓
每个父块再切分为小的子块(400 字符)
↓
子块存入向量库,父块存入内存映射
检索时:用子块精准匹配 → 返回对应父块的完整上下文
4. 安全的代码执行
# 受限的内置函数白名单
SAFE_BUILTINS = {
"int": int, "float": float, "str": str,
"len": len, "range": range, "sum": sum,
"min": min, "max": max, "abs": abs,
# ... 更多安全函数
}
# 允许的模块白名单
ALLOWED_MODULES = {"math", "random", "datetime", "statistics"}
# 执行代码
exec(code, {"__builtins__": SAFE_BUILTINS}, local_vars)📊 测试结果
✅ 8 个测试类全部通过
- TestCalculatorTool: 计算器测试
- TestStatisticsCalculator: 统计计算测试
- TestCodeExecutor: 代码执行测试
- TestKeywordRouter: 路由器测试
- TestToolResult: 工具结果测试
- TestParentChildRetriever: 父子索引测试
- TestContextCompressor: 上下文压缩测试
🔑 关键技术点
| 技术 | 解决的问题 | 实现难度 | ROI |
|---|---|---|---|
| 智能路由 | 问题分类错误 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Self-RAG | 答案质量不稳定 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 工具调用 | 无法处理特定任务 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 父子索引 | 检索精度与上下文的平衡 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 上下文压缩 | Token 浪费 | ⭐⭐ | ⭐⭐⭐ |
🔗 文件结构
src/stage_3/
├── __init__.py # 包初始化
├── config.py # Stage 3 配置
├── router.py # 智能路由器
├── self_rag.py # 自反思 RAG
├── tools/
│ ├── __init__.py # 工具包
│ ├── base.py # 工具基类
│ ├── search_tool.py # Web 搜索
│ ├── calculator_tool.py # 计算器
│ └── code_executor.py # 代码执行
├── parent_child_retriever.py # 父子索引
├── context_compressor.py # 上下文压缩
├── agentic_rag_chain.py # Agentic RAG 链
├── main.py # 主入口
└── tests/
└── test_agentic_rag.py # 单元测试
🚀 使用方法
# 运行 Agentic RAG 系统
cd /path/to/UltimateRAG
source .venv/bin/activate
python -m src.stage_3.main --data ./data/documents
# 可选参数
--no-routing # 禁用智能路由
--no-self-rag # 禁用自反思
--no-tools # 禁用工具调用
--no-parent-child # 禁用父子索引
--no-compression # 禁用上下文压缩
--reindex # 强制重新索引📈 Phase 3 vs Phase 2 对比
| 特性 | Phase 2 | Phase 3 |
|---|---|---|
| 检索方式 | 混合检索 | 智能路由 + 多策略 |
| 答案质量 | 一次性生成 | 自反思迭代优化 |
| 处理能力 | 仅文档问答 | 文档 + 搜索 + 计算 + 代码 |
| 上下文 | 检索即返回 | 父子索引 + 压缩优化 |
| 智能程度 | 被动响应 | 主动决策 |
⚠️ 注意事项
1. 性能考量
- 智能路由会增加一次 LLM 调用
- Self-RAG 迭代会显著增加响应时间
- 建议根据场景选择性启用功能
2. 安全考量
- 代码执行器使用沙箱环境
- 已禁用危险函数和模块
- 设置执行超时防止死循环
3. 成本考量
- 每个功能都会消耗额外 Token
- 建议监控 Token 使用量
- 可通过禁用不需要的功能控制成本
📖 参考资料
🎓 核心概念总结
Agentic RAG 的本质
传统 RAG 是 Pipeline(流水线):
Query → Retrieve → Generate → Answer
Agentic RAG 是 Agent(智能体):
┌─→ 知识库检索 ─┐
│ │
Query → 路由决策 ──→├─→ Web 搜索 ──┼─→ 质量评估 ─→ 迭代优化 ─→ Answer
│ │
├─→ 工具调用 ──┤
│ │
└─→ 直接回答 ──┘
关键洞察
- 智能路由是入口:好的分类决定了后续处理的效率和质量
- 自反思是保障:让系统能够自我纠错,提高答案可靠性
- 工具是扩展:突破纯文本问答的局限
- 父子索引是平衡:在检索精度和上下文完整性之间找到平衡点
- 压缩是优化:节省 Token,聚焦关键信息
🔮 Phase 4 展望
下一阶段将进入更高级的领域:
- GraphRAG:知识图谱增强检索
- 领域微调:Embedding 和 LLM 的定制化
- 多模态:图片、表格等非文本数据处理
Note
Phase 3 的组件化设计为后续扩展奠定了良好基础。
v0.2.0
Phase 2: 质量飞跃 (Advanced RAG)
📚 本阶段学习收获
Tip
Phase 2 实现了多项高级 RAG 技术,显著提升了检索质量。
🎯 完成的功能
1. 语义分块 (semantic_chunker.py)
- 基于句子嵌入判断语义边界
- 动态调整分块大小
- 保持段落完整性
2. 元数据提取 (metadata_extractor.py)
- 自动提取标题、日期、文件信息
- 支持元数据过滤检索
- 增强文档可追溯性
3. 混合检索 (hybrid_retriever.py)
- BM25 关键词检索 + 向量语义检索
- 倒排融合算法 (RRF) 合并结果
- 中英文混合分词支持
4. 查询重写 (query_rewriter.py)
- 多路查询生成
- HyDE 假设文档嵌入
- 查询扩展(同义词)
5. 重排序 (reranker.py)
- BGE-Reranker Cross-Encoder
- 精细化排序 Top-K
- 简单规则重排备选方案
💡 技术要点
混合检索融合算法 (RRF)
# 倒排融合公式: score = Σ 1/(k + rank)
for rank, (doc, _) in enumerate(results):
doc_scores[doc_key] += 1 / (k + rank + 1)语义分块判断
# 当相似度低于阈值且块足够大时断开
if similarity < threshold and len(chunk) >= min_size:
chunks.append(current_chunk)
current_chunk = new_sentenceHyDE 工作原理
用户问题 -> LLM生成假设答案 -> 用假设答案检索 -> 找到真实文档
📊 测试结果
✅ 6 个单元测试全部通过
- TestMetadataExtractor: 元数据提取测试
- TestHybridRetriever: 混合检索测试
- TestQueryRewriter: 查询重写测试
- TestReranker: 重排序测试
⚠️ 关键技术点
| 技术 | 解决的问题 | ROI |
|---|---|---|
| 混合检索 | 专有名词搜索不到 | ⭐⭐⭐⭐⭐ |
| Re-ranking | 粗检索结果排序不准 | ⭐⭐⭐⭐⭐ |
| 查询重写 | 用户表达不清晰 | ⭐⭐⭐⭐ |
| 语义分块 | 固定分块切断语义 | ⭐⭐⭐ |
🔗 文件结构
src/stage_2/
├── __init__.py # 包初始化
├── semantic_chunker.py # 语义分块器
├── metadata_extractor.py # 元数据提取
├── hybrid_retriever.py # 混合检索器
├── query_rewriter.py # 查询重写
├── reranker.py # 重排序器
├── advanced_rag_chain.py # 高级 RAG 链
├── main.py # 主入口
└── tests/
└── test_advanced_rag.py # 单元测试
🚀 使用方法
# 运行 Advanced RAG 系统
cd /path/to/UltimateRAG
source .venv/bin/activate
python -m src.stage_2.main --data ./data/documents
# 可选参数
--no-semantic # 禁用语义分块
--no-rerank # 禁用重排序
--reindex # 强制重新索引📖 参考资料
v0.1.0
Phase 1: 原型验证 (MVP)
📚 本阶段学习收获
Tip
Phase 1 完成了 RAG 系统的基础架构搭建,实现了从文档加载到问答的完整流程。
🎯 完成的功能
1. 配置管理 (config.py)
- 使用
dataclass定义配置结构 - 支持从环境变量加载配置
- 实现单例模式的配置管理
2. 文档加载 (document_loader.py)
- 支持 PDF、Markdown、TXT、DOCX 四种格式
- 自动识别文件类型并选择加载器
- 支持递归加载整个目录
3. 文本分块 (chunker.py)
- 使用
RecursiveCharacterTextSplitter智能分割 - 支持中英文混合切分
- 可配置块大小和重叠
4. 嵌入模型 (embedder.py)
- 封装 OpenAI Embeddings
- 支持自定义 base_url(兼容 DeepSeek 等)
- 懒加载机制
5. 向量存储 (vectorstore.py)
- 使用 ChromaDB 持久化存储
- 支持相似度检索和带分数检索
- 可转换为 LangChain Retriever
6. RAG 问答链 (rag_chain.py)
- 组装检索器、Prompt 和 LLM
- 支持普通问答和带来源问答
- 支持流式输出
💡 技术要点
LangChain 1.1.3 新特性
# 使用 langchain_core 的基础类
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 使用 LCEL (LangChain Expression Language) 构建链
chain = prompt | llm | StrOutputParser()ChromaDB 集成
from langchain_community.vectorstores import Chroma
vectorstore = Chroma(
collection_name="rag_documents",
embedding_function=embeddings,
persist_directory="./data/chroma_db"
)分块最佳实践
- chunk_size: 512 字符(约 100-200 token)
- chunk_overlap: 50 字符(约 10%)
- 分隔符优先级: 段落 → 句子 → 词语
⚠️ 遇到的问题与解决方案
问题 1: LangChain 版本兼容性
现象: 旧代码使用 from langchain.xxx 导入报错
解决: 使用新的模块结构
langchain_core: 核心基础类langchain_openai: OpenAI 集成langchain_community: 社区集成
问题 2: ChromaDB 持久化
现象: 每次运行都需要重新索引
解决: 指定 persist_directory 参数,自动保存和加载
📊 测试结果
✅ 8 个单元测试全部通过
- TestConfig: 配置加载测试
- TestDocumentLoader: 文档加载测试
- TestTextChunker: 分块测试
📈 Phase 1 局限性
- 分块太碎: 固定大小分块可能切断完整的语义
- 检索不准: 纯向量检索对关键词匹配不友好
- 无排序优化: 检索结果未经过重排序
Note
这些问题将在 Phase 2 (Advanced RAG) 中解决。
🔗 文件结构
src/stage_1/
├── __init__.py # 包初始化
├── config.py # 配置管理
├── document_loader.py # 文档加载器
├── chunker.py # 文本分块器
├── embedder.py # 嵌入模型
├── vectorstore.py # 向量存储
├── rag_chain.py # RAG 问答链
├── main.py # 主入口
└── tests/
└── test_rag.py # 单元测试
🚀 使用方法
# 1. 配置环境变量
cp .env.example .env
# 编辑 .env 填入你的 API Key
# 2. 运行 RAG 系统
cd /path/to/UltimateRAG
source venv/bin/activate
python -m src.stage_1.main --data ./data/documents
# 3. 开始问答
# 输入问题即可获得回答