Files
goodbuddy/docs/features/knowledge-base/external-knowledge-technical-design.md

24 KiB
Raw Permalink Blame History

外部知识库技术设计

文档信息

项目 内容
状态 三家生产 IPC 真实生成、Dify 完整 App 与当前 UI 自动回归已通过;远程待验收
版本 0.2
日期 2026-09-13
产品需求 外部知识库接入 PRD
行为规则 外部知识库逻辑设计
界面 外部知识库 UI 设计
验证状态 实施进度

1. 实现边界

外部知识库复用 GoodBuddy 知识库 ID、聊天范围和检索入口。网络、凭据、响应读取和 标准化在 Main 完成,Renderer 通过明确的 Preload 方法调用 IPC。远端只执行目录、详情和 检索请求,不创建、修改、上传或删除远端内容,不调用外部 App、Chat、Workflow 或 Agent。

本地 file | directory | url 仍表示需要导入和索引的资料来源。外部绑定独立存储, 不加入该枚举,也不生成本地文档、分块、向量或图谱。实际用于回答的片段随会话引用保存。

2. 当前模块

文件 职责
src/main/knowledge/external/external-knowledge-client.ts 一个 ExternalKnowledgeClient 内按 Provider 分支处理固定端点、地址规范化、分页、HTTP 错误、超时、取消、响应上限和结果映射;同文件定义 ExternalKnowledgeError
src/main/knowledge/external/external-knowledge-service.ts 实例保存与测试、凭据加解密、目录与详情、检索测试、创建或更新绑定、请求生命周期与配置变化检查
src/main/knowledge/external/external-knowledge-store.ts 在已有 SQLite 连接上读写实例 JSON 和绑定 JSON
src/main/knowledge/knowledge-database.ts v12 迁移、知识库与绑定事务、唯一约束及删除外键
src/main/knowledge/knowledge-service.ts 本地与外部检索分派、混合检索和总字符预算;阻止外部绑定进入本地维护操作
src/shared/external-knowledge-contracts.ts Provider 配置、实例与绑定输入、目录、测试结果和外部引用定位契约
src/shared/knowledge-contracts.ts、contracts.ts、assistant-contracts.ts 现有知识结果、列表和会话引用增加外部字段
src/shared/knowledge-reference.ts 预检索与 MCP 共用的引用转换和去重键
src/main/ipc.ts、src/main/knowledge/knowledge-ipc.ts、src/preload/index.ts、src/shared/ipc-channels.ts 可信发送方检查、Zod 输入解析、类型化跨进程调用
src/renderer/src/ExternalKnowledge.tsx 实例 Modal、绑定表单、Provider 配置、外部详情与检索结果
KnowledgeWorkspace.tsx、App.tsx、KnowledgeCitationDialog.tsx 统一列表、聊天选择、刷新及历史引用显示

Renderer 的 KnowledgeWorkspace.tsx 保留库选择、页签路由、检索与分块管理状态及回调。 原有子组件位于 src/renderer/src/knowledge-workspace/:

文件 职责
DocumentsView.tsx 来源导入、文档列表与解析结果预览
KnowledgeSettingsView.tsx 索引、分块及图谱配置,向量索引状态轮询
KnowledgeTasksView.tsx 任务筛选、层级展示、取消和重试
GraphView.tsx、GraphEditors.tsx 图谱浏览、实体与关系编辑
KnowledgeGraphChartLoader.tsx 图谱画布延迟加载及失败重试
LibraryDialogs.tsx、RemoveSourceDialog.tsx、GraphDestructiveDialog.tsx 建库表单、库编辑与删除、来源移除及图谱破坏性操作确认
types.ts 工作区 Props 与组件间使用的类型
formatting.ts、helpers.ts 多个组件共用的本地化格式化、标签、别名解析、进度和错误处理

子组件直接导入所需模块,不反向依赖工作区入口。单个视图专用的格式化与标签留在该视图, 检索结果转换留在入口。模块拆分保留组件 Props、挂载位置、key 与副作用顺序;文档与来源 合并后的列表结构及操作归属见 UI 设计。

ExternalKnowledge.tsx 的字段改用全局 .field,修正 body Portal 脱离知识库祖先后 丢失 scoped 控件样式的问题。CSS 提供 560px 紧凑弹窗和粘附底部操作区;实例选择复用 既有 picker 样式并实现菜单键盘行为。没有新增 Main 模块,具体控件规则由 UI 设计维护。

当前没有独立 Provider 文件、Adapter Registry、Normalizer、版本化能力清单或配置迁移框架。 本文件按现有三个 Main 模块描述实现,不把旧草案中的拆分方案列为待建基础设施。

