# LangChain 其他 Embedding 模型实践:Hugging Face、本地模型、云厂商与生产选型
在 RAG 系统里,Embedding 模型决定的是“知识能不能被召回来”的上限。
很多人一开始会直接使用 OpenAI Embedding,因为接入简单、效果稳定。但生产里经常会遇到这些约束:
- 数据不能出内网。
- 中文、代码、医疗、法律、金融等领域语义需要专项优化。
- 单量太大,外部 API 成本不可控。
- 企业已经有云厂商资源池。
- 需要离线批处理,吞吐比单次延迟更重要。
- 向量库已经固定维度,不能随意切模型。
这时就需要评估其他 Embedding 模型。LangChain 的价值不在于替你决定用哪个模型,而在于提供统一的 Embeddings 接口,让不同 provider 的模型都能接到同一条 RAG 链路里。
# 先抓住统一接口
无论底层是 OpenAI、Hugging Face、Ollama、Cohere、百度千帆,还是自研 Embedding 服务,接入 LangChain 后核心接口都类似:
query_vector = embeddings.embed_query("用户问题")
document_vectors = embeddings.embed_documents(
[
"文档片段 1",
"文档片段 2",
]
)
2
3
4
5
6
7
这两个方法的语义不同:
embed_query():把用户查询转成向量,通常用于检索时的 query vector。embed_documents():把文档 chunk 批量转成向量,通常用于入库。
有些模型会对 query 和 document 使用不同的 prompt、instruction 或 encode 参数。即使大多数 provider 在实现上差异不明显,生产设计时也要把这两个入口分开看。
# 当前 LangChain 的包结构
现在 LangChain 更推荐按 provider 安装独立包,而不是把所有模型都从一个大包里导入。
常见路径如下:
| 类型 | 安装包 | 常见导入 |
|---|---|---|
| OpenAI | langchain-openai | from langchain_openai import OpenAIEmbeddings |
| Azure OpenAI | langchain-openai | from langchain_openai import AzureOpenAIEmbeddings |
| Hugging Face 本地 | langchain-huggingface | from langchain_huggingface import HuggingFaceEmbeddings |
| Hugging Face Endpoint | langchain-huggingface | from langchain_huggingface import HuggingFaceEndpointEmbeddings |
| Ollama | langchain-ollama | from langchain_ollama import OllamaEmbeddings |
| Cohere | langchain-cohere | from langchain_cohere import CohereEmbeddings |
| Mistral AI | langchain-mistralai | from langchain_mistralai import MistralAIEmbeddings |
| Google Gemini | langchain-google-genai | from langchain_google_genai import GoogleGenerativeAIEmbeddings |
| AWS Bedrock | langchain-aws | from langchain_aws import BedrockEmbeddings |
| 百度千帆 | langchain-community | from langchain_community.embeddings.baidu_qianfan_endpoint import QianfanEmbeddingsEndpoint |
这张表的重点不是背包名,而是理解当前生态的方向:核心接口统一,provider 实现拆包。这样可以减少依赖体积,也能让各家模型集成独立迭代。
# Hugging Face 本地 Embedding
如果数据不能出内网,或者希望把 Embedding 成本控制在自有机器上,本地 Hugging Face 模型是常见选择。
安装:
pip install -U langchain-huggingface sentence-transformers
基础使用:
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-mpnet-base-v2",
encode_kwargs={"normalize_embeddings": True},
)
query_vector = embeddings.embed_query("Embedding 模型应该怎么选?")
document_vectors = embeddings.embed_documents(
[
"本地模型适合数据敏感场景。",
"云端模型适合快速接入和弹性扩容。",
]
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
本地模型第一次加载会下载模型权重,后续会从本地缓存读取。生产环境不要让服务启动时临时下载模型,应该在镜像构建、模型仓库同步或部署发布阶段提前准备好。
可以指定缓存目录:
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-large-zh-v1.5",
cache_folder="/data/models/huggingface",
encode_kwargs={"normalize_embeddings": True},
)
2
3
4
5
中文知识库可以优先评估 BGE、GTE、E5、Jina 等系列模型。代码检索、医学检索、法律检索则要额外找领域模型或做领域评估,不能只看通用榜单。
# Hugging Face Endpoint
如果不想在本地维护模型和推理环境,可以使用 Hugging Face 的远程推理服务。
安装:
pip install -U langchain-huggingface huggingface_hub
配置环境变量:
export HUGGINGFACEHUB_API_TOKEN="your-token"
使用:
from langchain_huggingface import HuggingFaceEndpointEmbeddings
embeddings = HuggingFaceEndpointEmbeddings(
model="sentence-transformers/all-MiniLM-L12-v2",
)
query_vector = embeddings.embed_query("远程 Embedding 服务适合什么场景?")
2
3
4
5
6
7
8
远程 endpoint 的优势是接入快、无需维护 GPU;代价是网络延迟、服务稳定性、费用和数据出境问题。它适合原型验证、低频任务、临时评估,不一定适合所有生产知识库。
# Ollama 本地 Embedding
如果你已经用 Ollama 管理本地模型,也可以用 LangChain 接入:
pip install -U langchain-ollama
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(model="nomic-embed-text")
query_vector = embeddings.embed_query("Ollama 本地 Embedding 怎么接入?")
2
3
4
5
6
Ollama 的好处是本地开发体验顺滑,适合桌面开发、PoC、内部小工具。生产里要关注模型服务的并发、冷启动、资源隔离、监控和版本固定。
# 云厂商 Embedding
如果企业已经使用某个云平台,云厂商 Embedding 往往是更容易落地的选择。
例如 Azure OpenAI:
import os
from langchain_openai import AzureOpenAIEmbeddings
embeddings = AzureOpenAIEmbeddings(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
openai_api_version=os.environ["AZURE_OPENAI_API_VERSION"],
)
2
3
4
5
6
7
8
9
10
例如 AWS Bedrock:
from langchain_aws import BedrockEmbeddings
embeddings = BedrockEmbeddings(
model_id="amazon.titan-embed-text-v2:0",
)
2
3
4
5
6
例如 Google Gemini:
from langchain_google_genai import GoogleGenerativeAIEmbeddings
embeddings = GoogleGenerativeAIEmbeddings(
model="models/gemini-embedding-001",
)
2
3
4
5
6
云厂商方案的优势是稳定、合规能力较好、权限和审计体系成熟。缺点是供应商绑定更强,不同区域、模型版本、限流策略会影响上线方案。
# 百度千帆 Embedding
国内业务如果已经在百度智能云体系内,可以评估千帆 Embedding。
当前在 LangChain API 参考里,QianfanEmbeddingsEndpoint 仍位于 langchain_community:
pip install -U langchain-community qianfan
from langchain_community.embeddings.baidu_qianfan_endpoint import (
QianfanEmbeddingsEndpoint,
)
embeddings = QianfanEmbeddingsEndpoint(
model="bge-large-zh",
)
query_vector = embeddings.embed_query("中文知识库应该如何选择 Embedding?")
2
3
4
5
6
7
8
9
10
千帆这类国内 provider 的价值通常不只是模型本身,还包括网络访问、数据合规、账单体系、企业合同和国内云资源协同。选型时要把这些因素纳入评估,而不是只比较一个 Recall@K。
# OpenAI 兼容接口
有些自研网关或第三方模型服务会提供 OpenAI 兼容接口。理论上可以直接使用供应商 SDK,也可以自己实现 LangChain 的 Embeddings 接口。
当 provider 没有官方 LangChain 包,最稳的方式是自定义一个适配器:
from langchain_core.embeddings import Embeddings
class CompanyEmbeddings(Embeddings):
def __init__(self, client, model: str):
self.client = client
self.model = model
def embed_documents(self, texts: list[str]) -> list[list[float]]:
response = self.client.embed(
model=self.model,
input=texts,
)
return [item.embedding for item in response.data]
def embed_query(self, text: str) -> list[float]:
return self.embed_documents([text])[0]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这样上层 RAG 链路不需要知道底层 provider 细节,只依赖 Embeddings 接口。
# 生产选型维度
Embedding 选型不要只看“哪个模型分数高”。生产里至少看八个维度:
| 维度 | 要问的问题 |
|---|---|
| 语言 | 中文、英文、多语言、混合中英是否稳定 |
| 领域 | 是否适合业务术语、代码、表格、法律、医疗等内容 |
| 维度 | 向量维度是否影响存储成本和向量库索引 |
| 上下文长度 | 单个 chunk 最大输入长度是多少 |
| 吞吐 | 批量入库时每秒能处理多少文本 |
| 延迟 | 在线 query embedding 延迟是否可接受 |
| 成本 | API 调用费、GPU 成本、存储成本是否可控 |
| 合规 | 数据是否允许出域,是否有审计和脱敏要求 |
如果只根据 demo 效果选模型,很容易在上线后被成本、限流、维度迁移、权限合规卡住。
# 和向量库的耦合
Embedding 模型一旦选定,就会和向量库形成强耦合。
原因很简单:向量库索引是按向量维度和向量分布建立的。换模型通常意味着:
- 向量维度可能变化。
- 相似度分布变化。
- 召回阈值要重新调。
- 已有索引需要重建。
- 评估集需要重新跑。
- 缓存 namespace 需要更新。
所以生产里不要把 Embedding 模型切换做成一个随手改配置的动作。它更像一次索引版本升级。
# 建议的工程封装
可以把不同 provider 封装成统一工厂:
from dataclasses import dataclass
from langchain_community.embeddings.baidu_qianfan_endpoint import (
QianfanEmbeddingsEndpoint,
)
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_ollama import OllamaEmbeddings
from langchain_openai import OpenAIEmbeddings
@dataclass(frozen=True)
class EmbeddingSettings:
provider: str
model: str
normalize: bool = True
def build_embeddings(settings: EmbeddingSettings):
if settings.provider == "openai":
return OpenAIEmbeddings(model=settings.model)
if settings.provider == "huggingface":
return HuggingFaceEmbeddings(
model_name=settings.model,
encode_kwargs={"normalize_embeddings": settings.normalize},
)
if settings.provider == "ollama":
return OllamaEmbeddings(model=settings.model)
if settings.provider == "qianfan":
return QianfanEmbeddingsEndpoint(model=settings.model)
raise ValueError(f"Unsupported embedding provider: {settings.provider}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
再把它和缓存、向量库、索引版本组合:
settings = EmbeddingSettings(
provider="huggingface",
model="BAAI/bge-large-zh-v1.5",
)
embeddings = build_embeddings(settings)
2
3
4
5
6
这样后续从 OpenAI 切到 Hugging Face,或者从本地模型切到云厂商,业务链路不需要大改。
# 评估方式
Embedding 模型必须用自己的数据评估。
建议准备一批 golden queries,每条 query 至少包含:
- 用户问题。
- 期望命中的文档 ID。
- 期望命中的 chunk。
- 必须命中的关键事实。
- 不应该命中的干扰文档。
- 权限和租户边界。
评估指标可以从这些开始:
Recall@K:正确 chunk 是否出现在前 K 个结果中。MRR:第一个正确结果排得有多靠前。nDCG:多个相关结果的排序质量。- 在线延迟:query embedding + vector search 的总耗时。
- 成本:每万条文档入库和每万次查询的成本。
- 稳定性:限流、超时、重试、批处理失败率。
不要只看模型排行榜。排行榜不能替你覆盖业务文档质量、chunk 策略、metadata filter 和用户真实问法。
# 问题
其他 Embedding 模型接入时,最容易踩这些坑。
第一,导入路径过时。很多旧示例还在使用 langchain_community.embeddings,新项目应优先查看 provider 独立包。
第二,只比较 demo 查询,不做固定评估集。Embedding 的好坏必须放到自己的知识库里测。
第三,忽略向量维度。模型一换,向量库 schema、索引、缓存和历史数据都可能要重建。
第四,忽略 normalize。部分模型需要归一化向量才能更稳定地使用 cosine similarity。
第五,混用 query 和 document 策略。有些模型对查询和文档有不同 instruction,统一当成普通文本可能损失效果。
第六,生产服务启动时才下载本地模型。模型下载失败会直接影响发布。
# 拓展
Embedding 选型可以继续往三个方向扩展。
第一,混合召回。Dense Embedding 适合语义相似,BM25 适合关键词精确匹配,生产里经常用 dense + sparse + rerank。
第二,领域微调。如果通用模型对业务术语理解不好,可以考虑对比掌握、hard negative mining 或使用领域模型。
第三,模型路由。不同知识库、不同语言、不同业务线可以使用不同 Embedding 模型,但必须用统一索引版本和评估体系治理。
# 实际生产是否使用
会,而且经常会使用多种 Embedding。
比如内部知识库可能用本地 Hugging Face 模型,公开文档问答用 OpenAI 或云厂商模型,中文业务知识库用国内 provider,代码检索单独使用代码向量模型。
不过生产里很少让业务代码直接散落各种 provider 初始化。更常见的做法是统一封装 Embedding 工厂、配置中心、缓存层和评估任务。
# 现在是否抛弃
没有抛弃,但旧写法需要更新。
HuggingFaceEmbeddings 仍然可用,不过新项目应优先使用 langchain_huggingface 包。HuggingFaceEndpointEmbeddings 也在 langchain_huggingface 中。百度千帆这类集成仍可通过 langchain_community 使用。
真正被淘汰的不是“其他 Embedding 模型”,而是把模型接入当作几行示例代码的思路。当前生产关注的是 provider 独立包、统一接口、版本治理、缓存、评估和可观测性。
# 最新生产如何实现
当前更推荐这样做:
第一,使用 provider 独立包接入模型,例如 langchain-huggingface、langchain-ollama、langchain-openai、langchain-cohere。
第二,通过统一工厂返回 Embeddings 实例,业务链路只依赖 embed_query() 和 embed_documents()。
第三,把 provider、model、dimension、normalize、preprocess_version、chunk_version 写入索引版本。
第四,用固定评估集比较候选模型,不直接在生产索引上盲切。
第五,模型切换时新建索引版本,重新入库,灰度切流,再下线旧索引。
第六,配合 CacheBackedEmbeddings 或自研缓存层,降低重复入库成本。
一个生产级最小结构可以是:
class RAGEmbeddingRuntime:
def __init__(self, settings, cache, metrics):
self.settings = settings
self.embeddings = build_embeddings(settings)
self.cache = cache
self.metrics = metrics
def embed_query(self, query: str) -> list[float]:
self.metrics.count("embedding.query", provider=self.settings.provider)
return self.embeddings.embed_query(query)
def embed_documents(self, texts: list[str]) -> list[list[float]]:
self.metrics.count(
"embedding.documents",
provider=self.settings.provider,
count=len(texts),
)
return self.embeddings.embed_documents(texts)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这层封装看起来简单,但它让后续的缓存、限流、重试、指标、trace、模型切换和索引版本治理都有地方落。
# 总结
选择其他 Embedding 模型,本质是在效果、成本、合规、延迟、吞吐和运维复杂度之间做工程取舍。
LangChain 提供的是统一接口和 provider 集成,不是自动选型答案。真正的生产能力来自三件事:把模型接入封装成稳定接口,把模型版本纳入索引治理,把模型效果放进固定评估集里持续验证。
参考: