05LangChain Query Analysis 知识点详解

LangChain Query Analysis 知识点详解

本文档整理自 LangChain 中文网教程:

本文面向初学者,只聚焦知识点和实现思路,不展开环境安装、API Key、Notebook 配置等准备内容。

一、Query Analysis 是什么

1. 是什么

Query Analysis,可以翻译成“查询分析”。

它的核心意思是:

在真正检索之前,先分析用户的问题,把自然语言问题转换成更适合程序执行的查询结构

也就是说,系统不会直接把用户原话拿去做搜索,而是先做一次“问题理解”。

比如用户问:

查找 2023 年发布的 RAG 相关视频

系统不会只是把整句话原样丢给向量数据库,而是会先拆成:

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

这里已经把一个自然语言问题拆成了两个部分:

  • query:真正的搜索主题
  • publish_year:附加过滤条件

2. 解决什么问题

它解决的是这样一种情况:

用户的问题不只是“搜什么”,还包含“怎么筛”

例如用户问题里可能包含:

  • 年份
  • 时间范围
  • 作者
  • 类别
  • 数据来源

如果系统只是做普通向量检索,往往只能理解“主题大概是什么”,却不一定能正确执行这些结构化条件。

3. 为什么重要

因为真实业务里的问题,往往不是纯主题检索,而是“主题 + 条件”的组合。

例如:

  • 2023 年的 RAG 视频
  • 某位作者写过的文档
  • 最近一个月的政策文件
  • 某一类合同模板

这些问题如果不先做查询分析,检索结果通常会“不完全错”,但经常“不够准”。

二、为什么普通向量检索不够

1. 是什么

普通向量检索的典型流程是:

文档转向量 -> 用户问题转向量 -> 计算相似度 -> 返回最相近的文档块

这个流程特别适合找“语义接近”的文本内容。

例如问题:

how do I build a RAG agent

如果某些视频字幕里大量出现:

  • RAG
  • build
  • retrieval
  • agent

那么普通相似度搜索通常能找到不错的结果。

2. 解决什么问题

普通向量检索擅长解决的是:

我想找和这个主题最相关的内容

比如:

  • 什么是 RAG
  • 如何构建 RAG
  • 多模态 LLM 教程

这些问题主要关注“主题相关性”。

3. 为什么它会失效

如果用户问题变成:

videos on RAG published in 2023

这里就不只是语义问题了,而是同时包含:

  1. 主题:RAG
  2. 条件:published in 2023

向量检索会优先关注整句文本和文档正文的语义接近程度,但它不会天然把:

published in 2023

理解成一个必须执行的“年份过滤条件”。

所以可能出现这种现象:

  • 找到的内容确实和 RAG 有关
  • 但返回的视频是 2024 年发布的

这就说明系统只做了“语义匹配”,没有做“结构化筛选”。

三、这篇教程的核心思路

1. 是什么

教程的核心思路可以概括成一句话:

先分析问题,再检索

更完整一点的流程是:

用户问题 -> 查询分析 -> 结构化查询 -> 检索执行 -> 返回结果

2. 解决什么问题

这样做以后,系统就不会只拿用户原话做搜索,而是会先问自己:

  • 主题到底是什么
  • 有没有年份条件
  • 有没有别的过滤条件

然后再决定应该怎么检索。

3. 为什么这比直接检索更稳

因为程序执行检索时,最怕的是“信息混在一起”。

例如:

查找 2023 年发布的 RAG 相关视频

如果把这整句话原样拿去搜,程序并不知道:

  • 哪部分是主题
  • 哪部分是过滤条件

但如果先分析成:

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

后续代码就很清楚:

  • query 去做相似度搜索
  • publish_year 去做元数据过滤

这就是查询分析真正提升效果的地方。

四、文档加载阶段

1. 是什么

教程先使用 YouTubeLoader 加载一批 LangChain 官方 YouTube 视频的字幕内容。

示例代码的核心逻辑是:

docs = []
for url in urls:
    docs.extend(YoutubeLoader.from_youtube_url(url, add_video_info=True).load())

这里得到的 docs 不是普通字符串列表,而是 LangChain 的 Document 对象列表。

每个 Document 通常包含两部分:

  • page_content:正文内容
  • metadata:元数据

2. 解决什么问题

