LangChain教程:ChromaDB向量数据库的检索

常用检索方法介绍

常用方法对比:

对比维度 similarity_search similarity_search_with_score as_retriever
核心能力 基础语义相似检索,返回相似文档列表 基础检索 + 相似度得分(距离值) 转换为LangChain标准检索器,支持多策略检索
返回信息 仅文档内容+元数据 文档内容+元数据+相似度得分(值越小越相似) 可配置返回文档列表(同前两者),适配链调用
检索策略扩展 仅支持基础相似检索 仅支持基础相似检索 支持3类核心策略:<br>1. 基础相似检索<br>2. MMR多样性检索(避免结果重复)<br>3. 得分阈值过滤(仅保留高相似结果)
LangChain适配 需手动拼接结果到Prompt,适配性弱 需手动处理得分+拼接,适配性更弱 无缝对接RAG链/Agent/流水线,生态适配性满分
操作复杂度 极低(直接调用,结果直观) 低(仅多处理得分字段) 中等(需配置检索类型/参数)

similarity_search 的场景

适用场景:

  • 快速验证:比如确认向量库中是否有某类相关数据、测试元数据过滤是否生效;
  • 极简检索需求:业务仅需“返回相似文档”,无需评估相似程度、无需接入LLM;
  • 新手调试:刚接触向量检索,先看基础结果,不关注复杂指标。

❌ 不适用:

  • 需要判断“文档到底有多相似”;
  • 要接入LangChain RAG链实现问答。

similarity_search_with_score 的场景

适用场景:

  • 检索质量调试:比如判断检索结果是否真的相关(得分>1.0大概率不相关);
  • 策略优化:比如调整嵌入模型/分块规则后,对比得分分布评估效果;
  • 业务规则过滤:比如仅保留得分 <0.8 的结果,过滤低相似冗余内容;
  • 问题排查:比如用户反馈回答不准确时,通过得分定位“检索到低相似文档”的问题。

不适用:

  • 生产环境直接对接LLM(得分无业务价值,且手动处理成本高);
  • 仅需基础检索,无需评估相似程度。

as_retriever 的场景

适用场景:

  • 生产级RAG开发:比如搭建智能问答系统、知识库助手等核心业务;
  • 复杂检索需求:比如需要“多样性结果”(MMR)、“仅高相似结果”(阈值过滤);
  • 流水线化开发:比如文档加载→分块→检索→问答的全流程封装;
  • 多组件协同:比如检索结果需接入Agent、记忆模块、缓存系统等。

不适用:

  • 仅做临时/一次性的检索验证(配置成本高于直接调用前两者);
  • 完全脱离LangChain生态开发(检索器优势无法体现)。

选择原则

  1. 临时验证/极简检索 → similarity_search(省心);
  2. 调试/评估检索质量 → similarity_search_with_score(能看得分);
  3. 生产RAG/复杂检索 → as_retriever(适配生态、可扩展);
  4. 核心原则:非临时场景优先选 as_retriever,兼顾当前需求和未来扩展,是LangChain+ChromaDB开发的最佳实践。

similarity_search()简单检索

基于向量余弦相似度,根据查询文本,返回最相似的文档列表

语法

db.similarity_search(
    query: str,                # 【必选】检索问句/关键词
    k: int = 4,                # 【可选】返回top k条结果(默认4)
    filter: Optional[Dict] = None,  # 【可选】元数据过滤条件(如{"type": "数据库"})
    **kwargs,
) -> List[Document]

例子

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv

# 加载环境变量(需配置 OPENAI_API_KEY)
load_dotenv()

# 初始化大模型
embeddings = DashScopeEmbeddings(model="text-embedding-v2")

# 创建数据库对象
db = Chroma(
    embedding_function=embeddings,
    persist_directory="./my_chroma_db",
    collection_name="test_collection"
)

results = db.similarity_search(
    query="ChromaDB",  # 搜索内容
    k=3)

# list 类型
print("results类型:", type(results))

# Document 类型
print("results 元素的类型:", type(results[0]))

print("结果数量:", len(results))

for item in results:
    print(item.page_content)

similarity_search_with_score() 带相似度得分

返回相似文档 + 相似度得分(得分越低,相似度越高)

语法

db.similarity_search_with_score(
    query: str,                # 【必选】检索问句
    k: int = 4,                # 【可选】返回数量(默认4)
    filter: Optional[Dict] = None,  # 【可选】元数据过滤
) -> List[Tuple[Document, float]]  # (文档对象, 相似度得分)

分数

属性 具体说明
取值范围 无严格上限(通常 0~2 为主),下限为 0(完全相同的文本)
核心规律 分数越低,相似度越高;分数越高,相似度越低
计算方式 ChromaDB 默认用「余弦距离」(也支持欧氏距离等),你无需手动配置,是内置逻辑
参考阈值 分数 ≤ 0.5:极高相似度(核心相关结果),0.5 < 分数 ≤ 1.0:中等相似度(相关但非核心),分数 > 1.0:低相似度(基本不相关)

例子

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv

# 加载环境变量(需配置 OPENAI_API_KEY)
load_dotenv()

