8年Java开发者AI转型第二周:向量数据库与Embedding实战 (Day 11-12)

8 阅读8分钟

8年Java开发者AI转型第二周:向量数据库与Embedding实战 (Day 11-12)

系列:8年Java开发者AI转型第二周
上一篇RAG系统深入 + 向量数据库实战(Day 8-10)
本期内容:PgVector 替代 ChromaDB、PostgreSQL 扩展向量检索、生产级 RAG 系统


一、背景

上一篇我们搭起了一个能工作的 RAG 系统,用的是 ChromaDB —— 轻量、本地、零配置,非常适合原型开发。

但原型和生产之间隔着一条巨大的鸿沟:

维度ChromaDBPostgreSQL + pgvector
部署本地文件,单机分布式集群,高可用
并发单进程读写连接池,多并发
事务无 ACID完整事务支持
权限RBAC 角色控制
运维手动备份快照、流复制、 PITR

打个 Java 类比:

  • ChromaDB = ArrayList<HashMap>,开发快,但扛不住量
  • PgVector = MySQL + MyBatis,企业级标配,能水平扩展

本期目标:把 RAG 系统的向量数据库从 ChromaDB 迁移到 PgVector,让系统更接近生产环境。


二、PgVector 前置知识

2.1 什么是 pgvector

pgvector 是 PostgreSQL 的一个扩展,给 Postgres 增加了向量数据类型向量相似度搜索能力。

安装只需要三步:

-- 1. 安装扩展(在任意数据库中执行)
CREATE EXTENSION vector;

-- 2. 创建一张表存储向量
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    embedding vector(1536)  -- 1536 是向量维度,取决于 Embedding 模型
);

-- 3. 创建索引加速检索
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops);

Java 类比

  • CREATE EXTENSION vector ≈ 在 Maven 里加一个依赖
  • vector(1536) ≈ 定义一个 float[1536] 字段
  • ivfflat 索引 ≈ 给数据库字段加 B-Tree 索引

2.2 向量相似度算法

pgvector 支持四种相似度度量:

算法符号说明Java 类比
余弦相似度<=>角度差,不看长度Vector3D.angleBetween()
内积<#>长度×角度dotProduct()
欧氏距离<->直线距离Math.hypot(dx, dy, dz)
曼哈顿距离<+>网格距离不太常用

生产推荐:余弦相似度(<=>),和 ChromaDB 的 cosine 一致。

2.3 索引类型

pgvector 有两种索引:

  • ivfflat:适合中小数据集(百万级以下),建索引快
  • hnsw:适合大数据集(千万级以上),查询更快但建索引慢
-- ivfflat 索引(快速上手)
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

-- hnsw 索引(高性能)
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

经验值lists = 向量数 / 10(ivfflat),m = 16, ef_construction = 64(hnsw)。


三、从 ChromaDB 到 PgVector 的迁移

3.1 架构对比

ChromaDB 架构:
  文档 → Embedding → ChromaDB (本地文件) → 余弦相似度检索

PgVector 架构:
  文档 → Embedding → PostgreSQL (vector 列) → IVFFlat/HNSW 索引检索

差别不大,核心变化只是存储层

3.2 LangChain 集成

LangChain 提供了 langchain-postgres 包,让 PgVector 的使用方式和 ChromaDB 几乎一样:

from langchain_postgres import PGVector
from langchain_postgres.vectorstores import PGVector as PGVectorStore

# ChromaDB 写法
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings,
)

# PgVector 写法

# PgVector 连接信息
PG_CONNECTION = (
    "postgresql://root:rootPassword@localhost:5432/rag_db"
)
COLLECTION_NAME = "kkfile_documents"
vectorstore = PGVectorStore(
    collection_name=COLLECTION_NAME,
    embeddings=embeddings,
    connection=PG_CONNECTION,
    use_jsonb=True,
)

看到没有?API 几乎一模一样。这就是 LangChain 的价值——屏蔽底层存储差异。

Java 类比:就像 JPA 的 EntityManager,底层从 H2 换成 MySQL,业务代码几乎不用改。

3.3 完整迁移代码

把上一篇的 RAGqa.py 改成 PgVector 版本:

# step07_pgvector.py — PgVector 完整 RAG 系统

import os
import json
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma  # 旧版
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

from BigModelEmbedding import BigModelEmbeddingFunc
from langchain_postgres.vectorstores import PGVector as PGVectorStore

# ── 配置 ──
with open("config_bigmodel.json", "r", encoding="utf-8") as f:
    config = json.load(f)

EMBEDDING_KEY = config["embedding"]["api_key"]
DEEPSEEK_KEY = config["deepseek"]["api_key"]
DEEPSEEK_URL = config["deepseek"]["base_url"]
DEEPSEEK_MODEL = config["deepseek"]["model"]

# PgVector 连接
PG_CONNECTION = "postgresql://root:rootPassword@localhost:5432/rag_db"
COLLECTION_NAME = "kkfile_documents"

