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

本站文章一览:

相关实践学习
阿里云百炼xAnalyticDB PostgreSQL构建AIGC应用
通过该实验体验在阿里云百炼中构建企业专属知识库构建及应用全流程。同时体验使用ADB-PG向量检索引擎提供专属安全存储,保障企业数据隐私安全。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
24天前
|
存储 人工智能 分布式计算
Parquet 文件格式详解与实战 | AI应用开发
Parquet 是一种列式存储文件格式,专为大规模数据处理设计,广泛应用于 Hadoop 生态系统及其他大数据平台。本文介绍 Parquet 的特点和作用,并演示如何在 Python 中使用 Pandas 库生成和读取 Parquet 文件,包括环境准备、生成和读取文件的具体步骤。【10月更文挑战第13天】
188 60
|
12天前
|
存储 人工智能 文字识别
AI与OCR:数字档案馆图像扫描与文字识别技术实现与项目案例
本文介绍了纸质档案数字化的技术流程,包括高精度扫描、图像预处理、自动边界检测与切割、文字与图片分离抽取、档案识别与文本提取,以及识别结果的自动保存。通过去噪、增强对比度、校正倾斜等预处理技术,提高图像质量,确保OCR识别的准确性。平台还支持多字体识别、批量处理和结构化存储,实现了高效、准确的档案数字化。具体应用案例显示,该技术在江西省某地质资料档案馆中显著提升了档案管理的效率和质量。
|
23天前
|
人工智能 资源调度 数据可视化
【AI应用落地实战】智能文档处理本地部署——可视化文档解析前端TextIn ParseX实践
2024长沙·中国1024程序员节以“智能应用新生态”为主题,吸引了众多技术大咖。合合信息展示了“智能文档处理百宝箱”的三大工具:可视化文档解析前端TextIn ParseX、向量化acge-embedding模型和文档解析测评工具markdown_tester,助力智能文档处理与知识管理。
|
1月前
|
人工智能 前端开发 JavaScript
前端大模型入门(二):掌握langchain的核心Runnable接口
Langchain.js 是 Langchain 框架的 JavaScript 版本,专为前端和后端 JavaScript 环境设计。最新 v0.3 版本引入了强大的 Runnable 接口,支持灵活的执行方式和异步操作,方便与不同模型和逻辑集成。本文将详细介绍 Runnable 接口,并通过实现自定义 Runnable 来帮助前端人员快速上手。
|
14天前
|
机器学习/深度学习 人工智能 算法
AI赋能大学计划·大模型技术与应用实战学生训练营——吉林大学站圆满结营
10月30日,由中国软件行业校园招聘与实习公共服务平台携手魔搭社区共同举办的AI赋能大学计划·大模型技术与产业趋势高校行AIGC项目实战营·吉林大学站圆满结营。
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
当前AI大模型在软件开发中的创新应用与挑战
2024年,AI大模型在软件开发领域的应用正重塑传统流程,从自动化编码、智能协作到代码审查和测试,显著提升了开发效率和代码质量。然而,技术挑战、伦理安全及模型可解释性等问题仍需解决。未来,AI将继续推动软件开发向更高效、智能化方向发展。
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
AI在医疗领域的应用及其挑战
【10月更文挑战第34天】本文将探讨人工智能(AI)在医疗领域的应用及其面临的挑战。我们将从AI技术的基本概念入手,然后详细介绍其在医疗领域的各种应用,如疾病诊断、药物研发、患者护理等。最后,我们将讨论AI在医疗领域面临的主要挑战,包括数据隐私、算法偏见、法规合规等问题。
28 1
|
7天前
|
机器学习/深度学习 人工智能 算法
AI在医疗领域的应用与挑战
本文探讨了人工智能(AI)在医疗领域的应用,包括其在疾病诊断、治疗方案制定、患者管理等方面的优势和潜力。同时,也分析了AI在医疗领域面临的挑战,如数据隐私、伦理问题以及技术局限性等。通过对这些内容的深入分析,旨在为读者提供一个全面了解AI在医疗领域现状和未来发展的视角。
36 10
|
7天前
|
机器学习/深度学习 人工智能 监控
探索AI在医疗领域的应用与挑战
本文深入探讨了人工智能(AI)在医疗领域中的应用现状和面临的挑战。通过分析AI技术如何助力疾病诊断、治疗方案优化、患者管理等方面的创新实践,揭示了AI技术为医疗行业带来的变革潜力。同时,文章也指出了数据隐私、算法透明度、跨学科合作等关键问题,并对未来的发展趋势进行了展望。
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
当前AI大模型在软件开发中的创新应用与挑战
【10月更文挑战第31天】2024年,AI大模型在软件开发领域的应用取得了显著进展,从自动化代码生成、智能代码审查到智能化测试,极大地提升了开发效率和代码质量。然而,技术挑战、伦理与安全问题以及模型可解释性仍是亟待解决的关键问题。开发者需不断学习和适应,以充分利用AI的优势。