# 初始化大模型
embeddings = DashScopeEmbeddings(model="text-embedding-v2")

# 创建数据库对象
db = Chroma(
    embedding_function=embeddings,
    persist_directory="./my_chroma_db",
    collection_name="test_collection"
)

results = db.similarity_search_with_score(
    query="ChromaDB",  # 搜索内容
    k=3)

# list 类型
print("results类型:", type(results))

# 元素是 tuple 类型,第一个元素是 Document 对象,第二个元素是分数
print("results 元素的类型:", type(results[0]))

print("结果数量:", len(results))

for item in results:
    doc = item[0]
    score = item[1]
    print(f"分数:{score}    id:{doc.id},内容:{doc.page_content}")

as_retriever() RAG 检索

将 Chroma 对象转换成 LangChain 标准检索器,直接接入 RAG 链

db.as_retriever(
    search_type: str = "similarity",  # 【可选】检索类型:similarity/MMR(默认similarity)
    search_kwargs: Dict = {},         # 【可选】检索参数(k/filter等)
) -> Retriever

参数详解:

  • search_type:见下面表格
类型 中文名称 核心作用 关键参数(search_kwargs) 适用场景
"similarity" 基础相似检索 等同于 similarity_search(),按向量余弦相似度,从高到低返回 top-K 结果(最常用) k(返回条数)、filter(元数据过滤) 通用问答、精准匹配场景
"mmr" 最大边缘相关性检索 兼顾相似度和多样性,避免结果重复,返回不同维度的相关文档 k、lambda_mult(多样性权重)、filter 开放式问答、多维度信息检索
"similarity_score_threshold" 得分阈值过滤检索 仅返回「相似度得分 ≤ 阈值」的结果,过滤低相似/不相关内容 k、score_threshold(得分阈值)、filter 高精准度需求、过滤无关结果
  • search_kwargs:透传检索参数,如 {"k": 3, "filter": {"type": "数据库"}}

similarity 例子


from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv

# 加载环境变量(需配置 OPENAI_API_KEY)
load_dotenv()

# 初始化大模型
embeddings = DashScopeEmbeddings(model="text-embedding-v2")

# 创建数据库对象
db = Chroma(
    embedding_function=embeddings,
    persist_directory="./my_chroma_db",
    collection_name="test_collection"
)

# 创建检索器(最简配置)
retriever = db.as_retriever(
    search_type="similarity",  # 基础相似检索(默认值,可省略)
    search_kwargs={
        "k": 3,  # 返回top 2条最相似结果
    })

# 用检索器(核心操作)
results = retriever.invoke("ChromaDB 是什么?")

# list 类型
print("results类型:", type(results))

# 元素是 tuple 类型,第一个元素是 Document 对象,第二个元素是分数
print("results 元素的类型:", type(results[0]))

print("结果数量:", len(results))

for item in results:
    print(item.page_content)
执行结果:
results类型: <class 'list'>
results 元素的类型: <class 'langchain_core.documents.base.Document'>
结果数量: 3
ChromaDB是轻量级开源向量数据库,支持本地持久化
ChromaDB是轻量级开源向量数据库,支持本地持久化
ChromaDB轻量易用

similarity_score_threshold例子


from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv

# 加载环境变量(需配置 OPENAI_API_KEY)
load_dotenv()

# 初始化大模型
embeddings = DashScopeEmbeddings(model="text-embedding-v2")

# 创建数据库对象
db = Chroma(
    embedding_function=embeddings,
    persist_directory="./my_chroma_db",
    collection_name="test_collection"
)

# 创建检索器(最简配置)
retriever = db.as_retriever(
    search_type="similarity_score_threshold",  # 基础相似检索(默认值,可省略)
    search_kwargs={
        "k": 3,  # 返回top 2条最相似结果
        "score_threshold": 0.5,  # 相似度得分 ≤ 阈值0.5 的结果
    })

# 用检索器(核心操作)
results = retriever.invoke("ChromaDB 是什么?")

# list 类型
print("results类型:", type(results))

# 元素是 tuple 类型,第一个元素是 Document 对象,第二个元素是分数
print("results 元素的类型:", type(results[0]))

print("结果数量:", len(results))

for item in results:
    print(item.page_content)

MMR用法

普通相似检索的缺点:只挑「和问题最像」的文档,哪怕这些文档内容几乎一样

MMR 检索:先挑 1条最相似的,再挑 既和问题相关,又和已选文档不重复 的文档,最终返回 相似且多样 的结果。

解决的问题

普通相似检索的痛点:

问题:ChromaDB 有哪些特性?
普通检索结果(重复):
1. ChromaDB 支持本地持久化存储
2. ChromaDB 可本地持久化保存数据

MMR 检索的优化:

MMR检索结果(多样):
1. ChromaDB 支持本地持久化存储
2. ChromaDB 支持MMR检索提升多样性

工作逻辑

  1. 先选出一批和问题「高相似」的候选文档(比如前20条);
  2. 从候选中选1条最相似的作为第一条结果;
  3. 选下一条时,优先选「和问题相似,但和已选文档差异大」的(既相关,又不重复);
  4. 重复步骤3,直到选够 k 条结果。

