小工具知识网

当前位置: 首页 > 行业知识 > 【AI大模型应用开发】【LangChain系列】实战案例3:深入LangChain源码,你不知道的WebResearchRetriever与RAG联合之力

行业知识

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

2026-11-14 00:05:17 来源:互联网转载
  • 大家好,我是同学小张,日常分享AI知识和实战案例
  • 欢迎 点赞 + 关注 ,持续学习,持续干货输出。
  • +v: jasper_8017 一起交流,一起进步。
  • 微信公众号也可搜【同学小张】

    本站文章一览:


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

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

    文章目录

    • 0. 环境准备
      • 0.1 获取Google API key
      • 0.2 获取 Google CSE ID
      • 0.3 安装依赖Python包
      • 1. 完整代码及解释
        • 1.1 完整代码
        • 1.2 代码研读
          • 1.2.1 WebResearchRetriever
          • 1.2.2 GoogleSearchAPIWrapper
          • 1.2.3 RetrievalQAWithSourcesChain
          • 2. 总结

            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.1pip install -i https://pypi.tuna.tsinghua.edu.cn/simple google-api-python-client==2.95.0

            完整的安装依赖参考:

            streamlit==1.25.0langchain==0.0.244chromadb==0.4.3openai==0.27.8html2text==2020.1.16google-api-core==2.11.1google-api-python-client==2.95.0google-auth==2.22.0google-auth-httplib2==0.1.0googleapis-common-protos==1.59.1tiktoken==0.4.0faiss-cpu==1.7.4

            1. 完整代码及解释

            1.1 完整代码

            from langchain.retrievers.web_research import WebResearchRetrieverfrom langchain_community.utilities import GoogleSearchAPIWrapperfrom langchain_community.vectorstores import Chromafrom langchain_openai import ChatOpenAI, OpenAIEmbeddings# Vectorstorevectorstore = Chroma(    embedding_function=OpenAIEmbeddings(), persist_directory="./chroma_db_oai")# LLMllm = ChatOpenAI(temperature=0)# Searchsearch = GoogleSearchAPIWrapper()# Initializeweb_research_retriever = WebResearchRetriever.from_llm(    vectorstore=vectorstore, llm=llm, search=search)# Runimport 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。

            它的使用方式如下:

            # Initializeweb_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_splitter,QuestionListOutputParser等Retriever过程所需的工具和内容。

              默认的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知识和实战案例
                    • 欢迎 点赞 + 关注 ,持续学习,持续干货输出。
                    • +v: jasper_8017 一起交流,一起进步。
                    • 微信公众号也可搜【同学小张】

                      本站文章一览:

上一篇:ktv服务器怎么建立?

下一篇:服务器怎么设置环境部署?