上一篇聊了 RAG 的全链路有读者问「Embedding 到底是什么把文本变成向量这个过程底层发生了什么」这个问题问得好。面试里大家都背过「把文本映射到高维空间的稠密向量」但真要解释清楚——为什么这个向量能代表语义为什么余弦相似度能衡量两段文字是不是在聊同一件事——很多人就含糊过去了。这篇从直觉开始到选型到代码落地把 Embedding 和向量数据库这块拆干净。一、Embedding 到底表示了什么先讲一个最直觉的例子。假设你要给动物建一个坐标系会飞 ↑ 鸟 | 蝙蝠 |有毛 ←──────────────┼──────────────→ 无毛 | 狗 | 鳄鱼 ↓ 不会飞在这个二维坐标系里「狗」和「猫」的坐标很近都在左下区「狗」和「鱼」很远。这就是向量的直觉——把对象的属性编码成坐标坐标越近属性越相似。真实的 Embedding 模型做的是同一件事只是维度从 2 维变成了 768 维或 1024 维每一维对应某种抽象的语义属性不一定能用人类语言描述。模型是从海量文本中自动学会如何分配这些坐标的——语义相近的词和句子在这个高维空间里自然聚在一起。几个关键直觉1. 语义在方向不在长度「我很开心」和「今天心情特别好」在 768 维空间里方向相近但向量的长度模可能不同。这就是为什么用余弦相似度而不是欧氏距离——余弦只看两个向量的夹角不管长度。余弦相似度 cos(θ) (A · B) / (‖A‖ × ‖B‖)夹角越小接近 0°相似度越接近 1方向相反180°相似度为 -1。2. 与 TF-IDF 的本质区别如果你做过传统搜索引擎对 TF-IDF 不陌生。TF-IDF 是词频统计没有语义理解——「快乐」和「高兴」在 TF-IDF 里是两个不同的词毫无关联。Embedding 是语义表示「快乐」和「高兴」的向量很近即使字面上不同。TF-IDF稀疏向量:「快乐的狗」→ [0, 0, 0, 1, 0, 1, 0, ...] # 只有「快乐」「狗」对应位置为 1Embedding稠密向量:「快乐的狗」→ [0.23, -0.18, 0.95, 0.02, ...] # 768 个连续浮点数每个都有值「高兴的小狗」→ [0.25, -0.16, 0.93, 0.04, ...] # 非常接近这也是为什么 RAG 系统不用 TF-IDF 做语义检索——纯关键词召回会漏掉大量语义相关的内容。3. 向量化是有代价的把一段 500 字的文档向量化要调用 Embedding 模型做一次前向推理——计算量不小。100 万篇文档全量向量化要认真估算时间和成本规模BGE-large本地 GPU T4text-embedding-3-smallOpenAI API10 万 chunk~5 分钟~$0.002100 万 chunk~50 分钟~$0.021000 万 chunk~8 小时~$0.2OpenAI 的 text-embedding-3-small 价格是 $0.02/百万 Token本地部署 bge-large 在 T4 GPU 上跑 batch256 大概 500ms 出一批。向量化是一次性的离线成本后续增量更新才是持续压力。二、代码 Embedding搭代码搜索系统的正确姿势通用 Embedding 模型对代码效果不好。原因很直接代码有自己的语法结构、变量命名习惯、API 调用模式通用模型在自然语言语料上训练对代码的语义理解浮于表面。一个典型的坑用通用 Embedding 搜索「Python 排序算法」可能返回一堆聊「算法复杂度」的文档而漏掉实际包含sort()调用的代码文件。2026 年代码 Embedding 选型模型特点推荐场景voyage-code-3Voyage AI跨语言代码最强支持 Python/Java/Go/TS 等 20 语言多语言代码库搜索、企业代码 RAGtext-embedding-3-largeOpenAI通用但对代码有一定支持上下文 8K代码和文档混合的知识库CodeBERT微软开源纯代码预训练支持 Python/Java/JS 等 6 种语言本地部署、数据不出境UniXcoder微软开源支持代码理解 代码生成跨模态代码搜索 代码补全系统jina-embeddings-v3通用但对代码有适配本地部署友好轻量场景选型建议你的代码库是什么语言├─ Python 为主 → voyage-code-3 或 CodeBERT两者 Python 最强├─ 多语言混合 → voyage-code-3覆盖最广│需要本地部署数据不出境├─ 是 → CodeBERT 或 UniXcoder微软开源└─ 否 → voyage-code-3API效果最好│代码和文档混合的知识库└─ text-embedding-3-large兼顾两者但代码效果略差于专用模型实测对比搜索「用 Python 实现二叉树的中序遍历」模型排名 #1 结果是否命中text-embedding-3-large「树形数据结构概述」文档❌ 漏掉代码voyage-code-3def inorder_traversal(root)...代码✅ 精准命中CodeBERTdef inorder_traversal(root)...代码✅ 精准命中如果你在搭代码智能搜索或 AI Code Review 系统代码专用 Embedding 是不可跳过的选型点。# voyage-code-3 使用示例import voyageaivo voyageai.Client(api_keyyour-api-key)# 代码文档向量化code_snippets [ def quicksort(arr): ..., public void mergeSort(int[] arr) { ... },]result vo.embed(code_snippets, modelvoyage-code-3, input_typedocument)embeddings result.embeddings # List[List[float]]每个 1024 维# 查询向量化input_typequery 与 document 不同query 用 Python 实现快速排序query_result vo.embed([query], modelvoyage-code-3, input_typequery)注意 voyage 区分input_typedocument和input_typequery——文档和查询用不同的编码方式这是 Asymmetric Embedding 的设计查询通常更短且语义模糊文档通常更长且具体分开编码效果更好。三、向量数据库轻量选项Chroma 什么时候用前面写了 pgvector/Milvus/Qdrant 的详细对比有读者问「Chroma 呢好多教程用这个。」Chroma 的定位很明确本地开发 / 快速原型 / 不需要运维的轻量场景。# Chroma 5 行代码建一个知识库import chromadbclient chromadb.Client() # 纯内存模式重启清空# 或 chromadb.PersistentClient(path./my_db) # 持久化到本地文件collection client.create_collection(my_docs)collection.add( documents[Python 是一种解释型编程语言, Go 适合高并发服务], ids[doc1, doc2],)results collection.query(query_texts[什么语言适合写服务端], n_results1)print(results[documents]) # [[Go 适合高并发服务]]Chroma 内置了 Embedding 模型默认用all-MiniLM-L6-v2不用自己处理向量化——传文本进去它自动向量化再存储。这是它在教程里大量出现的原因零配置三行代码跑起来。Chroma vs pgvector 决策场景选哪个理由本地开发、调试 RAG 逻辑Chroma零运维重启即清空调试友好写教程、演示 DemoChroma代码最简洁读者上手快生产环境已有 PostgreSQLpgvector复用基础设施零额外运维生产环境需要 ACID 事务pgvector和业务数据在同一个 DB数据量 100 万需要分布式Milvus/QdrantChroma 单机pgvector 千万级开始吃力Chroma 的硬伤不适合生产。它没有完善的高可用方案集群支持有限大规模下性能和稳定性都比不上 Qdrant/Milvus。把 Chroma 用在生产的团队一旦数据量涨上去迁移成本会很高。结论Chroma 是最好的「开发期向量库」pgvector 是「最低运维成本的生产向量库」。四、pgvector 实战用 PostgreSQL 构建最小知识库pgvector 是 PostgreSQL 的扩展向量作为一种数据类型直接存在 PG 表里。这意味着• 向量索引和业务数据在同一个数据库不用维护两套基础设施• 支持完整的 SQL 操作——WHERE、JOIN、事务全都可以用• 权限控制直接复用 PostgreSQL 的行级安全RLS• 运维团队已经熟悉 PG学习成本几乎为零适用场景数据量 500 万向量已有 PostgreSQL团队不想引入新的基础设施组件。4.1 环境准备# 方式一Docker推荐本地开发docker run -d \ --name pgvector-demo \ -e POSTGRES_PASSWORDyourpassword \ -p 5432:5432 \ pgvector/pgvector:pg16# 方式二已有 PostgreSQL安装扩展# Ubuntu/Debian:sudo apt install postgresql-16-pgvector# macOS (Homebrew):brew install pgvector plaintext -- 连接 PostgreSQL 后开启扩展CREATE EXTENSION IF NOT EXISTS vector;4.2 建表与索引-- 知识库文档表以 RAG 客服场景为例CREATE TABLE knowledge_chunks ( id BIGSERIAL PRIMARY KEY, source_file TEXT NOT NULL, -- 来源文档 section TEXT, -- 章节标题 content TEXT NOT NULL, -- 原文 embedding VECTOR(1024) NOT NULL, -- 向量bge-m3 输出 1024 维 metadata JSONB DEFAULT {}, -- 可扩展元数据权限/部门/时间等 created_at TIMESTAMPTZ DEFAULT NOW());-- 创建 HNSW 索引推荐查询更快-- 余弦相似度用 vector_cosine_opsCREATE INDEX ON knowledge_chunks USING hnsw (embedding vector_cosine_ops) WITH (m 16, ef_construction 64);-- 或者 IVFFlat内存更省精度略低-- CREATE INDEX ON knowledge_chunks-- USING ivfflat (embedding vector_cosine_ops)-- WITH (lists 100);-- 元数据过滤加速给高频过滤字段建普通索引CREATE INDEX ON knowledge_chunks ((metadata-dept));CREATE INDEX ON knowledge_chunks (created_at);选 HNSW 还是 IVFFlat•HNSW查询更快P99 20ms召回率更高97%支持增量写入首选•IVFFlat内存更省但需要先有足量数据才能训练建议 10 万条再用lists参数设sqrt(N)左右4.3 Python 写入向量import psycopg2from psycopg2.extras import execute_valuesfrom sentence_transformers import SentenceTransformerfrom typing import Listimport json# 连接配置conn psycopg2.connect( hostlocalhost, databasepostgres, userpostgres, passwordyourpassword,)conn.autocommit False# Embedding 模型中文场景用 bge-large-zh-v1.5 或 bge-m3embedder SentenceTransformer(BAAI/bge-large-zh-v1.5)def ingest_chunks(chunks: List[dict]): 批量写入文档块。 chunks 格式[{content: ..., source_file: ..., section: ..., metadata: {...}}] texts [c[content] for c in chunks] # 批量向量化batch 推理比逐条快 10x embeddings embedder.encode( texts, batch_size64, normalize_embeddingsTrue, # L2 归一化余弦相似度等价内积 show_progress_barTrue, ) rows [ ( c[source_file], c.get(section), c[content], embedding.tolist(), # VECTOR 类型接受 Python list json.dumps(c.get(metadata, {})), ) for c, embedding in zip(chunks, embeddings) ] with conn.cursor() as cur: execute_values( cur, INSERT INTO knowledge_chunks (source_file, section, content, embedding, metadata) VALUES %s , rows, template(%s, %s, %s, %s::vector, %s::jsonb), ) conn.commit() print(f已写入 {len(rows)} 条)# 使用示例ingest_chunks([ { content: 退换货政策自签收之日起 7 天内可申请无理由退货需保持原包装完好。, source_file: policy/refund.md, section: 退换货, metadata: {dept: 客服, version: 2026-Q2}, }, { content: 物流异常处理若显示已签收但未收到请 48 小时内联系客服并提供订单号。, source_file: policy/logistics.md, section: 物流, metadata: {dept: 物流, version: 2026-Q2}, },])4.4 语义查询def semantic_search( query: str, top_k: int 5, dept_filter: str None,) - List[dict]: 语义检索支持可选的部门元数据过滤。 query_embedding embedder.encode( [query], normalize_embeddingsTrue, )[0].tolist() # pgvector 的距离算子 # 余弦距离1 - cosine_similarity越小越相似 # - 欧氏距离L2 # # 内积的负值归一化后与余弦等价 sql SELECT id, source_file, section, content, metadata, 1 - (embedding %s::vector) AS cosine_similarity FROM knowledge_chunks {where_clause} ORDER BY embedding %s::vector LIMIT %s params [query_embedding, query_embedding, top_k] if dept_filter: # JSONB 字段过滤先走普通索引筛选再做向量检索 where_clause WHERE metadata-dept %s params [query_embedding, dept_filter, query_embedding, top_k] sql sql.format(where_clausewhere_clause) # 注意参数顺序第一个 %s 是 WHERE 前的 embedding第二个是 dept # 第三个是 ORDER BY 的 embedding第四个是 LIMIT sql SELECT id, source_file, section, content, metadata, 1 - (embedding %s::vector) AS cosine_similarity FROM knowledge_chunks WHERE metadata-dept %s ORDER BY embedding %s::vector LIMIT %s params [query_embedding, dept_filter, query_embedding, top_k] else: sql sql.format(where_clause) with conn.cursor() as cur: cur.execute(sql, params) rows cur.fetchall() return [ { id: r[0], source_file: r[1], section: r[2], content: r[3], metadata: r[4], similarity: float(r[5]), } for r in rows ]# 使用示例results semantic_search(退货需要什么条件, top_k3)for r in results: print(f[{r[similarity]:.3f}] {r[source_file]} - {r[content][:60]})# [0.921] policy/refund.md - 退换货政策自签收之日起 7 天内可申请无理由退货...4.5 与 LangChain 集成三行接入如果你的项目已经在用 LangChainpgvector 有官方集成不需要手写 SQLfrom langchain_community.vectorstores import PGVectorfrom langchain_community.embeddings import HuggingFaceEmbeddingsembedder HuggingFaceEmbeddings(model_nameBAAI/bge-large-zh-v1.5)# CONNECTION_STRING 格式postgresqlpsycopg2://user:pwdhost/dbnamevectorstore PGVector( connection_stringpostgresqlpsycopg2://postgres:yourpasswordlocalhost/postgres, embedding_functionembedder, collection_nameknowledge_chunks,)# 写入文档from langchain.schema import Documentvectorstore.add_documents([ Document(page_content退换货政策..., metadata{source: policy/refund.md}),])# 语义搜索docs vectorstore.similarity_search(退货条件, k3)LangChain 的 PGVector 会自动创建表和索引默认用 IVFFlat。如果想用 HNSW需要在数据库里手动建索引或等 LangChain 下游版本支持 HNSW 参数配置。4.6 pgvector 生产注意事项注意点 1先写数据再建 HNSW 索引IVFFlat 在数据量不足时效果很差需要先有足量数据做 K-MeansHNSW 没有这个限制但如果你的数据会大量更新建索引前先写完一批数据再建索引性能更好。注意点 2maintenance_work_mem要调大HNSW 索引构建很耗内存默认的 64MB 通常不够-- 建索引前临时调大建完恢复SET maintenance_work_mem 1GB;CREATE INDEX ON knowledge_chunks USING hnsw (embedding vector_cosine_ops);RESET maintenance_work_mem;注意点 3查询时设ef_searchHNSW 索引的ef_search参数控制查询精度默认值 40 在百万级数据时召回率可能不够-- 会话级设置提高查询召回率SET hnsw.ef_search 100;注意点 4数据量超 500 万考虑迁移pgvector 是单机方案超过 500 万向量后 P99 延迟开始显著上升超过 1000 万就该考虑迁 Qdrant 或 Milvus 了。迁移成本把数据导出重新向量化写入通常一个周末搞定。五、一张表总结选型需求推荐方案理由本地开发调试Chroma零运维三行代码起生产环境 100 万向量 已有 PGpgvector复用基础设施零额外运维生产环境需要 ACID 事务pgvector向量和业务数据同库生产环境 100 万-5000 万QdrantRust 性能 Payload 过滤无损生产环境 5000 万分布式Milvus唯一验证过十亿级的开源方案多语言代码库搜索voyage-code-3 任意向量库代码语义理解最强代码数据不出境CodeBERT pgvector全链路本地结尾今天这篇覆盖了几个容易被忽视的点• Embedding 的方向才是语义长度没有意义——这是余弦相似度的根本原因•代码 Embedding 要用专用模型通用模型对代码语义理解浮于表面•Chroma 是开发期的最佳拍档但别在生产用它•pgvector 是 500 万以下规模的最低阻力方案建库代码加上去不超过 50 行学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】
RAG实战,企业知识库搭建中向量化到底做了什么:从「猫狗距离」到 pgvector 建库
上一篇聊了 RAG 的全链路有读者问「Embedding 到底是什么把文本变成向量这个过程底层发生了什么」这个问题问得好。面试里大家都背过「把文本映射到高维空间的稠密向量」但真要解释清楚——为什么这个向量能代表语义为什么余弦相似度能衡量两段文字是不是在聊同一件事——很多人就含糊过去了。这篇从直觉开始到选型到代码落地把 Embedding 和向量数据库这块拆干净。一、Embedding 到底表示了什么先讲一个最直觉的例子。假设你要给动物建一个坐标系会飞 ↑ 鸟 | 蝙蝠 |有毛 ←──────────────┼──────────────→ 无毛 | 狗 | 鳄鱼 ↓ 不会飞在这个二维坐标系里「狗」和「猫」的坐标很近都在左下区「狗」和「鱼」很远。这就是向量的直觉——把对象的属性编码成坐标坐标越近属性越相似。真实的 Embedding 模型做的是同一件事只是维度从 2 维变成了 768 维或 1024 维每一维对应某种抽象的语义属性不一定能用人类语言描述。模型是从海量文本中自动学会如何分配这些坐标的——语义相近的词和句子在这个高维空间里自然聚在一起。几个关键直觉1. 语义在方向不在长度「我很开心」和「今天心情特别好」在 768 维空间里方向相近但向量的长度模可能不同。这就是为什么用余弦相似度而不是欧氏距离——余弦只看两个向量的夹角不管长度。余弦相似度 cos(θ) (A · B) / (‖A‖ × ‖B‖)夹角越小接近 0°相似度越接近 1方向相反180°相似度为 -1。2. 与 TF-IDF 的本质区别如果你做过传统搜索引擎对 TF-IDF 不陌生。TF-IDF 是词频统计没有语义理解——「快乐」和「高兴」在 TF-IDF 里是两个不同的词毫无关联。Embedding 是语义表示「快乐」和「高兴」的向量很近即使字面上不同。TF-IDF稀疏向量:「快乐的狗」→ [0, 0, 0, 1, 0, 1, 0, ...] # 只有「快乐」「狗」对应位置为 1Embedding稠密向量:「快乐的狗」→ [0.23, -0.18, 0.95, 0.02, ...] # 768 个连续浮点数每个都有值「高兴的小狗」→ [0.25, -0.16, 0.93, 0.04, ...] # 非常接近这也是为什么 RAG 系统不用 TF-IDF 做语义检索——纯关键词召回会漏掉大量语义相关的内容。3. 向量化是有代价的把一段 500 字的文档向量化要调用 Embedding 模型做一次前向推理——计算量不小。100 万篇文档全量向量化要认真估算时间和成本规模BGE-large本地 GPU T4text-embedding-3-smallOpenAI API10 万 chunk~5 分钟~$0.002100 万 chunk~50 分钟~$0.021000 万 chunk~8 小时~$0.2OpenAI 的 text-embedding-3-small 价格是 $0.02/百万 Token本地部署 bge-large 在 T4 GPU 上跑 batch256 大概 500ms 出一批。向量化是一次性的离线成本后续增量更新才是持续压力。二、代码 Embedding搭代码搜索系统的正确姿势通用 Embedding 模型对代码效果不好。原因很直接代码有自己的语法结构、变量命名习惯、API 调用模式通用模型在自然语言语料上训练对代码的语义理解浮于表面。一个典型的坑用通用 Embedding 搜索「Python 排序算法」可能返回一堆聊「算法复杂度」的文档而漏掉实际包含sort()调用的代码文件。2026 年代码 Embedding 选型模型特点推荐场景voyage-code-3Voyage AI跨语言代码最强支持 Python/Java/Go/TS 等 20 语言多语言代码库搜索、企业代码 RAGtext-embedding-3-largeOpenAI通用但对代码有一定支持上下文 8K代码和文档混合的知识库CodeBERT微软开源纯代码预训练支持 Python/Java/JS 等 6 种语言本地部署、数据不出境UniXcoder微软开源支持代码理解 代码生成跨模态代码搜索 代码补全系统jina-embeddings-v3通用但对代码有适配本地部署友好轻量场景选型建议你的代码库是什么语言├─ Python 为主 → voyage-code-3 或 CodeBERT两者 Python 最强├─ 多语言混合 → voyage-code-3覆盖最广│需要本地部署数据不出境├─ 是 → CodeBERT 或 UniXcoder微软开源└─ 否 → voyage-code-3API效果最好│代码和文档混合的知识库└─ text-embedding-3-large兼顾两者但代码效果略差于专用模型实测对比搜索「用 Python 实现二叉树的中序遍历」模型排名 #1 结果是否命中text-embedding-3-large「树形数据结构概述」文档❌ 漏掉代码voyage-code-3def inorder_traversal(root)...代码✅ 精准命中CodeBERTdef inorder_traversal(root)...代码✅ 精准命中如果你在搭代码智能搜索或 AI Code Review 系统代码专用 Embedding 是不可跳过的选型点。# voyage-code-3 使用示例import voyageaivo voyageai.Client(api_keyyour-api-key)# 代码文档向量化code_snippets [ def quicksort(arr): ..., public void mergeSort(int[] arr) { ... },]result vo.embed(code_snippets, modelvoyage-code-3, input_typedocument)embeddings result.embeddings # List[List[float]]每个 1024 维# 查询向量化input_typequery 与 document 不同query 用 Python 实现快速排序query_result vo.embed([query], modelvoyage-code-3, input_typequery)注意 voyage 区分input_typedocument和input_typequery——文档和查询用不同的编码方式这是 Asymmetric Embedding 的设计查询通常更短且语义模糊文档通常更长且具体分开编码效果更好。三、向量数据库轻量选项Chroma 什么时候用前面写了 pgvector/Milvus/Qdrant 的详细对比有读者问「Chroma 呢好多教程用这个。」Chroma 的定位很明确本地开发 / 快速原型 / 不需要运维的轻量场景。# Chroma 5 行代码建一个知识库import chromadbclient chromadb.Client() # 纯内存模式重启清空# 或 chromadb.PersistentClient(path./my_db) # 持久化到本地文件collection client.create_collection(my_docs)collection.add( documents[Python 是一种解释型编程语言, Go 适合高并发服务], ids[doc1, doc2],)results collection.query(query_texts[什么语言适合写服务端], n_results1)print(results[documents]) # [[Go 适合高并发服务]]Chroma 内置了 Embedding 模型默认用all-MiniLM-L6-v2不用自己处理向量化——传文本进去它自动向量化再存储。这是它在教程里大量出现的原因零配置三行代码跑起来。Chroma vs pgvector 决策场景选哪个理由本地开发、调试 RAG 逻辑Chroma零运维重启即清空调试友好写教程、演示 DemoChroma代码最简洁读者上手快生产环境已有 PostgreSQLpgvector复用基础设施零额外运维生产环境需要 ACID 事务pgvector和业务数据在同一个 DB数据量 100 万需要分布式Milvus/QdrantChroma 单机pgvector 千万级开始吃力Chroma 的硬伤不适合生产。它没有完善的高可用方案集群支持有限大规模下性能和稳定性都比不上 Qdrant/Milvus。把 Chroma 用在生产的团队一旦数据量涨上去迁移成本会很高。结论Chroma 是最好的「开发期向量库」pgvector 是「最低运维成本的生产向量库」。四、pgvector 实战用 PostgreSQL 构建最小知识库pgvector 是 PostgreSQL 的扩展向量作为一种数据类型直接存在 PG 表里。这意味着• 向量索引和业务数据在同一个数据库不用维护两套基础设施• 支持完整的 SQL 操作——WHERE、JOIN、事务全都可以用• 权限控制直接复用 PostgreSQL 的行级安全RLS• 运维团队已经熟悉 PG学习成本几乎为零适用场景数据量 500 万向量已有 PostgreSQL团队不想引入新的基础设施组件。4.1 环境准备# 方式一Docker推荐本地开发docker run -d \ --name pgvector-demo \ -e POSTGRES_PASSWORDyourpassword \ -p 5432:5432 \ pgvector/pgvector:pg16# 方式二已有 PostgreSQL安装扩展# Ubuntu/Debian:sudo apt install postgresql-16-pgvector# macOS (Homebrew):brew install pgvector plaintext -- 连接 PostgreSQL 后开启扩展CREATE EXTENSION IF NOT EXISTS vector;4.2 建表与索引-- 知识库文档表以 RAG 客服场景为例CREATE TABLE knowledge_chunks ( id BIGSERIAL PRIMARY KEY, source_file TEXT NOT NULL, -- 来源文档 section TEXT, -- 章节标题 content TEXT NOT NULL, -- 原文 embedding VECTOR(1024) NOT NULL, -- 向量bge-m3 输出 1024 维 metadata JSONB DEFAULT {}, -- 可扩展元数据权限/部门/时间等 created_at TIMESTAMPTZ DEFAULT NOW());-- 创建 HNSW 索引推荐查询更快-- 余弦相似度用 vector_cosine_opsCREATE INDEX ON knowledge_chunks USING hnsw (embedding vector_cosine_ops) WITH (m 16, ef_construction 64);-- 或者 IVFFlat内存更省精度略低-- CREATE INDEX ON knowledge_chunks-- USING ivfflat (embedding vector_cosine_ops)-- WITH (lists 100);-- 元数据过滤加速给高频过滤字段建普通索引CREATE INDEX ON knowledge_chunks ((metadata-dept));CREATE INDEX ON knowledge_chunks (created_at);选 HNSW 还是 IVFFlat•HNSW查询更快P99 20ms召回率更高97%支持增量写入首选•IVFFlat内存更省但需要先有足量数据才能训练建议 10 万条再用lists参数设sqrt(N)左右4.3 Python 写入向量import psycopg2from psycopg2.extras import execute_valuesfrom sentence_transformers import SentenceTransformerfrom typing import Listimport json# 连接配置conn psycopg2.connect( hostlocalhost, databasepostgres, userpostgres, passwordyourpassword,)conn.autocommit False# Embedding 模型中文场景用 bge-large-zh-v1.5 或 bge-m3embedder SentenceTransformer(BAAI/bge-large-zh-v1.5)def ingest_chunks(chunks: List[dict]): 批量写入文档块。 chunks 格式[{content: ..., source_file: ..., section: ..., metadata: {...}}] texts [c[content] for c in chunks] # 批量向量化batch 推理比逐条快 10x embeddings embedder.encode( texts, batch_size64, normalize_embeddingsTrue, # L2 归一化余弦相似度等价内积 show_progress_barTrue, ) rows [ ( c[source_file], c.get(section), c[content], embedding.tolist(), # VECTOR 类型接受 Python list json.dumps(c.get(metadata, {})), ) for c, embedding in zip(chunks, embeddings) ] with conn.cursor() as cur: execute_values( cur, INSERT INTO knowledge_chunks (source_file, section, content, embedding, metadata) VALUES %s , rows, template(%s, %s, %s, %s::vector, %s::jsonb), ) conn.commit() print(f已写入 {len(rows)} 条)# 使用示例ingest_chunks([ { content: 退换货政策自签收之日起 7 天内可申请无理由退货需保持原包装完好。, source_file: policy/refund.md, section: 退换货, metadata: {dept: 客服, version: 2026-Q2}, }, { content: 物流异常处理若显示已签收但未收到请 48 小时内联系客服并提供订单号。, source_file: policy/logistics.md, section: 物流, metadata: {dept: 物流, version: 2026-Q2}, },])4.4 语义查询def semantic_search( query: str, top_k: int 5, dept_filter: str None,) - List[dict]: 语义检索支持可选的部门元数据过滤。 query_embedding embedder.encode( [query], normalize_embeddingsTrue, )[0].tolist() # pgvector 的距离算子 # 余弦距离1 - cosine_similarity越小越相似 # - 欧氏距离L2 # # 内积的负值归一化后与余弦等价 sql SELECT id, source_file, section, content, metadata, 1 - (embedding %s::vector) AS cosine_similarity FROM knowledge_chunks {where_clause} ORDER BY embedding %s::vector LIMIT %s params [query_embedding, query_embedding, top_k] if dept_filter: # JSONB 字段过滤先走普通索引筛选再做向量检索 where_clause WHERE metadata-dept %s params [query_embedding, dept_filter, query_embedding, top_k] sql sql.format(where_clausewhere_clause) # 注意参数顺序第一个 %s 是 WHERE 前的 embedding第二个是 dept # 第三个是 ORDER BY 的 embedding第四个是 LIMIT sql SELECT id, source_file, section, content, metadata, 1 - (embedding %s::vector) AS cosine_similarity FROM knowledge_chunks WHERE metadata-dept %s ORDER BY embedding %s::vector LIMIT %s params [query_embedding, dept_filter, query_embedding, top_k] else: sql sql.format(where_clause) with conn.cursor() as cur: cur.execute(sql, params) rows cur.fetchall() return [ { id: r[0], source_file: r[1], section: r[2], content: r[3], metadata: r[4], similarity: float(r[5]), } for r in rows ]# 使用示例results semantic_search(退货需要什么条件, top_k3)for r in results: print(f[{r[similarity]:.3f}] {r[source_file]} - {r[content][:60]})# [0.921] policy/refund.md - 退换货政策自签收之日起 7 天内可申请无理由退货...4.5 与 LangChain 集成三行接入如果你的项目已经在用 LangChainpgvector 有官方集成不需要手写 SQLfrom langchain_community.vectorstores import PGVectorfrom langchain_community.embeddings import HuggingFaceEmbeddingsembedder HuggingFaceEmbeddings(model_nameBAAI/bge-large-zh-v1.5)# CONNECTION_STRING 格式postgresqlpsycopg2://user:pwdhost/dbnamevectorstore PGVector( connection_stringpostgresqlpsycopg2://postgres:yourpasswordlocalhost/postgres, embedding_functionembedder, collection_nameknowledge_chunks,)# 写入文档from langchain.schema import Documentvectorstore.add_documents([ Document(page_content退换货政策..., metadata{source: policy/refund.md}),])# 语义搜索docs vectorstore.similarity_search(退货条件, k3)LangChain 的 PGVector 会自动创建表和索引默认用 IVFFlat。如果想用 HNSW需要在数据库里手动建索引或等 LangChain 下游版本支持 HNSW 参数配置。4.6 pgvector 生产注意事项注意点 1先写数据再建 HNSW 索引IVFFlat 在数据量不足时效果很差需要先有足量数据做 K-MeansHNSW 没有这个限制但如果你的数据会大量更新建索引前先写完一批数据再建索引性能更好。注意点 2maintenance_work_mem要调大HNSW 索引构建很耗内存默认的 64MB 通常不够-- 建索引前临时调大建完恢复SET maintenance_work_mem 1GB;CREATE INDEX ON knowledge_chunks USING hnsw (embedding vector_cosine_ops);RESET maintenance_work_mem;注意点 3查询时设ef_searchHNSW 索引的ef_search参数控制查询精度默认值 40 在百万级数据时召回率可能不够-- 会话级设置提高查询召回率SET hnsw.ef_search 100;注意点 4数据量超 500 万考虑迁移pgvector 是单机方案超过 500 万向量后 P99 延迟开始显著上升超过 1000 万就该考虑迁 Qdrant 或 Milvus 了。迁移成本把数据导出重新向量化写入通常一个周末搞定。五、一张表总结选型需求推荐方案理由本地开发调试Chroma零运维三行代码起生产环境 100 万向量 已有 PGpgvector复用基础设施零额外运维生产环境需要 ACID 事务pgvector向量和业务数据同库生产环境 100 万-5000 万QdrantRust 性能 Payload 过滤无损生产环境 5000 万分布式Milvus唯一验证过十亿级的开源方案多语言代码库搜索voyage-code-3 任意向量库代码语义理解最强代码数据不出境CodeBERT pgvector全链路本地结尾今天这篇覆盖了几个容易被忽视的点• Embedding 的方向才是语义长度没有意义——这是余弦相似度的根本原因•代码 Embedding 要用专用模型通用模型对代码语义理解浮于表面•Chroma 是开发期的最佳拍档但别在生产用它•pgvector 是 500 万以下规模的最低阻力方案建库代码加上去不超过 50 行学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】