什么是RAG,
顾名思义,通过检索外部数据,增强大模型生成效果。可以理解为让大模型先翻书,再回答问题,比如将公司内部的工作流程大模型肯定是不知道,但是我先将工作手册先给他看,当我再询问他就知道了
为什么要引入RAG
首先大模型会面临着幻觉的问题,比如他会将西游记的孙悟空引入到三国演义中的赤壁之战之中。会出现事实不符,逻辑混乱看似合理缺乏真实依据的现象。形成这一现象的原因训练数据有噪声。模型从海量数据中学习数据,然而数据本身有一些问题,比如网络谣言,架空小说等情况,还有个原因是概率优先,大模型本身是概率生成式模型,而非事实真实性。
大模型知识更新缓慢,比如你问deekseek最新的知识库是什么时候?他会回答是在2024年7月,那么在2024年7月之后的知识他就没法回答
大模型对领域知识理解有限,因为大模型在训练时候,是广度优先的,训练的数据覆盖全网公开文本。单领域内部的数据可能不会对外公开,而且分布稀疏
总结: 所以RAG的引入初衷是增强大模型的事实性,时效性,减少幻觉而存在的。
RAG的流程
从上图可以看出RAG主要有三个部分,索引化,检索以及生成。
索引化:将文档切分成片段,再编码为向量,存储在向量数据库里。
检索:将问题向量化,在向量数据库中检索到与问题相似度最高的文本片段
生成:将用户提出的问题与检索出来的文本片段组装成提示词,抛向大模型,作为大模型的输入,让其组织语言生成最终的回答
代码实现(Python实现)
- 第一步,申请阿里云百炼API KEY
- 第二步,安装chromadb向量数据库
pip install chromadb
附带chromadb使用教程 zhuanlan.zhihu.com/p/680661442
- 第三步,准备文档进行分割
从 PDF 文件中(按指定页码)提取文字def extract_text_from_pdf(filename, page_numbers=None): paragraphs = [] 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().replace("\n", "").replace(" ", "") if full_text: text_chunks = sliding_window_chunks(full_text, 250, 100) for text in text_chunks: paragraphs.append(text) return paragraphsdef sliding_window_chunks(text, chunk_size, stride): return [text[i:i + chunk_size] for i in range(0, len(text), stride)]
- 第四步,使用chromadb向量数据库,封装获取向量和添加文档的操作
class MyVectorDBConnector: def __init__(self): # 创建数据库的链接 self.db = chromadb.Client() # 创建数据库 self.collection = self.db.get_or_create_collection(name="demo") def get_embeddings(self, texts, model="text-embedding-v2"): '''封装 qwen 的 Embedding 模型接口''' data = client.embeddings.create(input=texts, model=model).data return [x.embedding for x in data] def add_documents(self, instructions): # 将数据向量化 embeddings = self.get_embeddings(instructions) #把向量化的数据和原文存入向量数据库 self.collection.add( embeddings=embeddings, documents=instructions, ids=[f"id{i}" for i in range(len(instructions))] ) '''检索向量数据库''' def search(self, query, n_results): results = self.collection.query( query_embeddings=self.get_embeddings([query]), n_results=n_results, ) return results
- 第五步,创建机器人对象,封装与大模型API交互的方法
class RAG_ROBOT(): def __init__(self, vector_db, n_res): self.vector_db = vector_db self.n_res = n_res def get_completion(self, prompt, model="qwen-plus"): messages = [{"role": "user", "content": prompt}] response = client.chat.completions.create( model=model, messages=messages, temperature=0, ) return response.choices[0].message.content def chat(self, queyr): # 1.检索 search_Data = self.vector_db.search(queyr, self.n_res) # 2. 构建提示词 prompt = prompt_template.replace('__INTRO__', '\n'.join(search_Data['documents'][0])).replace('__QUERY__', queyr) ret = self.get_completion(prompt) print('机器人回答:', ret)
- 第六步,调用
if __name__ == '__main__': client = OpenAI(api_key="xxxxxx", #阿里云百炼API KEY base_url="https://dashscope.aliyuncs.com/compatible-mode/v1") prompt_template = """ 你是一个问答机器人,如果遇到无法回答的问题,请回复"我无法回答"。 已知信息: __INTRO__ 用户问: __QUERY__ 请用中文回答用户问题。 """ page_data = extract_text_from_pdf('财务管理文档.pdf', page_numbers=[0, 1, 2]) vector_db = MyVectorDBConnector() vector_db.add_documents(page_data) # 创建机器人对象 rag_bot = RAG_ROBOT(vector_db, n_res=2) rag_bot.chat('财务管理权限划分')
以上代码仅供学习参考,上述“财务管理文档”没找到上传的地方。