RAG:检索增强生成是什么?
RAG的主要流程
Retrieval-Augmented Generation: 检索增强生成
能够根据问题的特点还有上下文, 生成更加个性化和精确的回答
- 为LLM提供来自外部知识源的额外信息的概念。这允许它们生成更准确和有上下文的答案,同时减少幻觉
- (1)检索:外部相似搜索 (2)增强:提示词更新 (3)生成:更详细的提示词输入LLM
- 主要流程: 输入问题-> Retrieval检索向量数据库 -> 得到Context上下文 -> 生成详细的prompt提示 -> 再根据LLM生成对应的回答 -> 最后返回给用户
LangChain中RAG中的Retrieve实现
- Source: 很多数据源的支持
-
- github
- YouTube
- discord
- PPT等等
- Loader: 对Source数据源Load到LangChain这个系统里面
- Transform: 对数据进行向量转化, 对不同数据格式进行转化
- Embedding: 向量化, 嵌入到向量空间
- Store: 将向量数据存储到向量数据库里面
- Retrieve: 通过Retrieve方式进行检索
Loader: 让大模型具备实时学习能力
Loader加载MarkDown文件
# 我是一个markdown加载示例
- 第一项目
- 第二个项目
- 第三个项目## 第一个项目
AI研习社最厉害专业的AI研究基地## 第二个项目
AIGC打造未来AI应用天地## 第三个项目
AI研习社是一个非常牛逼的AI媒体
#使用loader来加载markdown文本
from langchain.document_loaders import TextLoaderloader = TextLoader("loader.md")
loader.load()
输出:
[Document(page_content='# 我是一个markdown加载示例\n- 第一项目\n- 第二个项目\n- 第三个项目\n\n## 第一个项目\nAI研习社最厉害专业的AI研究基地\n\n## 第二个项目\nAIGC打造未来AI应用天地\n\n## 第三个项目\nAI研习社是一个非常牛逼的AI媒体', metadata={'source': 'loader.md'})]
Loader加载CVS文件
loader.csv
#使用loader来加载cvs文件
from langchain.document_loaders.csv_loader import CSVLoader#loader = CSVLoader(file_path="loader.csv")loader = CSVLoader(file_path="loader.csv",source_column="Location")
data = loader.load()
print(data)
输出:
[Document(page_content='\ufeffProject: AI GC培训\nDES: 培训课程\nPrice: 500\nPeople: 100\nLocation: 北京', metadata={'source': '北京', 'row': 0}), Document(page_content='\ufeffProject: AI工程师认证\nDES: 微软AI认证\nPrice: 6000\nPeople: 200\nLocation: 西安', metadata={'source': '西安', 'row': 1}), Document(page_content='\ufeffProject: AI应用大会\nDES: AI应用创新大会\nPrice: 200门票\nPeople: 300\nLocation: 深圳', metadata={'source': '深圳', 'row': 2}), Document(page_content='\ufeffProject: AI 应用咨询服务\nDES: AI与场景结合\nPrice: 1000/小时\nPeople: 50\nLocation: 香港', metadata={'source': '香港', 'row': 3}), Document(page_content='\ufeffProject: AI项目可研\nDES: 可行性报告\nPrice: 20000\nPeople: 60\nLocation: 上海', metadata={'source': '上海', 'row': 4})]
Loader加载Excel文件
安装插件:
! pip install "unstructured[xlsx]"
示例:将example目录下的所有*.xlsx文件都加载进来
#某个目录下,有excel文件,我们需要把目录下所有的xlxs文件加载进来
#! pip install "unstructured[xlsx]"from langchain.document_loaders import DirectoryLoader#目录下的.html和.rst文件不会被这种loader加载
#loader = DirectoryLoader("目录地址",glob="指定加载说明格式的文件")
loader = DirectoryLoader(path="./example/",glob="*.xlsx")
docs = loader.load()
len(docs)
Loader加载HTML
#使用loader来加载html文件
#from langchain.document_loaders import UnstructuredHTMLLoader# 包含代码
#loader = UnstructuredHTMLLoader("loader.html")
# 只加载文本信息
from langchain.document_loaders import BSHTMLLoader
loader = BSHTMLLoader("loader.html")
data = loader.load()
data
Loader加载json数据
安装插件:
! pip install jq
#使用loader来加载json文件
#需要先安装 ! pip install jqfrom langchain.document_loaders import JSONLoader
loader = JSONLoader(file_path = "simple_prompt.json",jq_schema=".template",text_content=True
)
data = loader.load()
print(data)
Loader加载pdf文件
安装:
! pip install pypdf
#loader加载pdf文件from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("loader.pdf")
pages = loader.load_and_split()
pages[0]
LangChain文档转换实战
- 文档切割器和按字符分割
- 代码文档分割器
- 按token分割文档
- 文档总结、精炼、翻译
原理:
- 将文档分成小的、有意义的块(句子).
- 将小的块组合成为一个更大的块,直到达到一定的大小.
- 一旦达到一定的大小,接着开始创建与下一个块重叠的部分.
第一个文档切割
from langchain.text_splitter import RecursiveCharacterTextSplitter#加载要切割的文档
with open("test.txt") as f:zuizhonghuanxiang = f.read()#初始化切割器
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50,#切分的文本块大小,一般通过长度函数计算chunk_overlap=20,#切分的文本块重叠大小,一般通过长度函数计算length_function=len,#长度函数,也可以传递tokenize函数add_start_index=True,#是否添加起始索引
)text = text_splitter.create_documents([zuizhonghuanxiang])
print(text[0])
print(text[1])
字符串切割
from langchain.text_splitter import CharacterTextSplitter#加载要切分的文档
with open("test.txt") as f:zuizhonghuanxiang = f.read()#初始化切分器
text_splitter = CharacterTextSplitter(separator="。",#切割的标志字符,默认是\n\nchunk_size=50,#切分的文本块大小,一般通过长度函数计算chunk_overlap=20,#切分的文本块重叠大小,一般通过长度函数计算length_function=len,#长度函数,也可以传递tokenize函数add_start_index=True,#是否添加起始索引is_separator_regex=False,#是否是正则表达式
)
text = text_splitter.create_documents([zuizhonghuanxiang])
print(text[0])
代码文档切割
from langchain.text_splitter import (RecursiveCharacterTextSplitter,Language,
)#支持解析的编程语言
#[e.value for e in Language]#要切割的代码文档
PYTHON_CODE = """
def hello_world():print("Hello, World!")
#调用函数
hello_world()
"""
py_spliter = RecursiveCharacterTextSplitter.from_language(language=Language.PYTHON,chunk_size=50,chunk_overlap=10,
)
python_docs = py_spliter.create_documents([PYTHON_CODE])
python_docs
按token切割文档
from langchain.text_splitter import CharacterTextSplitter#要切割的文档
with open("test.txt") as f:zuizhonghuanxiang = f.read()#初始化切分器
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(chunk_size=4000,#切分的文本块大小,一般通过长度函数计算chunk_overlap=30,#切分的文本块重叠大小,一般通过长度函数计算
)text = text_splitter.create_documents([zuizhonghuanxiang])
print(text[0])
文档的总结、精炼和翻译
安装插件:
! pip install doctran==0.0.14
#加载文档
with open("letter.txt") as f:content = f.read()
from dotenv import load_dotenv
import os
load_dotenv("openai.env")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
OPENAI_API_BASE = os.environ.get("OPENAI_PROXY")
OPENAI_MODEL = "gpt-4"
OPENAI_TOKEN_LIMIT = 8000from doctran import Doctran
doctrans = Doctran(openai_api_key=OPENAI_API_KEY,openai_model=OPENAI_MODEL,openai_token_limit=OPENAI_TOKEN_LIMIT
)
documents = doctrans.parse(content=content)
#总结文档
summary = documents.summarize(token_limit=100).execute()
print(summary.transformed_content)
输出结果:
#翻译一下文档
translation = documents.translate(language="chinese").execute()
print(translation.transformed_content)
输出结果:
#精炼文档,删除除了某个主题或关键词之外的内容,仅保留与主题相关的内容
refined = documents.refine(topics=["marketing","Development"]).execute()
print(refined.transformed_content)
输出结果:
Lost in the middle 长上下文精度处理问题
如何处理长文本切分信息丢失?
LangChain解决方案: 先将文本碎片化, 然后根据问题将最相关的切分块放在头尾的位置, 这样回答的问题精度会比较高
安装长文本转换器:
! pip install sentence-transformers
长文本分块
from langchain.chains import LLMChain,StuffDocumentsChain
from langchain.document_transformers import (LongContextReorder
)
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.vectorstores import Chroma#使用huggingface托管的开源LLM来做嵌入,MiniLM-L6-v2是一个较小的LLM
embedings = HuggingFaceBgeEmbeddings(model_name="all-MiniLM-L6-v2")text = ["篮球是一项伟大的运动。","带我飞往月球是我最喜欢的歌曲之一。","凯尔特人队是我最喜欢的球队。","这是一篇关于波士顿凯尔特人的文件。","我非常喜欢去看电影。","波士顿凯尔特人队以20分的优势赢得了比赛。","这只是一段随机的文字。","《艾尔登之环》是过去15年最好的游戏之一。","L.科内特是凯尔特人队最好的球员之一。","拉里.伯德是一位标志性的NBA球员。"
]retrieval = Chroma.from_texts(text,embedings).as_retriever(search_kwargs={"k": 10}
)
query = "关于我的喜好都知道什么?"#根据相关性返回文本块
docs = retrieval.get_relevant_documents(query)
docs
输出结果:
根据相关性进行重排
#对检索结果进行重新排序,根据论文的方案
#问题相关性越低的内容块放在中间
#问题相关性越高的内容块放在头尾reordering = LongContextReorder()
reo_docs = reordering.transform_documents(docs)#头尾共有4个高相关性内容块
reo_docs
检测精度效果
from dotenv import load_dotenv
load_dotenv("openai.env")
import osapi_key = os.environ.get("OPENAI_API_KEY")
api_base = os.environ.get("OPENAI_API_BASE")#检测下这种方案的精度效果
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI#设置llm
llm = OpenAI(api_key=api_key,api_base=api_base,model="gpt-3.5-turbo-instruct",temperature=0
)document_prompt = PromptTemplate(input_variables=["page_content"],template="{page_content}"
)stuff_prompt_override ="""Given this text extracts:
----------------------------------------
{context}
----------------------------------------
Please answer the following questions:
{query}
"""prompt = PromptTemplate(template=stuff_prompt_override,input_variables=["context","query"]
)llm_chain = LLMChain(llm=llm,prompt=prompt
)WorkChain = StuffDocumentsChain(llm_chain=llm_chain,document_prompt=document_prompt,document_variable_name="context"
)#调用
WorkChain.run(input_documents=reo_docs,query="我最喜欢做什么事情?"
)
文本向量化实现方式
文本向量化: 一种更高效的检索方式
例如: 向量坐标离的越近越容易一起被搜到, 例如搜索宠物, 那么猫和狗都会被搜索到
引入需要的包
! pip install --upgrade langchain
! pip install --upgrade openai==0.27.8
! pip install -U langchain-openai
查看是否安装完成:
! pip show openai
! pip show langchain
! pip show langchain-openai
测试文本向量化:
from langchain_openai import OpenAIEmbeddingse_model = OpenAIEmbeddings()
ebeddings = e_model.embed_documents(["你好","你好啊","你叫什么名字?","我叫王大锤","很高兴认识你大锤",]
)
ebeddings
测试向量检索:
embedded_query = e_model.embed_query("这段对话中提到了什么名字?")
embedded_query[:5]
嵌入向量缓存
设置缓存到cache目录下
from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import LocalFileStore
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
u_embeddings = OpenAIEmbeddings()
fs = LocalFileStore("./cache/")
cached_embeddings = CacheBackedEmbeddings.from_bytes_store(u_embeddings,fs,namespace=u_embeddings.model,
)
list(fs.yield_keys())
加载文档, 向量化存入cache缓存中:
#加载文档,切分文档,将切分文档向量化病存储在缓存中raw_documents = TextLoader("letter.txt").load()
text_splitter = CharacterTextSplitter(chunk_size=600,chunk_overlap=0)
documents = text_splitter.split_documents(raw_documents)
安装faiss包, 主要用于向量算法:
! pip install faiss-cup
大概花费多少时间写入了缓存:
from langchain.vectorstores import FAISS
%timeit -r 1 -n 1 db= FAISS.from_documents(documents,cached_embeddings)
#查看缓存中的键
list(fs.yield_keys())
ChatDoc: 一个文档检索助手
实现
- 可以加载PDF或者xsl格式文档
- 可以对文档进行适当切分
- 使用openai进行向量化
- 使用Chomadb实现本地向量存储
- 使用智能检索实现和文档的对话
安装必要的包
#安装必须的包
! pip install wheel
! pip install docx2txt
! pip install pypdf
! pip install nltk
! pip install unstructured
测试加载文档
- Docx:
#倒入必须的包
from langchain.document_loaders import Docx2txtLoader#定义chatdoc
class ChatDoc():def getFile():#读取文件loader = Docx2txtLoader("example/fake.docx")text = loader.load()return text;ChatDoc.getFile()
- pdf:
#导入必须的包
from langchain.document_loaders import PyPDFLoader#定义chatdoc
class ChatDoc():def getFile():try:#读取文件loader = PyPDFLoader("example/fake.pdf")text = loader.load()return text;except Exception as e:print(f"Error loading files:{e}")
ChatDoc.getFile()
- excel:
#导入必须的包
from langchain.document_loaders import UnstructuredExcelLoader#定义chatdoc
class ChatDoc():def getFile():try:#读取文件loader = UnstructuredExcelLoader("example/fake.xlsx",mode="elements")text = loader.load()return text;except Exception as e:print(f"Error loading files:{e}")
ChatDoc.getFile()
- 整合优化
#导入必须的包
from langchain.document_loaders import UnstructuredExcelLoader,Docx2txtLoader,PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter#定义chatdoc
class ChatDoc():def __init__(self):self.doc = Noneself.splitText = [] #分割后的文本def getFile(self):doc = self.docloaders = {"docx":Docx2txtLoader,"pdf":PyPDFLoader,"xlsx":UnstructuredExcelLoader,}file_extension = doc.split(".")[-1]loader_class = loaders.get(file_extension)if loader_class:try:loader = loader_class(doc)text = loader.load()return textexcept Exception as e: print(f"Error loading {file_extension} files:{e}") else:print(f"Unsupported file extension: {file_extension}")return None #处理文档的函数def splitSentences(self):full_text = self.getFile() #获取文档内容if full_text != None:#对文档进行分割text_split = CharacterTextSplitter(chunk_size=150,chunk_overlap=20,)texts = text_split.split_documents(full_text)self.splitText = textschat_doc = ChatDoc()
chat_doc.doc = "example/fake.xlsx"
chat_doc.splitSentences()
print(chat_doc.splitText)