第 2 章 核心概念与整体架构


文档摘要

第 2 章 核心概念与整体架构 第 1 章你跑通了 demo,但那些 API 为什么长那样?这一章把 Zvec 的四个核心概念——Collection、Document、Schema、索引——一次讲透,并画出它们之间的依赖关系,让你此后看到任何一段 Zvec 代码都能立刻在脑子里建模。 2.1 四件套:Collection / Document / Schema / 索引 Zvec 用一套类关系数据库的模型来组织数据。理解这四个概念,就理解了 Zvec 的全部骨架。

第 2 章 核心概念与整体架构

第 1 章你跑通了 demo,但那些 API 为什么长那样?这一章把 Zvec 的四个核心概念——Collection、Document、Schema、索引——一次讲透,并画出它们之间的依赖关系,让你此后看到任何一段 Zvec 代码都能立刻在脑子里建模。

2.1 四件套:Collection / Document / Schema / 索引

Zvec 用一套类关系数据库的模型来组织数据。理解这四个概念,就理解了 Zvec 的全部骨架。

概念 类比 一句话定义
Collection(集合) 数据库的"表" 存放 Document 的具名容器,每个 Collection 自包含于一个磁盘目录
Document(文档) 表里的"一行" 数据存储的基本单元,由 id + vectors + fields 组成
Schema(模式) 表的"结构定义" 描述 Collection 里有哪些字段、什么类型、建什么索引
索引(Index) 加速查找的"数据结构" 让检索不必逐条扫描——向量有向量索引,标量有倒排/全文索引

💡 为什么不用"库/表/行"这种纯 SQL 术语? 因为向量检索有自己的语义——"相似度"而不是"相等"。Zvec 选了 Collection/Document 这套更贴近检索场景的命名,但骨架仍是强类型、有 Schema 约束的,这和"无 schema 的文档库"(如早期文档数据库)有本质区别。强类型意味着写入时会做类型安全检查,维度对不上、类型错配会直接报错而不是悄悄存错。

Document 的三段式结构

每个 Document 由三部分组成,记住这个结构,后面所有写入代码都是往这三部分里填值:

┌─────────────────────────────────────────────────┐ │ Doc │ │ ├── id 🔑 唯一字符串标识,写入后不可改 │ │ ├── vectors 📐 具名向量集合 {向量名: 向量值} │ │ └── fields 🗂️ 具名标量集合 {字段名: 值} │ └─────────────────────────────────────────────────┘

一个 RAG 知识库里的典型 Document 长这样:

