Skip to content

向量存储与索引 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与容量特性
  8. 增量更新、迁移与一致性
  9. 元数据模型与主键设计
  10. 备份恢复与清理策略
  11. 大规模存储方案与调优指南
  12. 故障排查
  13. 结论

引言 ​

本文聚焦 Agent 端 RAG 子系统的向量存储与索引实现,围绕 Vectra 存储适配器展开,解释向量数据的持久化方式、索引构建流程、查询优化路径、元数据存储结构、主键设计、数据一致性保证、增量更新机制、数据迁移策略和存储空间管理。文档同时给出架构图、数据表结构与性能调优建议,并讨论大规模向量数据的存储方案、备份恢复和数据清理策略。

项目结构 ​

RAG 的向量存储相关代码集中在 agent/src/rag 下:

  • store/vectra.js:VectraStore 适配器,封装 vectra@0.15.0 的 LocalIndex。
  • store/index.js:向量存储统一入口 createVectorStore。
  • manager.js:知识库生命周期、导入、重建、检索等编排逻辑。
  • config.js:RAG_ROOT、COLLECTIONS_REGISTRY、默认 embedding/chunk/search 配置等。
  • docs/RAG-Knowledge-Retrieval-Design.md:RAG 设计文档,包含 Vectra 存储布局、容量与性能说明。
graph TB
subgraph "Agent RAG"
M["manager.js<br/>知识库编排"]
S["store/index.js<br/>createVectorStore"]
V["store/vectra.js<br/>VectraStore"]
C["config.js<br/>RAG_ROOT/注册表/默认配置"]
D["docs/RAG-Knowledge-Retrieval-Design.md<br/>设计文档"]
end
M --> V
M --> C
S --> V
D -.参考.-> M
D -.参考.-> V

图表来源

章节来源

核心组件 ​

  • VectraStore:对 vectra@0.15.0 LocalIndex 的适配层,负责索引创建、分块 upsert、向量检索、按文档列出/删除、全量枚举、重建。
  • RagManager:知识库与文档的生命周期管理,串联解析、分块、向量化、写入 Vectra、原文备份、注册表计数更新;提供重建、恢复、统计、检索等能力。
  • VectorStore 工厂:createVectorStore 动态加载 VectraStore,为未来扩展其他后端(如 Qdrant)预留接口。
  • RAG 配置:定义 RAG_ROOT、collections.json 注册表路径、默认 embedding/chunk/search 参数、解析器路由等。

章节来源

架构总览 ​

RAG 的向量存储采用“本地文件型向量库 + 文件系统备份”的组合:

  • 每个知识库一个目录(kb_xxx),内含 Vectra 索引文件与 documents/ 原始文档备份。
  • collections.json 作为知识库注册表,保存知识库元数据、embeddingConfig、chunkConfig、documentCount、chunkCount 等。
  • RagManager 负责索引重建时的原子换入与崩溃恢复,确保旧索引可用、新索引可切换。
graph TB
Client["调用方工具/路由"]
Manager["RagManager<br/>manager.js"]
Store["VectraStore<br/>store/vectra.js"]
Index["LocalIndex(vectra)<br/>index.json / index.protobuf"]
Registry["collections.json<br/>知识库注册表"]
Docs["documents/<br/>原始文档备份"]
Client --> Manager
Manager --> Store
Store --> Index
Manager --> Registry
Manager --> Docs

图表来源

详细组件分析 ​

VectraStore 适配器 ​

VectraStore 是向量存储的核心适配器,封装 vectra@0.15.0 的 LocalIndex API,解决以下约束:

  • createIndex 对已存在索引会抛错,需先 isIndexCreated。
  • 无批量 upsert API,使用 beginUpdate/endUpdate 事务包裹逐条 upsertItem,避免每条一次落盘。
  • 无按元数据删除 API,通过 listItemsByMetadata + deleteItems 两步完成。
  • queryItems 为位置参数签名 (vector, query, topK, filter?, isBm25?),query 仅 BM25 路径使用。
  • metadata 中记录 documentId/startPos/endPos,为 BM25 混合检索预留;原始文档备份见 documents/。

