LangChain 09:文档嵌入

前言

切分得到的文本块要变成可检索的向量,才能写入向量库做相似度搜索。
嵌入模型(Embedding) 把句子或文档映射成固定维浮点向量;相近语义在向量空间中更接近。
本文讲 LangChain 1.x 下如何用 init_embeddings / OpenAIEmbeddings 初始化模型,以及对句子、文档做向量化。
聊天模型与嵌入模型是两套能力:ark-code-latest 不能当嵌入用。
整链见《LangChain 06:RAG 概要》;加载与切分见《LangChain 07:文档加载器》《LangChain 08:文本切分器》;入库见《LangChain 10:Milvus 入库》。
下文需要 Python 3.12+,依赖用 uv 管理。
示例脚本放在代码子目录,在该目录执行 uv run python xxx.py

依赖

建议使用 Python 3.12 及以上。
uv 初始化工程并声明依赖(版本请按项目实际调整)。

1
2
3
4
uv init langchain-embeddings
cd langchain-embeddings
uv venv --python 3.12
uv add "langchain>=1.0,<2.0" langchain-openai python-dotenv rich

后续可直接 uv run python demo.py
若习惯激活,Windows 使用 .venv\Scripts\activate,macOS / Linux 使用 source .venv/bin/activate

在项目根目录创建 .env(不要提交进 Git),按你实际开通的嵌入服务填写。
下面以硅基流动为例(OpenAI 兼容协议);也可换成 CloseAI 等其它兼容端点。

1
2
SILICONFLOW_API_KEY=你的硅基流动 API Key
SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1

若用 CloseAI,可改为 CLOSEAI_API_KEY / CLOSEAI_BASE_URL,模型名换成该平台提供的嵌入模型。

实现

初始化模型

推荐用 init_embeddings,前缀 openai: 表示走 OpenAI 兼容客户端。
Pro/BAAI/bge-m3 在硅基流动上常见输出维度为 1024,入库时维度必须与 Collection 一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os

from dotenv import load_dotenv
from langchain.embeddings import init_embeddings
from rich import print as rprint

load_dotenv(override=True)

embedding_model = init_embeddings(
model="openai:Pro/BAAI/bge-m3",
api_key=os.environ["SILICONFLOW_API_KEY"],
base_url=os.environ["SILICONFLOW_BASE_URL"],
)
rprint(type(embedding_model))

也可用 OpenAIEmbeddings 直连同一兼容端点(效果等价,写法更「OpenAI 风格」)。

1
2
3
4
5
6
7
8
9
10
11
12
import os

from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings

load_dotenv(override=True)

embedding_model_alt = OpenAIEmbeddings(
model="Pro/BAAI/bge-m3",
api_key=os.environ["SILICONFLOW_API_KEY"],
base_url=os.environ["SILICONFLOW_BASE_URL"],
)

CloseAI 等平台只需换 Key、Base URL 与模型名,例如 openai:text-embedding-3-large(维数以厂商文档为准,常与 bge-m3 不同)。

句子向量化

单条查询用 embed_query,返回 list[float]
检索时用户问题通常走这条 API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import os

from dotenv import load_dotenv
from langchain.embeddings import init_embeddings
from rich import print as rprint

load_dotenv(override=True)

embedding_model = init_embeddings(
model="openai:Pro/BAAI/bge-m3",
api_key=os.environ["SILICONFLOW_API_KEY"],
base_url=os.environ["SILICONFLOW_BASE_URL"],
)

text = "你好,很高兴认识你"
vec = embedding_model.embed_query(text)

rprint(len(vec))
rprint(vec[:5])

维数应与所选模型一致(bge-m3 多为 1024)。
前几维只用于确认调用成功,不必人工解读数值含义。

文档向量化

多条文本用 embed_documents,返回与输入等长的向量列表。
入库前对切分后的 page_content 列表调用即可。

1
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
import os

from dotenv import load_dotenv
from langchain.embeddings import init_embeddings
from rich import print as rprint

load_dotenv(override=True)

embedding_model = init_embeddings(
model="openai:Pro/BAAI/bge-m3",
api_key=os.environ["SILICONFLOW_API_KEY"],
base_url=os.environ["SILICONFLOW_BASE_URL"],
)

texts = [
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!",
]

vectors = embedding_model.embed_documents(texts)

for text, vec in zip(texts, vectors):
rprint(f"{text} -> {vec[:3]}")

结合 CSV 文档

可先把表格读成 Document,再对 page_content 批量嵌入。
样例文件放在代码上级的 data/,路径用 ../data/...

创建 articles.csv

../data/articles.csv 中写入:

1
2
3
id,title,content,author
1,Introduction to Python,Python is a popular programming language.,John Doe
2,Data Science Basics,Data science involves statistics and machine learning.,Jane Smith

加载并嵌入

避免已日落的 langchain-community,用标准库读 CSV 再嵌入。

1
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
35
import csv
import os
from pathlib import Path

from dotenv import load_dotenv
from langchain.embeddings import init_embeddings
from langchain_core.documents import Document
from rich import print as rprint

load_dotenv(override=True)

path = Path("../data/articles.csv")
docs: list[Document] = []
with path.open(encoding="utf-8", newline="") as f:
for i, row in enumerate(csv.DictReader(f)):
page_content = "\n".join(f"{k}: {v}" for k, v in row.items())
docs.append(
Document(
page_content=page_content,
metadata={"source": str(path), "row": i},
)
)

embedding_model = init_embeddings(
model="openai:Pro/BAAI/bge-m3",
api_key=os.environ["SILICONFLOW_API_KEY"],
base_url=os.environ["SILICONFLOW_BASE_URL"],
)

texts = [d.page_content for d in docs]
vectors = embedding_model.embed_documents(texts)

rprint(len(vectors))
for text, vec in zip(texts, vectors):
rprint(f"{text}\n{vec[:3]}\n")

真实 RAG 中通常先切分再 embed_documents,而不是整表一行不切。

本地还是在线

嵌入 多数情况优先在线 API;隐私/内网或长期高 QPS 再考虑本地 GPU。
选型对照见《LangChain 06:RAG 概要》中「本地还是在线」;本文示例按在线兼容端点书写。
换本地时仍须保证入库与检索使用同一模型与同一维数。

验证

  1. 配置 .env 后跑「句子向量化」,打印维数应符合模型说明。
  2. 跑「文档向量化」,向量条数等于文本条数。
  3. 跑「结合 CSV」,每行文档都有对应向量预览。

总结

  1. init_embeddings("openai:模型名", api_key=..., base_url=...) 对接兼容服务。
  2. 查询用 embed_query;批量文档用 embed_documents
  3. 维数必须与后续向量库 Collection 的 dimension 一致。
  4. 默认优先在线嵌入;换本地时仍保持同一模型与维数。
  5. 下一步把向量写入 Milvus,见《LangChain 10:Milvus 入库》。