关键参数

MMR 只有1个核心可调参数 lambda_mult(0~1):

  • lambda_mult=1:完全优先相似度(和普通相似检索一样,会重复);
  • lambda_mult=0:完全优先多样性(可能选到不相关的文档);
  • 默认值=0.5:平衡相似度和多样性(通用场景够用)。

例子

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from dotenv import load_dotenv

# 加载环境变量(需配置 OPENAI_API_KEY)
load_dotenv()

# 初始化大模型
embeddings = DashScopeEmbeddings(model="text-embedding-v2")

# 创建数据库对象
db = Chroma(
    embedding_function=embeddings,
    persist_directory="./my_chroma_db",
    collection_name="test_collection"
)

# 创建检索器(最简配置)
retriever = db.as_retriever(
    search_type="mmr",  # 基础相似检索(默认值,可省略)
    search_kwargs={
        "k": 3,  # 返回top 2条最相似结果
    })

# 用检索器(核心操作)
results = retriever.invoke("ChromaDB 是什么?")

# list 类型
print("results类型:", type(results))

# 元素是 tuple 类型,第一个元素是 Document 对象,第二个元素是分数
print("results 元素的类型:", type(results[0]))

print("结果数量:", len(results))

for item in results:
    print(item.page_content)

可能会搜索出不相关的答案

1. 向量层问题(最核心):文本转向量时“语义失真”

这是最常见的原因——嵌入模型没把文本的真实语义转换成准确的向量,导致“查询向量”和“不相关文档向量”被误判为相似:

  • 嵌入模型不匹配:比如用英文模型(如 all-MiniLM-L6-v2)处理中文文本,语义理解差,容易把无关中文内容判为相似;

  • 文本分块不合理:

    • 分块太大(比如整篇文档一个块):块内包含多个无关主题,向量代表的是“混合语义”;
    • 分块太小(比如一句话拆成多个块):语义不完整,向量无法准确表达核心含义;
  • 查询词表达不精准:比如想查“ChromaDB 持久化配置”,却只搜“ChromaDB 设置”,查询向量太宽泛,容易匹配到无关内容。

2. 检索层问题:检索策略没过滤掉低相似结果

即使向量有轻微失真,合理的检索策略也能过滤掉不相关结果,反之则会放行:

  • 未设置得分阈值:普通相似检索(similarity)会返回 top-K 结果,哪怕最后几条分数很高(相似度极低);
  • k 值设置过大:比如 k=10,强制返回10条结果,系统会 “凑数”,把低相似的不相关文档也纳入;
  • 元数据过滤缺失:比如库中有“技术文档”“营销文档”,却没过滤 filter={"type": "技术文档"},导致营销文档被检索出来。

3. 数据层问题:库中本身有低质量/无关数据

如果向量库中混入了和业务无关的文档,检索时自然可能命中:

  • 数据清洗不彻底:入库前没剔除广告、重复、无效文本;
  • 数据标注错误:元数据标错(比如把“营销文档”标成“技术文档”),过滤时无法精准筛选;
  • 无业务边界的数据:库中混入了和当前查询无关的领域内容(比如查数据库问题,却有前端框架的文档)。

解决办法

1. 快速见效:优化检索策略

  • 设置得分阈值过滤:改用 similarity_score_threshold 检索类型,只保留分数≤0.8(中文场景)/0.6(英文场景)的结果:
    retriever = db.as_retriever(
    search_type="similarity_score_threshold",
    search_kwargs={"k": 3, "score_threshold": 0.8}
    )
    
  • 缩小 k 值:把 k 从 5~10 降到 2~3,只保留最核心的相似结果;
  • 增加元数据过滤:入库时给文档打精准标签(如 type/domain/source),检索时过滤:
    retriever = db.as_retriever(
    search_kwargs={"k": 2, "filter": {"domain": "数据库", "type": "技术文档"}}
    )
    

2. 中期优化:修复向量层问题(核心)

  • 换适配的嵌入模型:中文场景优先用 BGE 系列(如 BAAI/bge-small-zh-v1.5),不要用默认的英文模型:
    from langchain_huggingface import HuggingFaceEmbeddings
    embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
    
  • 优化文本分块:中文按“段落”分块(每块 200~500 字),保证块内语义完整且不混杂;
  • 优化查询词:检索前对查询词做“语义增强”(比如“ChromaDB 设置”→“ChromaDB 本地持久化配置参数”)。

3. 长期保障:做好数据层治理

  • 入库前清洗:剔除重复、无效、无关文本,只保留核心业务内容;
  • 精准标注元数据:至少标注「领域(domain)」「文档类型(type)」「来源(source)」;
  • 分库存储:不同领域的内容放在不同的 Chroma 集合(collection)中,检索时只查对应集合。

总结

  1. 检索类方法:similarity_search()(基础)/ similarity_search_with_score()(带得分)/ as_retriever()(RAG适配),核心参数是 k(返回数量)和 filter(元数据过滤);
  2. 过滤语法:支持 $eq/$in/$and 等,是提升检索精准度的关键。

原文出处:/show_1GW2tDWp75AR.html