这一阶段解决的是“数据从哪里来”的问题。

因为检索系统必须先有可搜索的数据,后面才能谈 embedding、向量库、过滤、查询分析。

3. 为什么不只加载正文

教程特意打开了:

add_video_info=True

这样除了视频字幕正文之外,还能拿到一些元数据,例如:

  • title
  • publish_date
  • author
  • view_count
  • length

这些元数据非常重要,因为:

  1. 它们可以用于展示结果
  2. 它们可以作为过滤条件
  3. 它们为查询分析提供了“可被筛选的字段”

如果只有正文,没有元数据,那么后面就很难做“按年份筛选”这种事情。

五、metadata 是什么

1. 是什么

metadata 可以理解成“文档的附加说明信息”。

正文是视频说了什么,metadata 是这个视频本身的一些属性。

例如:

{
    "title": "OpenGPTs",
    "publish_date": "2024-01-31 00:00:00",
    "author": "LangChain",
    "length": 1530
}

2. 解决什么问题

metadata 解决的是:

正文只能回答“内容相关吗”,metadata 才能回答“它属于哪一类”

例如:

  • 这篇文档是谁写的
  • 这个视频是哪一年发布的
  • 这条记录属于什么类型

3. 为什么在 Query Analysis 里尤其重要

因为查询分析的目标之一,就是识别用户问题中的过滤条件。

而过滤条件最终必须落在某个具体字段上。

比如:

  • 2023 年 最后要落到 publish_year
  • 某位作者 最后要落到 author

所以没有 metadata,查询分析只能“分析出来”,却没地方真正执行。

六、publish_year 为什么要单独提取

1. 是什么

教程虽然已经拿到了 publish_date,但还是额外添加了:

doc.metadata["publish_year"]

它是从完整发布日期里拆出来的年份整数。

例如:

2024-01-31 00:00:00 -> 2024

2. 解决什么问题

它解决的是“日期字段不方便直接过滤”的问题。

很多时候,用户不是问:

2024-01-31 发布的视频

而是问:

2024 年发布的视频

如果直接拿完整日期字符串去比较,会比较麻烦。
而提前拆出 publish_year 之后,过滤条件就变得很简单。

3. 为什么这一点很重要

因为它体现了一个很重要的工程思想:

为了后面的检索和过滤更简单,前面可以先把数据处理成更适合检索的形态

也就是说,查询分析并不只是模型层面的事,数据预处理同样重要。

七、向量索引是什么

1. 是什么

教程接下来建立了一个向量索引,过程包括:

  1. 切分文本
  2. 生成 embedding
  3. 把向量和文档存进 Chroma

2. 解决什么问题

它解决的是“怎么让文档可搜索”的问题。

如果只是把文档放在普通列表里,程序并不知道:

  • 哪段文本和问题最接近
  • 哪些内容应该排在前面

向量索引让系统可以基于语义相似度找到最相关的文本块。

3. 为什么要先切块

教程用了:

RecursiveCharacterTextSplitter(chunk_size=2000)

这是因为视频字幕通常很长,如果整篇作为一个向量,会带来几个问题:

  1. 语义太分散
  2. 用户问题往往只对应其中一小段
  3. 检索结果太粗

切块之后,每块内容更集中,检索粒度更细,命中效果通常更好。

八、Embedding 是什么

1. 是什么

Embedding 可以理解成“把文本转换成一串能表示语义的数字”。

在教程里使用的是:

OpenAIEmbeddings(model="text-embedding-3-small")

2. 解决什么问题

它解决的是:

如何让程序比较两段文本在语义上是否接近

人类看得懂“这两段话都在讲 RAG”,但程序直接看字符串是做不到的。
Embedding 把文本转换成向量后,程序就能计算相似度。

3. 为什么它不是“生成答案”

很多初学者会把 embedding 模型和聊天模型混淆。

其实两者职责不同:

  • 聊天模型:负责理解、生成、回答
  • embedding 模型:负责把文本变成向量

Query Analysis 教程里,两种模型都在发挥作用,但作用完全不同。

九、Chroma 是什么

1. 是什么

Chroma 是教程中使用的向量数据库。

它的作用是保存:

  • 文本块
  • 向量
  • 元数据

2. 解决什么问题

它解决的是:

如何把“文档 + 向量 + 元数据”组织成一个可查询的索引

