微信扫码
添加专属顾问
我要投稿
探索下一代RAG系统:如何让AI具备自我评估能力,解决复杂信息检索难题。核心内容: 1. 传统RAG系统的三大组件与核心局限性 2. Agentic RAG如何通过自主决策链突破检索瓶颈 3. 基于GPT-4的自我评估机制实现路径与案例演示
随着大语言模型(LLMs)的发展,模型已经能够理解大量数据并进行逻辑推理。这些发展带来的最重要成果之一是检索增强生成(Retrieval-Augmented Generation,简称RAG)系统。
LLMs 在非常大的数据集上进行训练,但它们受限于训练数据的范围。
假设您拥有一家公司,并有一些政策文档。为了让员工找到正确的答案,他们要么需要非常熟悉这些文档,要么需要在文档中搜索答案。您希望通过一个聊天机器人简化这个系统。随着 LLMs 的发展,您可以使用它们,但 LLM 并不了解您的公司数据。当您询问与这些主题相关的问题时,LLM 很可能会出现“幻觉”(hallucinate)。您需要通过微调(fine-tuning)来教导模型您的数据,这是一个漫长且昂贵的过程,或者您可以使用 RAG 系统。得益于 RAG 系统,LLM 可以在您的文档中搜索,并根据这些文档提供答案。
听起来很棒,对吧?当然,事情并非那么简单。传统 RAG 系统根据用户查询检索相关文档或信息片段,并将这些信息传递给 LLM 以生成答案。然而,这种方法对于复杂的信息需求或模糊的查询可能不足以应对。这就是“代理RAG”(Agentic RAG)概念的用武之地。
代理RAG 是一种高级系统,它在传统 RAG 系统的基础上增加了自主决策、规划和自我评估能力,使信息检索过程更加智能和灵活。这些系统可以重新表述用户的查询,评估不同的信息来源,并衡量自身答案的准确性和质量。
在本文中,我们将通过一个代理RAG系统的示例,展示如何基于特定文章为用户的问题提供全面且准确的答案。在我们的示例中,我们使用 OpenAI 的 GPT-4 模型,它能够理解给定的文章,在必要时进行网络搜索,重新表述查询并评估自己的答案。我准备这个示例是为了让大家对代理RAG 有一些概念和方法的了解。它可以进一步改进或添加不同的方法。
检索增强生成(RAG)是一种方法,允许 LLMs 访问除训练数据之外的其他信息来源。传统 RAG 系统由三个基本组件组成:
•检索器(Retriever):根据用户查询从数据库中检索相关文档或信息片段的组件。•生成器(Generator):通常是一个 LLM,使用检索到的文档和用户查询生成答案。•索引器(Indexer):预处理文档并将它们存储在向量数据库中以实现高效访问。
正如我们在开头提到的,RAG 系统有许多优点和缺点:
•它们直接使用用户的查询,无法进行改进,因此可能难以获得答案。•对于复杂问题可能表现不足。•无法控制生成答案的质量或准确性。
代理是能够自主决策、执行这些决策并评估其正确性的系统。
•它们可以自主决策。•它们可以进行规划。•它们可以为不同任务使用不同的工具,并选择使用哪些工具。•它们逐步解决问题,并在必要时可以改变策略。
代理RAG 使用代理来消除传统 RAG 系统的局限性。
•更准确的答案:通过多次验证和自我评估,降低幻觉风险。•全面的信息访问:通过结合不同来源和策略,访问更全面的信息。•适应性:根据用户查询的质量或复杂性调整方法。•透明度:能够清晰说明思考过程和信息来源。
在之前的章节中,我们讨论了代理RAG的基础。现在是时候将它们付诸实践了。
•LLM 引擎:基于 GPT-4 的语言模型。•文档访问链:从文章数据库中检索相关内容的 RetrievalQA 链。•网络搜索工具:使用 Tavily API 执行网络搜索的组件。•查询重新表述模块:将模糊查询变得更具体的函数。•自我评估机制:评估生成答案质量的模块。•代理协调器:协调不同工具使用的 LangChain 代理。•内存系统:存储对话历史的 ConversationBufferMemory。
首先,让我们导入将使用的所有库。
import fitz
from langchain.embeddings importOpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter importRecursiveCharacterTextSplitter
from langchain.tools importTool
from langchain.chains importRetrievalQA
from langchain.chat_models importChatOpenAI
from langchain.agents import initialize_agent,Tool,AgentType
from langchain_community.tools.tavily_search importTavilySearchResults
from langchain.memory importConversationBufferMemory
import os
os.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 text
text = 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)
接下来,我们添加一个自定义函数,用于重写模糊或笼统的输入,使其更具体。
def query_reformulation(query):
response = llm.predict("Rewrite this query to be more specific: "+ query)
return response
此函数向 LLM 发送请求以优化查询。例如,它可以将像“什么是人工智能?”这样的笼统查询转换为更具体的查询,如“文章中描述的人工智能技术的主要特点和应用是什么?”
我们还有另一个函数,用于让系统评估其生成的答案。
def self_evaluate(input_text):
parts = input_text.split("|||")
query = parts[0]
response = parts[1]
sources = parts[2]if len(parts)>2else""
evaluation_prompt = f"""
评估以下对查询的响应:
查询:{query}
响应:{response}
来源:{sources}
基于以下标准评估:
1.事实准确性(是否与来源匹配?)
2.完整性(是否涵盖查询的所有方面?)
3.相关性(信息是否与查询相关?)
4.幻觉(是否包含来源不支持的信息?)
返回0-10的置信度评分和解释。
"""
evaluation = llm.predict(evaluation_prompt)
return evaluation
此函数评估答案的准确性、全面性、相关性和幻觉情况。评估包括 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
创建了一个能够逐步推理并根据情况使用工具的代理。
现在是运行代理的时候了。为此,我们编写了一个可用于自我评估的函数。
def get_evaluated_response(query):
response = agent.run(query)
try:
result = retrieval_qa_chain({"query": query})
sources =[doc.page_content for doc in result.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
}
此函数获取代理的响应并进行评估,然后返回结果。
我们可以通过以下函数查看自我评估的响应和代理的响应。
def transparent_response(query):
result = get_evaluated_response(query)
return f"""
响应:{result['response']}
置信度评估:{result['evaluation']}
"""
现在让我们试一试。
我给出的文章是一篇关于多代理系统的调查。
print(transparent_response("什么是多代理系统?"))
当我们如上运行系统时,会得到以下输出。由于输出内容太长,我无法在这里展示所有内容,但代理理解了需要从文章中回答我们的问题,并为此使用了“Article Retrieval”工具。然后,它为我们创建了一个最终答案。自我评估对生成的结果进行了评估,并给出了 9.75 分(满分 10 分)。
让我们尝试一个文章中没有的例子。
print(transparent_response("伊斯坦布尔当前的天气如何?"))
正如您所见,这次代理理解了需要使用网络工具,并从中为我们带来了答案。但由于未共享资源,我们的置信度评分较低。这是需要优化的部分。
现在让我们问一些 LLM 已经知道的内容。
print(transparent_response("YouTube 是什么时候创立的?"))
系统告诉我们它已经知道这个信息,并直接给出了答案,无需使用任何工具。我们的置信度评分相当高。
最后一个例子是尝试查询重新表述。为此,我输入了一个非常模糊的内容。
print(transparent_response("烤蛋糕"))
代理通过重写输入使其更高效,然后使用网络搜索工具。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2025-03-21
2025-03-20
2025-03-24
2025-03-24
2025-03-19
2025-03-24
2025-03-28
2025-04-01
2025-03-23
2025-04-13
2025-06-13
2025-06-09
2025-06-06
2025-05-30
2025-05-29
2025-05-29
2025-05-23
2025-05-16