zvec.Doc( id="chunk_042", vectors={ "dense": [0.012, -0.034, ..., 0.018], # 768 维稠密向量(语义) "sparse": {4017: 2.31, 309: 1.85}, # 稀疏向量(关键词权重) }, fields={ "text": "Zvec 是阿里开源的进程内向量数据库……", # 原文,喂给 LLM "source": "zvec-intro.md", # 出处 "publish_year": 2026, # 用于过滤 "category": ["rag", "vector-db"], # 数组字段,多标签 }, )

⚠️ 字段名必须和 Schema 完全一致。Schema 里叫 dense,写入和查询时就只能用 dense。这是新手最常踩的坑——大小写、拼写、下划线都得对齐。

2.2 整体架构图

把 Zvec 装进一个进程后,它的内部结构是这样的:

┌─────────────────────────────────────┐ │ 你的应用程序进程 │ │ │ import zvec ──────►│ ┌───────────────────────────────┐ │ │ │ Zvec 库 │ │ │ │ │ │ │ │ Collection A Collection B │ │ ← 多个 Collection 相互隔离 │ │ ├ Schema ├ Schema │ │ │ │ ├ 向量索引(HNSW)├ 向量索引 │ │ │ │ ├ 倒排索引 ├ 全文索引 │ │ │ │ └ Doc×N └ Doc×N │ │ │ └──────────┬────────────────────┘ │ └─────────────┼───────────────────────┘ │ 内存映射 / 文件 I/O ▼ ┌─────────────────────────────────────┐ │ 磁盘(一个文件夹) │ │ ./kb_a/ ← 自包含,可整体拷贝 │ │ ./kb_b/ │ └─────────────────────────────────────┘

三个关键设计决策,决定了 Zvec 的所有行为特征:

  1. 进程内嵌入,无独立服务。这意味着读写就是函数调用,没有网络往返,延迟极低;代价是默认单进程独占写,多进程要靠"只读共享"(详见第 8 章)。
  2. 每个 Collection 自包含于一个目录。你把 ./kb_a/ 整个文件夹拷到另一台机器,zvec.open(path="./kb_a") 就能原样打开。这对 RAG 的分发、备份、灰度极其友好——知识库就是一个文件夹。
  3. Collection 之间相互隔离。不同业务(比如"产品手册 RAG"和"客服对话 RAG")用不同 Collection,互不干扰,可独立调参。不支持跨 Collection 查询(没有 Join/Union),所以数据建模时要把同一检索场景的内容放进同一个 Collection。

2.3 三类索引:各管一段检索

Schema 里你会反复和"索引"打交道。Zvec 的索引分三类,理解它们的分工,就理解了第 6、7 章所有检索方式的底层

索引类型 作用对象 解决什么问题 触发的检索方式
向量索引(HNSW/IVF/DiskANN…) 向量字段 语义相似度检索(必建) Query(vector=...)
倒排索引(InvertIndex) 标量字段 精确值/范围/成员过滤 filter="..."
全文索引(FtsIndex) 文本字段 关键词检索 + BM25 相关性排序 Query(fts=...)

💡 三类索引对应 RAG 的三种召回需求:向量索引管"意思相近",倒排索引管"属性匹配"(如"只查 2024 年的、只查产品 A 的"),全文索引管"包含特定关键词"。生产级 RAG 往往三者都用——这也是为什么 Zvec 把它们做进同一个库。

为什么向量字段必须建索引?因为没有索引就只能暴力比对全部向量(Flat),数据量一大就慢到不可用。标量字段索引则是可选的——只给"经常用来过滤"的字段建,不建也能过滤但会很慢(详见第 6 章)。

2.4 模块依赖关系:谁依赖谁

把前面的概念理成依赖关系,你就知道设计 Schema 时该先想什么、后想什么:

Schema(结构定义) ╱ ╲ ╱ ╲ 字段定义(Field) 向量定义(Vector) │ │ ┌────────┼────────┐ │ │ │ │ │ 倒排索引 全文索引 (无索引) 向量索引 │ │ │ (HNSW/IVF/…) │ │ │ │ └────────┴────┬───┴─────────────┘ ▼ Collection(按 Schema 创建) │ ▼ 写入 Document × N (每个 Doc 都要符合 Schema) │ ▼ query() 检索 (按字段名决定走哪个索引)

这个依赖图给设计 Schema 提供了一条清晰的思路(第 4 章会展开):

  1. 先想清楚要存什么:哪些是向量(语义)、哪些是标量(过滤/展示)、哪些是文本(关键词检索)。
  2. 再决定每个字段建什么索引:向量必建;常过滤的标量建倒排;要关键词检索的文本建全文。
  3. 最后才写代码CollectionSchemacreate_and_openinsertquery

2.5 数据类型系统:强类型意味着什么

Zvec 是强类型的。写入时每个字段都要严格匹配 Schema 声明的 DataType,否则报错。这避免了"悄悄存错值、检索时才发现"的灾难。完整的类型表如下:

类别 类型 典型用途(RAG 场景)
标量基础类型 STRING / BOOL / INT32 / INT64 / UINT32 / UINT64 / FLOAT / DOUBLE 文本出处、是否可见、年份、评分
标量数组类型 ARRAY_STRING / ARRAY_BOOL / ARRAY_INT32 … 多标签(category)、权限列表
稠密向量 VECTOR_FP32 / VECTOR_FP16 / VECTOR_INT8 Embedding 模型输出(语义检索主力)
稀疏向量 SPARSE_VECTOR_FP32 / SPARSE_VECTOR_FP16 关键词权重向量(稀疏检索)

⚠️ 维度是稠密向量的硬约束。Schema 里声明 768 维,写入 512 维的向量会直接报错。而且同一个向量字段的维度一旦定下,就固定了——所以选 Embedding 模型时要想好(详见第 3 章)。稀疏向量则没有固定维度,只需存非零项。

2.6 概念性代码:用四件套搭一个 RAG 知识库

把这一章的概念串起来,下面是一段"只看概念、不抠细节"的 RAG 知识库骨架代码。重点看注释里"为什么这么设计"的因果,而不是语法

import zvec # ① Schema = 知识库的结构合同。RAG 通常需要:原文 + 出处 + 过滤字段 + 至少一个向量 schema = zvec.CollectionSchema( name="rag_kb", fields=[ # 原文:要喂给 LLM,存即可;不需要建索引(省存储/写入开销) zvec.FieldSchema(name="text", data_type=zvec.DataType.STRING), # 出处:可能按文档过滤,建倒排索引 zvec.FieldSchema(name="source", data_type=zvec.DataType.STRING, index_param=zvec.InvertIndexParam()), # 年份:要范围过滤(如"只查近两年的"),建倒排 + 范围优化 zvec.FieldSchema(name="publish_year", data_type=zvec.DataType.INT32, index_param=zvec.InvertIndexParam(enable_range_optimization=True)), ], vectors=[ # 语义向量:必建索引。度量选 COSINE(与大多数文本 Embedding 模型一致) zvec.VectorSchema(name="dense", data_type=zvec.DataType.VECTOR_FP32, dimension=768, index_param=zvec.HnswIndexParam(metric_type=zvec.MetricType.COSINE)), ], ) # ② 创建 Collection(数据落地到 ./rag_kb 文件夹,整个文件夹可迁移) kb = zvec.create_and_open(path="./rag_kb", schema=schema) # ③ Document = 知识的一条切片。注意字段名/向量名与 Schema 严格对齐 kb.insert(zvec.Doc( id="chunk_001", vectors={"dense": [0.0] * 768}, # 真实场景由 Embedding 模型生成 fields={"text": "Zvec 是阿里开源的进程内向量数据库……", "source": "intro.md", "publish_year": 2026}, )) # ④ optimize 后正式走向量索引(小数据看不出来,数据量大是必须的) kb.optimize() # ⑤ 检索:field_name="dense" 决定走向量索引;filter 决定先经倒排索引过滤 hits = kb.query( queries=zvec.Query(field_name="dense", vector=[0.0] * 768), filter="publish_year >= 2025", # 只在近两年的切片里找 topk=5, ) # 把 hits 里的 text 拼进 Prompt,交给 LLM 生成 —— 这就是 RAG 的第③④步

2.7 动手实验

  1. 故意写错类型:把 publish_year 的值写成字符串 "2026"(Schema 声明的是 INT32),运行观察报错信息——体会"强类型"的保护作用。
  2. 故意写错维度:Schema 里 dimension=768,但写入时给 [0.0]*512,观察报错。
  3. 拷贝文件夹验证自包含:把 ./rag_kb 文件夹复制成 ./rag_kb_copy,用 zvec.open(path="./rag_kb_copy") 打开,确认能正常检索(这是进程内数据库最实用的特性之一)。

本章小结

  • Zvec 的骨架是四件套:Collection(表)/ Document(行)/ Schema(结构)/ 索引(加速)
  • Document 是 id + vectors + fields 三段式;字段名必须与 Schema 严格一致
  • 架构三特征:进程内嵌入、每个 Collection 自包含于一个目录、Collection 间相互隔离(无跨库查询)。
  • 索引分三类:向量索引(语义)/ 倒排索引(标量过滤)/ 全文索引(关键词 BM25),对应 RAG 三种召回需求。
  • 强类型系统在写入时做安全检查,向量维度是硬约束。

配套可运行示例

本章的四件套概念可以用配套示例直接验证:

想验证 对应示例主题
Schema 怎么定义、Document 三段式怎么写 「五分钟建库检索闭环」示例
三种索引(向量 / 倒排 / FTS)的 Schema 配置 「Schema 设计与演进」示例
强类型保护:写错维度/类型会怎么报错 在建库检索示例里把 dimension 故意写错试试

💡 配套示例提供的占位向量生成函数(把文本哈希成定长向量),让你不装任何 Embedding 模型就能把 Schema、写入、检索的链路跑通——这正是「先打通链路,再上模型」调试法的基础。

下一章我们进入 RAG 的语义基石——向量与 Embedding。详见第 3 章


发布者: 作者: 转发
评论区 (0)
U