分块与向量索引

这条链路把「笔记 / 文件 / 网址的正文」变成「可分块检索的向量」:先分块,再逐块算 embedding,最后同时写正规表与 sqlite-vec 虚表。它的每一步都有可降级路径,目标是索引可以慢,但不能把数据写坏。

分块策略

项取值
首选方式配了「分块模型」且 provider 可用时走 LLM 分块(ChunkMethod = llm)
回退方式没有模型、调用失败或解析为空 → 结构化分块(ChunkMethod = structure),按标题与段落切分
结构参数MaxChunkSize = 1500、MinChunkSize = 200、OverlapSize = 100 字符
LLM 分块约束提示词要求每块 300–1500 字;原文 < 500 字时允许返回单块

写入顺序

  1. 按「知识项 + chunk 序号」写 NoteChunks(内容、分块方式、摘要)。
  2. 逐块计算 embedding,写 NoteChunkEmbeddings(模型名、维度、时间戳)与向量本体。
  3. 同步写入 sqlite-vec 虚表(vec_chunk_embeddings);虚表不支持 REPLACE,一律先 DELETE 再 INSERT。
  4. 单块内容时额外写一条笔记级向量(vec_note_embeddings),让整篇笔记也能被直接召回。
复用策略:内容与摘要都未变的旧分块会被保留,不重算向量——避免每次编辑笔记都把全文重新 embedding。笔记级检查还会顺手把「正规表里有、vec 表里缺」的向量补写回去,修复上次中断留下的缺口。

维度处理

顺序来源
1数据库中最近一条 embedding 的 Dimensions(最可信)
2用途为 embedding 的模型配置
3配置项 Embedding:Dimensions(默认 1536)

连接打开时若发现维度与虚表不一致,会 drop 并重建四张 vec 表(笔记 / 分块 / 记忆 / 代码)——这是「换 embedding 模型后必须重新索引」的技术原因。

失败与降级

情况行为
没有 embedding provider整条链路直接跳过,不写半成品数据
LLM 分块失败回退结构化分块(不会因为分块模型抖动而丢索引)
sqlite-vec 加载失败 / 虚表缺失 / 维度不符检索侧回退内存余弦相似度;正规表数据始终完整
单块向量写入失败记日志;批量写入连续失败 3 次则中止本批,避免雪崩
任务被取消OperationCanceledException 透传,不算失败

观测

  • 日志前缀 [Chunk] 与 [KI Embed];包含分块方式、块数、维度与失败原因。
  • 数据侧可见 NoteChunkDto:ChunkIndex / ChunkMethod / HasEmbedding / Summary,前端据此渲染分块列表与索引状态。
  • 后台任务类型:GenerateEmbedding(笔记)与 GenerateKnowledgeItemEmbedding(文件 / 网址),在「后台任务」页可看耗时与失败原因。

实作要点

  • 分块是「索引质量」的第一决定因素:结构化分块以标题为边界,长笔记建议保留小标题。
  • 换 embedding 模型后必须重新索引,并同步更新 Embedding:Dimensions。
  • 向量本体只用于检索;NoteChunkEmbeddings 是元数据与降级路径的依托,两者必须同时写。
  • 代码分块走独立的 vec_work_code_chunks,与笔记向量互不干扰。