《Learning Langchain》阅读笔记8-RAG(4)在vector store中存储embbdings

什么是 vector store?

与专门用于存储结构化数据(如 JSON 文档或符合关系型数据库模式的数据)的传统数据库不同,vector stores处理的是非结构化数据,包括文本和图像。像传统数据库一样,vector stores也能执行创建、读取、更新、删除(CRUD)以及搜索操作。

Vector stores解锁了各种各样的用例,包括可扩展的应用程序,这些应用程序利用AI来回答有关大型文档的问题,如图2-4所示。

目前,有大量的向量存储提供商可供选择,每一家都擅长不同的功能。你应根据应用程序的关键需求来进行选择,这些需求包括多租户支持、元数据筛选能力、性能、成本以及可扩展性。

尽管 vector store是专门用于管理向量数据的细分数据库,但使用它们仍存在一些缺点:

  • 大多数 vector store相对较新,可能无法经受时间的考验。

  • 管理和优化 vector store可能存在相对陡峭的学习曲线。

  • 管理一个独立的数据库会增加应用程序的复杂性,并可能消耗宝贵的资源。

幸运的是,向量存储的能力最近已经通过 pgvector 扩展,被扩展到 PostgreSQL(一个流行的开源关系型数据库)。这使你可以使用已经熟悉的同一个数据库,来同时支持事务表(例如用户表)和向量搜索表。

使用 PGVector

要使用 Postgres 和 PGVector,你需要按照以下步骤进行设置:

  • 确保在您的计算机上安装了 Docker

  • 在终端中运行以下命令; 它将在您的计算机上启动一个在端口 6024 运行的 Postgres 实例:

    bash 复制代码
    docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
  • 打开您的 Docker 仪表板容器,您应该在 pgvector-container 旁边看到一个绿色的运行状态。

  • 将连接字符串保存到代码中以供以后使用;我们以后需要使用它:

    复制代码
    postgresql+psycopg://langchain:langchain@localhost:6024/langchain

如果你是刚开始接触向量数据库或 LangChain,强烈建议先用 Docker,后期再考虑更复杂的本地部署或云服务集成(如 AWS RDS + pgvector)。Docker 是一个"容器"工具,它可以把应用程序和它运行所需要的一切(比如操作系统、依赖、库等)打包在一个"箱子"里运行。

假设你要运行一个 PostgreSQL + PGVector 的数据库,你需要:

  • 安装 PostgreSQL

  • 配置好数据库用户和端口

  • 安装 pgvector 插件

  • 保证版本兼容

这些都很麻烦,对吧?

但用了 Docker,只需要一条命令,它就帮你:

  • 自动下载带 pgvector 的 PostgreSQL

  • 自动配置好用户密码端口

  • 独立运行在它自己的"容器"里,不影响你电脑上其他软件

现在我们从第一步安装Docker开始,

1. 安装 Docker

去官网下载并安装:Docker官网

2. 运行 Docker 容器

复制代码
```bash
docker run \
--name pgvector-container \
-e POSTGRES_USER=langchain \
-e POSTGRES_PASSWORD=langchain \
-e POSTGRES_DB=langchain \
-p 6024:5432 \
-d pgvector/pgvector:pg16
```

这会创建一个名为 pgvector-container 的容器,在本地的 6024 端口 启动 PostgreSQL 数据库,数据库名和账户密码均为 langchain。

3. 确认数据库是否运行正常

打开 Docker 桌面,确认容器 pgvector-container 显示为绿色状态(Running)。

4. LangChain 中连接 pgvector

确保你已经安装了必要的包:

bash 复制代码
    pip install langchain psycopg2-binary sqlalchemy

上面的步骤全都配置结束,大约花费50分钟。

现在我们来在langchain中连接pgvector

记得首先pip install langchain-postgres

python 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_core.documents import Document
import uuid

# Load the document, split it into chunks
loader = TextLoader("./test.txt", encoding="utf-8")
raw_documents =  loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, 
    chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# embed each chunk and insert it into the vector store
model_name = "sentence-transformers/all-mpnet-base-v2"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}

embeddings_model = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)

connection = 'postgresql+psycopg://langchain:langchain@localhost:6024/langchain'

db = PGVector.from_documents(documents, embeddings_model, connection=connection)

报错:ModuleNotFoundError: No module named 'psycopg'