关键方法职责:

  • ensureCreated:幂等创建索引。
  • upsertChunks:事务批量写入一个文档的全部分块,系统保留键覆盖用户传入的同名键,防止篡改导致孤儿分块。
  • search:统一返回 {id, content, metadata, score},score 为归一化余弦相似度 [-1,1]。
  • listDocumentChunks/removeDocument:按 documentId 列出或删除所有分块。
  • listAllItems/recreate:用于统计与重建场景。
classDiagram
class VectraStore {
+constructor(collectionId, rootDir)
+ensureCreated() Promise~void~
+upsertChunks(documentId, chunks, embeddings, metadata) Promise~void~
+search(queryVector, queryText, options) Promise~Array~
+listDocumentChunks(documentId) Promise~Array~
+removeDocument(documentId) Promise~number~
+listAllItems() Promise~Array~
+recreate() Promise~void~
}

图表来源

章节来源

RagManager 与向量写入流程 ​

RagManager 将“解析 → 分块 → 向量化 → 写入 Vectra → 原文备份 → 更新注册表计数”串成完整链路,并提供进度上报、并发控制、错误处理。

sequenceDiagram
participant Caller as "调用方"
participant Manager as "RagManager"
participant Provider as "EmbeddingProvider"
participant Store as "VectraStore"
participant FS as "文件系统"
Caller->>Manager : ingestText/ingestFile/ingestUrl
Manager->>Manager : chunkText(text, chunkConfig)
Manager->>Provider : embedDocuments(chunks)
Provider-->>Manager : embeddings[]
Manager->>Store : upsertChunks(documentId, chunks, embeddings, meta)
Store->>FS : beginUpdate/endUpdate 事务写入
Manager->>FS : _saveDocumentBackup(doc.json)
Manager->>Manager : _updateRegistry(计数+1)
Manager-->>Caller : {documentId, name, chunkCount}

图表来源

章节来源

向量检索流程 ​

检索由 RagManager.search 委托给 Searcher,底层调用 VectraStore.search,最终走 LocalIndex.queryItems。

sequenceDiagram
participant Caller as "调用方"
participant Manager as "RagManager"
participant Searcher as "Searcher"
participant Store as "VectraStore"
participant Index as "LocalIndex"
Caller->>Manager : search(collectionId, query, options)
Manager->>Manager : normalizeSearchOptions(options)
Manager->>Searcher : new Searcher(store, provider)
Searcher->>Store : search(queryVector, queryText, options)
Store->>Index : queryItems(vector, query, topK, filter, isBm25)
Index-->>Store : results[]
Store-->>Searcher : [{id,content,metadata,score}]
Searcher-->>Manager : 结果集
Manager-->>Caller : 搜索结果

图表来源

章节来源

索引重建与原子换入 ​

当 embedding 或 chunk 配置发生向量空间级变更时,RagManager 触发后台重建:

  • 在临时目录 .rebuild_{collectionId} 内用新配置重放 documents/ 备份,生成新索引与新备份。
  • 写 rebuild_manifest.json 清单,随后原子 rename(finalDir, oldIndexKeep),再 rename(tmpRoot/collectionId, finalDir)。
  • 提交注册表后删除清单并清理临时目录;失败时保留现场供恢复。
flowchart TD
Start(["开始重建"]) --> ReadBackups["读取 documents/ 备份"]
ReadBackups --> BuildTmp["在临时目录构建新索引与备份"]
BuildTmp --> WriteManifest["写入 rebuild_manifest.json"]
WriteManifest --> SwapOld["rename(finalDir -> index_backup)"]
SwapOld --> SwapNew["rename(tmpRoot/collectionId -> finalDir)"]
SwapNew --> CommitReg["提交注册表新配置/计数"]
CommitReg --> Cleanup["删除清单/清理临时目录"]
Cleanup --> End(["完成"])
SwapOld --> |失败| Rollback["回滚旧索引"]
Rollback --> End

图表来源

章节来源

依赖关系分析 ​

  • RagManager 依赖:
    • VectraStore:写入/查询向量索引。
    • EmbeddingProvider:根据 collection.embeddingConfig 生成向量。
    • FileSystem:读写 collections.json、documents/、重建临时目录。
    • Security/url-guard:路径沙箱与 URL 导入安全。
  • VectraStore 依赖:
    • vectra.LocalIndex:本地索引 API。
    • path/homedir/os:路径拼接与根目录。
graph LR
Manager["RagManager"] --> VectraStore["VectraStore"]
Manager --> Embedding["EmbeddingProvider"]
Manager --> FS["文件系统"]
Manager --> Security["Security/URL Guard"]
VectraStore --> LocalIndex["vectra.LocalIndex"]

图表来源

章节来源

性能与容量特性 ​

  • Vectra 是轻量级本地文件向量库,非独立数据库服务。
  • 检索为暴力余弦相似度扫描(非 ANN),速度随数据量线性变慢,但检索质量不随数据量下降。
  • v0.15.0 优化:min-heap top-K 查询 O(N log K)、不再深拷贝向量、并行 metadata 读取。
  • 超过 10 万分块建议迁移外接方案(如 Qdrant HNSW)。

容量与性能参考(来自设计文档):

  • 小型:约 1,000 分块,磁盘 ~3MB,内存 ~5MB,查询 <1ms。
  • 中型:约 10,000 分块,磁盘 ~30MB,内存 ~50MB,查询 1-5ms。
  • 大型:约 100,000 分块,磁盘 ~300MB,内存 ~500MB,查询 5-50ms。
  • 超大:约 1,000,000 分块,磁盘 ~3GB,内存 ~5GB,查询 50-500ms。

章节来源

增量更新、迁移与一致性 ​

增量更新机制 ​

  • 导入文本/文件/URL 均走 _ingestText:分块 → 向量化 → 事务写入 Vectra → 原文备份 → 更新注册表计数。
  • 同一知识库的导入/重建互斥(_activeOps),避免并发写坏索引。
  • 注册表更新通过 _registryLock 串行化,防并发写坏。

章节来源

数据迁移策略 ​

  • 当 embedding 或 chunk 配置变化触发重建时,RagManager 在临时目录重建索引,完成后原子换入,最后提交注册表。
  • 重建期间新配置不写入注册表,完成后一次性生效;失败时旧索引与旧配置保持可用。

章节来源

数据一致性保证 ​

  • 事务写入:beginUpdate/endUpdate 包裹 upsertChunks,失败 cancelUpdate 丢弃半成品。
  • 原子换入:rename(oldIndexKeep) → rename(newIndex) → 提交注册表 → 清理临时目录。
  • 崩溃恢复:rebuild_manifest.json 与 index_backup 组合判定半完成状态,支持回滚/补提交/清理。

章节来源

元数据模型与主键设计 ​

知识库注册表(collections.json) ​

  • 字段包括:id、name、description、embeddingConfig、chunkConfig、createdAt、documentCount、chunkCount、enabled(可选)。
  • apiKey 在落盘前混淆,内存中保持明文;权限设置为 0o600。

章节来源

向量条目元数据(Vectra metadata) ​

  • 系统保留键:documentId、chunkIndex、totalChunks、text、startPos、endPos。
  • 业务键:documentName、source、createdAt 以及用户自定义 metadata(用户键在前,系统键在后,系统键不可被覆盖)。

章节来源

主键设计 ​

  • 知识库 ID:kb_ + 时间戳 + 随机字节,正则校验 kb_[a-z0-9]+。
  • 文档 ID:doc_ + 时间戳 + 随机字节,正则校验 doc_[a-z0-9]+。
  • 分块 ID:${documentId}_chunk_${i},唯一且有序。

章节来源

数据表结构(概念映射) ​

  • 知识库集合(collections.json):
    • id(主键)、name、description、embeddingConfig、chunkConfig、createdAt、documentCount、chunkCount、enabled。
  • 向量条目(Vectra 内部条目):
    • id(主键)、vector、metadata(documentId、chunkIndex、totalChunks、text、startPos、endPos、documentName、source、createdAt、...)。
  • 文档备份(documents/{documentId}.json):
    • id、name、text、source、metadata、chunkCount、createdAt。
erDiagram
COLLECTION {
string id PK
string name
string description
object embeddingConfig
object chunkConfig
timestamp createdAt
integer documentCount
integer chunkCount
boolean enabled
}
VECTOR_ITEM {
string id PK
float[] vector
object metadata
}
DOCUMENT_BACKUP {
string id PK
string name
text text
string source
object metadata
integer chunkCount
timestamp createdAt
}
COLLECTION ||--o{ VECTOR_ITEM : "包含"
COLLECTION ||--o{ DOCUMENT_BACKUP : "包含"

图表来源

备份恢复与清理策略 ​

重建清单与中断恢复 ​

  • rebuild_manifest.json:记录目标 embeddingConfig、chunkConfig、documentCount、chunkCount、startedAt。
  • 进程重启后 recoverInterruptedRebuilds 扫描 .rebuild_* 目录,按形态收敛:
    • finalDir 缺失 + tmpRoot/<id> 含清单:完成换入并补提交。
    • finalDir 缺失 + tmpRoot/index_backup:回滚旧索引。
    • finalDir 存在 + 清单存在:补提交并清理。
    • finalDir 正常 + 残留 tmpRoot:安全清理。

章节来源

文档删除与计数修正 ​

  • 删除文档:从 Vectra 删除该文档的所有分块,删除 documents/{documentId}.json,更新注册表计数。
  • getStats:实时聚合索引条目,修正注册表计数漂移。

章节来源

大规模存储方案与调优指南 ​

存储布局 ​

  • RAG_ROOT:默认 ~/.ai-helper-agent/rag,可通过环境变量 AI_HELPER_RAG_ROOT 覆盖。
  • 每个知识库目录:kb_xxx,包含 Vectra 索引文件(index.json 或 index.protobuf)与 documents/ 原始文档备份。
  • models/:transformers.js 模型缓存。

章节来源

容量与性能调优 ​

  • 小/中型库(≤10 万分块):Vectra 本地索引足够,查询延迟低。
  • 大型库(>10 万分块):建议迁移至外接方案(如 Qdrant HNSW),以降低查询延迟。
  • 检索参数:topK 上限 SEARCH_TOPK_MAX=50,threshold 钳制到 [-1,1],防止候选集放大与阈值异常。
  • 分块参数:默认 chunkSize=400、overlap=80,中文 512 token 上限内的安全边界。
  • Embedding 批大小:EMBED_BATCH_SIZE=10,兼容平台单次批量上限。

章节来源

备份恢复与清理 ​

  • 原文备份:documents/{documentId}.json,BM25 docReader 依赖。
  • 重建临时目录:.rebuild_{collectionId},含 index_backup 与 rebuild_manifest.json。
  • 清理策略:重建成功后删除清单与临时目录;失败时保留现场供恢复。

章节来源

故障排查 ​

  • 导入失败:检查内容大小限制(INGEST_MAX_TEXT_BYTES/INGEST_MAX_FILE_BYTES)、空内容、URL 无正文、路径沙箱拒绝。
  • 维度不匹配:embeddingDimensionMismatch,检查远端模型 dimensions 配置与实际输出维度。
  • 重建失败:查看 rebuild_manifest.json 与 index_backup 是否存在,确认是否处于半完成状态;recoverInterruptedRebuilds 可自动收敛。
  • 检索为空:检查 threshold 是否过大、topK 是否过小、知识库是否启用(enabled=false 不参与 LLM 自主检索)。

章节来源

结论 ​

VectraStore 适配器以 vectra@0.15.0 为基础,结合 RagManager 的事务写入、原子换入与崩溃恢复机制,实现了可靠的向量数据持久化、索引构建与查询优化。通过 collections.json 注册表与 documents/ 原文备份,系统在数据一致性、可恢复性与可观测性方面具备完善保障。对于小规模数据,Vectra 本地索引表现良好;对于大规模数据,建议迁移至外接向量数据库以获得更好的检索性能。