Learn
Langchain.js/21-vector-stores-advanced

向量库进阶:自查询与混合检索

第 10 章的向量检索只看「语义相似」。但用户常隐含过滤条件,例如「2024 年的产品文档」「价格低于 100 的手机」。本章介绍两种进阶检索能力。

1. SelfQueryRetriever:自然语言转元数据过滤

它让 LLM 把问题拆成两部分:①语义查询向量,②结构化过滤条件(如 year == 2024)。检索时同时生效:

import { SelfQueryRetriever } from "langchain/retrievers/self_query";
import { Metalimiter } from "langchain/retrievers/self_query/metdata";
import { OpenAIEmbeddings } from "@langchain/openai";
 
const attributeInfo = [
  { name: "year", type: "integer", description: "文档年份" },
  { name: "category", type: "string", description: "所属分类" },
];
 
const selfQuery = await SelfQueryRetriever.fromLLM(
  new ChatOpenAI({ model: "gpt-4o-mini" }),
  vectorStore,
  attributeInfo,
  "公司知识库",
  { k: 4 }
);
 
const docs = await selfQuery.getRelevantDocuments("2024 年关于定价的文档");
// 内部生成:query=定价, filter={ year: 2024 }
ℹ️先有元数据

自查询依赖文档写入时已带 metadata(year、category 等)。建库时务必在 addDocuments 时附上结构化字段。

2. EnsembleRetriever:混合检索融合

纯向量检索对「专有名词、编号、精确关键词」不敏感。混合检索把「向量语义」与「BM25 关键词」结果融合,取长补短:

import { EnsembleRetriever } from "langchain/retrievers/ensemble";
import { BM25Retriever } from "langchain/retrievers/bm25";
 
const bm25 = BM25Retriever.fromDocuments(docs, { k: 4 });
const vector = vectorStore.asRetriever({ k: 4 });
 
const hybrid = new EnsembleRetriever({
  retrievers: [bm25, vector],
  weights: [0.5, 0.5], // 两种结果各占一半
});
 
const docs = await hybrid.getRelevantDocuments("接口返回 500 怎么办");
💡权重怎么调

专有名词/错误码多的语料 → 调高 BM25 权重;长尾语义问题多 → 调高向量权重。用第 14 章回调对比命中率再定。

3. 对比选型

检索方式擅长短板
纯向量语义泛化精确词/编号弱
SelfQuery带过滤的语义依赖元数据质量
混合(Ensemble)语义+关键词实现稍复杂
🎯练习

给文档补上 year 元数据后用 SelfQueryRetriever 查询「去年发布的接口文档」,观察生成的过滤条件是否符合预期。

小结

  • SelfQueryRetriever 用 LLM 把问题拆成语义查询 + 元数据过滤
  • EnsembleRetriever 融合 BM25 与向量,实现混合检索
  • 自查询强依赖建库时的结构化元数据
  • 混合检索兼顾语义泛化与精确关键词
  • 下章进入 LangGraph,编排多步状态图 →