你这个错误信息非常明确,是因为 psycopg 无法正确加载 libpq 库(PostgreSQL 的底层通信库),所以在导入 PGVector 时报错。

你使用的是新版的 psycopg(也叫 psycopg3),它和旧版 psycopg2 不一样,需要本地系统有 PostgreSQL 的 C 库支持(libpq)。

改写为以下用法:

  1. 确保你安装了官方 vectorstore 依赖

    pip install langchain psycopg2-binary sqlalchemy

  2. 修改你的 import 语句

    换成官方推荐的 PGVector 接入方式:from langchain.vectorstores.pgvector import PGVector

python 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.vectorstores.pgvector import PGVector
from langchain_core.documents import Document
import uuid

# Load the document, split it into chunks
loader = TextLoader("./test.txt", encoding="utf-8")
raw_documents =  loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, 
    chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# embed each chunk and insert it into the vector store
model_name = "sentence-transformers/all-mpnet-base-v2"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}

embeddings_model = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)

connection = 'postgresql+psycopg2://langchain:langchain@localhost:6024/langchain'

db = PGVector.from_documents(
    documents=documents,
    embedding=embeddings_model,
    connection_string=connection,  # ✅ 这里改成 connection_string
    collection_name="my_collection",  # 可选,但推荐加
)

这个查询会在你之前加载的文档中,返回与"为什么会有"上下文窗口"(context window)"这个问题最相似的段落,如果它能正常返回结果,就说明数据确实已经存入 pgvector 并可用啦。

python 复制代码
# 查询一下看看有没有数据,k表示打印几条结果
results = db.similarity_search("为什么会有"上下文窗口"(context window)", k=2)

for i, doc in enumerate(results, 1):
    print(f"【第{i}条结果】", doc.page_content)
复制代码
【第1条结果】 > [!NOTE] **为什么会有"上下文窗口"(context window)这个限制?**
>
>1. 资源限制:计算成本高
 >   每次你和大语言模型对话,它其实是在处理一个非常复杂的数学计算过程,背后是巨大的矩阵运算。 
 >   输入越长,占用的 显存(GPU memory) 和 计算时间 就越多。
>输入+输出的长度太长,会让显卡扛不住,或者处理速度变得非常慢。
>所以模型必须设置一个"上限"------这就是 context window,也叫"上下文窗口"。
 >   
>
>2. 模型设计:Transformer 架构的限制
 >   大语言模型(如 GPT)基于 Transformer 架构。
  >  Transformer 是靠"注意力机制"处理每一个 token 的。
  >  每加一个 token,就要考虑它和之前所有 token 的关系,所以计算量是平方级别增长的(O(n²))。
   > 这意味着:
  >  上下文越长,注意力机制越慢,成本越高,效果也可能下降。
>3. 训练方式决定了这个限制
   > 模型训练时,是在固定长度的上下文窗口中学习的,比如 2048、4096 或 8192 个 token。
   > 如果你测试时突然给它一个比训练时长得多的输入,模型可能根本不知道怎么处理。
 >   所以训练时设置了窗口,使用时就要遵守这个规则。
>    
>    ---------------------
>一个简单类比:
>
>想象你在读一本书,但你只能记住最近的一页内容(上下文窗口),太久以前的内容就记不清了。
>
>模型也是这样,它记得的"最近内容"数量是有限的。
【第2条结果】 > [!NOTE] **为什么会有"上下文窗口"(context window)这个限制?**
>
>1. 资源限制:计算成本高
 >   每次你和大语言模型对话,它其实是在处理一个非常复杂的数学计算过程,背后是巨大的矩阵运算。 
 >   输入越长,占用的 显存(GPU memory) 和 计算时间 就越多。
>输入+输出的长度太长,会让显卡扛不住,或者处理速度变得非常慢。
>所以模型必须设置一个"上限"------这就是 context window,也叫"上下文窗口"。
 >   
>
>2. 模型设计:Transformer 架构的限制
 >   大语言模型(如 GPT)基于 Transformer 架构。
  >  Transformer 是靠"注意力机制"处理每一个 token 的。
  >  每加一个 token,就要考虑它和之前所有 token 的关系,所以计算量是平方级别增长的(O(n²))。
   > 这意味着:
  >  上下文越长,注意力机制越慢,成本越高,效果也可能下降。
