文本向量与向量数据库(Embedding / Redis / Pinecone / MMR)
文档切好之后,要把文字变成计算机能比较「语义相似度」的数字------这就是文本向量(Embedding) ;而存储、检索这些向量的地方,就是向量数据库 。
本篇讲解:向量、维度、向量空间与余弦相似度,OpenAI 嵌入模型的用法,以及 InMemory、Redis、Pinecone 三种向量存储和三种搜索方式。
目录
- 什么是文本向量(Embedding)
- 向量、维度、向量空间
- 余弦相似度
- OpenAIEmbeddings 嵌入模型
- embed_documents 与 embed_query
- 向量存储(Vector stores)概念
- InMemory 内存向量存储
- Redis 向量存储
- Pinecone 向量存储
- 相似性搜索 similarity_search
- 元数据过滤搜索
- MMR 最大边际相关性搜索
- 本篇小结
1. 什么是文本向量(Embedding)
文本向量是一种文本表示技术,把文本(单词、句子、文档)映射为高维空间中的实数向量,让计算机能捕捉文本的语义信息。
核心思想:语义相似的文本,在向量空间中距离也相近。
例如「猫」和「狗」都是宠物,向量距离很近;「猫」和「汽车」语义无关,距离很远。
嵌入模型(Embedding model)就是干这个的:输入一段文本,输出一串固定长度的数字。
2. 向量、维度、向量空间
- 向量 :一串有序数字,例如
[0.12, -0.45, 0.78, ...]。 - 维度:向量里数字的个数。维度越高,能表达的语义信息越丰富,但计算和存储成本也越高。
- 向量空间:所有向量所处的数学空间。可以想象成一张地图,每个词、每句话都是地图上的一个点,语义相近的点聚在一起。
例如 OpenAI 的 text-embedding-3-large 输出 3072 维向量,text-embedding-3-small 输出 1536 维。
3. 余弦相似度
衡量两个向量「方向是否一致」最常用的指标是余弦相似度(Cosine Similarity)。
公式:
A · B Σ(Aᵢ × Bᵢ)
cos(θ) = ─────────── = ─────────────────────────────
|A| × |B| √(ΣAᵢ²) × √(ΣBᵢ²)
- 取值范围:
[-1, 1]。 1:方向完全相同(语义高度相似)。0:方向正交(语义无关)。-1:方向完全相反。
大白话:余弦相似度不关心向量长短,只关心两个向量「指向」是否一致,指向越接近,文本语义越相似。这也是向量库里最常用的距离度量。
4. OpenAIEmbeddings 嵌入模型
bash
pip install -U langchain-openai
python
from langchain_openai import OpenAIEmbeddings
# 定义嵌入模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
常用参数:
model:嵌入模型名称。dimensions:可指定输出维度(部分模型支持缩短向量)。api_key/base_url:与 ChatOpenAI 类似,可接入兼容的嵌入服务。
国产模型同样提供 OpenAI 兼容的嵌入接口,改 base_url 和密钥即可。
5. embed_documents 与 embed_query
两个核心方法:
python
# embed_documents:批量把多个文本块转成向量(建库时用)
vector = embeddings.embed_documents(
[
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!",
]
)
print(len(vector)) # 5:5 个文本
print(len(vector[0])) # 3072:每个向量 3072 维
python
# embed_query:把单个查询转成向量(检索时用)
single_vector = embeddings.embed_query("What's our Q1 revenue?")
print(len(single_vector)) # 3072
为什么要分两个方法?因为文档和查询在某些模型里的嵌入方式不同,分开接口语义更清晰:建库用 embed_documents,查询用 embed_query。
6. 向量存储(Vector stores)概念
向量存储负责:
- 用嵌入模型把文档块转成向量。
- 存储向量及对应文档、元数据。
- 根据查询向量做相似性搜索,返回最相关的文档。
LangChain 提供统一的 VectorStore 接口,支持 InMemory、Redis、Pinecone、Chroma 等多种实现,接口方法基本一致:
add_documents(documents):添加文档。similarity_search(query, k):相似性搜索。max_marginal_relevance_search(...):MMR 搜索。delete(ids / delete_all):删除文档。as_retriever():转成检索器(下一篇讲)。
7. InMemory 内存向量存储
最简单、无需任何外部服务,数据存在内存里,适合学习和小规模原型。
python
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# 定义文档
document_1 = Document(
page_content="我今天早上吃了巧克力煎饼和炒鸡蛋。",
metadata={"source": "tweet"},
)
document_2 = Document(
page_content="天气预报:明天辽宁将有小雨,气温18-25度。",
metadata={"source": "news"},
)
document_3 = Document(
page_content="ChatAnthropic 接口初始化时 model 命名为 anthropic,api_key 从环境变量读取。",
metadata={"source": "langchain docs"},
)
documents = [document_1, document_2, document_3]
# 初始化内存向量库
vector_store = InMemoryVectorStore(embeddings)
# 添加文档,返回文档 ID 列表
ids = vector_store.add_documents(documents=documents)
print(ids)
删除文档:
python
# 全量删除
vector_store.delete(ids=ids)
# 或 result = vector_store.delete(delete_all=True)
8. Redis 向量存储
Redis 是生产中常用的内存数据库,通过 RediSearch / Redis Stack 支持向量检索,性能高、可持久化。
8.1 安装与准备
bash
pip install -U langchain-redis
需要一个运行中的 Redis(带向量检索能力的 Redis Stack),连接形如 redis://host:port。
8.2 初始化
python
from langchain_openai import OpenAIEmbeddings
from langchain_redis import RedisConfig, RedisVectorStore
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
redis_url = "redis://192.168.100.238:6379"
config = RedisConfig(
index_name="qa", # 索引名,类似「表名」
redis_url=redis_url,
metadata_schema=[
{"name": "category", "type": "tag"}, # tag:用于精确匹配
{"name": "num", "type": "numeric"}, # numeric:用于数值比较
],
)
vector_store = RedisVectorStore(embeddings, config=config)
8.3 添加文档(含元数据)
python
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_text_splitters import CharacterTextSplitter
# 分割文档
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base", chunk_size=200, chunk_overlap=50
)
data = UnstructuredMarkdownLoader("../Docs/Markdown/脚手架微服务租房平台Q&A.md").load()
documents = text_splitter.split_documents(data)
# 给每个块加元数据
for i, doc in enumerate(documents, start=1):
doc.metadata["category"] = "QA"
doc.metadata["num"] = i
ids = vector_store.add_documents(documents=documents)
print(f"共编排了{len(ids)}个文档索引")
删除:
python
vector_store.delete(delete_all=True)
vector_store.delete(ids=delete_ids)
9. Pinecone 向量存储
Pinecone 是全托管的生产级向量数据库,负责后端维护、扩展、监控,用户无需自己运维。
9.1 注册与环境变量
- 官网:https://www.pinecone.io/ (需科学上网),选择个人免费版。
- 注册后保存 API Key,设置环境变量
PINECONE_API_KEY。
bash
pip install -U pinecone langchain-pinecone
9.2 创建索引并初始化
python
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone()
index_name = "qa"
if not pc.has_index(index_name):
pc.create_index(
name=index_name,
dimension=3072, # 维度必须和嵌入模型一致
metric="cosine", # 度量方式:余弦相似度
spec=ServerlessSpec(
cloud="aws",
region="us-east-1",
),
)
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
index = pc.Index(index_name)
vector_store = PineconeVectorStore(embedding=embeddings, index=index)
添加、删除文档的方法与 Redis 完全一致(add_documents / delete),控制台可以看到写入的记录。
选型:学习原型用 InMemory;已有 Redis 基础设施用 Redis;想全托管、免运维用 Pinecone。
10. 相似性搜索 similarity_search
传入查询,返回最相似的 k 个文档:
python
results = vector_store.similarity_search(
query="数据库表怎么设计的?",
k=2,
)
for res in results:
print(f"* {res.page_content[:100]}")
print(f"* {res.metadata}")
想要带相似度分数,用 similarity_search_with_score:
python
results = vector_store.similarity_search_with_score(
query="数据库表怎么设计的?",
k=2,
)
for doc, score in results:
print(score, doc.page_content[:50])
11. 元数据过滤搜索
只在满足条件的文档里搜索,用 filter 参数。
Redis 中用字段条件:
python
from langchain_redis import Tag, Num
category_is_qa = Tag("category") == "qa"
num_is_under_50 = Num("num") < 50
filter_condition = category_is_qa & num_is_under_50
results = vector_store.similarity_search(
query="数据库表怎么设计的?",
k=2,
filter=filter_condition,
)
Pinecone 中直接传字典:
python
results = vector_store.similarity_search(
query="数据库表怎么设计的?",
k=2,
filter={"category": "QA"},
)
大白话:元数据过滤就像 SQL 里的 WHERE 条件,先筛范围、再做语义匹配。
12. MMR 最大边际相关性搜索
普通相似性搜索可能返回内容高度重复的几个块。MMR(最大边际相关性) 在「相关性」和「多样性」之间做平衡,避免结果同质化(原理见第 7 篇示例选择器)。
python
mmr_results = vector_store.max_marginal_relevance_search(
query="数据库表怎么设计的?",
k=2,
fetch_k=10,
filter=filter_condition,
)
for doc in mmr_results:
print(f"Content: {doc.page_content[:100]}...")
print(f"Metadata: {doc.metadata}")
关键参数 fetch_k:MMR 是两阶段过程------
- 初步获取 :先按纯相似度从全库取最相似的
fetch_k个候选(广撒网)。 - 重排筛选 :在这
fetch_k个候选里,用 MMR 挑出既相关、彼此又不太相似的k个作为最终结果。
目的:保证相关性的前提下提升结果多样性。
13. 本篇小结
- 文本向量把文本映射成高维数字向量,语义相近则向量相近;维度是向量长度,向量空间是向量所处的「语义地图」。
- 余弦相似度衡量向量方向一致性,取值 -1,1,是最常用的相似度度量。
OpenAIEmbeddings:embed_documents批量建库、embed_query单条查询。- 三种向量库:
InMemoryVectorStore(学习)、RedisVectorStore(自建、高性能)、PineconeVectorStore(全托管)。 - 统一方法:
add_documents/similarity_search/delete。 - 三种搜索:
- 相似性搜索(
similarity_search,带分数用 with_score)。 - 元数据过滤(
filter,相当于 WHERE)。 - MMR(
max_marginal_relevance_search,fetch_k先广撒网、再选 k 个多样化结果)。
- 相似性搜索(
下一篇是本系列收官篇------检索器(Retrievers)与 RAG 完整落地,把「检索 → 拼接提示 → 模型生成」整条链跑通。