3. 共享契约

3.1 实例与绑定

实例输入包含 id?、name、provider、baseUrl、enabled 和 credential: keep | replace | clear。实例摘要返回启停、凭据状态、probeStatus、绑定数、 最近测试时间和错误码,不返回凭据 envelope。当前没有 detectedVersion、adapterVersion 或 transportSecurity 字段。

绑定保存 knowledgeBaseId、instanceId、provider、remoteKnowledgeBaseId、 remoteName、commonConfig、providerConfig 和 lastVerifiedAt。GoodBuddy 名称和描述 保留在知识库记录。创建和更新输入都要求用户填写 testQuery;服务保存前重新执行检索, 因此先点击“测试检索”再保存会产生两次检索请求。成功零命中允许保存。

3.2 配置范围

配置 当前共享契约
通用 resultLimit 1 至 20,默认 6;requestTimeoutMs 1,000 至 60,000,默认 15,000;maxSnippetCharacters 100 至 8,000,默认 4,000
Dify 默认分支仅包含 provider 和 useDatasetDefaults: true;覆盖分支使用严格的 retrievalModel 对象,包含检索方式、Top K、阈值开关和重排开关,按启用条件校验阈值、重排模型或权重
FastGPT searchMode、tokenLimit(1 至 30,000)、similarity、usingRerank;不发送查询优化参数
RAGFlow similarityThreshold、vectorSimilarityWeight、knnTopK(1 至 2,048)、useKg、includeKnowledgeCompilation;当前无 Rerank、Metadata 编辑字段

Provider 必须与实例一致。Dify 加权配置还在客户端检查权重和为 1。共享查询输入上限为 4,000 字符,Dify 客户端另按 Unicode 码点拒绝超过 250 字符的查询,不静默压缩。

3.3 结果与引用

现有 KnowledgeRetrievalResult 的本地 documentId/sourceId/chunkId 改为可选,增加 external?: ExternalKnowledgeLocator,没有另建一套 UnifiedKnowledgeRetrievalResult。 外部定位包含 Provider、实例 ID、远端库 ID,以及实际返回的文档 ID、片段 ID、位置和评分。 FastGPT 数组评分存为 providerScores: {type, value, index?}[],保留顺序与类型;数值评分 存为 providerScore,不选取数组首项冒充总分。

外部结果沿用旧结果容器的空 channels 和 scores.fusedScore = 0。该零值是结构占位, 不表示质量;toKnowledgeReference 不把它写成引用总分。外部结果没有统一 relevance, 不同 Provider 的原始分数不参与跨库混排。引用去重键包含外部定位和片段,不伪造本地 ID。

4. SQLite 与凭据

数据库 v12 增加两张表:

CREATE TABLE external_knowledge_instances (
  id TEXT PRIMARY KEY,
  value_json TEXT NOT NULL
);
CREATE TABLE external_knowledge_bindings (
  knowledge_base_id TEXT PRIMARY KEY REFERENCES knowledge_bases(id) ON DELETE CASCADE,
  instance_id TEXT NOT NULL REFERENCES external_knowledge_instances(id) ON DELETE RESTRICT,
  remote_id TEXT NOT NULL,
  value_json TEXT NOT NULL,
  UNIQUE(instance_id, remote_id)
);

没有向 knowledge_bases 增加 kind 列;IPC 快照根据是否存在绑定投影 KnowledgeLibrary.kind 和 external。旧本地数据保留,知识库与绑定在同一事务中保存。 外部库沿用本地表所需的默认字段,但这些字段不进入 Provider 请求。

Main 启动把已有 secureCipher 注入 KnowledgeService。服务复用 SettingsCredentialCipher 和现有加密函数,把 envelope 放入实例 JSON;replace 要求 安全存储可用,keep 保留已有 envelope,clear 移除凭据。列表会尝试解密以区分 configured/missing/unavailable,请求前再次解密,没有独立明文凭据缓存。

5. IPC 与生命周期

Preload 暴露实例 List/Save/Test/SetEnabled/Delete、目录 List/Get、绑定 Create/Update 和 externalRetrievalTest。通道使用 knowledge:external-* 前缀, 准确名称以 ipc-channels.ts 为准。移除绑定复用 deleteLibrary,没有独立 remove 通道。

Handler 调用 assertTrustedSender 并解析共享输入;检索按实例 ID 在 Main 读取地址与凭据。 实例先保存为 untested,列表上的连接测试调用目录请求并更新摘要,不测试未保存草稿。 绑定检索测试是单独操作,不会改变实例探测状态。探测状态取值为 untested、 catalog-ready、list-restricted、auth-failed、unreachable、failed。

