【AI大模型应用开发】【LangChain系列】实战案例3:深入LangChain源码,你不知道的WebResearchRetriever与RAG联合之力

简介: 【AI大模型应用开发】【LangChain系列】实战案例3:深入LangChain源码,你不知道的WebResearchRetriever与RAG联合之力
  • 大家好,我是同学小张,日常分享AI知识和实战案例
  • 欢迎 点赞 + 关注 👏,持续学习持续干货输出
  • 一起交流💬,一起进步💪。
  • 微信公众号也可搜【同学小张】 🙏

本站文章一览:


上篇文章我们学习了如何利用 LangChain 通过 URL 获取网页内容。本文我们继续学习利用 LangChain 进行网络数据抓取:我们将利用 LangChain 抓取网络数据来回答我们指定的问题(也就是类似 网络 + RAG)。

本文参考教程:https://python.langchain.com/docs/use_cases/web_scraping

0. 环境准备

要想成功运行本文所示的代码,需要做一下准备。

0.1 获取Google API key

首先,需要获取一个 Google API key。

(1)打开链接,登录你的Google账号(没有Google账号的请自行注册):

https://console.cloud.google.com/apis/api/customsearch.googleapis.com/credentials

(2)创建一个Project

(3)在你创建的 Project 页面(创建完后会自动跳转),点 API key,创建API key即可

(4)配置API key到你的代码中:将这个API key放到你的程序 .env 文件中作为环境变量加载。

GOOGLE_API_KEY = "YOUR GOOGLE API KEY"

0.2 获取 Google CSE ID

(1)登录链接,创建一个新的 Search Engine

https://programmablesearchengine.google.com/

(2)创建完后,Search engine ID 即为所需的 CSE ID。

(3)配置 CSE ID 到你的代码中:将这个 CSE ID 放到你的程序 .env 文件中作为环境变量加载。

GOOGLE_CSE_ID = "xxxxxxx"

0.3 安装依赖Python包

我的安装以下两个基本就够了,因为之前安装过 langchain、openai之类的。

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

完整的安装依赖参考

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 完整代码

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 logging
logging.basicConfig()
logging.getLogger("langchain.retrievers.web_research").setLevel(logging.INFO)
from langchain.chains import RetrievalQAWithSourcesChain
user_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。

它的使用方式如下:

# Initialize
web_research_retriever = WebResearchRetriever.from_llm(
    vectorstore=vectorstore, llm=llm, search=search
)

接收三个主要参数:

  • 向量数据库:用来存储网页数据
  • llm
  • 检索引擎,这里的检索引擎 必须是 Google Search API
class WebResearchRetriever(BaseRetriever):
    """`Google Search API` retriever."""
  search: GoogleSearchAPIWrapper = Field(..., description="Google Search API Wrapper")

再看下其构造过程:from_llm 函数

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 pages
            llm: llm for search question generation
            search: GoogleSearchAPIWrapper
            prompt: prompt to generating search questions
            num_search_results: Number of pages per Google search
            text_splitter: Text splitter for splitting web pages into chunks
        Returns:
            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 prompt
        llm_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_splitterQuestionListOutputParserRetriever过程所需的工具和内容。

默认的Prompt模板内容如下:

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)步骤的源码如下,可以对照着看一下:

def _get_relevant_documents(
        self,
        query: str,
        *,
        run_manager: CallbackManagerForRetrieverRun,
    ) -> List[Document]:
        """Search Google for documents related to the query input.
        Args:
            query: user query
        Returns:
            Relevant documents from all various urls.
        """
        # Get search questions
        logger.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 urls
        logger.info("Searching for relevant urls...")
        urls_to_look = []
        for query in questions:
            # Google search
            search_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 urls
        urls = set(urls_to_look)
        # Check for any new urls that we have not processed
        new_urls = list(urls.difference(self.url_database))
        logger.info(f"New URLs to load: {new_urls}")
        # Load, split, and add new urls to vectorstore
        if 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 async
        logger.info("Grabbing most relevant splits from urls...")
        docs = []
        for query in questions:
            docs.extend(self.vectorstore.similarity_search(query))
        # Get unique docs
        unique_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的封装类。

class GoogleSearchAPIWrapper(BaseModel):
    """Wrapper for Google Search API."""

1.2.3 RetrievalQAWithSourcesChain

这是 LangChain 内封装的问答QA链,提问-给出答案,并带有答案来源Sources.

对检索到的文档进行问答,并引用其来源。当您希望答案响应在文本响应中具有来源时,请使用此选项。

使用方法:

qa_chain = RetrievalQAWithSourcesChain.from_chain_type(
    llm, retriever=web_research_retriever
)

接收两个参数:

  • llm:大模型
  • retriver:检索器

