LangChain教程:ChromaDB向量数据库的检索 作者:马育民 • 2026-03-05 12:12 • 阅读:10009 # 常用检索方法介绍 常用方法对比: | 对比维度 | similarity_search | similarity_search_with_score | as_retriever | |------------------|----------------------------------|-----------------------------------|-----------------------------------| | **核心能力** | 基础语义相似检索,返回相似文档列表 | 基础检索 + 相似度得分(距离值)| 转换为LangChain标准检索器,支持多策略检索 | | **返回信息** | 仅文档内容+元数据 | 文档内容+元数据+相似度得分(值越小越相似) | 可配置返回文档列表(同前两者),适配链调用 | | **检索策略扩展** | 仅支持基础相似检索 | 仅支持基础相似检索 | 支持3类核心策略:1. 基础相似检索2. MMR多样性检索(避免结果重复)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()简单检索 基于向量余弦相似度,根据查询文本,返回最相似的文档列表 ### 语法 ```python db.similarity_search( query: str, # 【必选】检索问句/关键词 k: int = 4, # 【可选】返回top k条结果(默认4) filter: Optional[Dict] = None, # 【可选】元数据过滤条件(如{"type": "数据库"}) **kwargs, ) -> List[Document] ``` ### 例子 ```python 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() 带相似度得分 返回相似文档 + 相似度得分(得分越低,相似度越高) ### 语法 ```python 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`:低相似度(基本不相关) | ### 例子 ```python 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}") ``` [](https://www.malaoshi.top/upload/0/0/1GW2rpk1ImLO.png) # as_retriever() RAG 检索 将 Chroma 对象转换成 LangChain 标准检索器,直接接入 RAG 链 ```python 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 例子 ```python 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类型: results 元素的类型: 结果数量: 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(英文场景)的结果: ```python 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`),检索时过滤: ```python retriever = db.as_retriever( search_kwargs={"k": 2, "filter": {"domain": "数据库", "type": "技术文档"}} ) ``` #### 2. 中期优化:修复向量层问题(核心) - **换适配的嵌入模型**:中文场景优先用 BGE 系列(如 `BAAI/bge-small-zh-v1.5`),不要用默认的英文模型: ```python 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` 等,是提升检索精准度的关键。 原文出处:http://malaoshi.top/show_1GW2tDWp75AR.html