LangChain SelfQueryRetriever:让模型自己写过滤条件

个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <

文章目录

LangChain SelfQueryRetriever:让模型自己写过滤条件

用户问"2024 年技术部发布的制度有哪些?"------

这句话里既有语义(制度),又有结构化条件(year=2024, dept=技术部)。

纯向量检索会把"2024""技术部"也当成语义去匹配,效果很差。

SelfQueryRetriever 让模型先把这句话拆成"语义查询 + 过滤条件",再分别执行。

一、问题长什么样

先看纯向量检索的失败案例:

python 复制代码
vectorstore.similarity_search("2024 年技术部发布的制度", k=4)

问题在于,"2024""技术部"这些词在向量空间里和"制度""流程"很近 ,

结果召回了:

  • 2022 年的制度(因为都是"制度");
  • 市场部的文件(因为都提到"部门");
  • 2024 年的预算表(因为都有"2024")。

用户要的"且"关系,向量检索做不到。

正确做法是把它拆成:

复制代码
语义查询(交给向量):制度 流程规范
过滤条件(交给数据库):year == 2024 AND dept == "技术部"

SelfQueryRetriever 就是自动做这个拆分的。

二、上手

python 复制代码
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_openai import ChatOpenAI

# 1. 声明 metadata 字段(关键)
metadata_field_info = [
    AttributeInfo(
        name="year",
        description="文档发布年份,整数,如 2022 / 2023 / 2024",
        type="integer",
    ),
    AttributeInfo(
        name="dept",
        description="发布部门,如 技术部 / 市场部 / 人力资源部",
        type="string",
    ),
    AttributeInfo(
        name="doc_type",
        description="文档类型:制度 / 通知 / 手册",
        type="string",
    ),
]

document_content_description = "公司内部制度与通知文档"

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

retriever = SelfQueryRetriever.from_llm(
    llm,
    vectorstore,
    document_content_description,
    metadata_field_info,
    enable_limit=True,        # 支持"前 N 条"这类表达
)

results = retriever.invoke("2024 年技术部发布的制度有哪些?")

模型内部会生成类似这样的结构化查询:

复制代码
query="制度 流程规范"
filter=AND(eq("year", 2024), eq("dept", "技术部"))
limit=None

然后向量库先按 filter 过滤,再在结果里做语义检索。

三、metadata 描述怎么写(最重要)

这一步决定了 SelfQuery 能不能用对。

AttributeInfo 的三个字段都要认真写:

python 复制代码
AttributeInfo(
    name="year",                 # 必须和 metadata 里的 key 完全一致
    description="...",           # 给模型看的说明
    type="integer",              # string / integer / float / boolean
)

3.1 description 的三个要点

① 说清楚取值范围

python 复制代码
# ❌ 太模糊
description="部门"

# ✅ 枚举出来
description="发布部门。可选值:技术部、市场部、人力资源部、财务部"

LLM 不知道你的部门有哪些 ,你不告诉它,它只能猜,

猜错就过滤出空结果。

② 说清楚类型和格式

python 复制代码
AttributeInfo(name="year", description="发布年份", type="integer")
# ✅ 明确是整数,模型才会生成 eq("year", 2024) 而不是 eq("year", "2024年")

③ 说清楚这个字段的用途

python 复制代码
AttributeInfo(
    name="is_active",
    description="该制度是否仍然有效。true=现行有效,false=已废止。"
                "当用户问'现行''有效''最新'时应过滤 true",
    type="boolean",
)

把"用户说什么话时该用这个字段"写进描述,模型判断会更准。

3.2 字段名必须和 metadata 一致

python 复制代码
# 入库时
Document(page_content="...", metadata={"publish_year": 2024})

# 声明时
AttributeInfo(name="publish_year", ...)    # ✅ 必须叫 publish_year

AttributeInfo(name="year", ...)            # ❌ 对不上,过滤失效

这是最常见的低级错误,而且不会报错,只是过滤没生效,很难发现。

四、支持的查询类型

SelfQuery 能生成的过滤器不止等于:

类型 例子
等于 eq("dept", "技术部")
不等于 ne("dept", "技术部")
大于/小于 gt("year", 2023)、lt("amount", 1000)
大于等于/小于等于 gte("year", 2023)
介于 gte("year", 2022) AND lte("year", 2024)
包含 in(["制度", "通知"])
且/或 AND(...)、OR(...)

对应的自然语言:

用户说 生成的过滤
"2024 年的" eq("year", 2024)
"2023 年之后的" gt("year", 2023)
"2022 到 2024 年的" gte("year", 2022) AND lte("year", 2024)
"技术部和市场部的" in(["技术部", "市场部"])
"不是技术部的" ne("dept", "技术部")

五、看看它生成了什么(调试必备)

python 复制代码
from langchain.chains.query_constructor.base import (
    StructuredQueryOutputParser, get_query_constructor_prompt,
)