其源码定义如下:

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增强检索,回答用户问题

如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~


  • 大家好,我是 同学小张,日常分享AI知识和实战案例
  • 欢迎 点赞 + 关注 👏,持续学习持续干货输出
  • 一起交流💬,一起进步💪。
  • 微信公众号也可搜【同学小张】 🙏

本站文章一览:

相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
3天前
|
机器学习/深度学习 人工智能 安全
Gandalf AI 通关详解(大模型安全)
Gandalf AI 通关详解(大模型安全)
|
4天前
|
Java 程序员 API
全民上手大模型--ollama+langchain+通义千问零费用java+python跑通本机大模型
全民上手大模型--ollama+langchain+通义千问零费用java+python跑通本机大模型
33 0
|
5天前
|
人工智能 自然语言处理 安全
【大模型】 基于AI和全球化进程的权衡:开源大模型与闭源大模型
【大模型】 基于AI和全球化进程的权衡:开源大模型与闭源大模型
27 0
|
5天前
|
机器学习/深度学习 人工智能 算法
构建一个基于AI的语音识别系统:技术深度解析与实战指南
【5月更文挑战第28天】本文深入探讨了构建基于AI的语音识别系统,涵盖基本原理、关键技术及实战指南。关键步骤包括语音信号预处理、特征提取、声学模型、语言模型和解码器。深度学习在声学和语言模型中发挥关键作用,如RNN、LSTM和Transformer。实战部分涉及数据收集、预处理、模型训练、解码器实现及系统评估。通过本文,读者可了解构建语音识别系统的基本流程和技巧。
|
6天前
|
数据采集 SQL 人工智能
AI+低代码,打通企业大模型应用最后一公里!
在2024微软Build大会上,微软宣布50多项AI更新,包括GPT-4上云、Cobalt芯片、Team Copilot和小模型等。Team Copilot成为团队助手,管理会议并协助解决问题。GPT-4模型支持多模态处理,引领AI体验新标准。微软构建三个平台增强AI功能,推动AI应用于协同办公、低代码开发等领域。织信低代码平台结合AI技术,实现智能数据补充、内容分析和快速算法,提升企业效率,降低了对IT资源的需求。AI与低代码技术共同促进数字化转型,织信低代码已在多个行业广泛应用,其AI助手强化了用户体验和效率。未来,AI将成为软件交互的核心,低代码与AI的结合将进一步推动软件平民化。
|
7天前
|
机器学习/深度学习 存储 人工智能
构建高效AI系统:深度学习模型压缩技术
【5月更文挑战第26天】 在资源受限的应用场景中,深度学习模型往往面临存储空间和计算能力的双重挑战。本文针对这一问题,深入探讨了深度学习模型压缩技术,旨在通过降低模型复杂度来优化其性能。文中首先分析了模型压缩的必要性,随后详细介绍了知识蒸馏、网络剪枝、量化等主流压缩方法,并通过实验验证了所提技术的有效性。最后,文章展望了模型压缩领域的未来发展方向,为相关研究提供了新的视角和思路。
|
7天前
|
机器学习/深度学习 数据采集 人工智能
构建高效AI模型:深度学习优化策略和实践
【5月更文挑战第26天】 在人工智能的浪潮中,深度学习作为一项核心技术,其模型构建与优化一直是研究的热点。本文旨在探讨如何通过一系列创新性的优化策略提升深度学习模型的性能及效率。我们将从理论与实践两个维度出发,详细阐述包括数据预处理、网络结构设计、损失函数选择、正则化技巧以及超参数调整等方面的优化措施。通过这些策略的综合运用,可以显著提高模型的准确性,降低过拟合风险,并缩短训练时间,为AI领域的研究者和工程师提供有价值的参考。
|
9天前
|
人工智能 图形学
阿里AI模型EMO免费上线通义APP
阿里AI模型EMO免费上线通义APP
37 1
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
【LangChain系列】第五篇:大语言模型中的提示词,模型及输出简介及实践
【5月更文挑战第19天】LangChain是一个Python库,简化了与大型语言模型(LLM)如GPT-3.5-turbo的交互。通过ChatOpenAI类,开发者可以创建确定性输出的应用。提示词是指导LLM执行任务的关键,ChatPromptTemplate允许创建可重用的提示模板。输出解析器如StructuredOutputParser将模型的响应转化为结构化数据,便于应用处理。LangChain提供可重用性、一致性、可扩展性,并有一系列预建功能。它使得利用LLM构建复杂、直观的应用变得更加容易。
52 0
|
12天前
|
机器学习/深度学习 人工智能 编解码
Sora - 探索AI视频模型的无限可能
Sora - 探索AI视频模型的无限可能
35 0