服务按实例记录 AbortController。保存实例、停用、删除以及服务关闭会取消相应请求; 调用方取消信号与总超时合并。取消即是配置变更的唯一处理方式:被取消的连接测试直接抛出 EXTERNAL_KB_CANCELLED,不写回探测结果,因此不需要额外的前后配置快照比较。绑定保存在 验证请求前后各检查一次同一远端目标是否已被占用,最终以 UNIQUE(instance_id, remote_id) 索引为准。共享 IPC 没有 requestId 或取消方法;Renderer 用局部 generation/active 标记 丢弃旧响应,这与中止 Main 请求是两种行为。

删除仍被引用的实例返回 EXTERNAL_KB_INSTANCE_IN_USE。UI 确认后逐个调用本地 deleteLibrary,再删除实例;这是顺序操作,发生中途失败会刷新已移除的绑定,不是跨调用事务。

6. HTTP 边界

客户端只接受 HTTP(S),拒绝地址内的用户名、密码、查询与片段;按 Provider 规范化 API 路径,仅拼接内置端点。请求使用 Bearer 认证,redirect: 'error',无自动重试或公共云回退。 默认超时 15 秒,单个响应默认上限 5,000,000 字节;响应头和流式读取均检查上限,取消 期间终止读取。解析后只投影需要的有界字段,最多返回 20 条、每片段 8,000 字符。

当前使用 fetcher ?? fetch,没有单独连接全局内网兼容模式、证书例外或按地址保存传输 确认。2026-09-13 当前生产服务在真实 Electron 中对两个 Dify 和 FastGPT HTTPS 配置完成 严格证书校验,RAGFlow 使用 HTTP。此证据覆盖这些配置的请求,不表示旧 PRD 的地址确认 或证书例外流程已实现;相关产品差异仍由功能 owner 核对。

7. 检索编排

KnowledgeService.retrieve 按知识库 ID 单行查询本地绑定,存在绑定时调用 ExternalKnowledgeService.retrieve,否则执行现有本地路径。外部结果附加 diagnostics.external,不运行本地向量、全文或图谱检索。

ExternalKnowledgeService 区分两个入口:retrieve 是生产检索路径,只发出一次检索请求; testRetrieval 仅用于保存绑定前的验证,额外在同一超时预算内确认 RAGFlow 图谱与知识编排 能力可用,能力缺失时返回 EXTERNAL_KB_CAPABILITY_UNAVAILABLE。生产检索不执行该能力预检, 因此正常对话不会多一次 detail 请求,也不会出现仅属于保存校验的错误码。

retrieveMany 对传入知识库 ID 去重并读取知识库,使用 Promise.all 逐库检索;只有选中 需要向量的本地库时才准备本地查询 Embedding。当前没有额外的 Provider 并发队列。单库 失败写入 response.diagnostics.failure 并保留空结果,成功零命中没有 failure;调用方 取消仍向上传播。当前返回 {knowledgeBaseId, response}[],没有独立的 Outcome 状态联合。

服务检索与绑定验证共用同一裁剪逻辑:先按绑定的结果数与单片段限制裁剪,并限制该次片段 总量为 48,000 字符。 retrieveMany 展平各库结果并按库内 rank 稳定排序,再轮转分配 48,000 字符的片段预算: 先处理各库第 1 条,再处理各库第 2 条,同 rank 保持输入库顺序。不会先耗完整个库再轮到 下一个库;仍没有固定的每库最低字符配额。关联的本地上下文 groups 随相应结果使用另一份 48,000 字符预算。二次裁剪会标记 context.truncated,但第一次标准化裁剪不提供完整的 远端原始条数与截断诊断。

聊天预检索、检索测试相关 IPC 和 KnowledgeMcpGateway 已使用统一检索入口。引用转换 复用 knowledge-reference.ts,聊天与 MCP 仍按现有授权范围和各自输出预算处理结果。 MCP 在总预算裁剪后按库内 rank 交错选择结果,再应用工具条数和字节上限;有失败且 没有任何片段时返回工具错误。预检索只在全部库失败时报告整体失败,成功零命中伴随其他 库失败时保留降级诊断。这两种上层呈现并不完全相同。

8. 引用显示与历史数据

会话契约和数据库保存外部定位及有界片段。外部引用弹窗读取保存的片段,显示 Provider、 实例 ID、远端库和实际返回的定位及评分;不请求本地 referenceContext,不显示本地 openSource 操作。当前没有远端来源打开或引用刷新请求,sourceUrl 是可选契约字段, 三家现有映射没有填充它。