prompt = get_query_constructor_prompt(
    document_content_description,
    metadata_field_info,
)
output_parser = StructuredQueryOutputParser.from_components()
query_constructor = prompt | llm | output_parser

result = query_constructor.invoke({"query": "2024 年技术部的制度"})
print("语义:", result.query)
print("过滤:", result.filter)
print("限制:", result.limit)

调试 SelfQuery 一定要先看这一步的输出 ------

如果过滤条件生成错了,后面再怎么调都没用,

问题一定出在 AttributeInfo 的描述上。

六、向量库的支持程度

不是所有向量库都支持 SelfQuery。

它需要一个 StructuredQuery 翻译器。

向量库 支持 备注
Chroma ✅ 开箱可用
PGVector ✅ 生产推荐
Elasticsearch ✅ 过滤器能力强
Pinecone / Weaviate / Qdrant ✅ 各有 translator
FAISS ❌ 不支持,原生无 metadata 过滤

如果用 FAISS,SelfQuery 用不了 ------这是很多人踩的坑。

要么换 Chroma/PGVector,要么自己在检索后手动过滤:

python 复制代码
def manual_filter(query: str, filters: dict, k=4):
    docs = vectorstore.similarity_search(query, k=k * 10)
    out = []
    for d in docs:
        if all(d.metadata.get(key) == val for key, val in filters.items()):
            out.append(d)
        if len(out) >= k:
            break
    return out

七、成本与延迟

SelfQuery 每次检索都要多一次 LLM 调用(生成结构化查询)。

方案 额外成本 额外延迟
普通向量检索 --- ---
SelfQuery 1 次 LLM 调用 +0.5~1.5 秒

优化办法:缓存结构化查询。

python 复制代码
from functools import lru_cache

@lru_cache(maxsize=200)
def parse_query(question: str):
    return query_constructor.invoke({"query": question})

相似问题会命中缓存,省掉这次调用。

八、什么时候用

场景 用不用
文档有明确的 metadata(年份、部门、类型、状态) 强烈推荐
用户提问经常带限定词("2024 年的""技术部的") 强烈推荐
metadata 只有 source 一个字段 没必要
用户提问都很模糊 收益有限
用 FAISS 用不了

前提条件:你的 metadata 得有价值。

如果入库时只存了 source,SelfQuery 无从发挥------

回到前面讲过的那句:metadata 要在加载阶段就埋好。

九、常见坑

现象 原因 解法
过滤没生效 字段名和 metadata 不一致 逐个核对
过滤出空结果 描述里没说取值范围,模型猜错 枚举可选值
年份比较出错 type 写成 string 改 type="integer"
FAISS 报错 不支持结构化查询 换 Chroma/PGVector
每次都很慢 多了 LLM 调用 缓存结构化查询
模型不用过滤 描述太简单 说明"用户说什么时该用它"

十、小结

  • SelfQuery 解决"语义 + 结构化条件 "的混合查询,
    纯向量检索做不好"2024 年技术部"这类"且"关系;
  • 核心是 AttributeInfo:字段名要和 metadata 完全一致,
    描述要枚举取值范围、说清类型、说明什么时候用
    ;
  • 支持 eq / ne / gt / lt / gte / lte / in / AND / OR;
  • 调试时先打印生成的结构化查询,问题多半在描述上;
  • ⚠️ FAISS 不支持 (无原生 metadata 过滤),
    要用就换 Chroma 或 PGVector;
  • 每次检索多一次 LLM 调用,用缓存省掉重复解析。

下一篇讲时间加权检索------让最近的记忆更容易被想起。

相关推荐
天天代码码天天2 小时前
让照片动起来:LivePortrait 人像精简版分享,解压即可使用
人工智能
李福春2 小时前
斗破Google ADK Java苍穹-第5境斗灵-凝物通脉-Context与State
java·开发语言·人工智能·java-ee·google adk·深圳同盟·腾讯云架构师技术同盟
u1301302 小时前
GitHub 热榜项目:周榜(2026-10-11)
人工智能·驱动开发·github
空 白II2 小时前
10.11 大语言模型研究简报:Anthropic 披露 Claude Agent 四类越权失序行为
人工智能·语言模型·自然语言处理
具身AGI2 小时前
世界模型改当裁判,物理AI 物理推理 做判据
人工智能·深度学习·机器学习
狂奔蜗牛(bradley)2 小时前
EtherCAT 主站拓扑判定的三级防护:从去抖到振荡检测
人工智能·嵌入式硬件·fpga开发·架构
XMAIPC_Robot2 小时前
工商业储能EMS边缘计算:本地AI负荷预测,彻底摆脱云端依赖
人工智能·fpga开发·机器人·边缘计算·arm+fpga·rk3588+fpga
奈落242 小时前
AI 办公助手选型指南:从聊天问答到主动式工作流,企业办公自动化该怎么挑、边界在哪
人工智能·自动化·工作流·ai 办公
正经人_x2 小时前
学习日记59:PAVL
人工智能·学习·计算机视觉