惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
腾讯CDC
V
V2EX
Martin Fowler
Martin Fowler
A
About on SuperTechFans
大猫的无限游戏
大猫的无限游戏
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
博客园 - 【当耐特】
Cisco Talos Blog
Cisco Talos Blog
The Hacker News
The Hacker News
K
Kaspersky official blog
Security Latest
Security Latest
H
Help Net Security
博客园_首页
美团技术团队
Spread Privacy
Spread Privacy
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
G
Google Developers Blog
NISL@THU
NISL@THU
爱范儿
爱范儿
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
N
News and Events Feed by Topic
P
Privacy International News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
雷峰网
雷峰网
人人都是产品经理
人人都是产品经理
V
Vulnerabilities – Threatpost
W
WeLiveSecurity
P
Palo Alto Networks Blog
G
GRAHAM CLULEY
Hacker News: Ask HN
Hacker News: Ask HN
I
InfoQ
The Cloudflare Blog
F
Full Disclosure
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
N
Netflix TechBlog - Medium

博客园 - 沐子馨

Agent Loop:从对话式 AI 到自主智能体的范式跃迁 解锁图数据建模的奥秘 基于 Neo4j 与 Milvus 的图RAG系统搭建指南 解决复杂推理难题:KG-RAG 在大模型中的应用 RAG 评估常用工具简介 RAG系统效果不好?一文看懂如何进行系统评估 写出让 AI “秒懂”的技能Skill 深入理解 RAG 中的格式化生成与函数调用 解锁 RAG 系统中的高级检索与重排序策略 掌握查询重构与智能路由的艺术 告别黑盒:手把手实现一个可解释、可调试的 Text2SQL 代理系统 深入理解 RAG 中的混合搜索策略 告别基础检索:掌握 RAG 中的句子窗口与递归路由策略 构建多模态检索系统:Milvus 部署、Schema 设计与混合检索实践 向量数据库原理与实战:从核心机制到 FAISS 应用 多模态向量嵌入:从文本到图像的语义统一与 RAG 应用 构建高效 RAG 的基石:深度解析 Embedding 模型原理与优化 All-in-RAG:解锁大模型“开卷考试”的终极能力 AI Agent 是如何思考的?一文读懂推理与决策引擎 终端革命:AI Agent 正在重新定义命令行 给企业装上“AI 大脑”:AgentSpace 如何从“手动检索”跨越到“智能决策”? Agentic 框架快速概览 AI 智能体交互如何带领它走出对话框,从屏幕像素迈向真实物理世界 高级提示词技巧如何带领大模型走出“一本正经胡说八道”的误区? 当 AI 学会“开疆拓土”:探索与发现模式如何从源头打破静态知识的桎梏,在陌生领域催生新洞见? 拒绝 AI “手忙脚乱”:优先级模式如何为智能体打造毫秒级的“任务调度官”? 构建生产级可信 Agent:评估与监控中的自动化审计与闭环优化机制 当 AI 学会“三思后言”:安全护栏如何从源头掐灭偏见、幻觉与恶意攻击? 拒绝“张口就来”:推理技术如何让 AI 像人类一样拆解复杂难题? 当家方知柴米贵:资源感知优化如何让 AI 智能体告别“算力浪费”? 开启多智能体互联时代:A2A 协议与 AI 协作网络的底层逻辑 为智能体装上“实时百科全书”:RAG 如何打破 AI 的知识边界? 构建负责任的AI:人类监督、干预与反馈闭环机制 当工具失效、网络中断:AI智能体的应急与自修复指南 赋予AI“北极星”:如何让智能体自主设定并追踪目标 打破“M×N”集成魔咒:MCP如何重塑AI应用架构 赋予模型“生命力”:大语言模型如何通过学习实现持续适应 赋予模型“长期记忆”:大语言模型的记忆管理机制 AI的“社会性”:深入理解多智能体协作机制 理解AI智能体的规划机制 理解大模型的工具使用 智能体的元认知:反思模式与自我优化机制 智能体并行化模式:提升复杂工作流性能的关键技术 超越线性执行:使用LLM实现智能体路由模式 解构复杂任务:深入理解提示词链(Prompt Chaining) 别再被 AI 术语唬住了!一文理清你需要了解的所有概念 不止是多写几句:谷歌如何用69页白皮书重塑提示工程 [转]长PDF文档的总结与评估策略 Context 工程:如何把正确的上下文喂给 AI 前端如何写出优秀的 AI Agent Skills mac安装python后command not found: pip keep-alive 原理剖析 如何优雅地在 React 中使用TypeScript,看这一篇就够了! react useContext React Context 详细介绍(状态共享、数据传递) 以用户为中心的性能指标【译】 element下拉框远程搜索debounce防抖控制 vue-router打开新窗口 propmise: allSettled()与all()的区别 使用 new Date() 在chrome、安卓和 IOS 中表现不同
告别简单向量搜索:RAG 中的高级查询构建与优化策略
沐子馨 · 2026-05-23 · via 博客园 - 沐子馨

在实际应用中,我们常常需要处理更加复杂和多样化的数据,包括结构化数据(如SQL数据库)、半结构化数据(如带有元数据的文档)以及图数据。用户的查询也可能不仅仅是简单的语义匹配,而是包含复杂的过滤条件、聚合操作或关系查询。

查询构建(Query Construction)1 正是应对这一挑战的关键技术。它利用大语言模型(LLM)的强大理解能力,将用户的自然语言查询“翻译”成针对特定数据源的结构化查询语言或带有过滤条件的请求。这使得RAG系统能够无缝地连接和利用各种类型的数据,从而极大地扩展了其应用场景和能力。

下图展示了查询构建在一个高级RAG流程中所处的位置:

Advanced RAG Pipeline

一、文本到元数据过滤器

在构建向量索引时,常常会为文档块(Chunks)附加元数据(Metadata),例如文档来源、发布日期、作者、章节、类别等。这些元数据为我们提供了在语义搜索之外进行精确过滤的可能。

自查询检索器(Self-Query Retriever) 是LangChain中实现这一功能的核心组件。它的工作流程如下:

  1. 定义元数据结构:首先,需要向LLM清晰地描述文档内容和每个元数据字段的含义及类型。
  2. 查询解析:当用户输入一个自然语言查询时,自查询检索器会调用LLM,将查询分解为两部分:
    • 查询字符串(Query String):用于进行语义搜索的部分。
    • 元数据过滤器(Metadata Filter):从查询中提取出的结构化过滤条件。
  3. 执行查询:检索器将解析出的查询字符串和元数据过滤器发送给向量数据库,执行一次同时包含语义搜索和元数据过滤的查询。

例如,对于查询“关于2022年发布的机器学习的论文”,自查询检索器会将其解析为:

  • 查询字符串: "机器学习的论文"
  • 元数据过滤器: year == 2022

代码示例

接下来以B站视频为例来看看如何使用SelfQueryRetriever

import os
from langchain_deepseek import ChatDeepSeek 
from langchain_community.document_loaders import BiliBiliLoader
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
import logging

logging.basicConfig(level=logging.INFO)

# 1. 初始化视频数据
video_urls = [
    "https://www.bilibili.com/video/BV1Bo4y1A7FU", 
    "https://www.bilibili.com/video/BV1ug4y157xA",
    "https://www.bilibili.com/video/BV1yh411V7ge",
]

bili = []
try:
    loader = BiliBiliLoader(video_urls=video_urls)
    docs = loader.load()
    
    for doc in docs:
        original = doc.metadata
        
        # 提取基本元数据字段
        metadata = {
            'title': original.get('title', '未知标题'),
            'author': original.get('owner', {}).get('name', '未知作者'),
            'source': original.get('bvid', '未知ID'),
            'view_count': original.get('stat', {}).get('view', 0),
            'length': original.get('duration', 0),
        }
        
        doc.metadata = metadata
        bili.append(doc)
        
except Exception as e:
    print(f"加载BiliBili视频失败: {str(e)}")

if not bili:
    print("没有成功加载任何视频,程序退出")
    exit()

# 2. 创建向量存储
embed_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_documents(bili, embed_model)

在上面的代码中,首先使用 BiliBiliLoader 加载了几个B站视频的文档和元数据。需要注意的是,由于 BiliBiliLoader 返回的原始元数据结构较为复杂(例如,作者和观看数信息嵌套在其他字典中),所以进行了一些预处理工作:遍历每个文档,手动提取需要的字段(如title, author, view_count, length),并构建一个干净、扁平化的新 metadata 字典。这个过程确保了后续的自查询检索器能够直接、可靠地访问这些字段。最后,将处理好的文档和元数据存入 Chroma 向量数据库中,为下一步的查询构建做好准备。

# 3. 配置元数据字段信息
metadata_field_info = [
    AttributeInfo(
        name="title",
        description="视频标题(字符串)",
        type="string", 
    ),
    AttributeInfo(
        name="author",
        description="视频作者(字符串)",
        type="string",
    ),
    AttributeInfo(
        name="view_count",
        description="视频观看次数(整数)",
        type="integer",
    ),
    AttributeInfo(
        name="length",
        description="视频长度,以秒为单位的整数",
        type="integer"
    )
]

# 4. 创建自查询检索器
llm = ChatDeepSeek(
    model="deepseek-chat", 
    temperature=0, 
    api_key=os.getenv("DEEPSEEK_API_KEY")
    )

retriever = SelfQueryRetriever.from_llm(
    llm=llm,
    vectorstore=vectorstore,
    document_contents="记录视频标题、作者、观看次数等信息的视频元数据",
    metadata_field_info=metadata_field_info,
    enable_limit=True,
    verbose=True
)

# 5. 执行查询示例
queries = [
    "时间最短的视频",
    "时长大于600秒的视频"
]

