新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python后端AI专题20:为什么主项目选 pgvector:表结构、距离与 HNSW

发布时间:2026/11/15 4:16:25来源:尧图网络
Python后端AI专题20:为什么主项目选 pgvector:表结构、距离与 HNSW
Python后端AI专题20为什么主项目选 pgvector表结构、距离与 HNSWKnowFlow 已经能在内存里写向量但企业项目还要同时管理租户、知识库、文档状态、来源和事务。主项目选择 PostgreSQL pgvector不是因为它在所有规模都最快而是当前阶段“业务过滤与向量检索同库完成”比再引入一套分布式向量系统更容易保证一致性和权限。续建决策与指纹答案变更决策原因同一 503 后重试继续旧 job数据语义未变batch_size 32→16谨慎重建批次计划已有游标不能直接解释游标按批次编号不再指向相同 chunk 范围chunk_size 800→512新索引版本chunk 内容与 ordinal 全变384→768 维新索引版本/新列旧列类型和向量空间均不兼容Celery 并发数变化继续旧 job调度变化不改变索引语义为了避免只比较一个模型名新增完整指纹模块dataclass(frozenTrue,slotsTrue)classIndexPipelineVersion:parser_version:strcleaner_version:strchunk_size:intchunk_overlap:intembedding_model:strembedding_dimensions:intdeffingerprint(self)-str:canonicaljson.dumps(asdict(self),ensure_asciiFalse,sort_keysTrue,separators(,,:),)returnhashlib.sha256(canonical.encode(utf-8)).hexdigest()排序键与紧凑 JSON 保证相同配置序列化稳定。测试证明 chunk_size 或维数改变会得到不同的 64 位十六进制哈希1 passed in 0.08s。为什么此时不优先选独立向量库pgvector 的优势tenant_id/knowledge_base_id/status与向量距离能在一条 SQL 中过滤文档、chunk 与索引任务可用外键和事务约束团队已有 PostgreSQL 运维经验当前数据规模不要求独立集群的水平扩展。当向量达到数亿、需要跨区域水平扩展、专用稀疏向量或复杂分片时Qdrant/Milvus/Weaviate 等可能更合适。Provider 合同保留了替换空间但课程不会为了简历关键词同时运行四个数据库。距离与相似度不要混用pgvector 的 cosine distance 越小越近业务 API 常返回 similarity 越大越近。本项目查询后转换score1.0-float(distance_value)排序必须仍按distance ASC不能一边写距离、一边按降序排。索引操作符类也要一致vector_cosine_ops对应余弦距离如果查询改成内积或 L2索引和归一化策略都要重新审查。HNSW 是近似索引不是魔法没有 ANN 索引时数据库要与大量向量逐一计算。HNSW 构建多层邻接图加速近邻搜索代价是更多内存、写入成本和近似误差。参数会影响召回与性能因此必须用评测集验证不能只看 SQL 延迟。模型声明Index(ix_document_chunks_embedding_hnsw,embedding,postgresql_usinghnsw,postgresql_ops{embedding:vector_cosine_ops},)完整知识模型模块from__future__importannotationsimportuuidfrompgvector.sqlalchemyimportVectorfromsqlalchemyimport(BigInteger,CheckConstraint,ForeignKey,Index,Integer,JSON,String,Text,UniqueConstraint,)fromsqlalchemy.ormimportMapped,mapped_columnfromapp.models.baseimportBase,TimestampMixin,UUIDPrimaryKeyMixinclassKnowledgeBase(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__knowledge_bases__table_args__(UniqueConstraint(tenant_id,name),Index(ix_knowledge_bases_tenant_id,tenant_id),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)name:Mapped[str]mapped_column(String(160),nullableFalse)description:Mapped[str]mapped_column(Text,default,nullableFalse)classStoredDocument(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__documents__table_args__(UniqueConstraint(knowledge_base_id,content_hash),CheckConstraint(status IN (pending_upload,uploaded,indexing,ready,failed),namevalid_document_status,),Index(ix_documents_tenant_id_kb,tenant_id,knowledge_base_id),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)knowledge_base_id:Mapped[uuid.UUID]mapped_column(ForeignKey(knowledge_bases.id,ondeleteCASCADE),nullableFalse)filename:Mapped[str]mapped_column(String(255),nullableFalse)content_type:Mapped[str]mapped_column(String(100),nullableFalse)content_hash:Mapped[str]mapped_column(String(64),nullableFalse)object_key:Mapped[str]mapped_column(String(500),nullableFalse,uniqueTrue)size_bytes:Mapped[int]mapped_column(BigInteger,nullableFalse)status:Mapped[str]mapped_column(String(24),defaultuploaded,nullableFalse)error_code:Mapped[str|None]mapped_column(String(80))classDocumentChunk(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__document_chunks__table_args__(UniqueConstraint(document_id,ordinal),Index(ix_document_chunks_tenant_id_kb,tenant_id,knowledge_base_id),Index(ix_document_chunks_embedding_hnsw,embedding,postgresql_usinghnsw,postgresql_ops{embedding:vector_cosine_ops},),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)knowledge_base_id:Mapped[uuid.UUID]mapped_column(ForeignKey(knowledge_bases.id,ondeleteCASCADE),nullableFalse)document_id:Mapped[uuid.UUID]mapped_column(ForeignKey(documents.id,ondeleteCASCADE),nullableFalse)ordinal:Mapped[int]mapped_column(Integer,nullableFalse)text:Mapped[str]mapped_column(Text,nullableFalse)page:Mapped[int|None]mapped_column(Integer)section:Mapped[str|None]mapped_column(String(500))start_char:Mapped[int]mapped_column(Integer,nullableFalse)end_char:Mapped[int]mapped_column(Integer,nullableFalse)embedding_model:Mapped[str]mapped_column(String(160),nullableFalse)embedding:Mapped[list[float]]mapped_column(Vector(384),nullableFalse)source_metadata:Mapped[dict[str,object]]mapped_column(JSON,defaultdict,nullableFalse)classIndexJob(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__index_jobs__table_args__(CheckConstraint(status IN (queued,parsing,chunking,embedding,ready,failed),namevalid_index_job_status,),Index(ix_index_jobs_document_id,document_id),)document_id:Mapped[uuid.UUID]mapped_column(ForeignKey(documents.id,ondeleteCASCADE),nullableFalse)status:Mapped[str]mapped_column(String(24),defaultqueued,nullableFalse)next_batch:Mapped[int]mapped_column(Integer,default0,nullableFalse)total_chunks:Mapped[int]mapped_column(Integer,default0,nullableFalse)attempts:Mapped[int]mapped_column(Integer,default0,nullableFalse)error:Mapped[str|None]mapped_column(Text)迁移先执行CREATE EXTENSION IF NOT EXISTS vector再由 metadata 建表从空库验证了 10 张产品表、vector(384)与 HNSW。模型与真实 PostgreSQL 检查当前结果........ [100%] 8 passed in 1.01s本篇最终完整模块knowledge.py前面的代码片段用于解释本次改动下面是本篇结束时可直接核对和替换的磁盘完整版本。from__future__importannotationsimportuuidfromdatetimeimportdatetimefrompgvector.sqlalchemyimportVectorfromsqlalchemyimportBigInteger,CheckConstraint,DateTime,ForeignKey,Index,Integer,JSON,String,Text,UniqueConstraintfromsqlalchemy.ormimportMapped,mapped_columnfromapp.models.baseimportBase,TimestampMixin,UUIDPrimaryKeyMixinclassKnowledgeBase(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__knowledge_bases__table_args__(UniqueConstraint(tenant_id,name),Index(ix_knowledge_bases_tenant_id,tenant_id),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)name:Mapped[str]mapped_column(String(160),nullableFalse)description:Mapped[str]mapped_column(Text,default,nullableFalse)classStoredDocument(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__documents__table_args__(UniqueConstraint(knowledge_base_id,content_hash),CheckConstraint(status IN (pending_upload,uploaded,indexing,ready,failed),namevalid_document_status,),Index(ix_documents_tenant_id_kb,tenant_id,knowledge_base_id),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)knowledge_base_id:Mapped[uuid.UUID]mapped_column(ForeignKey(knowledge_bases.id,ondeleteCASCADE),nullableFalse)filename:Mapped[str]mapped_column(String(255),nullableFalse)content_type:Mapped[str]mapped_column(String(100),nullableFalse)content_hash:Mapped[str]mapped_column(String(64),nullableFalse)object_key:Mapped[str]mapped_column(String(500),nullableFalse,uniqueTrue)size_bytes:Mapped[int]mapped_column(BigInteger,nullableFalse)status:Mapped[str]mapped_column(String(24),defaultuploaded,nullableFalse)error_code:Mapped[str|None]mapped_column(String(80))classDocumentChunk(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__document_chunks__table_args__(UniqueConstraint(document_id,ordinal),Index(ix_document_chunks_tenant_id_kb,tenant_id,knowledge_base_id),Index(ix_document_chunks_embedding_hnsw,embedding,postgresql_usinghnsw,postgresql_ops{embedding:vector_cosine_ops},),)tenant_id:Mapped[uuid.UUID]mapped_column(ForeignKey(tenants.id,ondeleteCASCADE),nullableFalse)knowledge_base_id:Mapped[uuid.UUID]mapped_column(ForeignKey(knowledge_bases.id,ondeleteCASCADE),nullableFalse)document_id:Mapped[uuid.UUID]mapped_column(ForeignKey(documents.id,ondeleteCASCADE),nullableFalse)ordinal:Mapped[int]mapped_column(Integer,nullableFalse)text:Mapped[str]mapped_column(Text,nullableFalse)page:Mapped[int|None]mapped_column(Integer)section:Mapped[str|None]mapped_column(String(500))start_char:Mapped[int]mapped_column(Integer,nullableFalse)end_char:Mapped[int]mapped_column(Integer,nullableFalse)embedding_model:Mapped[str]mapped_column(String(160),nullableFalse)embedding:Mapped[list[float]]mapped_column(Vector(384),nullableFalse)source_metadata:Mapped[dict[str,object]]mapped_column(JSON,defaultdict,nullableFalse)classIndexJob(UUIDPrimaryKeyMixin,TimestampMixin,Base):__tablename__index_jobs__table_args__(CheckConstraint(status IN (queued,parsing,chunking,embedding,ready,failed),namevalid_index_job_status,),CheckConstraint(dispatch_status IN (pending,claimed,dispatched,failed),namevalid_index_job_dispatch_status,),Index(ix_index_jobs_document_id,document_id),Index(ix_index_jobs_dispatch_pending,status,dispatch_status,dispatch_lease_until,),)document_id:Mapped[uuid.UUID]mapped_column(ForeignKey(documents.id,ondeleteCASCADE),nullableFalse)status:Mapped[str]mapped_column(String(24),defaultqueued,nullableFalse)next_batch:Mapped[int]mapped_column(Integer,default0,nullableFalse)total_chunks:Mapped[int]mapped_column(Integer,default0,nullableFalse)attempts:Mapped[int]mapped_column(Integer,default0,nullableFalse)error:Mapped[str|None]mapped_column(Text)dispatch_status:Mapped[str]mapped_column(String(16),defaultpending,server_defaultpending,nullableFalse)dispatch_token:Mapped[str|None]mapped_column(String(32))dispatch_attempts:Mapped[int]mapped_column(Integer,default0,server_default0,nullableFalse)dispatch_lease_until:Mapped[datetime|None]mapped_column(DateTime(timezoneTrue))dispatched_at:Mapped[datetime|None]mapped_column(DateTime(timezoneTrue))dispatch_error:Mapped[str|None]mapped_column(String(80))本篇练习手算距离并检查 SQL 顺序给定查询向量(1,0)A(0.8,0.6)B(1,0)C(-1,0)手算三者余弦相似度与 distance1-similarity写出按distance ASC的顺序。然后在真实 pgvector 测试中插入两个租户各一条极相似记录断言租户 A 查询永远拿不到租户 B即使 B 距离更小。下一篇给出计算和完整集成测试并实现第一个受权限约束的向量检索接口。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/14 3:51:47

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/15 2:04:12

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/14 10:11:15

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/14 17:14:38

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/14 16:35:50

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/15 4:06:36

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