一架梯子,一头程序猿,仰望星空!
LangChain查询分析 > 内容正文

快速开始


快速入门

本教程,通过LangChain实现一个简单的YouTube视频搜索,遇到的搜索结果准确性问题,讲解如何使用查询分析技术优化,提到问题生成的准确性。

设置

安装依赖

# %pip install -qU langchain langchain-community langchain-openai youtube-transcript-api pytube chromadb

设置环境变量

我们将在本示例中使用 OpenAI:

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()

加载文档

我们可以使用 YouTubeLoader 来加载一些 LangChain 视频的字幕:

from langchain_community.document_loaders import YoutubeLoader

urls = [
    "https://www.youtube.com/watch?v=HAn9vnJy6S4",
    "https://www.youtube.com/watch?v=dA1cHGACXCo",
    "https://www.youtube.com/watch?v=ZcEMLz27sL4",
    "https://www.youtube.com/watch?v=hvAPnpSfSGo",
    "https://www.youtube.com/watch?v=EhlPDL4QrWY",
    "https://www.youtube.com/watch?v=mmBo8nlu2j0",
    "https://www.youtube.com/watch?v=rQdibOsL1ps",
    "https://www.youtube.com/watch?v=28lC4fqukoc",
    "https://www.youtube.com/watch?v=es-9MgxB-uc",
    "https://www.youtube.com/watch?v=wLRHwKuKvOE",
    "https://www.youtube.com/watch?v=ObIltMaRJvY",
    "https://www.youtube.com/watch?v=DjuXACWYkkU",
    "https://www.youtube.com/watch?v=o7C9ld6Ln-M",
]
docs = []
for url in urls:
    docs.extend(YoutubeLoader.from_youtube_url(url, add_video_info=True).load())
import datetime

for doc in docs:
    doc.metadata["publish_year"] = int(
        datetime.datetime.strptime(
            doc.metadata["publish_date"], "%Y-%m-%d %H:%M:%S"
        ).strftime("%Y")
    )

这里是我们加载的视频标题:

[doc.metadata["title"] for doc in docs]
['OpenGPTs',
 'Building a web RAG chatbot: using LangChain, Exa (prev. Metaphor), LangSmith, and Hosted Langserve',
 'Streaming Events: Introducing a new `stream_events` method',
 'LangGraph: Multi-Agent Workflows',
 'Build and Deploy a RAG app with Pinecone Serverless',
 'Auto-Prompt Builder (with Hosted LangServe)',
 'Build a Full Stack RAG App With TypeScript',
 'Getting Started with Multi-Modal LLMs',
 'SQL Research Assistant',
 'Skeleton-of-Thought: Building a New Template from Scratch',
 'Benchmarking RAG over LangChain Docs',
 'Building a Research Assistant from Scratch',
 'LangServe and LangChain Templates Webinar']

以下是每个视频相关的元数据。我们可以看到每个文档还有标题、观看次数、发布日期和长度:

docs[0].metadata
{'source': 'HAn9vnJy6S4',
 'title': 'OpenGPTs',
 'description': 'Unknown',
 'view_count': 7210,
 'thumbnail_url': 'https://i.ytimg.com/vi/HAn9vnJy6S4/hq720.jpg',
 'publish_date': '2024-01-31 00:00:00',
 'length': 1530,
 'author': 'LangChain',
 'publish_year': 2024}

这是一个文档内容的示例:

docs[0].page_content[:500]
"hello today I want to talk about open gpts open gpts is a project that we built here at linkchain uh that replicates the GPT store in a few ways so it creates uh end user-facing friendly interface to create different Bots and these Bots can have access to different tools and they can uh be given files to retrieve things over and basically it's a way to create a variety of bots and expose the configuration of these Bots to end users it's all open source um it can be used with open AI it can be us"

文档索引

下面我将加载的视频字幕数据导入向量数据库,方便根据用户的问题搜索相似的视频内容, 这块知识,如果不了解,建议系统的学习下LangChain教程

from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 文本拆分器,每个文本分片大小2000
text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# 拆分视频字幕内容
chunked_docs = text_splitter.split_documents(docs)
# 使用OpenAI的embedding模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 导入chroma向量数据库,同时返回
vectorstore = Chroma.from_documents(
    chunked_docs,
    embeddings,
)

没有进行查询分析优化的情况

我们可以直接对用户问题执行相似度搜索,以找到与问题相关的内容分块:

search_results = vectorstore.similarity_search("我如何建立一个RAG Agent")
print(search_results[0].metadata["title"])
print(search_results[0].page_content[:500])
使用 Pinecone Serverless 构建和部署 RAG 应用
嗨,我是 Lang 链团队的 Lance,今天我们将从头开始使用 Pinecone Serverless 构建和部署 rag 应用,所以我们将逐步讲解完成此操作所需的所有代码,并使用这些幻灯片作为指南来打下基础。那么 rag 到底是什么呢?Capoy 展示了一个非常好的可视化展示,显示 LMS 是一个新型操作系统的内核,当然,我们操作系统的核心组件之一就是 th

