Skip to main content

构建一个查询分析系统

本页面将展示如何在一个基本的端到端示例中使用查询分析。这将涵盖创建一个简单的搜索引擎,展示将原始用户问题传递给搜索引擎时出现的故障模式,以及查询分析如何帮助解决这个问题的示例。有很多不同的查询分析技术,这个端到端示例不会展示所有的技术。

在这个示例中,我们将在 LangChain 的 YouTube 视频上进行检索。

设置

安装依赖

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

设置环境变量

我们将在这个示例中使用 OpenAI:

import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass()
# 可选的,取消注释以使用 LangSmith 进行跟踪运行。在这里注册:https://smith.langchain.com。
# os.environ["LANGCHAIN_TRACING_V2"] = "true"
# os.environ["LANGCHAIN_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"

索引文档

每当我们进行检索时,我们需要创建一个文档索引,以便进行查询。我们将使用一个向量存储来索引我们的文档,并且我们将首先对它们进行分块,以使我们的检索更简洁和准确:

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
chunked_docs = text_splitter.split_documents(docs)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
chunked_docs,
embeddings,
)

无查询分析的检索

我们可以直接对用户问题执行相似性搜索,以找到与问题相关的片段:

search_results = vectorstore.similarity_search("如何构建 RAG 代理")
print(search_results[0].metadata["title"])
print(search_results[0].page_content[:500])
使用 Pinecone 无服务器构建和部署 RAG 应用
嗨,我是 Lang 链团队的 Lance,今天我们将从头开始构建和部署一个 rag 应用,使用 Pinecone 无服务器列表,所以我们将逐步讲解完成此操作所需的所有代码,并使用这些幻灯片作为指南,为我们奠定基础。首先,什么是 rag?所以 Under 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 年的视频),与输入不太相关。由于我们只是针对文档内容进行搜索,所以无法根据任何文档属性对结果进行过滤。

这只是可能出现的一个失败模式。现在让我们看看如何通过基本形式的查询分析来解决这个问题!

查询分析

我们可以使用查询分析来改善检索结果。这将涉及定义一个包含一些日期过滤器的查询模式,并使用一个函数调用模型将用户问题转换为结构化查询。

查询模式

在这种情况下,我们将为发布日期明确定义最小和最大属性,以便可以进行过滤。

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
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)
query_analyzer = {"question": RunnablePassthrough()} | prompt | structured_llm
/Users/bagatur/langchain/libs/core/langchain_core/_api/beta_decorator.py:86: LangChainBetaWarning: 函数 `with_structured_output` 处于测试阶段。它正在积极开发中,因此 API 可能会更改。
warn_beta(

让我们看看我们的分析器为我们之前搜索的问题生成了什么查询:

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

使用查询分析进行检索

我们的查询分析看起来相当不错;现在让我们尝试使用我们生成的查询来实际执行检索。

注意: 在我们的示例中,我们指定了 tool_choice="Search"。这将强制 LLM 调用一个 - 仅一个 - 工具,这意味着我们将始终有一个优化的查询来查找。请注意,情况并非总是如此 - 请参阅其他指南,了解如何处理未返回或返回多个优化查询的情况。

from typing import List
from langchain_core.documents import Document
def retrieval(search: Search) -> List[Document]:
if search.publish_year is not None:
# 这是特定于 Chroma 的语法,
# 我们正在使用的向量数据库。
_filter = {"publish_year": {"$eq": search.publish_year}}
else:
_filter = None
return vectorstore.similarity_search(search.query, filter=_filter)
retrieval_chain = query_analyzer | retrieval

现在,我们可以对之前的问题输入运行此链,看到它只产生了那一年的结果!

results = retrieval_chain.invoke("RAG tutorial published in 2023")
[(doc.metadata["title"], doc.metadata["publish_date"]) for doc in results]
[('Getting Started with Multi-Modal LLMs', '2023-12-20 00:00:00'),
('LangServe and LangChain Templates Webinar', '2023-11-02 00:00:00'),
('Getting Started with Multi-Modal LLMs', '2023-12-20 00:00:00'),
('Building a Research Assistant from Scratch', '2023-11-16 00:00:00')]

Was this page helpful?


You can leave detailed feedback on GitHub.