这样后面就可以做两类操作:

  1. 相似度检索
  2. 基于 metadata 的过滤

3. 为什么它在这篇教程里特别合适

因为这篇教程既要做:

  • 语义搜索

又要做:

  • publish_year 过滤

Chroma 正好支持这种方式:

vectorstore.similarity_search(query, filter=_filter)

所以它很适合拿来演示“查询分析 + 元数据过滤”的组合思路。

十、Search 模型是什么

1. 是什么

教程定义了一个 Pydantic 模型:

class Search(BaseModel):
    query: str
    publish_year: Optional[int]

这就是本教程最核心的数据结构之一。

2. 解决什么问题

它解决的是“如何把用户问题变成结构化查询”的问题。

如果没有这个结构,模型可能会返回一大段自然语言解释,后面的程序很难稳定处理。

有了这个结构之后,模型输出会更像:

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

程序就可以明确知道每个字段分别是什么意思。

3. 为什么只设计两个字段

因为这篇教程是入门示例,它只想演示最基本的思想:

  • query 用于语义搜索
  • publish_year 用于年份过滤

这两个字段已经足够把“Query Analysis 的核心价值”展示出来。

后面如果你做更复杂的系统,还可以继续增加字段,例如:

  • author
  • start_year
  • end_year
  • topic

十一、with_structured_output 是什么

1. 是什么

教程使用了:

llm.with_structured_output(Search)

它的意思可以简单理解成:

让模型尽量按 Search 这个结构返回结果

2. 解决什么问题

它解决的是“大模型输出格式不稳定”的问题。

如果没有结构化输出,模型可能会返回:

  • 一段自然语言说明
  • 一个半结构化句子
  • 格式不固定的文本

这样后面的代码就很难稳定处理。

3. 为什么它在 Query Analysis 中很关键

因为 Query Analysis 的重点不是“让模型自由发挥”,而是“让模型把问题整理成程序能消费的格式”。

所以结构化输出几乎是这篇教程的关键桥梁:

  • 前面连接自然语言问题
  • 后面连接检索函数

十二、查询生成链是什么

1. 是什么

教程用下面这些组件构成了一个“查询生成链”:

  • ChatPromptTemplate
  • RunnablePassthrough
  • ChatOpenAI
  • with_structured_output(Search)

最后形成:

query_analyzer = {"question": RunnablePassthrough()} | prompt | structured_llm

2. 解决什么问题

它解决的是:

如何把用户原始问题,自动转换成 Search 结构

也就是说,用户输入的是一句自然语言,链输出的是一个结构化对象。

3. 为什么这一步很重要

因为这一步相当于在“用户”和“检索系统”之间加了一层翻译器。

用户只需要自然地提问,而系统内部会自动把问题整理成:

  • 搜索主题
  • 过滤条件

这正是 Query Analysis 的核心落地点。

十三、retrieval 函数是什么

1. 是什么

教程最后定义了:

def retrieval(search: Search) -> List[Document]:
    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)

2. 解决什么问题

它解决的是:

如何把结构化查询真正执行到向量数据库上

也就是说,前面模型已经分析出了:

  • 主题是什么
  • 条件是什么

现在这一步要做的是把这些分析结果真正变成检索行为。

3. 为什么它是整篇教程最关键的落地代码

因为只有写到这里,查询分析才不是“纸上谈兵”。

前面的代码更多是在做准备:

  • 读文档
  • 建索引
  • 定义结构
  • 生成结构化输出

retrieval() 才真正决定:

  • query 怎么用于搜索
  • publish_year 怎么用于过滤

它把“问题分析”连接到了“实际检索”。

十四、filter 是什么

1. 是什么

教程中的过滤条件写法是:

{"publish_year": {"$eq": search.publish_year}}

这是 Chroma 支持的一种过滤语法。

其中:

  • publish_year 是元数据字段名
  • $eq 表示等于

2. 解决什么问题

它解决的是:

只在满足某个 metadata 条件的文档里做检索

也就是说,不是先搜全库再人工看年份,而是系统在检索阶段就把不符合条件的文档排除掉。

3. 为什么这比“只做语义搜索”更准确

因为它把“主题相关”和“条件满足”同时考虑进来了。

最终系统不只是找:

  • 最像 RAG 的内容