# ── 1. 加载文档 ──
doc_path = "doc/kkfile.md"
docs = TextLoader(doc_path, encoding="utf-8").load()
print(f"加载了 {len(docs)} 篇文档")

# ── 2. 文本分割 ──
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
print(f"分成 {len(chunks)} 个块")

# ── 3. 向量化 + 存储到 PgVector ──
embeddings = BigModelEmbeddingFunc(api_key=EMBEDDING_KEY)

vectorstore = PGVectorStore(
    collection_name=COLLECTION_NAME,
    embeddings=embeddings,
    connection=PG_CONNECTION,
    use_jsonb=True,
)

# 检查是否已有数据
count = vectorstore.collection_metadata.get("item_count", 0)
if count > 0:
    print(f"加载已有 PgVector 集合,共 {count} 条记录")
else:
    vectorstore.add_documents(chunks)
    print(f"新建 PgVector 集合,存入 {len(chunks)} 个分块")

# ── 4. 构建 RAG 管道 ──
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 5},
)

prompt = PromptTemplate.from_template("""请严格依据下面参考资料回答用户问题,给出回答的依据文件,不要编造内容:
参考资料:{context}
用户问题:{input}
""")

llm = ChatOpenAI(
    model=DEEPSEEK_MODEL,
    temperature=0.7,
    api_key=DEEPSEEK_KEY,
    base_url=DEEPSEEK_URL,
)

