【LangChain系列】实战案例3:深入LangChain源码,你不知道的WebResearchRetriever与RAG联合之力
0. 环境准备
要想成功运行本文所示的代码,需要做一下准备。
0.1 获取Google API key
首先,需要获取一个 Google API key。
(1)打开链接,登录你的Google账号(没有Google账号的请自行注册):
console.cloud.google.com/apis/api/cu…
(2)创建一个Project
(3)在你创建的 Project 页面(创建完后会自动跳转),点 API key,创建API key即可
(4)配置API key到你的代码中:将这个
API key放到你的程序 .env 文件中作为环境变量加载。
python代码解读
复制代码GOOGLE_API_KEY = "YOUR GOOGLE API KEY"
0.2 获取 Google CSE ID
(1)登录链接,创建一个新的 Search Engine
programmablesearchengine.google.com/
(2)创建完后,Search engine ID 即为所需的 CSE ID。
(3)配置 CSE ID 到你的代码中:将这个 CSE ID 放到你的程序 .env 文件中作为环境变量加载。
python代码解读
复制代码GOOGLE_CSE_ID = "xxxxxxx"
0.3 安装依赖Python包
我的安装以下两个基本就够了,因为之前安装过 langchain、openai之类的。
python代码解读复制代码pip install google-api-core==2.11.1
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple google-api-python-client==2.95.0
完整的安装依赖参考:
python代码解读复制代码streamlit==1.25.0
langchain==0.0.244
chromadb==0.4.3
openai==0.27.8
html2text==2020.1.16
google-api-core==2.11.1
google-api-python-client==2.95.0
google-auth==2.22.0
google-auth-httplib2==0.1.0
googleapis-common-protos==1.59.1
tiktoken==0.4.0
faiss-cpu==1.7.4
1. 完整代码及解释
1.1 完整代码
python代码解读复制代码from langchain.retrievers.web_research import WebResearchRetriever
from langchain_community.utilities import GoogleSearchAPIWrapper
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI, OpenAIEmbeddings# Vectorstore
vectorstore = Chroma(embedding_function=OpenAIEmbeddings(), persist_directory="./chroma_db_oai"
)# LLM
llm = ChatOpenAI(temperature=0)# Search
search = GoogleSearchAPIWrapper()# Initialize
web_research_retriever = WebResearchRetriever.from_llm(vectorstore=vectorstore, llm=llm, search=search
)# Run
import logginglogging.basicConfig()
logging.getLogger("langchain.retrievers.web_research").setLevel(logging.INFO)
from langchain.chains import RetrievalQAWithSourcesChainuser_input = "How do LLM Powered Autonomous Agents work?"
qa_chain = RetrievalQAWithSourcesChain.from_chain_type(llm, retriever=web_research_retriever
)
result = qa_chain({"question": user_input})
print(result)
1.2 代码研读
1.2.1 WebResearchRetriever
首先是代码中最重要的一个封装类:WebResearchRetriever。
它的使用方式如下:
python代码解读复制代码# Initialize
web_research_retriever = WebResearchRetriever.from_llm(vectorstore=vectorstore, llm=llm, search=search
)
接收三个主要参数:
- 向量数据库:用来存储网页数据
- llm
- 检索引擎,这里的检索引擎 必须是 Google Search API
python代码解读复制代码class WebResearchRetriever(BaseRetriever):"""`Google Search API` retriever."""search: GoogleSearchAPIWrapper = Field(..., description="Google Search API Wrapper")
再看下其构造过程:from_llm
函数
python代码解读复制代码def from_llm(cls,vectorstore: VectorStore,llm: BaseLLM,search: GoogleSearchAPIWrapper,prompt: Optional[BasePromptTemplate] = None,num_search_results: int = 1,text_splitter: RecursiveCharacterTextSplitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150),) -> "WebResearchRetriever":"""Initialize from llm using default template.Args:vectorstore: Vector store for storing web pagesllm: llm for search question generationsearch: GoogleSearchAPIWrapperprompt: prompt to generating search questionsnum_search_results: Number of pages per Google searchtext_splitter: Text splitter for splitting web pages into chunksReturns:WebResearchRetriever"""if not prompt:QUESTION_PROMPT_SELECTOR = ConditionalPromptSelector(default_prompt=DEFAULT_SEARCH_PROMPT,conditionals=[(lambda llm: isinstance(llm, LlamaCpp), DEFAULT_LLAMA_SEARCH_PROMPT)],)prompt = QUESTION_PROMPT_SELECTOR.get_prompt(llm)# Use chat model promptllm_chain = LLMChain(llm=llm,prompt=prompt,output_parser=QuestionListOutputParser(),)return cls(vectorstore=vectorstore,llm_chain=llm_chain,search=search,num_search_results=num_search_results,text_splitter=text_splitter,)
这个函数用来初始化 WebResearchRetriever
,除了上面说的三个主要参数外,其额外提供了默认的Prompt模板
,text_splitter
,QuestionListOutputParser
等Retriever
过程所需的工具和内容。
默认的Prompt模板内容如下:
python代码解读复制代码DEFAULT_SEARCH_PROMPT = PromptTemplate(input_variables=["question"],template="""You are an assistant tasked with improving Google search \
results. Generate THREE Google search queries that are similar to \
this question. The output should be a numbered list of questions and each \
should have a question mark at the end: {question}""",
)
从这个Prompt大致可以看出WebResearchRetriever
的工作过程:
(1)根据用户的问题,利用大模型将该问题转化为3个与用户问题相近的Google搜索语句
(2)利用 Google CSE 搜索这几个问题,会得到一系列相关 URL
(3)利用上篇文章我们爬取网页内容的方法,将每个URL中的文本抓取出来
(4)对抓取出来的文本进行分块,向量存储(WebResearchRetriever
的工作到这里就结束了)
(5)然后就是其它模块使用RAG的流程:用户提问 —> 查询向量数据库 —> 大模型回答问题
整体流程示意图如下:
(1)-(4)步骤的源码如下,可以对照着看一下:
python代码解读复制代码def _get_relevant_documents(self,query: str,*,run_manager: CallbackManagerForRetrieverRun,) -> List[Document]:"""Search Google for documents related to the query input.Args:query: user queryReturns:Relevant documents from all various urls."""# Get search questionslogger.info("Generating questions for Google Search ...")result = self.llm_chain({"question": query})logger.info(f"Questions for Google Search (raw): {result}")questions = result["text"]logger.info(f"Questions for Google Search: {questions}")# Get urlslogger.info("Searching for relevant urls...")urls_to_look = []for query in questions:# Google searchsearch_results = self.search_tool(query, self.num_search_results)logger.info("Searching for relevant urls...")logger.info(f"Search results: {search_results}")for res in search_results:if res.get("link", None):urls_to_look.append(res["link"])# Relevant urlsurls = set(urls_to_look)# Check for any new urls that we have not processednew_urls = list(urls.difference(self.url_database))logger.info(f"New URLs to load: {new_urls}")# Load, split, and add new urls to vectorstoreif new_urls:loader = AsyncHtmlLoader(new_urls, ignore_load_errors=True)html2text = Html2TextTransformer()logger.info("Indexing new urls...")docs = loader.load()docs = list(html2text.transform_documents(docs))docs = self.text_splitter.split_documents(docs)self.vectorstore.add_documents(docs)self.url_database.extend(new_urls)# Search for relevant splits# TODO: make this asynclogger.info("Grabbing most relevant splits from urls...")docs = []for query in questions:docs.extend(self.vectorstore.similarity_search(query))# Get unique docsunique_documents_dict = {(doc.page_content, tuple(sorted(doc.metadata.items()))): doc for doc in docs}unique_documents = list(unique_documents_dict.values())return unique_documents
1.2.2 GoogleSearchAPIWrapper
这是 Google CSE 检索API的封装类。
python代码解读复制代码class GoogleSearchAPIWrapper(BaseModel):"""Wrapper for Google Search API."""
1.2.3 RetrievalQAWithSourcesChain
这是 LangChain 内封装的问答QA链,提问-给出答案,并带有答案来源Sources.
对检索到的文档进行问答,并引用其来源。当您希望答案响应在文本响应中具有来源时,请使用此选项。
使用方法:
python代码解读复制代码qa_chain = RetrievalQAWithSourcesChain.from_chain_type(llm, retriever=web_research_retriever
)
接收两个参数:
- llm:大模型
- retriver:检索器
其源码定义如下:
python代码解读复制代码class RetrievalQAWithSourcesChain(BaseQAWithSourcesChain):"""Question-answering with sources over an index."""retriever: BaseRetriever = Field(exclude=True)"""Index to connect to."""reduce_k_below_max_tokens: bool = False"""Reduce the number of results to return from store based on tokens limit"""max_tokens_limit: int = 3375"""Restrict the docs to return from store based on tokens,enforced only for StuffDocumentChain and if reduce_k_below_max_tokens is to true"""
2. 总结
本文我们主要学习了利用 LangChain进行网络文档 + RAG 的使用,重点看了 LangChain中WebResearchRetriever
的封装和实现原理。里面虽然使用的Google搜索,在国内有诸多限制,但是里面的实现思路是值得借鉴的:
(1)找到与用户问题相关的网页
- 用户提问转换为相似的搜索语句
- 通过检索API找到相关的网页URL
(2)文本获取与存储
- 爬取URL文本内容
- 分割文本并向量存储
(3)使用以上相关内容进行RAG增强检索,回答用户问题
大模型资源分享
针对所有自学遇到困难的同学,我为大家系统梳理了大模型学习的脉络,并且分享这份LLM大模型资料:其中包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等。😝有需要的小伙伴,可以扫描下方二维码免费领取↓↓↓
一、全套 AGI 大模型学习路线
AI 大模型时代的精彩学习之旅:从根基铸就到前沿探索,牢牢掌握人工智能核心技能!
二、640 套 AI 大模型报告合集
此套涵盖 640 份报告的精彩合集,全面涉及 AI 大模型的理论研究、技术实现以及行业应用等诸多方面。无论你是科研工作者、工程师,还是对 AI 大模型满怀热忱的爱好者,这套报告合集都将为你呈上宝贵的信息与深刻的启示。
三、AI 大模型经典 PDF 书籍
伴随人工智能技术的迅猛发展,AI 大模型已然成为当今科技领域的一大热点。这些大型预训练模型,诸如 GPT-3、BERT、XLNet 等,凭借其强大的语言理解与生成能力,正在重塑我们对人工智能的认知。而以下这些 PDF 书籍无疑是极为出色的学习资源。
阶段 1:AI 大模型时代的基础认知
-
目标:深入洞悉 AI 大模型的基本概念、发展历程以及核心原理。
-
内容
:
- L1.1 人工智能概述与大模型起源探寻。
- L1.2 大模型与通用人工智能的紧密关联。
- L1.3 GPT 模型的辉煌发展历程。
- L1.4 模型工程解析。
- L1.4.1 知识大模型阐释。
- L1.4.2 生产大模型剖析。
- L1.4.3 模型工程方法论阐述。
- L1.4.4 模型工程实践展示。
- L1.5 GPT 应用案例分享。
阶段 2:AI 大模型 API 应用开发工程
-
目标:熟练掌握 AI 大模型 API 的运用与开发,以及相关编程技能。
-
内容
:- L2.1 API 接口详解。
- L2.1.1 OpenAI API 接口解读。
- L2.1.2 Python 接口接入指南。
- L2.1.3 BOT 工具类框架介绍。
- L2.1.4 代码示例呈现。
- L2.2 Prompt 框架阐释。
- L2.2.1 何为 Prompt。
- L2.2.2 Prompt 框架应用现状分析。
- L2.2.3 基于 GPTAS 的 Prompt 框架剖析。
- L2.2.4 Prompt 框架与 Thought 的关联探讨。
- L2.2.5 Prompt 框架与提示词的深入解读。
- L2.3 流水线工程阐述。
- L2.3.1 流水线工程的概念解析。
- L2.3.2 流水线工程的优势展现。
- L2.3.3 流水线工程的应用场景探索。
- L2.4 总结与展望。
阶段 3:AI 大模型应用架构实践
-
目标:深刻理解 AI 大模型的应用架构,并能够实现私有化部署。
-
内容
:- L3.1 Agent 模型框架解读。
- L3.1.1 Agent 模型框架的设计理念阐述。
- L3.1.2 Agent 模型框架的核心组件剖析。
- L3.1.3 Agent 模型框架的实现细节展示。
- L3.2 MetaGPT 详解。
- L3.2.1 MetaGPT 的基本概念阐释。
- L3.2.2 MetaGPT 的工作原理剖析。
- L3.2.3 MetaGPT 的应用场景探讨。
- L3.3 ChatGLM 解析。
- L3.3.1 ChatGLM 的特色呈现。
- L3.3.2 ChatGLM 的开发环境介绍。
- L3.3.3 ChatGLM 的使用示例展示。
- L3.4 LLAMA 阐释。
- L3.4.1 LLAMA 的特点剖析。
- L3.4.2 LLAMA 的开发环境说明。
- L3.4.3 LLAMA 的使用示例呈现。
- L3.5 其他大模型介绍。
阶段 4:AI 大模型私有化部署
-
目标:熟练掌握多种 AI 大模型的私有化部署,包括多模态和特定领域模型。
-
内容
:- L4.1 模型私有化部署概述。
- L4.2 模型私有化部署的关键技术解析。
- L4.3 模型私有化部署的实施步骤详解。
- L4.4 模型私有化部署的应用场景探讨。
学习计划:
- 阶段 1:历时 1 至 2 个月,构建起 AI 大模型的基础知识体系。
- 阶段 2:花费 2 至 3 个月,专注于提升 API 应用开发能力。
- 阶段 3:用 3 至 4 个月,深入实践 AI 大模型的应用架构与私有化部署。
- 阶段 4:历经 4 至 5 个月,专注于高级模型的应用与部署。