LangChain / RAG 学习总结
整理自近期几个核心问题的梳理:从环境警告、文本切分、相似度检索到 MMR 算法。
目录
- [langchain-community 的 sunset 警告](#langchain-community 的 sunset 警告)
- [CharacterTextSplitter 的参数坑](#CharacterTextSplitter 的参数坑)
- [Embedding 的 ctx_length 开关](#Embedding 的 ctx_length 开关)
- [similarity_search_with_score 带分数检索](#similarity_search_with_score 带分数检索)
- [MMR 搜索:相关性与多样性的平衡](#MMR 搜索:相关性与多样性的平衡)
- 核心速查表
1. langchain-community 的 sunset 警告
现象
运行代码时出现:
DeprecationWarning: `langchain-community` is being sunset and is no longer actively maintained.
以及:
USER_AGENT environment variable not set
原因
langchain-community 这个包官方决定停止维护、逐步淘汰 。根本原因是这个包里塞了太多集成(各种加载器、向量库、模型接口),导致迭代困难、质量参差。LangChain 现在改成"一个集成一个独立包"的模式。
官方声明要点:
- 过去两年已不再接收新功能,实质上 deprecation 状态已持续一年多
- 现在正式官宣 sunset,立即生效
- 新集成工作已全部转向独立包(如
langchain_faiss、langchain_huggingface、langchain_ollama)
为什么 warnings.filterwarnings 管不住
这条提示不是走 Python 标准 warnings 模块 ,而是 LangChain 内部机制在 import 时直接刷到 stderr,绕过了过滤器。
正确处理
python
import os
os.environ["LANGCHAIN_SUPPRESS_DEPRECATION_WARNINGS"] = "true" # 官方总开关
os.environ["USER_AGENT"] = "MyRAGApp/1.0"
# 上面必须在 import 之前
from langchain_community.document_loaders import TextLoader
学习/搭建 demo 阶段可直接忽略该警告;上生产环境再按官方迁移指南逐个换成独立包。
2. CharacterTextSplitter 的参数坑
报错代码
python
fixed_splitter = CharacterTextSplitter(
chunk_size=100,
chunk_overlap=20,
separators="", # ❌ 错误
length_function=len
)
错误原因
CharacterTextSplitter 的构造函数签名是:
python
def __init__(self, separator: str = "\n\n",
is_separator_regex: bool = False, **kwargs):
参数名是 separator(单数、字符串),不是 separators(复数、列表)。
separators= 被 **kwargs 一路传到父类 TextSplitter,父类不认识这个参数,于是抛出:
TypeError: TextSplitter.__init__() got an unexpected keyword argument 'separators'
(separators 是 RecursiveCharacterTextSplitter 的参数,两个类极易记混。)
修正后
python
fixed_splitter = CharacterTextSplitter(
separator="\n\n", # 单数!
chunk_size=300,
chunk_overlap=60,
length_function=len,
is_separator_regex=False
)
fixed_chunks = fixed_splitter.split_documents(pdf_docs)
两个类的参数对照
| 参数 | CharacterTextSplitter |
RecursiveCharacterTextSplitter |
|---|---|---|
| 分隔符 | separator: str(单数) |
separators: list[str](复数) |
| 正则开关 | is_separator_regex |
is_separator_regex |
| 保留分隔符 | 继承父类 keep_separator |
自身 keep_separator,默认 True |
选择建议
- 中文文档 + 接大模型 → 推荐
RecursiveCharacterTextSplitter,对段落结构更友好 length_function=len按字符数算;接 token 计费模型时注意块数会暴增
3. Embedding 的 ctx_length 开关
原文
Whether to check the token length of inputs and automatically split inputs
longer than
embedding_ctx_length.Set to
Falseto send raw text strings directly to the API instead oftokenizing. Useful for many non-OpenAI providers (e.g. OpenRouter, Ollama,
vLLM).
翻译
是否检查输入的 token 长度,并自动将超过 embedding_ctx_length 的输入进行拆分。
设为 False 时,会直接将原始文本字符串发送给 API,而不做分词处理。这对许多非 OpenAI 的提供商(例如 OpenRouter、Ollama、vLLM)非常有用。
要点
embedding_ctx_length:嵌入模型的最大 token 上限True(默认):超出自动切段,防止报错,适合 OpenAIFalse:交给服务端处理 token 化,适合 Ollama / vLLM / OpenRouter
4. similarity_search_with_score 带分数检索
作用
similarity_search_with_score = "带距离分数的相似度搜索 ",返回 (Document, score) 元组列表。
与普通搜索对比
| 方法 | 返回内容 | 场景 |
|---|---|---|
similarity_search(query) |
Document 列表 |
只拿结果 |
similarity_search_with_score(query) |
(Document, score) 列表 |
看相关性高低、调试、过滤 |
用法
python
results = vectorstore.similarity_search_with_score("苹果手机用什么芯片", k=3)
for doc, score in results:
# Chroma 返回的是距离,越小越相似
print(f"分数: {score:.4f} | 内容: {doc.page_content}")
关键认知
- 返回的是距离,不是"相似度百分比"
- 没有上限 ,值域
[0, +∞) - 0 = 完全相同,越大越不相关
- 距离函数取决于创建 Chroma 时的配置:
- L2 距离 (默认):
[0, +∞) - 余弦距离 :
[0, 2]
- L2 距离 (默认):
实战用途
python
# 1. 阈值过滤
if score < 0.5:
print(doc.page_content)
# 2. 调试召回质量
# 最高分都 > 1.5 → 切块太碎 / embedding 不合适 / 库里没这内容
# 分数普遍很小 → 召回质量好,可放心喂给 LLM
5. MMR 搜索:相关性与多样性的平衡
两个方法的关系
max_marginal_relevance_search |
max_marginal_relevance_search_by_vector |
|
|---|---|---|
| 输入 | 文本字符串 | 向量 |
| 内部行为 | 先 embed 再搜 | 直接用给定向量 |
| 多一次 embedding | ✅ 有 | ❌ 无 |
前者是后者的封装:
python
def max_marginal_relevance_search(self, query, k=4, fetch_k=20, lambda_mult=0.5):
query_vector = self.embedding.embed_query(query) # 多这一步
return self.max_marginal_relevance_search_by_vector(query_vector, ...)
核心参数
python
vectorstore.max_marginal_relevance_search(
query,
k=4, # 最终返回几条
fetch_k=20, # 先拉多少条候选(再从中 MMR 筛选)
lambda_mult=0.5 # 相关性 vs 多样性
)
λ(lambda_mult)调节
| λ 值 | 行为 |
|---|---|
1.0 |
退化成普通搜索,纯看相关性 |
0.5 |
均衡(默认) |
0.3 |
更强调多样性,可能混入不相关内容 |
0.0 |
纯看多样性,可能全是跑题的 |
MMR 在解决什么问题
普通搜索的痛点:返回结果高度重复。
普通搜索(挤在一团): MMR(散开覆盖):
●A 都在讲 3nm+A17 ●A 芯片本身
●B ┐ 信息重叠 ●D 代际对比
●C ┘ ●E 竞品视角
(浪费 token) (信息互补)
MMR 的目标函数
MMR = λ · 相关性(文档, 查询) − (1−λ) · 最大相似度(文档, 已选文档)
每一步选文档时同时问两个问题:
- 这条跟问题像不像?→ 保证相关性
- 这条跟已选的像不像?→ 保证多样性(主动排斥"近亲")
使用场景
| 场景 | 推荐 |
|---|---|
| 知识库主题集中、切块较多 | ✅ MMR |
| 想让 LLM 看到多角度信息 | ✅ MMR |
| 内容本身很分散 | 普通搜索即可 |
| 对延迟极度敏感 | 普通搜索 |
| 只需 1~2 条精准答案 | 普通搜索 |