你好,我是方可乐。
一个正在深耕AI编程的30+的AI应用开发工程师。
用AI编程开发过Web应用网站,浏览器插件,微信小程序,AI智能体,RAG系统。
关注公众号&添加微信:ThinkFun666
领取【AI编程资料包】
去年6月份,我做了一个连自己都觉得疯狂的决定:裸辞,All in AI。
现在已经换赛道成功,继续在工作中探索。
继上一篇文章的RAG精排,基本上RAG的流程和常用方法都讲得差不多了,但RAG依旧有很多地方会遇到坑.
比如,有些细心的同学实践后会发现这个坑:
即使模型和算法再强,检索结果有时还是“不够聚焦”。
比如你问:
「公司请假制度是什么?」
它可能会检索出「年假政策」或「考勤规则」,虽然相关,但不一定是你想要的那一类信息。
这时候,我们可以考虑用 metadata(元数据)过滤,进一步收紧我们的筛选范围。
什么是 Metadata?
在 LangChain 的文档体系中,每个文档 Document 都由两部分组成:
{
"page_content": "这是文档的正文内容。",
"metadata": {
"source": "employee_policy.pdf",
"page": 12,
"category": "人事制度"
}
}
其中:
-
• page_content是真正被嵌入向量的文本; -
• metadata是描述文本属性的“标签信息”。
你可以把它理解成:
给每一段文本打上“来源”“分类”“时间”等标签。
为什么要用 Metadata?
仅靠文本相似度,模型只能判断“像不像”,
却不知道“你想在哪个范围里搜”。
举个例子:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
当你问「请假流程是什么」时, 如果我们加上过滤条件:category="人事制度", 就能让检索更精准,不会混入不相关的部门文档。
这就像数据库里的 WHERE 条件一样:
SELECT * FROM documents WHERE category = '人事制度';
LangChain 的 metadata 过滤,本质上就是类似的逻辑过滤操作。
不同向量库对 Metadata 的支持
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Metadata 在入库时怎么写?
在 LangChain 中,你可以在插入文本时传入 metadatas 参数:
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
texts = [
"公司人事制度规定员工需提前申请年假。",
"财务制度要求报销需提供完整发票。",
"技术部门采用敏捷开发流程。",
"员工请假需提前 3 天申请。",
"公司每年提供 10 天带薪年假。",
]
metadatas = [
{"category": "人事制度", "source": "hr_policy.pdf"},
{"category": "财务制度", "source": "finance_rule.pdf"},
{"category": "技术规范", "source": "dev_guide.pdf"},
{"category": "人事制度", "source": "hr_policy.pdf"},
{"category": "人事制度", "source": "hr_policy.pdf"},
]
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_texts(
texts=texts,
metadatas=metadatas, # ✅ 在这里写入 metadata
embedding=embedding_model,
persist_directory="./chroma_db_18"
)
检索时加上 Metadata 过滤
入库后,我们就能像数据库一样加上过滤条件:
retriever = vectorstore.as_retriever(
search_kwargs={
"k": 3,
"filter": {"category": "人事制度"} # ✅ 指定筛选条件
}
)
这样它就只会检索「人事制度」类别的文档。
实战代码
老规矩,下面我们上代码。新建文件:18_metadata.py
import os
from dotenv import load_dotenv
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
def _trim(text: str, max_len: int = 60) -> str:
return text if len(text) < max_len else text[:max_len] + "..."
def main():
load_dotenv()
# ========== 1. 构建带 metadata 的文档 ==========
texts = [
"公司人事制度规定员工需提前申请年假。",
"财务制度要求报销需提供完整发票。",
"技术部门采用敏捷开发流程。",
"员工请假需提前 3 天申请。",
"公司每年提供 10 天带薪年假。",
]
metadatas = [
{"category": "人事制度", "source": "hr_policy.pdf"},
{"category": "财务制度", "source": "finance_rule.pdf"},
{"category": "技术规范", "source": "dev_guide.pdf"},
{"category": "人事制度", "source": "hr_policy.pdf"},
{"category": "人事制度", "source": "hr_policy.pdf"},
]
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectorstore = Chroma.from_texts(
texts=texts,
metadatas=metadatas,
embedding=embedding_model,
persist_directory="./chroma_db_18"
)
# ========== 2. 初始化 LLM ==========
llm = ChatOpenAI(
model="deepseek-chat",
base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0,
)
query = "请假和报销需要什么材料?"
# ========== 3. 不加过滤 ==========
retriever_all = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_all = RetrievalQA.from_chain_type(llm=llm, retriever=retriever_all)
print("\n=== 不加过滤(混合类别) ===")
results_all = vectorstore.similarity_search_with_relevance_scores(query, k=3)
for doc, score in results_all:
print(f"score={score:.4f} | {_trim(doc.page_content)} | {doc.metadata}")
print("→ LLM 回答:")
print(qa_all.invoke(query))
# ========== 4. 加 metadata 过滤 ==========
retriever_filtered = vectorstore.as_retriever(
search_kwargs={"k": 3, "filter": {"category": "人事制度"}}
)
qa_filtered = RetrievalQA.from_chain_type(llm=llm, retriever=retriever_filtered)
print("\n=== 基于 metadata 的精准检索 ===")
results_filtered = retriever_filtered.invoke(query)
for doc in results_filtered:
print(f"{_trim(doc.page_content)} | {doc.metadata}")
print("→ LLM 回答:")
print(qa_filtered.invoke(query))
if __name__ == "__main__":
main()
运行效果
可以看到,加了metadata筛选,就能够精准从知识库的指定文档中检索内容,提升检索效率。
当知识库的文档越多,metadata的检索越是有必要。
对AI应用开发感兴趣的同学,欢迎加微信申请入群交流学习。
想要学习AI应用开发的同学,可以参照我的代码跑起来,举一反三,一天一个脚印的进步,我相信,会足够坚实。
接下来100天,我会记录从Java程序员到AI应用开发工程师的完整转型路:每一个困惑、每一次突破、每一个真实瞬间。
当前是37/100。
如果你也想了解AI应用开发到底是什么,如果你也在考虑转型但还在犹豫,那就跟着我的记录,一起探索。
继续折腾中,有问题随时交流 🤝
微信号:ThinkFun666
我的免费AI编程交流群,欢迎加入讨论AI编程玩法(说明需求和来意,申请入群)
推荐阅读:
我就喜欢干这种傻事!为什么转型AI应用开发,我不建议你直接从看书开始学起?

