自查询检索 Self-Querying:结构化过滤与语义的融合

FreeGuideOnline 最新 2026-06-13

什么是自查询检索?

自查询检索(Self-Querying Retrieval)是一种将结构化过滤语义搜索相结合的信息检索技术。它的核心思想是让语言模型在理解用户自然语言查询的同时,自动抽取出可用于数据库过滤的结构化条件,从而在不牺牲语义匹配质量的前提下,大幅提升检索的精准度和效率。

传统的关键词搜索引擎只能处理显式的字段匹配,而纯粹的向量语义检索又忽略了元数据(如日期、类别、价格区间)。自查询检索正好弥补了两者的不足:它先用 LLM 解析出用户意图中的“过滤器”(filter),再在该过滤器的范围内进行向量相似度搜索,最终返回既语义相关又严格符合约束条件的结果。

为什么需要自查询检索?

在面对大规模、多类型的数据时,单纯依赖语义向量相似度会遇到几个痛点:

  • 冷启动问题:对于全新或冷门内容,向量表示可能不够精准,返回大量无关结果。
  • 结构化条件缺失:用户说“去年发布的 Python 异步教程”,纯语义搜索无法理解“去年”这一时间条件。
  • 多维度过滤困难:当查询涉及价格、评分、地理位置等多个维度时,手工构造多重过滤器既繁琐又容易出错。

自查询检索让系统自动将自然语言转换为查询过滤条件,用户只需像平常说话一样提问,就能得到精确的范围结果。

自查询检索的工作原理

整个流程可以分为四个阶段:

  1. 查询理解与过滤提取 用户输入自然语言查询,如:“推荐几本 2023 年出版、评分高于 4.5 的 Python 入门书”。LLM 提取出结构化过滤器:

    • 出版年份 = 2023
    • 评分 > 4.5
    • 标签包含 “Python” 和 “入门”
  2. 源数据过滤 利用提取出的过滤器对数据库(比如支持元数据过滤的向量数据库)执行 filter 操作,大幅缩小候选集。

  3. 语义检索 在过滤后的数据集上,使用查询的语义向量进行相似度搜索,找到最相关的记录。

  4. 结果合成与返回 将检索到的文档(可能还包括原始查询的解释)一起返回给用户或下游 LLM 进行回答生成。

自查询检索的典型实现方式

目前主流的自查询检索通常借助 LangChain 等框架快速搭建,但底层原理是通用的。下面以 LangChain 提供的 SelfQueryRetriever 为例,展示核心步骤。

基础环境与数据准备

假设我们有一批视频教程的元数据,结构如下:

from langchain.schema import Document

docs = [
    Document(
        page_content="本教程深入讲解 Python 异步编程,包含大量实战案例。",
        metadata={
            "title": "Python 异步实战",
            "year": 2023,
            "rating": 4.8,
            "language": "中文"
        }
    ),
    Document(
        page_content="面向初学者的 Python 基础入门,从零开始。",
        metadata={
            "title": "Python 零基础入门",
            "year": 2022,
            "rating": 4.2,
            "language": "中文"
        }
    ),
    # 更多文档...
]

这些文档需要存入向量数据库(如 Chroma、Pinecone、Weaviate),并确保向量数据库支持元数据字段的筛选。

构建自查询检索器

from langchain.llms import OpenAI
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

# 描述元数据字段,帮助 LLM 理解可用的过滤条件
metadata_field_info = [
    AttributeInfo(
        name="title",
        description="视频教程的标题",
        type="string",
    ),
    AttributeInfo(
        name="year",
        description="发布年份",
        type="integer",
    ),
    AttributeInfo(
        name="rating",
        description="用户评分,0-5 之间的浮点数",
        type="float",
    ),
    AttributeInfo(
        name="language",
        description="视频语言",
        type="string",
    ),
]

# 描述文档的主要内容(用于生成语义查询)
document_content_description = "编程教程的详细内容描述"

# 初始化 LLM 和向量存储
llm = OpenAI(temperature=0)
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)