App 的 source-references 事件使用共享 knowledgeReferenceKey 去重传入引用,再合并 尚未被替代的旧引用,最多保留 20 条。键包含 Provider、实例、远端库、远端文档和片段 ID、 定位与片段正文;外部结果缺少本地 ID 时也不会仅因本地 ID 相同而被合并。引用弹窗按 context.locator ?? external.location ?? locator 选择一个定位,只显示一次。

移除绑定后历史片段仍可阅读;当前弹窗不查询实例状态,也不生成“连接已移除”标记。 实例名称没有作为独立字段存入外部 locator,不能承诺删除实例后仍显示其名称。

2026-09-13 Dify 桌面报告已验证实时搜索引用通过生产 conversations.saveLocal 保存、 重载并打开弹窗。会话由验证程序标记为测试记录,没有模型生成答案;最终引用去重与定位 修正后的回归状态单独记录在实施进度。

后续 kb-full-app-live 则通过完整 App 输入区真实发送并保存模型答案,再点击引用。 引用片段、远端文档和片段 ID 与真实检索记录一致,无伪造会话或事件;此证据覆盖 Dify 单库短答案。三家生产 IPC 生成检查还验证了 task_events 中的文本、引用和选中库范围。

9. Provider 映射

本节区分当前客户端行为与历史接口探测。历史响应和官方文档只说明当时观察到的接口, 不证明当前桌面已完成验收。

9.0 真实实例验证基线

历史日期 证据与限制
2026-09-09 基线报告:累计 107 次只读请求,最终归档脚本 19 次;当时 HTTPS 放宽证书校验
2026-09-10 基础报告与扩展报告:共 33 次只读请求,其中检索 17 次;HTTPS 显式开启证书校验,RAGFlow 为 HTTP

历史结果包含 Dify 0.15.8 详情 405、检索零命中,Dify 1.17.0 详情成功及 3 条非空结果; FastGPT 三种模式和重排非空;RAGFlow 图谱开关产生不同结果,但 Knowledge Compilation 没有配置证据。FastGPT/RAGFlow 产品版本、Dify 0.15.8 非空结构及真实错误矩阵仍未确认。 逐项数量、脚本命令和当时测试结果由实施进度维护。

2026-09-13 新增当前产品证据:生产验证摘要 记录真实 Electron safeStorage 持久化重开、生产服务与本机 HTTP MCP,以及 Dify 桌面。 服务结果与 MCP 引用数分别为 Dify 配置 1 的 0/0、Dify 配置 2 的 2/2、FastGPT 的 2/2、 RAGFlow 的 1/1。报告配置编号不是版本识别,历史版本差异不据此重新认证。

9.1 Dify

固定使用 GET /v1/datasets、GET /v1/datasets/{id} 和 POST /v1/datasets/{id}/retrieve。默认省略整个 retrieval_model,远端优先使用知识库 当时配置,未保存时由远端选默认;覆盖时发送完整的本地 retrievalModel,不逐字段合并。 结果从 records[].segment、文档和分数取值。

客户端把详情 405 分类为不兼容,绑定表单允许详情失败后继续检索验证,不实现版本探测或 自动遍历目录的详情回退。Scoped Key 可尝试手工 ID,但当前产品的真实受限密钥验收仍待完成。 Metadata、多模态、摘要索引、子块、附件和 Pipeline 没有进入当前简化目录或结果映射。

官方来源:Knowledge API、 列表、 详情、 检索、 缺省配置实现。

9.2 FastGPT

固定使用 POST /api/core/dataset/list、GET /api/core/dataset/detail?id=... 和 POST /api/core/dataset/searchTest。列表 data[] 保留 Dataset 与文件夹,按 parentId 读取层级,在当前父级响应内搜索和分页;文件夹只用于浏览。

检索接受业务码 200 的 data.list[] 与旧手工文档的 data[],不递归猜测嵌套结构。 q 必需,a 仅在存在时追加;评分数组保留类型、值和可选 index,数值评分独立处理。 当前客户端已通过 FastGPT 真实服务与本机 HTTP MCP,均返回 2 条。报告确认结果契约与 引用映射有效,没有逐项归档评分包装,故旧包装和全部评分变体仍以定向测试证据为限。

请求映射 datasetId/text/limit/similarity/searchMode/usingReRank,不发送查询扩展。 Embedding、Rerank 和远端 API 审计仍可能产生用量;不调用 FastGPT 生成最终回答。

官方来源:API 说明、 旧手工接口页、 生成 Schema、 检索处理器、 结果 Schema。 4.15.0 起应核对部署实例生成文档;历史检查只取得云端 /apidoc/ 应用壳。