而是找:

  • 最像 RAG,并且年份等于 2023 的内容

这才真正符合用户原始意图。

十五、retrieval_chain 是什么

1. 是什么

教程把查询分析器和检索函数串起来:

retrieval_chain = query_analyzer | retrieval

这个链的含义可以理解成:

先分析问题,再执行检索

2. 解决什么问题

它解决的是“把多个步骤拼成一个完整工作流”的问题。

用户只需要输入一句问题,系统内部就会自动完成:

  1. 问题分析
  2. 结构化查询生成
  3. 检索执行

3. 为什么这种写法适合初学者理解

因为它把整个流程串得非常清楚。

你可以把它看成:

自然语言问题 -> Search 对象 -> 检索结果

这比一堆零散函数更容易帮助初学者理解整体链路。

十六、这篇教程最核心的思想

1. 是什么

整篇教程真正想表达的核心思想是:

检索效果不仅取决于向量库和 embedding,也取决于用户问题是否先被正确翻译成检索请求

2. 解决什么问题

它解决的是很多初学者的一个误区:

只要向量库搭好了,检索自然就会准

实际上并不是这样。

用户问题如果本身没有被拆解清楚,即使向量库很好,检索也还是会偏。

3. 为什么这是学习 RAG 很重要的一课

因为它提醒你:

  • RAG 不只是“建向量库”
  • 检索前的问题理解也非常关键

也就是说,一个好的检索系统,往往不只是:

  • 文档切块
  • embedding
  • 向量搜索

还包括:

  • 查询重写
  • 条件抽取
  • 元数据过滤

十七、和你当前 analysis.py 的对应关系

1. 是什么

你当前写的 analysis.py,本质上就是在复现这篇教程的主线结构。

2. 对应关系

可以大致这样对应:

  • 加载视频字幕和元数据
    对应 YoutubeLoader

  • 增加 publish_year
    对应元数据预处理

  • 切分文档、生成向量、建立 Chroma
    对应向量索引阶段

  • 普通 similarity_search(...)
    对应“无查询分析的检索”

  • class Search(BaseModel)
    对应结构化查询模式

  • with_structured_output(Search)
    对应查询分析输出

  • retrieval(search: Search)
    对应真正的过滤检索逻辑

3. 为什么这对你有帮助

因为你现在不是在零散地学几个 LangChain API,而是在学一个完整思路:

当用户问题里含有结构化条件时,先分析问题,再检索

这会让你以后学:

  • Self Query Retriever
  • 多查询生成
  • 复杂 metadata 过滤
  • 完整 RAG 链

都更容易理解。

十八、初学者最该记住的结论

1. 结论一

用户原问题不一定适合直接拿去检索。

2. 结论二

普通向量检索主要擅长“主题匹配”,不天然擅长“条件过滤”。

3. 结论三

Query Analysis 的本质,是把自然语言问题变成结构化查询。

4. 结论四

结构化查询里的不同字段,通常承担不同职责:

  • query 负责语义搜索
  • publish_year 负责 metadata 过滤

5. 结论五

更稳的检索通常不是只靠一个组件,而是:

语义搜索 + 元数据过滤 + 查询分析

一起工作。

十九、你接下来可以怎么继续学

1. 先做什么

先把你当前的 analysis.py 跑通,并且确认自己能看懂这几部分:

  • 文档加载
  • metadata 增加
  • 向量索引
  • 普通检索
  • 查询分析
  • 过滤检索

2. 再做什么

建议你自己尝试修改问题,例如:

  • 查找 2024 年的 RAG 视频
  • 查找多模态相关视频
  • 查找 SQL 相关教程
  • 查找 2023 年的多模态教程

这样你会更直观地观察:

  • 普通检索什么时候够用
  • 查询分析什么时候明显更有价值

3. 最后再学什么

等你熟悉这篇教程之后,可以继续往这些方向学:

  • 多条件过滤
  • 多查询生成
  • Self Query Retriever
  • 把查询分析接入完整 RAG 回答链

二十、一句话总结

这篇教程真正要教你的不是某几个 API,而是一个非常重要的检索思想:

当用户问题里既有“搜索主题”,又有“结构化条件”时,不要直接把原问题丢给向量库,而应该先做查询分析,再执行“语义搜索 + 元数据过滤”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

终生成长者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值