# 创建自查询检索器
retriever = SelfQueryRetriever.from_llm(
    llm,
    vectorstore,
    document_content_description,
    metadata_field_info,
    verbose=True
)

执行查询

现在可以直接用自然语言查询,检索器会自动拆解过滤器并执行语义搜索。

# 查询:找 2023 年高分(>4.5)的中文 Python 教程
result = retriever.get_relevant_documents(
    "推荐 2023 年发布,评分高于 4.5 的中文 Python 教程"
)
for doc in result:
    print(doc.metadata["title"])

在此过程中,LLM 会构造类似以下结构的查询对象:

{
    "query": "Python 教程",
    "filter": "and(gt('rating', 4.5), eq('year', 2023), eq('language', '中文'))"
}

然后向量数据库先应用过滤器,再在过滤后的集合中执行 query 的语义搜索。

设计高效自查询检索的关键点

精心定义元数据字段描述

metadata_field_info 的质量直接决定 LLM 能否准确生成过滤器。每个字段的 description 要清晰、具体,避免歧义。例如,不要只写 "year",而要写明 "发布年份"

简化过滤逻辑

尽量避免要求 LLM 生成极其复杂的嵌套逻辑(如多层 AND/OR 组合)。如果业务需要,可以后置用程序对原始过滤器再进行组合,而不是完全依赖 LLM。

选择合适的向量数据库

不是所有向量库都支持原生结构化过滤。常用的支持方案包括:

  • Chroma:支持基本的 where 过滤。
  • Weaviate:GraphQL 接口,支持丰富的操作符(GreaterThanLike 等)。
  • Pinecone:通过元数据过滤实现。
  • Milvus:具备标量过滤能力。

选择时请确认其过滤器表述语言与自查询检索框架的兼容性。

异常处理与兜底

当 LLM 无法提取有效过滤器时(例如查询模糊或超出字段范围),自查询检索器可能会降级为纯语义搜索。可以在代码中加入兜底逻辑,保证系统在任何情况下都能返回结果,即使只是纯语义相关的结果。

常见应用场景

  • 电商搜索:用户说“500 元以内的蓝牙耳机,降噪好”,自查询检索会过滤价格 < 500,再语义匹配“降噪好”。
  • 知识库问答:用户问“去年第三季度上线的功能有哪些?”,系统筛选时间范围后再搜索文档。
  • 内容推荐:“评分 4 星以上、时长 10-20 分钟的健身视频”,多条件过滤 + 语义理解。
  • 招聘系统:自动解析“3 年以上经验,本科以上,Python 后端”等条件。

自查询检索 vs. 其他检索增强技术

方法 优点 缺点
关键词检索 (BM25) 速度快,对精确词匹配好 不理解语义,无法处理同义词
纯语义检索 (向量) 理解语义,容错性强 忽略结构化约束,冷门内容召回差
手工过滤 + 语义搜索 精准可控 需要用户或开发者构造复杂查询
自查询检索 全自动,平衡语义与结构,用户体验好 依赖 LLM 解析准确性,复杂嵌套逻辑可能偏差

自查询检索特别适合用户无需了解底层数据结构,但希望得到精确结果的场景。

调试与优化建议

  1. 打印中间查询结构:在开发阶段开启 verbose=True,观察 LLM 生成的过滤条件是否符合预期。
  2. 使用小样本提示(Few-shot Examples):如果 LLM 总是误解某些字段,可以在 SelfQueryRetriever 中提供示例来引导。
  3. 限制元数据字段数量:只暴露真正需要过滤的字段,避免 LLM 混淆无关信息。
  4. 后验证过滤器:收到过滤器后,可用正则或解析器检查是否合法,过滤掉无效条件。

总结

自查询检索通过将自然语言理解与数据库过滤深度结合,为现代信息检索提供了一种优雅且强大的范式。它让“像对话一样搜索”成为可能,用户在表达复杂需求时不再需要学习专用查询语法。对于希望构建智能、精准且对用户友好的搜索体验的开发者来说,掌握自查询检索是重要的一步。

现在,你可以在自己的项目中尝试引入自查询检索,用更少的代码实现更聪明的搜索。