24 KiB
知识库检索与分块增强 PRD
文档信息
| 项目 | 内容 |
|---|---|
| 状态 | 实施中 |
| 版本 | 0.1 |
| 日期 | 2026-08-11 |
| 适用产品 | GoodBuddy 桌面端 |
| 实施范围 | 第一阶段:可用、可见、可诊断;第二阶段:可调、可优化、可维护 |
1. 背景
GoodBuddy 已具备本地多知识库、文件与目录同步、网页导入、SQLite FTS5、 OpenAI 兼容向量模型、RRF 混合检索、知识图谱、任务状态和来源引用。现有实现 优先建立了本地数据主权、安全边界和跨 Runtime 工具授权,但用户仍难以稳定 获得“导入资料后即可准确问答”的体验。
当前主要问题不是缺少知识图谱,而是基础 RAG 链路缺少完整闭环:
- 在对话中启用知识库只会开放搜索工具,是否检索仍由模型自行决定。
- 默认向量检索关闭,中文全文检索对自然语言问法和同义表达的召回不足。
- 向量请求失败会降级为全文检索,但知识库页面仍可能显示索引完成。
- 大于 5,000 个向量分块的知识库会跳过向量召回。
- 用户不能独立测试召回、查看各通道得分或确认实际送入模型的上下文。
- 分块参数固定,缺少结构化、父子分块、分块预览和人工修正。
- 引用只能阅读片段,不能查看完整上下文或打开原始来源。
本项目先完成稳定性和可观测性,再增加高级分块、重排与维护能力。知识图谱 继续作为可选召回通道,但不替代全文和向量检索的基础质量。
2. 已确认的产品决策
- 保持本地优先,不引入必须联网的托管知识库服务。
- 保持 Electron Main、Preload、Renderer 的安全边界,Renderer 不直接读取 数据库、原文件或向量。
- 保留“模型按需检索”,并新增“每次先检索”模式。后者必须由 Main 进程 预检索,不能只依赖提示词要求模型调用工具。
- 知识库新建后不默认启用全部已有知识库;对话中的范围继续由用户显式选择。
- 向量服务不可用时保留全文检索,但必须返回明确降级状态。
- 中文召回使用应用内可控的 CJK n-gram 索引,不新增远程服务依赖。
- 混合检索保留 RRF 候选融合,并增加本地确定性重排、可选的 Cohere/Jina 兼容学习型重排、最低相关度和上下文预算。学习型重排失败时 安全降级,不影响全文、向量和图谱召回。
- 向量搜索取消 5,000 分块静默失效,使用有界内存的分页扫描。在没有稳定 跨平台向量扩展前,接受本地 CPU 线性扫描,并持续显示性能诊断。
- 向量索引兼容性同时校验 Provider、Model、维度和 Provider Fingerprint。 同名模型切换端点后,旧向量不能继续参与召回。
- 失败或取消的重建不能停用上一版已就绪索引。新索引只有完整校验成功后才 原子替换当前服务版本。
- 分块设置属于知识库,修改后不会伪装为立即生效。用户需要显式重建索引。
- 分块允许预览、编辑、启用、停用和删除。来源再次同步可能覆盖人工修改, UI 必须在修改前持续说明该行为。
- 第一阶段和第二阶段均不新增付费或外部模型调用。现有 Embeddings 调用仍由 用户配置决定。
- Ask 的运行时边界保持只读。知识库内容始终被标记为不可信证据, 不能成为系统指令。
3. 目标
3.1 用户目标
- 明确知道本次回答是否检索、检索了哪些知识库,以及是否发生降级。
- 在知识库页面输入真实问题,查看命中分块、通道、得分和最终上下文。
- 为不同文档选择适合的分块模式,并在导入前理解影响。
- 查看和修正错误分块,不需要删除并重新导入整个来源。
- 从回答引用查看完整上下文,并打开对应本地文件或网页。
- 在向量、解析或图谱失败时获得可恢复的状态和明确操作。
3.2 产品目标
- 默认中文问法在没有向量模型时仍具有可用的关键词召回。
- 向量服务故障、大知识库和模型变更不再产生静默空结果。
- 建立可复现的检索调试入口,支持固定问题进行回归测试。
- 将解析、全文、向量和图谱状态拆分,避免“索引完成”误导。
- 为后续元数据过滤、远程 Rerank Provider 和自动评测保留稳定契约。
3.3 质量目标
- 中文同义改写测试集的 Recall@5 相比现有全文检索基线提升至少 30%。
- 检索测试结果必须在本机重复执行时保持稳定排序。
- 任意向量失败都必须在检索诊断或任务状态中可见。
- 10,000 个分块的知识库不得因固定上限返回空向量结果。
- 每条展示引用都能找到仍存在且属于已授权知识库的分块和文档。
- 检索输出和上下文拼装均遵守字符、结果数和 IPC 大小上限。
4. 非目标
本项目不包含:
- 团队共享知识库、SSO、SCIM 或跨设备同步。
- 企业级 ACL、文档级角色继承和远程权限同步。
- 云端网站爬虫、Notion、飞书、语雀等第三方连接器。
- MinerU、PaddleOCR-VL 或其他远程文档解析服务。
- 专用向量数据库、外部 Elasticsearch 或打包平台原生向量扩展。
- 托管重排服务账户、计费或供应商绑定;仅提供通用兼容接口配置。
- 自动问题生成、FAQ 生成和训练数据标注平台。
- 完整 RAG 离线评测平台。第二阶段只提供手动检索测试与可导出的诊断信息。
- 在应用内高保真渲染所有原始 Office 和 PDF 文档。
5. 竞品基线与 GoodBuddy 定位
截至 2026-08-11,Dify、FastGPT 和 RAGFlow 的公开文档均把检索测试、可配置 分块和可调检索参数作为知识库基础能力:
| 能力 | Dify | FastGPT | RAGFlow | GoodBuddy 本期 |
|---|---|---|---|---|
| 检索测试 | 支持 | 支持 | 支持 | 第一阶段支持 |
| Top K / 阈值 | 支持 | 支持 | 支持 | 第一阶段支持 |
| 全文 + 向量 | 支持 | 支持 | 支持 | 已有,第一阶段增强中文 |
| Rerank | 模型 Rerank | 模型 Rerank | 模型 Rerank | 本地确定性与可选兼容模型重排 |
| 父子分块 | 支持 | 可通过索引与大分块组合 | 支持多种切分策略 | 第二阶段支持 |
| 分块维护 | 支持内容维护 | 支持数据维护 | 支持块级检查 | 第二阶段支持 |
| 深度文档理解 | 中等 | 中等 | 强 | 继续复用本地解析与 OCR |
| 本地目录监听 | 非核心 | 非核心 | 非核心 | GoodBuddy 差异化能力 |
| 本地可编辑图谱 | 非核心 | 非核心 | 部分版本支持 GraphRAG | GoodBuddy 差异化能力 |
本期不复制竞品的云端工作流平台,而是将其成熟 RAG 交互映射为桌面、本地、 受控的数据链路。
参考公开文档:
- Dify Knowledge: https://docs.dify.ai/en/use-dify/knowledge/readme
- Dify 检索测试: https://docs.dify.ai/en/use-dify/knowledge/test-retrieval
- Dify 分块设置: https://docs.dify.ai/en/use-dify/knowledge/create-knowledge/chunking-and-cleaning
- FastGPT 知识库搜索方案和参数: https://doc.fastgpt.io/docs/introduction/guide/knowledge_base/dataset_engine
- RAGFlow Dataset 配置: https://ragflow.io/docs/configure_knowledge_base
- RAGFlow 检索测试: https://ragflow.io/docs/run_retrieval_test
6. 信息架构
知识工作区继续使用主从布局和现有四个页签:
知识库
├─ 文档与来源
│ ├─ 来源管理
│ ├─ 检索测试入口
│ ├─ 文档状态
│ └─ 分块查看与维护
├─ 知识图谱
├─ 任务中心
└─ 设置
├─ 检索设置
├─ 分块设置
└─ 图谱设置
“检索测试”是当前知识库的高频诊断操作,通过知识库标题区次操作打开独立 工作台,不新增第五个一级页签。
对话输入区的知识范围弹层包含:
- 已启用知识库多选。
- 检索方式:模型按需检索、每次先检索。
- 当前范围为空、索引降级或向量未配置时的短说明。
7. 第一阶段:可用、可见、可诊断
7.1 检索方式
新增请求级 knowledgeRetrievalMode:
| 值 | 用户文案 | 行为 |
|---|---|---|
auto |
模型按需检索 | 保留当前 knowledge_search 工具,由模型决定是否调用 |
always |
每次先检索 | Main 在启动 Runtime 前使用原始用户问题检索一次,再把有界证据作为不可信上下文提供给 Runtime |
规则:
- 没有启用知识库时不显示为“已检索”。
always预检索后仍保留knowledge_search,模型可以改写查询再次检索。- 预检索零结果不阻止回答,但必须显示“已检索,未找到相关内容”。
- 预检索失败不得自动扩大范围或访问未选知识库。
- 图片生成能力不执行知识预检索。
- Ask 和 Execute 使用相同的只读检索范围。
7.2 中文全文检索
在现有 unicode61 FTS 之外增加本地 CJK n-gram 检索文本:
- 连续汉字生成二元词组,保留必要的单字符短查询回退。
- 拉丁字母和数字使用 NFKC、大小写归一化和现有 FTS。
- 多个查询词使用召回优先的 OR 候选,再通过覆盖率和短语命中重排。
- 不把整句中文问题转换成“所有汉字必须同时出现”的条件。
- 索引更新、分块编辑、停用和删除必须同步更新 CJK 索引。
- 数据库迁移必须为已有分块有界回填,不要求用户重新导入。
7.3 检索设置
每个知识库保存以下设置:
| 字段 | 范围 | 默认值 |
|---|---|---|
topK |
1 至 20 | 6 |
minimumVectorSimilarity |
0 至 1 | 0(不过滤低相似度结果) |
ftsWeight |
0 至 2 | 1 |
vectorWeight |
0 至 2 | 1 |
graphWeight |
0 至 2 | 0.8 |
candidateMultiplier |
2 至 10 | 4 |
contextMaxCharacters |
2,000 至 48,000 | 16,000 |
adjacentChunkCount |
0 至 2 | 0 |
localRerankEnabled |
布尔值 | false |
至少一个召回通道权重大于 0。图谱未启用时,图谱权重只读显示为不可用。 向量模型未启用或索引不兼容时,向量权重保留但当前请求降级。
7.4 检索测试工作台
用户输入最多 4,000 字符的问题,工作台显示:
- 当前知识库和生效设置。
- 总耗时、各通道耗时和候选数。
- 请求通道、实际使用通道和降级原因。
- 最终结果序号、文档、定位、片段和最终相关度。
- FTS、CJK、向量、图谱的独立排名与向量相似度。
- 本地重排前后排名。
- 相邻分块或父块合并后的实际上下文。
- “查看分块”“打开来源”操作。
检索测试不创建聊天消息、不写入会话历史、不调用 LLM,也不改变知识库内容。
7.5 可扩展向量搜索
移除“超过 5,000 个候选则返回空结果”的逻辑:
- 按稳定游标分页读取同一知识库、Provider、Model 和维度的向量。
- 每批计算余弦相似度。
- 内存中只保留候选上限所需的最佳结果。
- 支持取消和应用关闭。
- 维度、校验和或索引状态不匹配的向量不参与结果。
- 诊断返回扫描数量和向量耗时。
- Provider Fingerprint 不匹配时标记索引不兼容,不回退到同名旧模型向量。
线性扫描是本期跨平台保底实现。后续接入稳定向量扩展时不得改变上层契约。
7.6 状态与降级
文档状态拆分为:
| 状态 | 含义 |
|---|---|
| 解析 | 等待、运行、完成、失败 |
| 全文索引 | 等待、完成、失败 |
| 向量索引 | 未启用、等待、运行、完成、失败、不兼容 |
| 图谱 | 未启用、按需、等待、运行、完成、失败 |
知识库汇总不得仅以“文档 metadata 不是 failed”计算完成。UI 至少显示:
- 可用于全文检索的文档数。
- 已完成向量化的文档数。
- 失败文档数。
- 当前向量模型与索引是否兼容。
降级事件包括:
- 未配置向量模型。
- 查询向量生成失败。
- 当前模型没有匹配索引。
- 部分文档向量失败。
- 图谱关闭或没有证据。
- 结果被相关度或上下文预算过滤。
7.7 引用查看
每条引用增加稳定 chunkId、最终相关度和检索通道。用户展开引用后可以:
- 查看命中分块。
- 查看相邻分块或父块形成的完整上下文。
- 查看知识库、文档、来源和定位。
- 对本地文件调用 Main 校验后的
shell.openPath。 - 对 HTTP(S) 来源调用 Main 校验后的外部打开。
Renderer 不能提交任意路径或 URL。Main 必须根据 libraryId、documentId 和
chunkId 重新读取已保存来源并验证归属。
界面把该列表描述为“本次检索证据”或“已查阅来源”,不把仅被召回的片段 自动宣称为回答中某个句子的精确出处。后续只有经过稳定 Citation ID 校验的 句级标注才能使用更强的“该句引用”语义。
8. 第二阶段:可调、可优化、可维护
8.1 分块模式
每个知识库选择一种模式:
| 模式 | 行为 | 适用内容 |
|---|---|---|
| 固定分块 | 按目标长度、重叠和自然边界切分 | 普通文本、日志、代码 |
| 结构分块 | 优先保持解析 section、Markdown 标题和段落结构 | 手册、制度、长文档 |
| 父子分块 | 小块用于召回,大块用于模型上下文 | 长篇说明、合同、研究资料 |
设置:
| 字段 | 范围 | 默认值 |
|---|---|---|
mode |
fixed / structure / parent-child |
structure |
targetCharacters |
400 至 8,000 | 1,600 |
overlapCharacters |
0 至目标长度的 40% | 160 |
parentCharacters |
1,600 至 16,000 | 4,800 |
childCharacters |
300 至 4,000 | 900 |
父子分块要求:
- 父块只作为上下文,不进入 FTS、CJK 或向量候选。
- 子块用于召回,并保存父块关联。
- 引用默认突出子块,同时允许查看父块全文。
- 父块和子块总输出仍受上下文预算限制。
8.2 本地与学习型重排
第二阶段提供不调用外部模型的可选本地重排。评分特征包括:
- 原始 RRF 排名。
- 中文和拉丁词覆盖率。
- 完整短语命中。
- 文档标题、分块标题和路径命中。
- 向量相似度。
- 同文档重复结果惩罚。
重排结果必须:
- 归一化为 0 至 1 的
relevance。 - 对相同输入和索引保持确定性。
- 保留重排前排名和各特征得分用于诊断。
- 在关闭时完全保留原有 RRF 排序。
学习型模式使用 Main 进程中的 Cohere/Jina 兼容客户端,凭据只进入加密设置和 Main 进程。请求限制为 100 个候选、每个候选 8,000 字符,并具有 15 秒默认 超时、取消传播和有界响应。失败时可回退本地重排或 RRF,并只返回脱敏诊断。
8.3 相邻分块合并与上下文预算
- 对最终候选按文档和 ordinal 合并相邻分块。
- 不把同一分块重复放入上下文。
- 保留每个命中分块的引用定位。
- 按相关度从高到低消耗
contextMaxCharacters。 - 单个超长父块按安全边界截断并标记
truncated。 - 不允许低排名结果挤掉已经选中的高排名证据。
8.4 分块管理
文档行提供“查看分块”,打开分块管理对话框:
- 显示 ordinal、角色、标题、定位、字符数、启用状态和内容预览。
- 支持分页和文档内搜索。
- 支持编辑内容。
- 支持启用或停用。
- 支持删除,并说明来源同步可能重新创建分块。
- 编辑后更新 FTS 和 CJK 索引,并使旧向量失效。
- 已配置向量模型时,编辑操作完成后为该文档重建向量。
- 删除最后一个可检索分块时,文档显示“无可检索内容”,不能显示完全就绪。
高影响删除使用具体确认文案。普通启停使用共享 Switch,并声明
role="switch"。
8.5 单文档与全库重建
- 单文档重建重新读取来源、解析、分块、全文索引、向量和图谱。
- 全库重建按来源顺序执行,并显示文档级进度。
- 修改分块模式或关键参数后,知识库显示“设置已更新,等待重建”。
- 重建采用文档级原子替换,失败时保留上一版可用分块和向量。
- 用户可以取消全库重建;已经成功替换的文档保持可用。
- 文件不存在、网页失败或 OCR 不可用时保留可重试错误。
- 单来源允许的 2,000 个文件必须全部参与增量同步、删除检测和校验和跳过, 不受普通页面 500 项列表上限影响。
9. 数据模型与兼容性
9.1 KnowledgeBase
知识库增加版本化设置:
type KnowledgeRetrievalSettings = {
version: 1
topK: number
minimumVectorSimilarity: number
ftsWeight: number
vectorWeight: number
graphWeight: number
candidateMultiplier: number
contextMaxCharacters: number
adjacentChunkCount: number
localRerankEnabled: boolean
}
type KnowledgeChunkingSettings = {
version: 1
mode: 'fixed' | 'structure' | 'parent-child'
targetCharacters: number
overlapCharacters: number
parentCharacters: number
childCharacters: number
}
SQLite 使用 JSON 列保存设置,读写均经过共享 Zod Schema。迁移后的旧知识库使用 与当前行为接近的兼容默认值,不自动重建已有分块。
9.2 Chunk
分块增加以下语义:
type KnowledgeChunkRole = 'standalone' | 'parent' | 'child'
type KnowledgeChunkState = {
enabled: boolean
role: KnowledgeChunkRole
parentChunkId?: string
manuallyEdited: boolean
updatedAt?: string
}
实现可以使用显式列或受校验 metadata,但查询必须为旧数据提供默认值:
- 缺少
enabled时视为true。 - 缺少
role时视为standalone。 - 父块不参与召回索引。
9.3 检索响应
type KnowledgeRetrievalResponse = {
query: string
durationMs: number
settings: KnowledgeRetrievalSettings
diagnostics: {
requestedChannels: KnowledgeRetrievalChannel[]
usedChannels: KnowledgeRetrievalChannel[]
degradedChannels: Array<{
channel: KnowledgeRetrievalChannel
reason: string
}>
candidateCounts: Partial<Record<KnowledgeRetrievalChannel, number>>
}
results: KnowledgeRetrievalResult[]
context: {
characterCount: number
truncated: boolean
groups: KnowledgeContextGroup[]
}
}
错误、诊断和引用不得包含 API Key、Authorization Header、完整私人文档或未经 限制的 Provider 响应。
10. IPC 与安全边界
新增或扩展的 IPC:
knowledge:retrieveknowledge:settings:updateknowledge:document:rebuildknowledge:library:rebuildknowledge:chunks:listknowledge:chunk:updateknowledge:chunk:deleteknowledge:reference:contextknowledge:reference:open
要求:
- 所有输入由共享 Zod Schema 校验。
- 所有处理器校验可信 Renderer sender。
- ID 必须重新检查知识库、来源、文档和分块归属。
- 列表使用有界分页,单次最多返回 200 个分块。
- 内容编辑限制单块最大字符数。
- 外部打开只接受数据库已保存的本地普通文件或 HTTP(S) URL。
- 不向 Preload 暴露原始数据库、Electron
shell或文件系统 API。 - 更新与重建遵守取消、超时、应用关闭和有界错误规则。
11. 交互与无障碍
- 复用
PageTabs、SegmentedControl、共享 Switch 和应用通知。 - 检索方式是互斥选项,使用
SegmentedControl或语义化单选组。 - 分块启停是持久二元状态,使用
role="switch"。 - 检索结果列表使用可访问名称,得分不得只用颜色表达。
- 检索工作台打开后焦点进入问题输入框,关闭后返回触发按钮。
- 分块编辑和删除对话框遵守焦点陷阱、Escape 和焦点恢复。
- 异步成功使用应用通知;字段错误、检索进度和可就地恢复错误保留在工作台。
- 窄窗口下检索结果改为单列,配置摘要保持可读,不隐藏降级状态。
12. 失败与恢复
| 场景 | 行为 |
|---|---|
| 向量查询失败 | 继续全文和图谱检索,显示降级原因 |
| 部分文档无向量 | 使用可用文档,显示完成数和失败数 |
| CJK 索引迁移失败 | 回滚迁移,不损坏旧 FTS |
| 重排失败 | 回退 RRF 排序并显示诊断 |
| 分块编辑后向量失败 | 保留编辑和全文索引,标记向量失败 |
| 单文档重建失败 | 保留上一版可用索引 |
| 同名模型端点变化 | 旧 Fingerprint 索引标记不兼容,等待重建 |
| 新向量重建失败 | 保留上一版就绪向量继续服务,单独记录失败尝试 |
| 原文件已移动 | 显示来源不可用,提供重试或移除 |
| 引用对象已删除 | 显示引用已失效,不打开任意替代路径 |
| 上下文超预算 | 按排名截断并明确标记 |
| 请求取消或应用关闭 | 停止新批次,释放句柄,不留下半替换索引 |
13. 埋点与评测
GoodBuddy 不上传私人检索查询或文档内容。本地诊断至少记录有界统计:
- 检索模式。
- 启用知识库数量。
- 各通道候选数和耗时。
- 是否发生降级。
- 最终结果数和上下文字符数。
- 重建文档数、成功数、失败数和取消状态。
手动验收使用仓库内不含私人内容的固定样例集,覆盖:
- 中文自然语言改写和同义词。
- 中英文混合产品名。
- 精确编号、路径和代码标识。
- 多文档冲突信息。
- 无答案问题。
- 10,000 个以上分块。
- 向量服务断开和模型维度变化。
14. 实施顺序
14.1 第一阶段
- 共享设置、请求和检索响应契约。
- SQLite 迁移和 CJK 索引。
- 可扩展向量扫描、检索诊断和状态模型。
- 检索设置与工作台。
- 对话“每次先检索”。
- 引用上下文和打开来源。
- 第一阶段单元、IPC 和 Renderer 测试。
14.2 第二阶段
- 结构分块和父子分块。
- 本地重排与相关度。
- 相邻块合并和上下文预算。
- 分块预览、编辑、启停和删除。
- 单文档与全库重建。
- 第二阶段回归、性能和生产构建验证。
15. 验收标准
15.1 第一阶段
- 用户可在对话中选择“模型按需检索”或“每次先检索”。
- “每次先检索”在 Runtime 启动前产生检索诊断和引用,即使模型未调用工具。
- 未配置向量模型时,中文改写问题仍能通过 CJK 索引召回相关分块。
- 向量查询失败时回答可继续,界面明确显示已降级。
- 10,000 个分块的向量测试能够返回正确 Top K,不出现固定上限空结果。
- 同名模型切换端点后,不会读取 Fingerprint 不匹配的旧向量。
- 重建失败时,上一版已就绪向量仍能继续召回。
- 包含 2,000 个文件的目录同步能够处理第 501 至 2,000 个文档的修改与删除。
- 检索测试展示通道、候选数、排名、相关度、上下文和降级原因。
- 引用可以查看完整上下文并打开 Main 校验后的来源。
- 查询长度在共享契约、IPC、MCP 和数据库层保持一致。
15.2 第二阶段
- 用户可选择固定、结构或父子分块并显式重建。
- 父块不参与召回,子块命中后可提供父块上下文。
- 本地重排可以开启或关闭,并显示重排前后排名。
- 上下文严格遵守字符预算,重复和相邻片段按规则合并。
- 用户可预览、编辑、启停和删除分块。
- 分块修改后 FTS、CJK 和向量状态保持一致。
- 单文档重建失败不会破坏上一版可用索引。
- 所有新增操作可用键盘完成,并在浅色、深色和窄窗口下可用。
15.3 工程验证
所有源代码变更完成后必须通过:
npm test
npm run typecheck
npm run lint
npm run build
外部或付费模型调用不属于自动验证,只有获得明确授权后才运行。