9.3 RAGFlow

目录和按 ID 详情均使用 GET /api/v1/datasets,检索使用 POST /api/v1/retrieval。 请求发送 question、单个 dataset_ids、page: 1、page_size: 20、阈值、向量权重、 knn_top_k 及显式的图谱、知识编译布尔值。HTTP 成功且业务码为 0 才读取 data.chunks。 文档名映射 document_keyword,位置保存返回的 positions,分数取原始 similarity。

graphEnabled 根据有效非零 graphrag_task_finish_at 判断;仅配置 parser_config.graphrag.use_graphrag 不算完成。知识编译依据非空 compilation_template_group_id。启用任一能力时,服务在检索前重新读取详情确认。 当前不保存完整能力快照,不区分图谱未配置与构建中,也不检测 Provider 版本。

官方来源:HTTP API、 当前文档原文、 v0.24.0 文档。 旧文档只声明 top_k,当前客户端发送 knn_top_k;请求成功不证明候选数参数生效, 也不是旧部署版本兼容承诺。

10. 验证与验收

三家 Provider 已通过生产 KnowledgeService 与本机 HTTP MCP,真实 Electron safeStorage 加解密和持久化重开成功。Dify 桌面已完成绑定、参数更新、重启、三档窗口宽度 及历史引用检查。验证方法、原始报告边界和调用计数见实施进度及 脱敏摘要。本轮文档只读取报告,没有 另发 Provider 或模型请求,也未改动历史探测 JSON。

当前 Portal 与控件修改后的聚焦测试、全量回归、类型检查、lint、工作区及暂存区 diff 检查均已通过。精确计数、运行命令、耗时、执行期间文件稳定性和原失败修正记录统一见 实施进度。

后续真实生成使用已保存的默认模型,凭据在 Main 通过 safeStorage 解密。三家 Provider 的 always 及 Dify auto 已产生有依据和引用的短答案;Dify auto 经进程内 ModelToolProvider → KnowledgeMcpGateway 执行,不能当成本机 HTTP MCP 或远程 ACP 验收。FastGPT/RAGFlow 首轮因直接比较序列化字符串产生验证断言失败,解码请求和嵌入 证据 JSON 后重跑通过;报告保留初始结果。完整 App 输入区的发送、真实答案及引用点击 仅覆盖 Dify。Dify/FastGPT 使用严格 HTTPS,RAGFlow 和模型使用已配置的 HTTP。

请求精确增量与视觉 fixture 边界见后续验证摘要 及实施进度。不公开模型端点、私有配置 标识、凭据和答案正文。这些短答案检查没有评估广泛问答质量。

FastGPT/RAGFlow 完整桌面专属操作、复杂或混合问答、删除和真实错误矩阵仍需补验。 Linux Host 的两条网络路径可达,但验证程序无法解密已有 SSH 凭据,在认证前返回 E_SAFE_STORAGE_DECRYPT_FAILED,真实远程 forced-preflight 未执行。本机 HTTP MCP 成功不覆盖该远程路径。剩余 Provider 版本和高级参数效果也不能由基础检索成功推断。

原设计的分项探测 UI、最低版本矩阵、详细诊断、地址确认和取消交互未全部落地。它们是 待核对的产品差异,不能以补建 Adapter Registry 或版本框架代替验收决策。

scripts/external-knowledge-probe.mjs 仍可只读探测列表、详情、检索;--extended 增加 Dify 配置覆盖、FastGPT 重排和 RAGFlow 两种开关。报告区分成功零命中与失败,不输出 凭据或业务正文。脚本不打包到桌面或 Agent,脚本执行本身不覆盖 UI、IPC、存储和引用链路。

11. 需求追踪

需求 当前实现落点 待验收重点
EK-FR-01 至 EK-FR-05 实例 Service/Store、目录 Client、IPC 与管理表单 服务已验证;补充 FastGPT/RAGFlow 桌面和受限目录手工 ID
EK-FR-06 至 EK-FR-10 共享 Provider 配置、绑定事务、统一列表与两栏表单 Dify 参数与布局已验证;其他 Provider 专属参数及未提供字段待核对
EK-FR-11 至 EK-FR-14 retrieveMany、预检索、MCP、共享引用与 failure 字段 三家 IPC 短答案与 Dify 完整输入区引用已验证;混合失败、广泛问答与远程待补
EK-FR-15 唯一约束、外键保护、顺序本地删除 中途失败刷新、远端内容不变
EK-FR-16 Main 固定端点、有界请求与数据说明 当前桌面传输行为与 PRD 差异