for query in queries:
    print(f"\n--- 查询: '{query}' ---")
    results = retriever.invoke(query)
    if results:
        for doc in results:
            title = doc.metadata.get('title', '未知标题')
            author = doc.metadata.get('author', '未知作者')
            view_count = doc.metadata.get('view_count', '未知')
            length = doc.metadata.get('length', '未知')
            print(f"标题: {title}")
            print(f"作者: {author}")
            print(f"观看次数: {view_count}")
            print(f"时长: {length}秒")
            print("="*50)
    else:
        print("未找到匹配的视频")

这部分代码是实现自查询检索的核心。主要分为三个步骤:

  1. 配置元数据字段 (metadata_field_info) :这是与LLM沟通的蓝图。通过 AttributeInfo 为每个元数据字段定义名称、类型和一份清晰的自然语言 description。LLM 将依赖这份描述来理解如何处理用户的查询,例如,它会根据“视频长度(整数)”的描述来解析关于“时长”的过滤和排序请求。因此,一份准确、无歧义的描述很重要。

  2. 创建自查询检索器 (SelfQueryRetriever.from_llm) :from_llm 方法在底层执行了两个核心操作:

    • 加载查询构造器:利用传入的 llmdocument_contentsmetadata_field_info,创建一个专门的“查询构造链”。这个链的核心职责是将用户的自然语言查询(如“时长大于600秒的视频”)转换为一个通用的、结构化的查询对象。
    • 获取内置翻译器:接着,检查使用的向量数据库(这里是 Chroma),并为其匹配一个内置的“翻译器”。这个翻译器负责将上一步生成的通用查询对象,翻译成 Chroma 数据库能够原生理解和执行的过滤语法。
  3. 执行查询 (retriever.invoke) :最后,用自然语言发起调用。检索器内部会依次执行“构造”和“翻译”两个步骤,最终向 Chroma 发起一个同时包含语义搜索和精确元数据过滤的复合查询,从而返回最相关的结果。

提示:在代码中可以看到 temperature 参数被设置为 0。这个值是用于控制模型输出的随机性。值越高(如 0.8),输出越随机、越有创意;值越低,输出越确定、越集中。设置为 0 可以让模型的输出变得完全确定,即对于相同的输入,总是生成完全相同的输出。在自查询这种需要精确地将自然语言转换为结构化查询的场景下,可以确保转换结果的稳定和可复现。

输出结果:

--- 查询: '时间最短的视频' ---
INFO:httpx:HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO:langchain.retrievers.self_query.base:Generated Query: query=' ' filter=None limit=1
标题: 《吴恩达 x OpenAI Prompt课程》【专业翻译,配套代码笔记】02.Prompt 的构建原则
作者: 二次元的Datawhale
观看次数: 18788
时长: 1063秒
==================================================

--- 查询: '时长大于600秒的视频' ---
INFO:httpx:HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO:langchain.retrievers.self_query.base:Generated Query: query=' ' filter=Comparison(comparator=<Comparator.GT: 'gt'>, attribute='length', value=600) limit=None
WARNING:chromadb.segment.impl.vector.local_hnsw:Number of requested results 4 is greater than number of elements in index 3, updating n_results = 3
标题: 《吴恩达 x OpenAI Prompt课程》【专业翻译,配套代码笔记】03.Prompt如何迭代优化
作者: 二次元的Datawhale
观看次数: 7090
时长: 806秒
==================================================
标题: 《吴恩达 x OpenAI Prompt课程》【专业翻译,配套代码笔记】02.Prompt 的构建原则
作者: 二次元的Datawhale
观看次数: 18788
时长: 1063秒

二、文本到Cypher

除了处理扁平化的元数据,查询构建技术还能应用于更复杂的数据结构,如图数据库。

2.1 什么是 Cypher?

Cypher 是图数据库(如 Neo4j)中最常用的查询语言,其地位类似于 SQL 之于关系数据库。它采用一种直观的方式来匹配图中的模式和关系,例如 (:Person {name:"Tomaz"})-[:LIVES_IN]->(:Country {name:"Slovenia"}) 描述了一个人和一个国家以及他们之间的“居住在”关系。

2.2 “文本到Cypher”的原理

与“文本到元数据过滤器”类似,“文本到Cypher”技术利用大语言模型(LLM)将用户的自然语言问题直接翻译成一句精准的 Cypher 查询语句。LangChain 提供了相应的工具链(如 GraphCypherQAChain),其工作流程通常是:

  1. 接收用户的自然语言问题。
  2. LLM 根据预先提供的图谱模式(Schema),将问题转换为 Cypher 查询。
  3. 在图数据库上执行该查询,获取精确的结构化数据。
  4. (可选)将查询结果再次交由 LLM,生成通顺的自然语言答案。

由于生成有效的 Cypher 查询是一项复杂的任务,通常使用性能较强的 LLM 来确保转换的准确性。通过这种方式,用户可以用最自然的方式与高度结构化的图数据进行交互,极大地降低了数据查询的门槛。

参考文献