Adaptive RAG
自适应 RAG(Adaptive RAG)
概述
自适应 RAG是一种RAG策略,结合了(1)查询分析与(2)主动/自我纠正的RAG方法。在论文中,他们报告了通过查询分析来路由到以下几种策略:
- 不进行检索(No Retrieval)
- 一次性 RAG(Single-shot RAG)
- 迭代 RAG(Iterative RAG)
我们将使用LangGraph基于这些概念进行扩展和实现。在我们的实现中,我们将路由到以下两种策略:
- 网络搜索(Web search):用于与近期事件相关的问题。
- 自我纠正 RAG(Self-corrective RAG):用于与我们索引相关的问题。
系统架构图
系统的图形化表示如下所示:

设置环境(Setup)
首先,下载所需的包并设置必要的API密钥。
1. 安装必要的包
在Jupyter Notebook或终端中运行以下命令安装所需的包:
pip install -U langchain_community tiktoken langchain-openai langchain-cohere langchainhub chromadb langchain langgraph tavily-python
2. 设置API密钥
接下来,设置OpenAI、Cohere和Tavily的API密钥。以下代码将提示您输入API密钥并将其存储在环境变量中:
import getpass
import os
def _set_env(var: str):
if var not in os.environ:
os.environ[var] = getpass.getpass(f"{
var}: ")
_set_env("OPENAI_API_KEY")
_set_env("COHERE_API_KEY")
_set_env("TAVILY_API_KEY")
3. 设置LangSmith用于LangGraph开发
LangSmith是一个用于调试、测试和监控LangGraph项目的工具。通过注册LangSmith,您可以使用跟踪数据来优化LangGraph应用程序的性能。详细的注册和使用方法请参考LangSmith入门指南。
创建索引(Create Index)
1. 构建索引
我们首先需要构建一个文档索引,以便后续的检索和生成过程。
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
# 设置嵌入模型
embd = OpenAIEmbeddings()
# 要索引的文档URL
urls = [
"https://lilianweng.github.io/posts/2023-06-23-agent/",
"https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/",
"https://lilianweng.github.io/posts/2023-10-25-adv-attack-llm/",
]
# 使用WebBaseLoader加载文档
docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]
# 使用RecursiveCharacterTextSplitter拆分文档
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=500, chunk_overlap=0
)
doc_splits = text_splitter.split_documents(docs_list)
# 将拆分后的文档添加到向量存储中
vectorstore = Chroma.from_documents(
documents=doc_splits,
collection_name="rag-chroma",
embedding=embd,
)
retriever = vectorstore.as_retriever()
解释:
- WebBaseLoader:从指定的URL递归加载网页内容。
- RecursiveCharacterTextSplitter:将长文档拆分成较小的块,以便LLM更高效地处理。
- Chroma:使用向量存储(vectorstore)管理文档的嵌入向量,并提供高效的相似度检索。
- retriever:将向量存储作为检索器,供LLM调用以获取相关文档。
LLMs配置
使用Pydantic与LangChain
此部分使用Pydantic v2的BaseModel,需要langchain-core >= 0.3。使用langchain-core < 0.3将导致因混合使用Pydantic v1和v2而出错。
1. 路由器(Router)
路由器负责分析用户查询,并决定将查询路由到哪种检索策略(网络搜索或自我纠正RAG)。
from typing import Literal
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# 数据模型
class RouteQuery(BaseModel):
"""将用户查询路由到最相关的数据源。"""
datasource: Literal["vectorstore", "web_search"] = Field(
...,
description="根据用户问题选择将其路由到web search或vectorstore。",
)
# 初始化LLM并绑定结构化输出
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_router = llm.with_structured_output(RouteQuery)
# 定义系统消息模板
system = """You are an expert at routing a user question to a vectorstore or web search.
The vectorstore contains documents related to agents, prompt engineering, and adversarial attacks.
Use the vectorstore for questions on these topics. Otherwise, use web-search."""
route_prompt = ChatPromptTemplate.from_messages(
[
("system", system),
("human", "{question}"),
]
)
# 构建路由链
question_router = route_prompt | structured_llm_router
# 示例调用
print(
question_router.invoke(
{
"question": "Who will the Bears draft first in the NFL draft?"}
)
)
print(question_router.invoke({
"question": "What are the types of agent memory?"}))
输出示例:
datasource='web_search'
datasource='vectorstore'
解释:
- RouteQuery:定义了路由器的输出结构,包括
datasource字段,指示将查询路由到vectorstore还是web_search。 - ChatPromptTemplate:定义了与LLM交互的模板,包括系统消息和用户消息的占位符。
- question_router:结合了提示模板和LLM的路由链。
- 示例调用:根据用户问题,LLM决定将查询路由到
web_search或vectorstore。
2. 检索评分器(Retrieval Grader)
检索评分器用于评估检索到的文档是否与用户问题相关。
# 数据模型
class GradeDocuments(BaseModel):
"""评估检索到的文档相关性的二进制评分。"""
binary_score: str = Field(
description="文档是否与问题相关,'yes'或'no'"
)
# 初始化LLM并绑定结构化输出
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
structured_llm_grader = llm.with_structured_output(GradeDocuments)
# 定义系统消息模板
system = """You are a grader assessing relevance of a retrieved document to a user question. \n
If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \n
It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \n
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""
grade_prompt = ChatPromptTemplate.from_messages(
[
("system", system),
("human", "Retrieved document: \n\n {document} \n\n User question: {question}"),
]
)
# 构建评分链
retrieval_grader = grade_prompt | structured_llm_grader
# 示例调用
question = "agent memory"
docs = retriever.invoke(question)
doc_txt = docs[1].page_content
print(retrieval_grader.invoke({
"question": question, "document": doc_txt}))
binary_score='no'
输出示例:
GradeDocuments(binary_score='no')
解释:
- GradeDocuments:定义了评分器的输出结构,包括
binary_score字段,值为"yes"或"no"。 - grade_prompt:定义了用于评估文档相关性的提示模板。
- retrieval_grader:结合了提示模板和LLM的评分链。
- 示例调用:评估特定文档是否与用户问题相关。
3. 生成回答节点(Generate)
生成回答节点基于检索到的文档生成最终回答。
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
# 获取提示模板
prompt = hub.pull("rlm/rag-prompt")
# 初始化LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# 后处理函数:格式化文档
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 构建RAG链
rag_chain = prompt | llm | StrOutputParser()
# 运行RAG链
generation = rag_chain.invoke({
"context": docs, "question": question})
print(generation)
输出示例:
The design of generative agents combines LLM with memory, planning, and reflection mechanisms to enable agents to behave based on past experience and interact with other agents. Memory stream is a lon

最低0.47元/天 解锁文章
1805

被折叠的 条评论
为什么被折叠?



