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

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
简介: 【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知识和实战案例
  • 欢迎 点赞 + 关注 👏,持续学习持续干货输出
  • 一起交流💬,一起进步💪。
  • 微信公众号也可搜【同学小张】 🙏

本站文章一览:

相关实践学习
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
9天前
|
人工智能 缓存 监控
使用LangChain4j构建Java AI智能体:让大模型学会使用工具
AI智能体是大模型技术的重要演进方向,它使模型能够主动使用工具、与环境交互,以完成复杂任务。本文详细介绍如何在Java应用中,借助LangChain4j框架构建一个具备工具使用能力的AI智能体。我们将创建一个能够进行数学计算和实时信息查询的智能体,涵盖工具定义、智能体组装、记忆管理以及Spring Boot集成等关键步骤,并展示如何通过简单的对话界面与智能体交互。
194 1
|
9天前
|
人工智能 Java API
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
本文介绍AI大模型的核心概念、分类及开发者学习路径,重点讲解如何选择与接入大模型。项目基于Spring Boot,使用阿里云灵积模型(Qwen-Plus),对比SDK、HTTP、Spring AI和LangChain4j四种接入方式,助力开发者高效构建AI应用。
376 122
AI 超级智能体全栈项目阶段一:AI大模型概述、选型、项目初始化以及基于阿里云灵积模型 Qwen-Plus实现模型接入四种方式(SDK/HTTP/SpringAI/langchain4j)
|
10天前
|
人工智能 自然语言处理 数据库
超越传统搜索:RAG如何让AI更懂你
超越传统搜索:RAG如何让AI更懂你
227 109
|
10天前
|
人工智能 自然语言处理 数据库
超越关键词搜索:RAG如何让AI真正“理解”你的问题
超越关键词搜索:RAG如何让AI真正“理解”你的问题
188 102
|
10天前
|
人工智能 自然语言处理 搜索推荐
超越幻觉:RAG如何为AI大模型注入“真实”的灵魂
超越幻觉:RAG如何为AI大模型注入“真实”的灵魂
151 81
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
AI Compass前沿速览:Qwen3-Max、Mixboard、Qwen3-VL、Audio2Face、Vidu Q2 AI视频生成模型、Qwen3-LiveTranslate-全模态同传大模型
AI Compass前沿速览:Qwen3-Max、Mixboard、Qwen3-VL、Audio2Face、Vidu Q2 AI视频生成模型、Qwen3-LiveTranslate-全模态同传大模型
139 13
AI Compass前沿速览:Qwen3-Max、Mixboard、Qwen3-VL、Audio2Face、Vidu Q2 AI视频生成模型、Qwen3-LiveTranslate-全模态同传大模型
|
17天前
|
存储 人工智能 监控
如何用RAG增强的动态能力与大模型结合打造企业AI产品?
客户的问题往往涉及最新的政策变化、复杂的业务规则,数据量越来越多,而大模型对这些私有知识和上下文信息的理解总是差强人意。
52 2
|
1月前
|
人工智能 自然语言处理 IDE
模型微调不再被代码难住!PAI和Qwen3-Coder加速AI开发新体验
通义千问 AI 编程大模型 Qwen3-Coder 正式开源,阿里云人工智能平台 PAI 支持云上一键部署 Qwen3-Coder 模型,并可在交互式建模环境中使用 Qwen3-Coder 模型。
351 109
|
9天前
|
分布式计算 测试技术 Spark
科大讯飞开源星火化学大模型、文生音效模型
近期,科大讯飞在魔搭社区(ModelScope)和Gitcode上开源两款模型:讯飞星火化学大模型Spark Chemistry-X1-13B、讯飞文生音频模型AudioFly,助力前沿化学技术研究,以及声音生成技术和应用的探索。
82 2

热门文章

最新文章