1)LLM的知识不是实时的;
2)LLM可能不知道你的私有领域/业务知识。
RAG(Retrieval Augmented Generation),通过检索的方式增强生成模型的能力。
类似于开卷考试,让LLM先翻书,再回答问题。同时可以减少LLM胡编乱造的情况。
3.1 项目需要达到的效果
上传本地知识,加载业务数据。
图中左侧是对话的结果,右侧是检索的结果。
3.2 文本关键字搜索的搭建过程
3.2.1 文档加载,并按一定条件切割成片段
安装pdf解析库:
pip install pdfminer.six -i https://mirrors.aliyun.com/pypi/simple
from pdfminer.high_level import extract_pages from pdfminer.layout import LTTextContainer def extract_text_from_pdf(filename, page_numbers=None, min_line_length=1): #从 PDF 文件中(按指定页码)提取文字 paragraphs = [] buffer = '' full_text = '' # 提取全部文本 for i, page_layout in enumerate(extract_pages(filename)): # 如果指定了页码范围,跳过范围外的页 if page_numbers is not None and i not in page_numbers: continue for element in page_layout: if isinstance(element, LTTextContainer): full_text +=element.get_text()+' ' # 按空行分隔,将文本重新组织成段落 lines = full_text.split(' ') for text in lines: if len(text)>= min_line_length: buffer += (''+text) if not text.endswith('-') else text.strip('-') elif buffer: paragraphs.append(buffer) buffer = '' if buffer: paragraphs.append(buffer) return paragraphs if __name__ == "__main__": file = f"2404.16130v1.pdf" paragraphs = extract_text_from_pdf(file, [1, 2], min_line_length=20) for para in paragraphs[:3]: print(para)
3.2.2 将切割的文本片段灌入检索引擎
安装搜索引擎ES客户端
pip3 install elasticsearch7 -i https://mirrors.aliyun.com/pypi/simple
安装NLTK 文本方法处理库
pip install nltk -i https://mirrors.aliyun.com/pypi/simple
中文实现:
将文本灌入搜索引擎
3.2.3 封装检索接口
其中,前三部是离线操作的,需要再系统上线前做好。
3.2.4 构建调用流程
query--> 检索-->prompt-->LLM-->回复
rag pipeline
3.2.5 关键字检索的局限性
同一个语义,用词不同,可以导致检索不到有效的信息。
3.3 向量检索
3.3.1 文本向量的表示
且OpenAI的向量模型是支持跨语言的。
3.3.2 向量数据库
专门为向量检索做的中间件。
安装向量数据库中间件。
3.3.2.1 下面是将向量数据库建在内存中
from rag_test import paragraphs from embedding import get_embeddings import chromadb from chromadb.config import Settings # 此方法将向量数据库建在内存中 class MyVectorDBConector: def __init__(self, collection_name, embeddig_fn): chroma_client = chromadb.Client(Settings(allow_reset=True)) # 为了演示,实际不需要每次reset chroma_client.reset() # 创建一个collection self.collection = chroma_client.get_or_create_collection(name=collection_name) self.embedding_fn = embeddig_fn # 传入向量模型 def add_document(self, documents, metadata={}): " 向collection中添加文档与向量 " self.collection.add( embeddings=self.embedding_fn(documents), # 每个文档的向量 documents=documents, # 文档的原文 ids=[f"id{i}" for i in range(len(documents))] ) def search(self, query, top_n): ''' 检索向量数据库 ''' results = self.collection.query( query_embeddings=self.embedding_fn([query]), n_results=top_n ) return results if __name__ == "__main__": # 创建一个向量数据库 vector_db = MyVectorDBConector("demo", get_embeddings) # 向向量数据库总添加文档 vector_db.add_document(paragraphs) user_query = "llama2有多少个参数" results = vector_db.search(user_query, 2) for para in results['documents'][0]: print(para + " ")
3.3.2.2 搭建向量数据库服务
在server端:
chroma run --path /db_path
在client端:
3.3.3 主流向量数据库对比
3.3.4 基于向量检索的RAG
搭建一个rag机器人
3.4 RAG系统性能提升
3.4.1 文本粒度
文本粒度太大->检索精度不准确,文本粒度太小->信息不全面,问题的答案可能跨越两个片段。
改进思路:
1)按一定粒度,部分重叠式地切割文本,使上下文更加完整。
3.4.2 检索后排序
有时候最合适的答案不一定排在最前面
改进思路:
1)检索时,过召回一部分文本;
2)通过一个排序模型对query和document重新打分排序。
安装cross-encoder模块