Skip to content

Latest commit

 

History

History
123 lines (83 loc) · 5.82 KB

File metadata and controls

123 lines (83 loc) · 5.82 KB

09 知识库与 RAG

一、两种 RAG 需求

Chatbox 中有两条相近但不同的检索路径:

  1. Knowledge Base:用户长期维护的知识库,文件被解析、切块、embedding 后写入向量库,多个会话可复用。
  2. Session Attachment RAG:当前会话上传的大文件索引,生命周期与 session attachment 关联,避免把整个文件直接塞进上下文。

它们共享“解析 → chunk → embedding → search”的思想,但在存储、状态、权限和 UI 生命周期上分开。

二、长期知识库数据流

flowchart LR
  File[用户添加文件]
  DB[Knowledge Base DB\nfile + task 状态]
  Worker[后台 worker]
  Loader[格式 loader\ntext/markdown/html/json/office]
  Embed[Embedding Model]
  Vector[libsql vector store]
  Query[AI knowledge-base tool]
  File --> DB --> Worker --> Loader --> Embed --> Vector
  Query --> Vector --> Results[score + chunk + filename]
Loading

原始设计文档中明确了 worker 任务模型:文件先落数据库,任务进入待处理,worker 被触发时处理一批并停止,下一次触发继续。这种设计避免常驻 worker 复杂化 Electron 生命周期。

三、Main 与 Renderer 的分工

libsql 等向量数据库可能包含原生模块或需要直接访问 db 文件,因此知识库服务放在 Main。Renderer 通过 Platform.getKnowledgeBaseController() 调用它,并提供当前 provider 的 embedding/rerank 配置。

Renderer Settings / Provider
  → 初始化 KB controller
  → 通过 IPC 把 provider host/key/model 传给 Main
  → Main parser + worker + libsql
  → Renderer 得到 task status / search results

这不是把 API key 复制给数据库,而是把“如何创建 embedding 模型”的依赖在调用边界上注入进去。

四、文件解析策略

文件解析根据后缀或 MIME 选择 loader:

  • 文本、Markdown、HTML、JSON:MDocument 等本地 loader。
  • Office:officeparser。
  • 其他类型:可选的 Chatbox AI/Unstructured/MinerU 服务。
  • 解析失败:写入结构化错误状态,不让任务永久卡在 processing。

解析结果不是最终答案,而是后续 chunking 和 embedding 的输入。解析层需要保留 filename、mimeType、fileId 和 chunk index,检索结果才能回到 UI 做引用和调试。

五、Embedding 与模型注册表的关系

Provider 模型信息把 model 分为 chat、embedding、rerank、image 等类型。知识库页面不能把任意 chat model 当 embedding model;它应从模型注册信息中过滤可用类型,默认选择 provider 设置里的第一个合适模型。

这说明 Model Registry 不只是 UI 下拉框的来源,它实际决定 RAG pipeline 是否可以启动。

六、模型如何调用知识库

Chatbox 支持两种形态:模型可直接调用 knowledge_base_search 之类的工具,或者在不可靠工具调用的场景中使用 prompt engineering,让模型输出一个结构化 search action:

生成搜索动作
  → { action: search, query }
  → controller.search(knowledgeBaseId, query)
  → 格式化 document blocks
  → 与用户最新问题放入新的 user message
  → 再调用模型回答

constructMessagesWithKnowledgeBaseResults() 会把结果包装为 [document N begin] ... end,并保留用户最新问题。这是“检索结果和问题在同一轮交给模型”的上下文协议。

七、Session Attachment RAG

上传的大文件不能总是作为 inline content 发送。消息文件 schema 记录:

  • ragMode: inline | session-retrieval
  • sessionAttachmentId
  • sessionAttachmentAvailability
  • indexStatus:pending、indexing、ready、failed。
  • chunk count、total chunks、embedded chunks 和 indexing stage。

refreshSessionAttachmentStatuses() 在生成前刷新状态;getSessionAttachmentRagIds() 只把可用、非 blocked 的 attachment ID 交给工具构建器。模型看到的是检索工具,不是完整文件内容。

八、可用性与阻断

附件 RAG 不是“有 ID 就能搜”。状态需要经过:

pending → indexing → ready
             └────→ failed
blocked:策略/格式/权限导致不可用

如果 attachment 被 blocked,工具构建器不应把它注入为可检索资源;UI 需要展示 warning reason,避免用户误以为模型已经读过文件。

九、RAG 的上下文预算

检索结果也会吃 token,因此需要:

  • 限制 top-k 和每个 chunk 的字符数。
  • 保留 filename、score、chunk index 等来源信息。
  • 大文件正文存储在索引或 blob,不把完整文档放进 Message。
  • 结合 maxContextMessageCount 和上下文压缩,避免“RAG 结果把历史挤出去”。

RAG 不是上下文管理的替代品,而是一个需要被上下文管理器约束的数据源。

十、设计取舍

  • Main 执行向量库:隔离原生依赖和 db 文件;代价是 IPC 初始化与错误传播更复杂。
  • 长期 KB 与 Session Attachment 分开:生命周期清晰;代价是需要两套状态/维护入口。
  • 工具 + prompt engineering 双路径:兼容弱工具模型;代价是必须维护两种结果协议。
  • 结果保留元数据:便于引用和调试;代价是模型上下文更长,需要预算控制。

关键源码路径

src/main/knowledge-base/src/main/session-attachment-rag/src/renderer/platform/knowledge-base/src/renderer/platform/session-attachment-rag/src/renderer/packages/model-calls/toolsets/knowledge-base.tssession-attachment-rag.tssrc/renderer/stores/session/attachment-resolver.ts

小结

RAG 的主线不是“调用一次向量搜索”,而是把文件生命周期、模型类型、后台任务、IPC、检索结果和上下文预算连接起来。下一章回到所有这些状态的共同基础:存储、迁移和备份。