正如我们在开头提到的,RAG 系统有许多优点和缺点:
•它们直接使用用户的查询,无法进行改进,因此可能难以获得答案。•对于复杂问题可能表现不足。•无法控制生成答案的质量或准确性。
代理是能够自主决策、执行这些决策并评估其正确性的系统。
•它们可以自主决策。•它们可以进行规划。•它们可以为不同任务使用不同的工具,并选择使用哪些工具。•它们逐步解决问题,并在必要时可以改变策略。
代理RAG 使用代理来消除传统 RAG 系统的局限性。
•更准确的答案:通过多次验证和自我评估,降低幻觉风险。•全面的信息访问:通过结合不同来源和策略,访问更全面的信息。•适应性:根据用户查询的质量或复杂性调整方法。•透明度:能够清晰说明思考过程和信息来源。
在之前的章节中,我们讨论了代理RAG的基础。现在是时候将它们付诸实践了。
•LLM 引擎:基于 GPT-4 的语言模型。•文档访问链:从文章数据库中检索相关内容的 RetrievalQA 链。•网络搜索工具:使用 Tavily API 执行网络搜索的组件。•查询重新表述模块:将模糊查询变得更具体的函数。•自我评估机制:评估生成答案质量的模块。•代理协调器:协调不同工具使用的 LangChain 代理。•内存系统:存储对话历史的 ConversationBufferMemory。
首先,让我们导入将使用的所有库。
importfitzfromlangchain.embeddingsimportOpenAIEmbeddingsfromlangchain.vectorstoresimportFAISSfromlangchain.text_splitterimportRecursiveCharacterTextSplitterfromlangchain.toolsimportToolfromlangchain.chainsimportRetrievalQAfromlangchain.chat_modelsimportChatOpenAIfromlangchain.agentsimportinitialize_agent,Tool,AgentTypefromlangchain_community.tools.tavily_searchimportTavilySearchResultsfromlangchain.memoryimportConversationBufferMemoryimportosos.environ["TAVILY_API_KEY"]="your-api-key"
我们代理RAG 示例的基础是一个准确且高效的数据准备和索引过程。此过程包括处理、分割和将我们的 PDF 文章转换为向量数据库。
首先,我们将使用 PyMuPDF 库从 PDF 格式文档中提取内容。
def extract_text_from_pdf(pdf_path):doc = fitz.open(pdf_path)text ="\n".join([page.get_text()for page in doc])return texttext =extract_text_from_pdf("pdf_files/article_2.pdf")
此函数提取并合并给定 PDF 文件所有页面中的文本。
PDF 中的文本可能非常长,整体处理如此长的数据效率低下。因此,我们使用 LangChain 的 RecursiveCharacterTextSplitter 类将文本分割成更小、更易管理的片段:
text_splitter=RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=50)docs=text_splitter.create_documents([text])
这里:
•chunk_size=500:确保每个文本片段最多包含 500 个字符。•chunk_overlap=50:通过确保连续部分之间有 50 个字符的重叠,防止因分割句子或段落而丢失意义。
我们还使用 OpenAIEmbeddings() 类将文本转换为嵌入(embeddings)。
embeddings=OpenAIEmbeddings()
在完成所有这些步骤后,我们通过将每个文本片段转换为向量表示,创建了 FAISS 向量数据库,以便高效查询分段文本:
vectorstore=FAISS.from_documents(docs,embeddings)vectorstore.save_local("faiss_index")FAISS(Facebook AI Similarity Search)是一个为高维向量执行高效相似性搜索设计的库。
在应用程序运行时,我们按如下方式加载之前创建的 FAISS 索引:
vectorstore=FAISS.load_local("faiss_index",embeddings,allow_dangerous_deserialization=True)retriever=vectorstore.as_retriever()通过将加载的向量数据库转换为检索器对象,我们可以高效地找到与用户查询相关的文本片段。
这个数据准备和索引过程构成了我们代理RAG 系统的信息检索基础。
首先,我们初始化所选的 LLM 模型。在我们的示例中,它是 GPT-4。
llm=ChatOpenAI(model="gpt-4")
接下来,我们需要创建 RetrievalQA 链以便从文章数据库中检索信息。
retrieval_qa_chain=RetrievalQA.from_chain_type(llm=llm,retriever=retriever,return_source_documents=True)
此链接受用户查询,提取相关文档,并通过 LLM 生成响应。
我们还使用 TavilySearchResults 来搜索文章中未找到的信息。此工具与 Tavily API 集成,每个查询返回两个最相关的结果。为此,您必须先访问 Tavily 网站,创建账户并获取 API 密钥,否则会报错。
search=TavilySearchResults(max_results=2)
接下来,我们添加一个自定义函数,用于重写模糊或笼统的输入,使其更具体。
defquery_reformulation(query):response=llm.predict("Rewritethisquerytobemorespecific:"+query)returnresponse此函数向 LLM 发送请求以优化查询。例如,它可以将像“什么是人工智能?”这样的笼统查询转换为更具体的查询,如“文章中描述的人工智能技术的主要特点和应用是什么?”
我们还有另一个函数,用于让系统评估其生成的答案。
defself_evaluate(input_text):parts = input_text.split("|||")query = parts[0]response = parts[1]sources = parts[2]iflen(parts)>2else""evaluation_prompt =f"""评估以下对查询的响应:查询:{query}响应:{response}来源:{sources}基于以下标准评估:1.事实准确性(是否与来源匹配?)2.完整性(是否涵盖查询的所有方面?)3.相关性(信息是否与查询相关?)4.幻觉(是否包含来源不支持的信息?)返回0-10的置信度评分和解释。"""evaluation = llm.predict(evaluation_prompt)returnevaluation
此函数评估答案的准确性、全面性、相关性和幻觉情况。评估包括 0-10 的置信度评分和解释。
现在我们已经定义了所有函数,可以将工具整合在一起。
tools=[Tool(name="Article Retrieval",func=lambda q: retrieval_qa_chain({"query": q})["result"],description="从文章数据库中检索知识。"),Tool(name="Web search",func=search,description="如果在文档中找不到请求的信息,则说明这一点并执行网络搜索。"),Tool(name="Query reformulation",func=query_reformulation,description="将查询重新表述为更具体和针对性更强的形式。")]
我们的工具包括文章检索、网络搜索和查询重新表述。代理将决定在何种情况下使用哪种工具。
我们还从 LangChain 库中添加了 ConversationBufferMemory,以保存与用户的聊天历史。
memory=ConversationBufferMemory(memory_key="chat_history",return_messages=True)
现在我们已经创建了所有组件,是时候启动代理了。
agent=initialize_agent(tools=tools,llm=llm,agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,verbose=True,memory=memory)
这里的代理类型STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION创建了一个能够逐步推理并根据情况使用工具的代理。
现在是运行代理的时候了。为此,我们编写了一个可用于自我评估的函数。
defget_evaluated_response(query):response = agent.run(query)try:result = retrieval_qa_chain({"query": query})sources =[doc.page_contentfordocinresult.get("source_documents",[])]sources_text ="\n".join(sources)exceptExceptionas e:sources_text ="无可用来源"evaluation = self_evaluate(f"{query}|||{response}|||{sources_text}")return{"query": query,"response": response,"evaluation": evaluation,"sources": sources_text}
此函数获取代理的响应并进行评估,然后返回结果。
我们可以通过以下函数查看自我评估的响应和代理的响应。
deftransparent_response(query):result = get_evaluated_response(query)returnf"""响应:{result['response']}置信度评估:{result['evaluation']}"""
现在让我们试一试。
我给出的文章是一篇关于多代理系统的调查。
print(transparent_response("什么是多代理系统?"))当我们如上运行系统时,会得到以下输出。由于输出内容太长,我无法在这里展示所有内容,但代理理解了需要从文章中回答我们的问题,并为此使用了“Article Retrieval”工具。然后,它为我们创建了一个最终答案。自我评估对生成的结果进行了评估,并给出了 9.75 分(满分 10 分)。
让我们尝试一个文章中没有的例子。
print(transparent_response("伊斯坦布尔当前的天气如何?"))正如您所见,这次代理理解了需要使用网络工具,并从中为我们带来了答案。但由于未共享资源,我们的置信度评分较低。这是需要优化的部分。
现在让我们问一些 LLM 已经知道的内容。
print(transparent_response("YouTube是什么时候创立的?"))系统告诉我们它已经知道这个信息,并直接给出了答案,无需使用任何工具。我们的置信度评分相当高。
最后一个例子是尝试查询重新表述。为此,我输入了一个非常模糊的内容。
print(transparent_response("烤蛋糕"))代理通过重写输入使其更高效,然后使用网络搜索工具。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |