在自然语言处理领域,文本嵌入(Text Embeddings)是一项核心技术,它能够将文本转换为高维向量表示,使计算机能够更好地理解和处理文本数据。本文将通过实践示例,展示如何使用LangChain框架来实现文本嵌入,并对比OpenAI和BGE(BAAI)两种不同的嵌入模型。
使用 OpenAI 的 text-embedding-3-small 模型
首先,让我们来看看如何使用 OpenAI 的最新文本嵌入模型。在这个示例中,我们将使用 text-embedding-3-small 模型,这是 OpenAI 推出的一个轻量级但高效的嵌入模型。
环境配置
首先需要设置必要的环境变量:
export OPENAI_API_KEY=xxx
export OPENAI_BASE_URL=https://models.github.ai/inference
export EMBEDDING_MODEL_NAME=openai/text-embedding-3-small代码实现与结果
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import TextLoader
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain_postgres.vectorstores import PGVector
from langchain_text_splitters import RecursiveCharacterTextSplitter
load_dotenv()
EMBEDDING_MODEL_NAME = os.getenv("EMBEDDING_MODEL_NAME")
def main():
embedding_model = OpenAIEmbeddings(model=EMBEDDING_MODEL_NAME)
embeddings = embedding_model.embed_documents(
[
"The sun rises in the east.",
"Apples are my favorite fruit.",
"Can you help me with this task?",
"Python is a popular programming language.",
"Let's meet at the coffee shop around noon.",
]
)
for embedding in embeddings:
print_embedding(embedding)
def print_embedding(embedding, limit=2):
dim = len(embedding)
if dim <= 2 * limit:
print(f"dim={dim}, {embedding}")
else:
head = embedding[:limit]
tail = embedding[-limit:]
print(
f"dim={dim}, [{', '.join(map(str, head))}, ..., {', '.join(map(str, tail))}]"
)
if __name__ == "__main__":
main()上面代码执行输出:
dim=1536, [-0.0470157265663147, 0.00039500289130955935, ..., 0.025846343487501144, 0.005138499196618795]
dim=1536, [0.01677563786506653, -0.02164749801158905, ..., 0.013688168488442898, 0.019945280626416206]
dim=1536, [0.009912223555147648, 0.03735930845141411, ..., 0.00619862973690033, 0.024403614923357964]
dim=1536, [0.02195780724287033, -0.029236186295747757, ..., 0.015210994519293308, 0.0041860900819301605]
dim=1536, [-0.02176843397319317, -0.038796138018369675, ..., 0.005539442412555218, -0.009550170972943306]从输出可以看到,OpenAI 的 text-embedding-3-small 模型生成的是 1536 维的向量。每个文本片段都被转换成了一个高维向量,这些向量可以用于后续的相似度计算、文本检索等任务。
使用 BGE-Large-ZH 模型
接下来,我们将使用 BAAI(北京智源)开源的 BGE-Large-ZH 模型来生成文本嵌入。这是一个专门针对中文优化的强大嵌入模型。
环境配置与分词器(tokenizer)相关配置和词表文件
为了使用 BGE 模型,我们需要设置相关环境变量:
export OPENAI_API_KEY=xxx
export OPENAI_BASE_URL=https://10.0.10.8:8080/v1
export EMBEDDING_MODEL_NAME=BAAI/bge-large-zh-v1.5
export HF_HUB_OFFLINE=1在使用BGE-Large-ZH这类Hugging Face上的模型时,HF_HUB_OFFLINE=1 的作用是让Transformers只从本地缓存加载所需的模型和分词器资源,不会联网下载。
使用Hugging Face CLI下载模型所需要的分词器(tokenizer)相关配置和词表文件:
export HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download BAAI/bge-large-zh-v1.5 tokenizer_config.json vocab.txt tokenizer.json special_tokens_map.jsontokenizer_config.json: 分词器的配置文件,指定了使用哪种类型的 tokenizer 以及额外的参数(比如是否大小写敏感)。vocab.txt: 分词器的词表文件(常见于 BERT 类 tokenizer),列出了每个token的字符串表示。tokenizer.json: 是一个Hugging Face通用格式的tokenizer文件,包含了 tokenization 的规则、词表、预处理逻辑等(用于快速加载)。special_tokens_map.json: 映射特殊token(如[PAD],[CLS],[SEP],[UNK]等)到词表中的实际token。
代码实现与依赖安装
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import TextLoader
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain_postgres.vectorstores import PGVector
from langchain_text_splitters import RecursiveCharacterTextSplitter
load_dotenv()
EMBEDDING_MODEL_NAME = os.getenv("EMBEDDING_MODEL_NAME")
def main():
embedding_model = OpenAIEmbeddings(
model=EMBEDDING_MODEL_NAME, tiktoken_enabled=False
)
embeddings = embedding_model.embed_documents(
[
"The sun rises in the east.",
"Apples are my favorite fruit.",
"Can you help me with this task?",
"Python is a popular programming language.",
"Let's meet at the coffee shop around noon.",
]
)
for embedding in embeddings:
print_embedding(embedding)
def print_embedding(embedding, limit=2):
dim = len(embedding)
if dim <= 2 * limit:
print(f"dim={dim}, {embedding}")
else:
head = embedding[:limit]
tail = embedding[-limit:]
print(
f"dim={dim}, [{', '.join(map(str, head))}, ..., {', '.join(map(str, tail))}]"
)
if __name__ == "__main__":
main()代码中使用tiktoken_enabled=False 是因为 BGE-Large-ZH 不是 OpenAI 的模型,不能用 tiktoken 分词器(tiktoken 只支持 OpenAI 的模型分词方式),而是需要用 transformers 生态的分词器。因此,项目依赖项中需要安装 transformers(如 uv add transformers),并且提前用 huggingface-cli 把分词器文件下载到本地缓存,配合 HF_HUB_OFFLINE=1 实现分词器离线加载。
uv add transformers
Resolved 74 packages in 1.94s
Prepared 7 packages in 2.07s
Installed 7 packages in 54ms
+ filelock==3.18.0
+ fsspec==2025.7.0
+ hf-xet==1.1.5
+ huggingface-hub==0.33.4
+ safetensors==0.5.3
+ tokenizers==0.21.2
+ transformers==4.53.2综上,HF_HUB_OFFLINE=1、tiktoken_enabled=False 和 uv add transformers 这三者共同保证了:
- 分词器文件离线可用,执行代码时无需联网
- 使用transformers分词和推理,兼容Hugging Face生态的中文嵌入模型
- 环境依赖齐全,避免tiktoken相关报错
注意如果不安装transformers的依赖运行代码就会报下名的错误
ValueError: Could not import transformers python package.
This is needed for OpenAIEmbeddings to work without `tiktoken`.
Please install it with `pip install transformers`. 前面的示例代码执行输出:
dim=1024, [-0.01738620176911354, 0.00904729962348938, ..., 0.031994983553886414, 8.408486610278487e-05]
dim=1024, [0.029070701450109482, -0.007537286728620529, ..., -0.006933676544576883, 0.020322958007454872]
dim=1024, [0.031692489981651306, -0.0404939241707325, ..., -0.043061383068561554, 0.04803839698433876]
dim=1024, [-0.021623020991683006, -0.010026389732956886, ..., 0.03169519826769829, 0.002862850669771433]
dim=1024, [0.05992703512310982, 0.012870600447058678, ..., -0.0002291803975822404, 0.004276012070477009]从输出结果可以看到,BGE-Large-ZH 模型生成的是 1024 维的向量,相比OpenAI的模型维度更小,但仍然能够有效地捕捉文本的语义信息。
两种模型的对比
维度差异:
- OpenAI text-embedding-3-small:1536 维
- BGE-Large-ZH:1024 维
部署方式:
- OpenAI 模型:需要API密钥,依赖在线服务
- BGE 模型:可以离线部署,支持本地运行
语言优化:
- OpenAI 模型:通用多语言支持
- BGE 模型:专门针对中文场景优化
思考
为什么tiktoken_enabled=False时,transformers需要下载词表文件和分词器配置到本地呢?实际的嵌入不是调用自托管的API在服务端生成的吗?
为了支持token count限制检测或模型输入预处理(如切分),LangChain 需要提前知道使用的模型的tokenizer是什么、token限制是多少,所以当你设置tiktoken_enabled=False后,LangChain会尝试使用Hugging Face的 transformers来加载tokenizer。因此即使是调用远程API,LangChain在以下情况也必须知道分词方式:
- 判断token是否超长
- 切割文本时,按token而非按字符
- 对输入批次进行token数统计
- 有些实现(如
embed_query)会先自己处理query,再发给接口
虽然实际的嵌入是在服务端生成的,但服务端前的输入预处理、分词控制(特别是多段文本嵌入)常常是在本地做的。
总结
通过本文的实践,我们展示了如何使用 LangChain 框架来实现文本嵌入,并对比了两种不同的嵌入模型。无论是选择 OpenAI 的商业服务还是开源的 BGE 模型,LangChain都提供了统一的接口,使得我们能够方便地集成和使用这些模型。选择哪种模型主要取决于具体的应用场景、语言需求以及部署环境的限制。
- 如果调用的是 OpenAI 官方 API,并设置
tiktoken_enabled=True,LangChain 会使用 tiktoken,本地不需要 transformers 分词器文件。 - 如果用的是本地部署的 HuggingFace 模型 API(如 BGE),LangChain 会用 transformers 加载 tokenizer 来做本地 token 统计与控制逻辑,所以需要提前下载并离线缓存 tokenizer 文件。