>3. 训练方式决定了这个限制
   > 模型训练时,是在固定长度的上下文窗口中学习的,比如 2048、4096 或 8192 个 token。
   > 如果你测试时突然给它一个比训练时长得多的输入,模型可能根本不知道怎么处理。
 >   所以训练时设置了窗口,使用时就要遵守这个规则。
>    
>    ---------------------
>一个简单类比:
>
>想象你在读一本书,但你只能记住最近的一页内容(上下文窗口),太久以前的内容就记不清了。
>
>模型也是这样,它记得的"最近内容"数量是有限的。

db.similarity_search() 这个方法将通过以下过程找到最相关的文档(之前已对其进行索引):

  • 搜索查询 (在这种情况下是一个词或短语)将被发送到嵌入模型以生成其向量表示。

  • 接着,系统会在 Postgres 数据库中运行查询,找出与该向量最相似的 N 个已存储的嵌入(本例中为2个)。

  • 最后,它会提取每个相似嵌入所对应的原始文本内容和元数据

  • 模型将返回一个文档列表 ,并按照它们与查询向量的相似度高低进行排序------最相似的排在前面,依此类推。

!NOTE\] **如何找出与该向量最相似的 N 个已存储的嵌入?** 你现在已经进入了「向量数据库的核心机制」------\*\*相似度搜索(vector similarity search)\*\*的本质。 简要回答:是通过 向量之间的相似度计算公式 + pgvector 插件中的索引与比较操作符 实现的。 步骤拆解如下: ① **查询向量的生成(embedding)** 你输入一段查询(query,比如"向量数据库的作用是什么?"),LangChain 会: `query_embedding = embeddings_model.embed_query("向量数据库的作用是什么?")` 这个 query_embedding 是一个 高维向量数组,比如: `[0.123, -0.341, ..., 0.442] # 维度可能是 384、768、1024...` ② **和数据库中所有嵌入进行相似度对比** PostgreSQL + pgvector 使用 SQL 语句执行相似度计算,例如: SELECT *, embedding <#> '[0.123, 0.341, ..., 0.442]' FROM langchain_pg_embedding_my_collection ORDER BY embedding <#> '[...]' LIMIT 2; `<#>` 是 pgvector 提供的 "余弦距离"操作符。值越小,表示越相似。 相似度通常用 余弦相似度(cosine) 或 欧氏距离(L2) ③ **查找最相似的 Top-N 条** 执行 SQL 时用 ORDER BY + LIMIT N,就能找出最相似的 N 条记录。

可视化向量数据库中的数据:使用pgAdmin

假如我们想可视化向量数据库中的数据,可以下载单独的可视化软件,比如:

这里我选择第一个免费开源软件的windows版。

下载安装好后,连接到server。

随后我想要查看一下向量数据库存储的是什么数据,接下来手把手带你操作如何查看:

方法一:图形化方式(适合浏览数据)

步骤:

1.左侧树状结构中,点击展开:

  • langchain → Databases → langchain

  • 然后继续展开:Schemas → public → Tables

2.找到你插入文档时自动创建的表,名称通常是类似于你设定的 collection_name,例如:

复制代码
langchain_pg_embedding

3.右键点击这个表 → 选择 View/Edit Data → All Rows

然后你就可以看到你之前插入的向量、文本内容、metadata 等字段了!

一共有16条数据:

方法二:用 SQL 查询(适合精准操作)

步骤:

在 pgAdmin 顶部点击:🧾 SQL(或按快捷键 F5快速执行)

输入并执行下面这句 SQL:

复制代码
SELECT * FROM langchain_pg_embedding LIMIT 10;

可以看到已经显示前10条记录了。

接下来介绍几条常用的SQL语句(以表名langchain_pg_embedding为例):

1.查询(Read)

sql 复制代码
-- 查询所有数据
SELECT * FROM langchain_pg_embedding;

-- 查询包含关键词 "向量" 的记录(全文内容)
SELECT * FROM langchain_pg_embedding
WHERE document LIKE '%向量%';

-- 查询某个 ID 的向量内容
SELECT * FROM langchain_pg_embedding
WHERE id = 'your-vector-id';
  1. 插入(Insert)
    一般不建议手写插入向量(除非你很熟),但可以这样手动插入一个文档:
sql 复制代码
INSERT INTO langchain_pg_embedding (document, metadata, embedding)
VALUES (
  '这是一段测试文本',
  '{"source": "manual"}',
  '[0.1, 0.2, 0.3, ...]'  -- 注意这里向量必须是 float[],长度必须匹配模型维度
);
  1. 删除(Delete)
