前段时间,因为项目涉及到RAG系统构建,因此整理了关于RAG的分析文章《大模型LLM在垂直领域的应用(RAG、微调等)分析》以及搜索增强的文章《ToB的大模型系统非常有必要引入搜索推荐算法能力》。近期关注到RAG的一些新的动向【1】,比如将RAG与Agent结合,对于复杂问题的求解,可以利用Agent的规划能力,拆分为多个子问题,然后再利用RAG从相关知识库中检索出相关的知识片段,再将复杂问题与检索出的相关知识片段组合成新的Prompt,喂给大模型用于生成最终的答案。关于此类提示链的Prompt,我们在《创建有效的大模型提示词Prompt》中也做了阐述。
大模型与智能体的进展
1. Agentic RAG(智能体RAG)
大模型已经改变了我们与信息的互动方式。但是由于其存在幻觉问题以及训练语料局限性,仅依赖内部知识的限制可能会限制其回答的准确性和深度,特别是在处理复杂问题时。因此检索增强生成(RAG)技术特别适合引入使用。RAG通过让大模型访问和处理外部来源的信息,弥补了这一信息差距,能够带来更加准确、有事实依据和信息丰富的答案。
虽然标准的RAG在处理一些文档的简单查询方面表现较好,但引入智能体之后的RAG则更上一层楼,成为复杂问题的有效解决方案。通过使用智能体充当自主决策者,并策略性地选择最有效的工具进行进一步的数据检索,这种多步推理能力使Agentic RAG能够应对复杂的研究任务,如总结、比较多个文档中的信息。智能体的引入,将大模型从被动响应者转变为积极调查者,能够深入复杂信息并提供全面、合理的答案。智能体RAG在这类应用中潜力巨大,近期和蚂蚁、字节的朋友聊,商业化思路都有涉及到Agent+RAG,能够全面理解复杂主题,获得深刻洞察并做出明智决策。
智能体RAG是研究、数据分析和知识探索的强大工具。基于该框架,也许能够出现更强的研究助手和虚拟助手。推理、适应和利用外部知识的能力能够显著增强我们与信息的互动和分析能力。
本文将深入探讨智能体RAG,探索其内部工作机制、应用以及它为用户带来的益处。将详细说明它是什么,它与传统RAG有何不同,智能体如何融入RAG框架,它们在框架内如何运作,不同的功能、实施策略等。
1.1 Agentic RAG定义
Agentic RAG(基于智能体的RAG实现)是通过引入智能体框架来改变处理问答方式的技术。与仅依赖大模型的传统方法不同,Agentic RAG利用智能体来应对需要复杂规划、多步骤推理和外部工具使用的复杂问题。这些智能体能够处理多个文档,比较信息,生成摘要,并提供全面准确的答案。可以将其比作拥有一个由专家组成的团队,每个成员具备独特的技能和能力,共同合作满足信息需求。
Agentic RAG最关键的特性:
- 协调问答流程:通过将问答过程分解为可管理的步骤,分配适当的智能体执行每项任务,并确保协调达到最优结果。
- 目标导向:智能体能够理解并锁定特定目标,执行更复杂和有意义的交互动作。
- 规划与推理:框架中的智能体具备复杂的规划和多步骤推理能力,能够制定最佳信息检索、分析和整合策略,以有效回答复杂问题。
- 工具使用与适应性:Agentic RAG代理可以利用外部工具和资源,如搜索引擎、数据库和专门的API,增强信息采集和处理能力。
- 上下文感知:系统能够考虑当前情境、过去的交互和用户偏好,做出有效决策并采取合适的行动。
总而言之,Agentic RAG 的核心在于为 RAG 框架注入智能和自主性,就像具身智能的核心在于为机器人注入智能和自主性,所以Agentic RAG更像是一种虚拟环境的具身智能。
1.2 传统RAG的局限性及Agentic的应对方式
传统 RAG 实现的最大局限之一是无法真正理解并考虑更广泛的对话上下文【2,3】。而 Agentic RAG 则设计为上下文感知的,能够掌握对话的细微差别,考虑历史记录,并相应调整行为。这也意味着可以提供更连贯且相关的响应。
此外,在信息优先级排序方面,传统RAG经常难以管理和优先处理大型数据集中的信息,导致性能下降。而且传统 RAG 系统依赖静态规则进行检索。而Agentic RAG 使用智能检索策略,动态评估用户的查询、可用的工具(数据源)和上下文线索,以确定最合适的检索操作。
并且,传统RAG处理复杂问题时基本就捉襟见肘。复杂的查询往往涉及多个文档或数据源,在 Agentic RAG 中引入了“多智能体协调”的机制。多个专门的智能体,每个在自己的领域或数据源中都是专家,共同合作,综合自己的发现,提供全面的响应。
更进一步,Agentic RAG 还具备超越简单检索和生成的推理能力,可以对检索到的数据进行评估、纠正和质量检查,确保输出是准确可靠的。就像我们之前在有效提示词中谈到的“自检”能力。
【1】给出了关于传统RAG和Agentic RAG的对比:
1.3 Agentic RAG 架构解析
在理解了Agentic RAG 是什么,以及与传统RAG的区别后,再来看下其架构。
在这个架构的核心是 Agentic RAG 智能体协调器,其接收用户查询并决定适当的行动路线。可以把它想象成高效的协调者,协调所有不同的工具,共同完成一个有效的任务。
智能体配备了一套工具,每个工具都与特定的文档或数据源相关联。这些工具可以从其各自的数据源中检索、处理和生成信息。也可以理解为有一系列功能接口,比如负责访问和处理报表、处理用户数据等。Agentic RAG 可以根据查询动态选择和组合这些工具,允许它从多个来源综合信息,提供全面的响应。
关于内部的智能体类型,可以细分为以下类型【1】:
路由代理: 路由代理使用大模型来决定选择哪条下游的RAG管道。这一过程构成智能体推理,大模型通过分析输入查询来做出选择最合适的RAG管道的决策。另外,智能体也可以处理类似在总结和问答RAG管道之间做出选择的场景。智能体评估输入查询,决定将其定向到摘要查询引擎或向量查询引擎,两者都被配置为工具。
查询规划智能体: 查询规划智能体可以将复杂查询分解为可并行执行的子查询,每个子查询可以基于不同的数据源在各类RAG管道上执行。然后,这些管道的响应会被整合为最终的回答。基本上,查询规划的第一步是将查询分解为子查询,分别在合适的RAG管道上执行每个子查询,并将结果综合为完整的响应。
工具使用智能体: 在RAG中,查询被提交以检索与查询语义匹配的最相关文档。然而,有时需要从外部数据源(如API、SQL数据库或带API接口的应用程序)获取额外的数据作为上下文,以增强输入查询的处理。在这种情况下,智能体可以使用RAG工具。
关于工具使用function calling),【4】中给出了一些例子可以参考:
import openai
import os
from math import *
from icecream import ic
import json
from math import *
import requests
import logging
# 设置日志记录配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 加载 .env 文件
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
# 从环境变量中获得你的 OpenAI Key
openai.api_key = os.getenv('OPENAI_API_KEY')
openai.api_base = os.getenv('OPENAI_API_URL')
model = os.getenv('MODEL')
amap_key = os.getenv('GAODE_MAP_API_KEY')
def get_completion(messages, model=model):
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=0,# 模型输出的随机性,0 表示随机性最小
tools=[{
"type": "function",
"function": {
"name": "add_contact",
"description": "添加联系人",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "联系人姓名"
},
"address": {
"type": "string",
"description": "联系人地址"
},
"tel": {
"type": "string",
"description": "联系人电话"
},
}
}
}
}],
)
return response.choices[0].message
def function_test_promopt():
prompt = "请寄给上海黄浦区科学会堂的老范,地址是上海市黄浦区科学会堂国际厅3楼,电话18888888888。"
messages = [
{"role": "system", "content": "你是一个联系人录入员。"},
{"role": "user", "content": prompt}
]
response = get_completion(messages)
logging.info("====GPT回复====")
logging.info(response)
args = json.loads(response.tool_calls[0].function.arguments)
logging.info("====函数参数====")
logging.info(args)
if __name__ == '__main__':
function_test_promopt()
输出类似这样:
====GPT回复====INFO - {"role": "assistant","content": null,"tool_calls": [{"id": "call_QEnFRbZJDAKjsKDSR","type": "function","function": {"name": "add_contact","arguments": "{\"name\":老范"",\"address\":"上海市黄浦区科学会堂国际厅3楼",\"tel\":\"18888888888\"}"}}]}
INFO - ====函数参数====
INFO - {'name': '老范', 'address': '上海市黄浦区科学会堂国际厅3楼', 'tel': '18888888888'}
ReAct智能体 ReAct = Reason + Act(推理 + 行动)结合LLMs
更高层次的智能体通过迭代地处理复杂查询来结合推理与行动。实际上将路由、查询规划和工具使用组合为一个实体。ReAct代理能够处理多步顺序查询,并在处理过程中保持状态(存储在内存中)。该过程包括以下步骤:
- 在接收到用户输入查询后,智能体决定是否需要使用工具,并收集该工具所需的输入。
- 工具被调用,并使用必要的输入,其输出被存储。
- 智能体接收工具的历史记录(包括输入和输出),并基于这些信息决定下一步的行动。
- 这一过程不断迭代,直到智能体完成任务并向用户提供回复。
还有更复杂的规划者和执行者的实现:
- 制定完成输入查询计划所需的步骤,实际上是在创建整个计算图或有向无环图(DAG)。
- 确定执行计划中每一步所需的工具,并使用所需的输入来执行这些步骤。
1.4 如何实现Agentic RAG
构建Agentic RAG需要特定的框架和工具来帮助创建和协调多个代理。有一些现有框架可以使用,但也会有一些局限性,仅供参考。
LlamaIndex: LlamaIndex 提供了一套丰富的功能。帮助开发者创建文档代理、管理代理交互,并实现诸如思维链(Chain-of-Thought)等高级推理机制。该框架提供了许多预构建工具,便于与各种数据源交互,包括流行的搜索引擎(如Google)和知识库(如Wikipedia)。还能与多种数据库无缝集成,包括SQL和向量数据库,并支持通过Python REPL执行代码。LlamaIndex的Chains功能可以轻松将不同的工具和LLMs链接起来,促进复杂工作流程的创建。此外,其内存组件可跟踪代理的操作和对话历史,从而支持上下文感知的决策。
LangChain 与LlamaIndex类似,LangChain 提供了一个完整的工具包,用于构建基于代理的系统并协调它们之间的交互。工具阵列与LangChain生态系统中的外部资源无缝集成,使代理能够访问广泛的功能,包括搜索、数据库管理和代码执行。LangChain能够将多种数据结构和查询引擎结合在一起,能够访问和处理来自不同来源信息的复杂代理。
2. Multi-Source RAG(多数据源RAG)
在Agentic RAG中,我们提到了复杂的查询往往涉及多个文档或多数据源。这里主要介绍一下MSDAG【5】的工作。多源检索增强生成(MSRAG)框架通过整合多种检索源,结合GPT-3.5和基于网络的检索方法,混合方法能够提高检索信息的精细度和相关性,同时减少了信息噪声和幻觉现象【6】。
MSRAG的关键组成部分
- 基于LLM的检索:MSRAG并不仅依赖传统的检索器,还利用LLM来生成检索信息。这种方法借助大模型庞大的语料库知识,生成更具上下文相关性的内容。
- 网络检索:为了补充大模型知识,MSRAG引入了一个网络检索模块。该模块利用网络搜索引擎收集实时信息,提高系统处理动态查询的能力。
- 语义分割:框架通过将查询进行语义分割为子问题,分别通过网络检索进行查询。此方法通过分解复杂问题提高了检索信息的准确性。
- 信息综合:从大模型和网络来源检索到的信息会使用大模型进行总结和综合,形成连贯且全面的数据集。
- 损失函数优化:为了确保回答的最高质量,框架计算生成答案与正确答案的余弦相似度,选择相似度最高的回答作为最终结果。
有了更多数据信息的参考,效果还是很明显的:
【7】提出方法 UniMS-RAG,设计了三个优化任务:1)知识源选择;2)相关性评分预测;以及3)响应生成。使用橙色来表示执行tokens,使用蓝色来表示评估tokens。在训练过程中,拥有所有标签,以教师强制(teacher-forcing)方式优化这三个子任务。
【8,9】中介绍了使用langchain实现多数据源多搜索智能体的RAG。
代码示例(创建了一个基于LangChain框架的代理,整合了维基百科、Arxiv和LangSmith的搜索工具。使用GPT-3.5-turbo处理用户的输入,并返回相关信息):
# 导入Wikipedia查询工具,用于访问维基百科
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
# 导入WebBaseLoader以加载网页内容
from langchain_community.document_loaders import WebBaseLoader
# 导入FAISS,用于创建向量数据库
from langchain_community.vectorstores import FAISS
# 导入OpenAIEmbeddings以生成文档的嵌入
from langchain_openai import OpenAIEmbeddings
# 导入文本分割器,用于将文档拆分成较小块
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 导入检索工具生成函数
from langchain.tools.retriever import create_retriever_tool
## Arxiv工具相关的导入
from langchain_community.utilities import ArxivAPIWrapper
from langchain_community.tools import ArxivQueryRun
# 初始化Wikipedia API封装器,限制为返回最多1个结果,且文档内容字符数不超过200
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=200)
# 实例化维基百科查询运行工具
wiki = WikipediaQueryRun(api_wrapper=api_wrapper)
# 使用WebBaseLoader加载网页的内容(URL为LangSmith文档)
loader = WebBaseLoader("https://docs.smith.langchain.com/")
docs = loader.load()
# 将加载的文档使用RecursiveCharacterTextSplitter进行分割,设置每块大小为1000字符,重叠为200字符
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)
# 基于文档内容生成向量数据库,使用OpenAIEmbeddings进行嵌入
vectordb = FAISS.from_documents(documents, OpenAIEmbeddings())
# 将向量数据库作为检索器
retriever = vectordb.as_retriever()
# 创建检索工具,指定名称为"langsmith_search",并配置工具用于LangSmith相关问题
retriever_tool = create_retriever_tool(retriever, "langsmith_search",
"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!")
# 初始化Arxiv API封装器,配置为返回最多1个结果,文档内容字符数不超过200
arxiv_wrapper = ArxivAPIWrapper(top_k_results=1, doc_content_chars_max=200)
# 实例化Arxiv查询运行工具
arxiv = ArxivQueryRun(api_wrapper=arxiv_wrapper)
# 将工具添加到工具列表中,包含wiki(维基百科查询工具)、arxiv(Arxiv查询工具)和retriever_tool(LangSmith检索工具)
tools = [wiki, arxiv, retriever_tool]
# 加载环境变量
from dotenv import load_dotenv
load_dotenv()
# 使用os模块获取OPENAI_API_KEY,并设置为环境变量
import os
os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY")
# 导入ChatOpenAI以初始化模型,使用GPT-3.5 turbo版本,设置温度为0
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
# 导入hub以获取预设的提示词
from langchain import hub
# 获取用于agent模型的提示词
prompt = hub.pull("hwchase17/openai-functions-agent")
prompt.messages
### Agent
# 从langchain.agents导入create_openai_tools_agent以创建agent
from langchain.agents import create_openai_tools_agent
agent = create_openai_tools_agent(llm, tools, prompt)
## agent执行器
# 导入AgentExecutor,用于执行agent
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 执行agent,输入查询“Tell me about Langsmith”
agent_executor.invoke({"input": "Tell me about Langsmith"})
# 执行agent,输入查询“What's the paper 1605.08386 about?”
agent_executor.invoke({"input": "What's the paper 1605.08386 about?"})
3. 参考材料
【1】Agentic RAG: What it is, its types, applications and implementation
【2】Agentic RAG
【3】What is agentic RAG? A complete guide
【4】大模型为什么能把业务系统拉通?Function Calling全面解析
【5】A Multi-Source Retrieval QuestionAnsweringFramework Based on RAG
【6】Enhancing RAG-Based Systems: A Multi-Source Retrieval Framework
【7】UniMS-RAG: A Unified Multi-source Retrieval-Augmented Generation for Personalized Dialogue Systems
【8】Building Advanced RAG With Multiple Data Source Using Langchain
【9】https://github.com/krishnaik06/Updated-Langchain/blob/main/agents/agents.ipynb