学习文档地址:第八章
在学习完了前面章节之后,对RAG技术已经有了比较深入的了解,现在可以通过一个项目将这些知识结合起来。
程序员在家做饭方法指南是一个由程序员发起的菜谱项目,项目收集了大大小小各类菜谱共400余道,每道菜谱都是一个MD文档,且有明确的格式要求。
我们可以通过这个项目的菜谱数据,来构建一个今天吃点啥
的RAG
项目。
项目架构
code/C8/
├── config.py # 配置管理
├── main.py # 主程序入口
├── requirements.txt # 依赖列表
├── rag_modules/ # 核心模块
│ ├── __init__.py
│ ├── data_preparation.py # 数据准备模块
│ ├── index_construction.py # 索引构建模块
│ ├── retrieval_optimization.py # 检索优化模块
│ └── generation_integration.py # 生成集成模块
└── vector_index/ # 向量索引缓存(自动生成)
数据模块
分块
因为文档都是标准的MarkDown
格式,所以可以采用MarkdownHeaderTextSplitter
来进行切分,按照标题能够很好的分块。
元数据
在文档中,有明确的文件夹区分,通过这个可以获取菜谱的分类,属于早餐还是主食等等;以及每篇文档都会有难度标记,这些都可以纳入元数据,为后续元数据检索做准备。
智能去重
在第一部分块之后,同一文档会被切分多块,在检索时可能会检索到同一文章的不同语句,这时候需要进行去重,只返回一篇文章。
在切分时,为每篇文章都生成一个随机ID作为parent_id
,也就是每篇文章的唯一索引。
在检索去重时,通过比较parent_id
是否相同进行去重,同时根据数量进行排序,优先返回数量最多的。
检索与优化
文档加载完成后,需要进行索引构建,并存入向量数据库。
索引构建
实战项目使用FAISS
作为向量数据库,并将索引存到本地,后续启用时直接加载。
索引优化
实战项目采用两种检索方式的混合检索,通过RFF
来融合并计算最终结果。
使用向量检索获取基础意图,使用BM25
作为关键词匹配,精确查找。
元数据过滤
在文档加载时,我们像文档注入了元数据,包括难易程度、菜名、分类等,可以在此处先进行元数据筛选,然后再从筛选后的文档中进行检索,提高检索效率。
生成集成 模块
在通过检索获取到相关文档后,我们需要将用户的输入查询(用户意图)与文档内容结合,生成符合意图的高质量回答。
根据上一章提到查询路由,这里也对查询进行分发,形成三个类型:list
、detail
、general
。
- 列表模式:适用于推荐类查询,返回简洁的菜品列表
- 详细模式:适用于制作类查询,提供分步骤的详细指导
- 基础模式:适用于一般性问题,提供常规回答
查询路由实现
核心还是让LLM基于用户的输入进行判断,通过提示词,让LLM进行分发。
def query_router(self, query: str) -> str:
"""查询路由 - 根据查询类型选择不同的处理方式"""
prompt = ChatPromptTemplate.from_template("""
根据用户的问题,将其分类为以下三种类型之一:
1. 'list' - 用户想要获取菜品列表或推荐,只需要菜名
例如:推荐几个素菜、有什么川菜、给我3个简单的菜
2. 'detail' - 用户想要具体的制作方法或详细信息
例如:宫保鸡丁怎么做、制作步骤、需要什么食材
3. 'general' - 其他一般性问题
例如:什么是川菜、制作技巧、营养价值
请只返回分类结果:list、detail 或 general
用户问题: {query}
分类结果:""")
# ... (LCEL链式调用)
return result
查询重写
与路由相似,也是让LLM基于用户输入,判断查询语句是否完整准确,否则让LLM根据提示词进行重写。
template="""
你是一个智能查询分析助手。请分析用户的查询,判断是否需要重写以提高食谱搜索效果。
原始查询: {query}
分析规则:
1. **具体明确的查询**(直接返回原查询):
- 包含具体菜品名称:如"宫保鸡丁怎么做"、"红烧肉的制作方法"
- 明确的制作询问:如"蛋炒饭需要什么食材"、"糖醋排骨的步骤"
- 具体的烹饪技巧:如"如何炒菜不粘锅"、"怎样调制糖醋汁"
2. **模糊不清的查询**(需要重写):
- 过于宽泛:如"做菜"、"有什么好吃的"、"推荐个菜"
- 缺乏具体信息:如"川菜"、"素菜"、"简单的"
- 口语化表达:如"想吃点什么"、"有饮品推荐吗"
重写原则:
- 保持原意不变
- 增加相关烹饪术语
- 优先推荐简单易做的
- 保持简洁性
示例:
- "做菜" → "简单易做的家常菜谱"
- "有饮品推荐吗" → "简单饮品制作方法"
- "推荐个菜" → "简单家常菜推荐"
- "川菜" → "经典川菜菜谱"
- "宫保鸡丁怎么做" → "宫保鸡丁怎么做"(保持原查询)
- "红烧肉需要什么食材" → "红烧肉需要什么食材"(保持原查询)
请输出最终查询(如果不需要重写就返回原查询):