sql 复制代码
-- 根据 ID 删除
DELETE FROM langchain_pg_embedding
WHERE id = 'your-vector-id';

-- 删除所有数据(⚠️危险操作)
DELETE FROM langchain_pg_embedding;
  1. 修改(Update)
sql 复制代码
-- 修改 metadata
UPDATE langchain_pg_embedding
SET metadata = '{"source": "updated"}'
WHERE id = 'your-vector-id';

-- 修改文档内容
UPDATE langchain_pg_embedding
SET document = '这是新的文档内容'
WHERE id = 'your-vector-id';

我们的数据库中有这些字段名:

还可以使用langchain语句向现有数据库添加更多文档。在这个例子中,我们使用可选的 ids 参数为每个文档分配标识符,这使我们可以在以后更新或删除它们。

让我们看这个例子:

python 复制代码
# 生成两个唯一的 UUID 作为向量的主键(id)
# ids 是可选的,如果你不传,LangChain 会自动生成 UUID
# 但传 ids 的好处是后面可以用:db.delete(ids=["your-id"])  # 删除指定向量
# 这样你能精确管理向量库中的内容
ids = [str(uuid.uuid4()), str(uuid.uuid4())]

# 将新的文本片段(两个句子)嵌入,并插入到你已经连接的 pgvector 数据表中
db.add_documents(
    [
        Document(
            page_content="there are cats in the pond",
            metadata={"location": "pond", "topic": "animals"},
        ),
        Document(
            page_content="ducks are also found in the pond",
            metadata={"location": "pond", "topic": "animals"},
        ),
    ],
    ids=ids, 
)
复制代码
['0cdf295d-5e3c-4757-bc8c-457c05927eb7',
 'a7a67123-a7bf-4b16-990b-56d430a78f67']

我们的table中的embedding数量从16增加到了18。

我们在这里使用的db.add_documents() 方法将遵循与PGVector.from_documents() 类似的过程:

  • 为传递的每个文档创建嵌入,使用选择的模型。
  • 将嵌入、文档元数据和文档文本内容存储在 Postgres 中,以便进行搜索。

删除操作的例子:

python 复制代码
db.delete(ids=['0cdf295d-5e3c-4757-bc8c-457c05927eb7'])
db.delete(ids=['a7a67123-a7bf-4b16-990b-56d430a78f67'])

这样就从向量数据库中删除指定 ID 的嵌入向量记录,就是我们刚刚插入的那两条。

总结

本小节我们学习了如何在vector store中存储embeddings,并且使用了PostgreSQL数据库。

通过docker连接数据库,还通过插件PGVector这个为 PostgreSQL 数据库设计的向量(embedding)存储与相似度搜索插件,使得你可以在 PostgreSQL 中直接存储向量,并进行高效的相似度检索(vector similarity search)。

最后还使用了免费的可视化PostgreSQL数据库的软件pgAdmin对数据库的tables中的数据进行查看。

收获满满的一章。感谢阅读!下一节我们来讲讲如何跟踪文档的更改。

相关推荐
Python自动化办公社区1 分钟前
Python 3.14:探索新版本的魅力与革新
开发语言·python
weixin_贾1 小时前
最新AI-Python机器学习与深度学习技术在植被参数反演中的核心技术应用
python·机器学习·植被参数·遥感反演
张槊哲1 小时前
函数的定义与使用(python)
开发语言·python
船长@Quant1 小时前
文档构建:Sphinx全面使用指南 — 实战篇
python·markdown·sphinx·文档构建
偶尔微微一笑2 小时前
AI网络渗透kali应用(gptshell)
linux·人工智能·python·自然语言处理·编辑器
Want5952 小时前
从ChatGPT到GPT-4:大模型如何重塑人类认知边界?
chatgpt·aigc
船长@Quant4 小时前
文档构建:Sphinx全面使用指南 — 基础篇
python·markdown·sphinx·文档构建
喵手4 小时前
从 Java 到 Kotlin:在现有项目中迁移的最佳实践!
java·python·kotlin
liuweidong08024 小时前
【Pandas】pandas DataFrame rsub
开发语言·python·pandas
CH3_CH2_CHO4 小时前
不吃【Numpy】版
开发语言·python·numpy