rag_chain = (
    {"context": retriever, "input": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# ── 5. 问答测试 ──
print("========== RAG问答系统 (PgVector版) ==========")
questions = [
    "kkFileView支持哪些文档格式?",
    "kkFileView的官网地址是什么?",
    "v5.0.0版本有什么新功能?",
]
for q in questions:
    print(f"\n  Q: {q}")
    result = rag_chain.invoke(q)
    print(f"  A: {result[:200]}...")

# ── 6. 元数据过滤查询 ──
print("\n[6] 元数据过滤查询...")
retriever_filtered = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={
        "k": 3,
        "filter": {"source_file": "doc/kkfile.md"},
    },
)
filtered_result = retriever_filtered.invoke("kkFileView支持哪些文档格式?")
print(f"  过滤后检索到 {len(filtered_result)} 条相关文档")
for i, doc in enumerate(filtered_result[:2]):
    print(f"  [{i+1}] 来源: {doc.metadata.get('source_file', 'N/A')}")
    print(f"      内容: {doc.page_content[:80]}...")
print("\n" + "=" * 60)
print("Step 07 完成 — 这就是 RAG 系统!")
print("=" * 60)

3.4 运行效果

============================================================
Step 07PgVector 完整 RAG 系统
============================================================

[1] 加载文档...
    加载了 1 篇文档
[2] 文本分割 (chunk_size=500, overlap=50)...
    分成 40 个块
[3] 向量化 + 存储到 PgVector...
    新建 PgVector 集合,存入 40 个分块
[4] 构建 RAG 管道...

[5] 问答测试...

  Q: kkFileView支持哪些文档格式?
  A: 根据参考资料,kkFileView 支持以下文档格式:

*   **Office 办公文档**:doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx, xlam, xla, pages, pptm
*   **国产 WPS Office 办公文档**:wps, dps, et, ett, wpt
*...

  Q: kkFileView的官网地址是什么?
  A: 根据参考资料,kkFileView 的官网地址是 **https://kkview.cn**。

**依据文件:**
- 文档中明确列出:“15. 官网建设:[https://kkview.cn](https://kkview.cn/)”(来自 `2020年05月20日` 更新记录)。
- 在“官网及文档”部分也直接给出:“地址:[https://kkview.cn](https://kkview...

  Q: v5.0.0版本有什么新功能?
  A: 根据参考资料中 v5.0.0 版本的发布记录,该版本仅列出了优化内容,**未明确提及任何“新功能”**。具体优化项如下:

1. xlsx 前端解析优化 - 提升Excel文件前端渲染性能  
2. 图片解析优化 - 改进图片处理机制  
3. tif 解析优化 - 增强TIF格式支持  
4. svg 解析优化 - 优化SVG矢量图渲染  
5. json 解析优化 - 改进JSON文件处理  ...

[6] 元数据过滤查询...
    过滤后检索到 0 条相关文档

============================================================
Step 07 完成 — 这就是 RAG 系统!
============================================================

四、PgVector 的高级功能

4.1 元数据过滤

ChromaDB 支持 where 过滤,PgVector 通过 filter 参数实现,语法更灵活:

# ChromaDB 写法
retriever = vectorstore.as_retriever(
    search_kwargs={"k": 5, "where": {"source": "note1"}}
)

# PgVector 写法(支持 $and, $or, $gte 等操作符)
retriever = vectorstore.as_retriever(
    search_kwargs={
        "k": 5,
        "filter": {
            "$and": [
                {"source_file": {"$like": "%.pdf"}},
                {"file_type": ".pdf"},
            ]
        },
    }
)

Java 类比:这就像 MyBatis 的 <if> 动态 SQL,filter 就是 WHERE 子句。

4.2 混合检索

PgVector 支持向量检索 + 全文检索的混合模式:

-- 在 PostgreSQL 中,用 tsvector 做全文检索
ALTER TABLE documents ADD COLUMN content_tsvector tsvector;

UPDATE documents
SET content_tsvector = to_tsvector('chinese', content);

CREATE INDEX idx_content_search ON documents USING gin(content_tsvector);

-- 混合查询:向量相似度 + 关键词匹配
SELECT *,
    0.7 * (1 - (embedding <=> target_embedding)) +
    0.3 * ts_rank(content_tsvector, plainto_tsquery('chinese', '关键词'))
    AS hybrid_score
FROM documents
ORDER BY hybrid_score DESC
LIMIT 5;

中文全文检索需要用 zhparserpg_jieba 分词插件,这是 ChromaDB 做不到的。

4.3 批量操作

# 批量添加(和 ChromaDB 一样高效)
vectorstore.add_documents(new_chunks)

# 批量更新
vectorstore.update(collection_name="kkfile_documents", ...)

# 批量删除
vectorstore.delete(where={"source_file": "old_doc.md"})

五、ChromaDB vs PgVector 对比

5.1 功能对比

功能ChromaDBPgVector
向量存储
余弦相似度
HNSW 索引
元数据过滤wherefilter(更强大)
全文检索✅(配合 tsvector)
事务支持✅(ACID)
多租户隔离手动✅(Schema/Row Level Security)
连接池✅(PgBouncer)
运维本地文件标准 PostgreSQL 运维体系

5.2 选型建议

原型开发 / 个人项目 / 小规模 (< 10万条)
    → ChromaDB(零配置,上手快)

生产环境 / 企业级 / 大规模 (> 10万条)
    → PgVector(已有 PostgreSQL 的话)

需要全文检索 + 向量检索混合
    → PgVector(PostgreSQL 原生支持)

需要多租户隔离
    → PgVector(Schema 级隔离)

5.3 Java 类比总结

概念ChromaDBPgVector
数据库实例PersistentClient(path=...)postgres://localhost:5432/rag_db
集合/表CollectionTABLE documents
插入coll.add(documents=[...])INSERT INTO documents (content, embedding)
查询coll.query(query_texts=[...])SELECT * FROM documents ORDER BY embedding <=> ? LIMIT 5
过滤where={"source": "note1"}WHERE source_file LIKE '%.pdf'
索引自动创建CREATE INDEX ... USING ivfflat

六、知识地图回顾

经过前五期的学习,你已经掌握了 RAG 的全套基础技能:

期数知识点类比(Java)
Day 1-2Python 速学 + 环境搭建学会新语言
Day 3-5API 调用 + Prompt 工程 + 参数调优学会跟 AI 对话
Day 5-7Embedding + ChromaDB + LangChain RAG学会让 AI 读你的文档
Day 8-10多格式解析 + 分割策略 + 元数据让 AI 读懂各种格式
Day 11-12PgVector 向量数据库实战从原型到生产

七、RAG 全流程一图流

┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│  kkfile.md  │───▶│ TextSplitter │───▶│   Chunks    │
│  (原始文档)  │    │  (切分成小块)  │    │ (500字/块)  │
└─────────────┘    └──────────────┘    └──────┬──────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │  BigModelEmbedding │
                                    │  (文本→向量)       │
                                    └─────────┬─────────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │  PostgreSQL      │
                                    │  + pgvector 扩展  │
                                    │  (向量持久化存储)  │
                                    └─────────┬─────────┘
                                              │
                            ┌─────────────────▼─────────────────┐
                            │       用户提问                     │
                            │       "kkFileView官网地址?"       │
                            └─────────────────┬─────────────────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │     Retriever      │
                                    │  (PgVector 向量检索)│
                                    └─────────┬─────────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │   PromptTemplate   │
                                    │  (组装上下文+问题)  │
                                    └─────────┬─────────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │    ChatOpenAI      │
                                    │  (DeepSeek生成)    │
                                    └─────────┬─────────┘
                                              │
                                    ┌─────────▼─────────┐
                                    │   精准回答          │
                                    │   "官网地址是..."   │
                                    └───────────────────┘

八、下一步

下期我们将深入 RAG 系统的进阶优化

  • 混合检索:向量检索 + BM25 关键词检索,兼顾语义和精确匹配
  • 重排序(Reranking):Cohere Rerank / BGE Reranker,召回 Top-10 后精排 Top-2
  • 查询改写(Query Rewriting):让用户的问题更适合检索
  • 引用溯源:让 AI 回答标注来源,增强可信度

本文代码基于 LangChain 1.x + DeepSeek API + PgVector,完整项目见:ai-learning

系列文章:Day 1-2 · Day 3-4 · Day 5-7· Day 8-10