这效果非常好!我们的第一个结果与问题非常相关。

如果我们想要搜索特定时间段内的结果呢?

search_results = vectorstore.similarity_search("2023年在RAG上发布的视频")
print(search_results[0].metadata["title"])
print(search_results[0].metadata["publish_date"])
print(search_results[0].page_content[:500])
OpenGPTs
2024-01-31
硬编码让它总是在这里执行检索步骤,助手决定是否要执行检索步骤,有时这样做是好的,有时这样做是不好的,有时你不需要执行检索步骤,当我说嗨的时候,它不需要调用工具,但有时候,你知道 llm 可能会出错,并且意识不到它需要执行检索步骤,所以 rag 机器人将始终执行检索步骤,因此更加专注,因为这也是一个更简单的架构,所以它总是

我们的第一个结果是来自2024年(尽管我们要求的是来自2023年的视频),并且与输入问题不太相关。由于我们只是针对文档内容进行相似搜索,会出现搜索到的内容相关性不高, 对于这个用户问题, 如果使用2023这个时间作为属性进行过滤, 就可以提高向量搜索返回数据的准确度。

通过查询分析优化相似搜索召回数据准确度

我们可以使用查询分析来改进向量相似检索的结果。对于本例子,我们定义一个查询模式, 包含用户的查询问题和视频发布时间,然后我们通过LLM的自然语言处理能力,识别用户的意图,提取用户想查询什么时间的视频参数, 这样后面做向量搜索的时候,可以使用时间作为过滤条件,提高搜索数据的准确性。

查询模式

定义一个类,用于表达用户的意图,主要目的是从用户的问题中识别关键信息,方便后续用于查询。

from typing import Optional

from langchain_core.pydantic_v1 import BaseModel, Field


# 下面定义两个字段,提取用户视频搜索需求 和 视频发布年份
class Search(BaseModel):
    """搜索关于软件库的教程视频的数据库。"""

    query: str = Field(
        ...,
        description="应用于视频转录的相似度搜索查询。",
    )
    publish_year: Optional[int] = Field(None, description="视频发布年份")

分析用户意图

要将用户的问题转换为结构化查询(分析用户意图的过程),我们将利用 OpenAI 的工具调用 API。具体来说,我们将使用新的 ChatModel.with_structured_output() 构造函数来处理将模式传递给模型并解析输出。

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI

# 定义prompt 提示词
system = """你擅长将用户问题转换为数据库查询。你可以访问一个关于构建 LLM 强化应用软件库的教程视频数据库。给定一个问题,返回一个经过优化以检索最相关结果的数据库查询列表。

如果有缩写或您不熟悉的词语,请不要试图重新表达它们。"""
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{question}"),
    ]
)

# 定义模型
llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
# 绑定前面定义的输出数据格式,意思就是要求模型根据类结构返回数据。
structured_llm = llm.with_structured_output(Search)
# 定义chain
query_analyzer = {"question": RunnablePassthrough()} | prompt | structured_llm

下面测试下用户的意图分析效果:

query_analyzer.invoke("如何构建 RAG 代理")

下面是分析出来的结果,没有识别出发布时间

Search(query='构建 RAG 代理', publish_year=None)
query_analyzer.invoke("2023 年发布的 RAG 视频")

下面识别出了,需要查询2023年的视频数据,用户查询需求是:RAG

Search(query='RAG', publish_year=2023)

查询分析与检索

下面我们使用前面查询分析结果(用户意图识别),进行向量相似度查询

from typing import List

from langchain_core.documents import Document
def retrieval(search: Search) -> List[Document]:
    # 根据Search用户搜索意图识别结果,拼接向量查询条件, 下面就把时间条件拼接上去
    if search.publish_year is not None:
        _filter = {"publish_year": {"$eq": search.publish_year}}
    else:
        _filter = None
    return vectorstore.similarity_search(search.query, filter=_filter)
# 定义一个向量数据查询的chain
retrieval_chain = query_analyzer | retrieval

现在,我们可以再次查询数据,测试一下

results = retrieval_chain.invoke("2023 年发布的 RAG 教程")
[(doc.metadata["title"], doc.metadata["publish_date"]) for doc in results]

这里,返回的数据就是2023年的数据

[('多模态 LLM 入门指南', '2023-12-20 00:00:00'),
 ('LangServe 和 LangChain 模板网络研讨会', '2023-11-02 00:00:00'),
 ('多模态 LLM 入门指南', '2023-12-20 00:00:00'),
 ('从零开始构建研究助理', '2023-11-16 00:00:00')]


关联主题