Adapt RAG讲解

Adaptive RAG

自适应 RAG(Adaptive RAG)

概述

自适应 RAG是一种RAG策略,结合了(1)查询分析与(2)主动/自我纠正的RAG方法。在论文中,他们报告了通过查询分析来路由到以下几种策略:

  1. 不进行检索(No Retrieval)
  2. 一次性 RAG(Single-shot RAG)
  3. 迭代 RAG(Iterative RAG)

我们将使用LangGraph基于这些概念进行扩展和实现。在我们的实现中,我们将路由到以下两种策略:

  1. 网络搜索(Web search):用于与近期事件相关的问题。
  2. 自我纠正 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_searchvectorstore

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值