# 外部知识库逻辑设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 状态 | 当前实现规则;三家短答案、Dify 完整 App 与当前 UI 自动回归已通过,远程待验收 | | 版本 | 0.2 | | 日期 | 2026-09-13 | | 关联需求 | [外部知识库接入 PRD](./external-knowledge-prd.md) | | 关联场景 | [外部知识库 User Stories](./external-knowledge-user-stories.md) | | 技术契约 | [技术设计](./external-knowledge-technical-design.md) | ## 1. 不变量 | 稳定 ID | 规则 | 对应需求 | | --- | --- | --- | | `EK-I-01` 单一知识库身份 | 本地与外部都以 GoodBuddy `knowledgeBaseId` 参与聊天范围与工具授权,实例和远端 ID 不扩大选中范围 | `EK-FR-10`、`EK-FR-11` | | `EK-I-02` 外部绑定只读 | 添加、配置和移除仅修改本机记录;请求限于目录、详情、检索 | `EK-FR-15` | | `EK-I-03` 凭据边界 | 新密钥在输入表单和保存 IPC 中短暂存在;加密后只在 Main 解密使用,摘要与引用不返回密钥 | `EK-FR-02`、`EK-FR-11` | | `EK-I-04` 本地定义界面 | Provider 只返回数据,控件和配置由本地共享契约及组件定义 | `EK-FR-09` | | `EK-I-05` 无静默切换 | 请求使用保存的实例、目标和配置;失败不更换实例、公共云地址或其他知识库 | `EK-FR-14`、`EK-FR-16` | | `EK-I-06` 零结果与失败分离 | 合法成功响应无片段是零命中;请求或响应失败保留失败信息 | `EK-FR-14` | | `EK-I-07` 外部证据不可信 | 标准化片段按已有知识证据边界进入 Runtime,不改变工具权限 | `EK-FR-12` | | `EK-I-08` 配置不跨类型复用 | Provider 配置必须与实例一致,类型切换不迁移同名字段 | `EK-FR-06`、`EK-FR-09` | ## 2. 对象关系 一个实例可以有零个或多个绑定;一个绑定对应一个 GoodBuddy 知识库和一个远端目标。 同一实例与远端 ID 的组合唯一,不同实例可以使用相同 ID。本地知识库没有外部绑定。 类型由绑定关系推导,旧本地知识库不需要改成另一套对象。 GoodBuddy 显示名称与 `remoteName` 分开保存。刷新详情仅更新当前视图,不自动改写本地 名称或持久化远端名称;重新保存绑定时才写入当前表单使用的远端名称。 ## 3. 实例状态与连接测试 | 状态维度 | 当前值与行为 | | --- | --- | | 启停 | `enabled` 为 false 时 Main 拒绝目录和检索,并取消当前实例请求 | | 凭据 | `configured`、`missing`、`unavailable`;列表尝试解密判断可用性 | | 最近探测 | `untested`、`catalog-ready`、`healthy`、`list-restricted`、`auth-failed`、`unreachable`、`failed`;其中 `healthy` 在类型中保留,当前操作没有写入它 | 实例先保存为 `untested`,不要求保存前完成网络或认证测试。保存、替换或清除凭据后 重置探测状态。保存后的“测试连接”只尝试目录读取:成功写入 `catalog-ready`;403 写入 `list-restricted`;401 写入 `auth-failed`;网络或超时写入 `unreachable`;其他错误 写入 `failed`。时间和错误码随本次结果保存。 探测结果是最近一次观察,不是 Main 的永久禁用条件。Main 请求检查实例是否存在、启用 和凭据能否解密;即使最近为 `auth-failed` 或 `failed`,仍可重新请求。UI 会据该状态 显示不可用并禁用聊天勾选,用户可编辑或重新测试实例。 绑定创建另做目标检索验证,成功零命中可保存。保存实例不代表目录可用,目录成功也不 代表绑定检索或整个产品验收通过。 ## 4. 目录与手工 ID 目录每页最多 100 项,UI 当前请求 30 项,页码上限 500;当前没有“总目录最多 500 项” 的累计限制。Dify/RAGFlow 由远端分页,搜索过滤当前页;FastGPT 按 `parentId` 读取 当前父级,在返回结果内搜索和分页。文件夹只能进入浏览,不能作为目录绑定目标。 目录加载、空结果、搜索无结果和失败分别展示;失败提供重试,用户可主动切换手工 ID。 已经绑定的目录项禁用。手工 ID 不依赖详情接口一定可用,但保存前仍必须通过实际检索。 详情失败不阻断基础检索,例如旧 Dify 的详情 405。 Renderer 丢弃已失效的目录、详情和测试响应,不把切换前的结果写回当前表单。它没有 通过 IPC 取消已发出的目录请求,Main 请求仍受超时和服务生命周期管理。 ## 5. 创建与编辑 新建绑定时,用户尚未编辑的显示名称跟随当前目录选择更新;更换实例或手工输入另一个 目标 ID 时清除旧自动名称。用户手动填写过名称后保留该草稿。编辑或重新绑定已有库时, 始终保留已保存的本地名称,除非用户主动修改;此修正不自动重命名已有知识库。 流程为选择 `local | dify | fastgpt | ragflow`,外部类型再选择实例、目录项或手工 ID、 显示名称、配置与测试查询。表单测试成功后允许保存;服务保存前再次执行同一查询验证, 检查实例、绑定和唯一性是否在等待期间改变,再写入数据库。 | 表单变化 | 当前失效规则 | | --- | --- | | Provider 类型 | 重新挂载绑定表单,清空旧类型状态 | | 实例 | 清空目标、详情、目录位置和搜索,恢复该 Provider 默认配置,清除测试结果 | | 远端目标、测试查询、通用或 Provider 参数 | 清除测试结果,需要重新测试 | | 显示名称 | 保留测试结果 | | 已保存实例摘要变化 | 旧测试结果不再视为有效 | | 更换现有绑定实例或远端目标 | 需要重新测试和重新绑定确认 | 成功新建后仅把新库加入当前对话选择,不重新启用已取消的其他知识库。当前表单保存期间 禁用字段和操作;generation 检查用于避免旧响应更新 UI,不构成可取消的保存事务。 ## 6. 配置与优先级 当前配置的精确字段、数值范围和默认值由[技术设计第 3 节](./external-knowledge-technical-design.md#3-共享契约) 对应的共享 Schema 定义。恢复默认只重置本地表单,保存成功后才影响该绑定后续请求。 Dify `useDatasetDefaults=true` 时省略整个覆盖对象,使用远端当时配置;远端管理员修改 默认值会影响后续结果。关闭后按完整覆盖对象发送,不与远端默认逐字段合并。 FastGPT 使用本地保存的模式、Token 预算、相似度和重排开关,不开放查询优化。 RAGFlow 图谱与知识编译分别保存且默认关闭。图谱完成时间有效才允许开启图谱;非空 知识编译模板组 ID 才允许开启知识编译。服务在发送启用请求前重新读详情确认;仅配置 GraphRAG 或请求返回 200 都不能替代对应能力证据。证据不足时 UI 仍允许关闭已保存的开关。 当前没有自动版本识别、Adapter 版本矩阵或配置迁移状态。共享契约拒绝未知输入与 Provider 错配,不能据此承诺所有部署版本均支持每个显式参数。 ## 7. 绑定可用性 绑定只持久化配置和 `lastVerifiedAt`,没有独立 `binding_status`、最后成功检索时间或 远端缺失状态字段。详情与列表当前按实例推导: | 条件 | UI 状态 | | --- | --- | | 实例不存在 | `temporarily-unavailable` | | 实例停用 | `instance-disabled` | | 凭据未配置、无法解密或最近认证失败 | `credential-error` | | 最近探测失败或不可达 | `temporarily-unavailable` | | 其余且绑定有验证时间 | `ready` | | 其余且无验证时间 | `untested` | 聊天选择器保留已选 ID,但不可用行禁用勾选。检索时的 401、404、超时等通过本次失败 报告,不自动写成持久 `remote-missing/config-invalid` 状态。`ready` 表示当前可尝试使用, 不保证下一次请求成功。 ## 8. 多库检索与失败 请求使用已冻结的 GoodBuddy ID 集合,`retrieveMany` 去重后并行检索各库。本地与外部 共享入口,外部库不触发本地 Embedding。预算按各库结果的 rank 轮转分配,先各库第 1 条、 再各库第 2 条,同 rank 按输入库顺序处理;不先耗尽整个库,也不比较 Provider 原始分数。 当前没有每库最低字符配额。精确预算见 [技术设计第 7 节](./external-knowledge-technical-design.md#7-检索编排)。 成功零命中返回空 results 且无 failure。单库错误返回 `diagnostics.failure`,其他库 仍可返回结果;上层据此区分部分失败与全部失败。MCP 在有失败且没有任何片段时返回 工具错误,即使另有成功零命中的库;预检索则保留降级诊断。用户取消或服务关闭会向上 传播取消,不把整次取消解释为成功零命中。 客户端错误码包括 `NETWORK/TIMEOUT/CANCELLED/AUTH/FORBIDDEN/NOT_FOUND/RATE_LIMITED/` `INCOMPATIBLE/INVALID_RESPONSE/SERVER`,均以 `EXTERNAL_KB_` 为前缀。服务另报告凭据 不可用、停用、配置变化、配置错配、能力不可用、重复绑定和实例被引用。远端错误正文 不直接成为 UI 错误;部分本地校验错误仍是 Zod、TypeError 或 RangeError,不能承诺 所有失败都已有独立本地化诊断。 ## 9. 删除与引用 停用和清除凭据保留绑定,UI 清除凭据需要确认。有绑定的实例不能直接删除;管理界面 列出受影响知识库并确认后,依次移除本地库再删实例。若中途失败,已成功移除的本地 记录保持删除,刷新列表后显示当前状态。任何一步均不发送远端内容删除请求。 历史引用保存当时的片段与外部定位,移除实例或绑定后仍能阅读。当前不重新读取远端 片段,不打开来源 URL,不查询实例是否已删除;因此没有自动“连接已移除”标签。 App 与 Main 引用处理共用 `knowledgeReferenceKey`,键包含远端库、文档和片段 ID、 定位及正文,避免外部结果缺少本地 ID 时被错误合并。弹窗只显示优先选中的一个定位。 早期 Dify 历史引用保存和重载检查使用测试会话;后续完整 App 已从实际输入区生成真实 短答案并点击引用验证。两轮证据分别保留,不把测试会话当成模型回答。 ## 10. 需求追踪与完整性 | 需求 | 规则 | 场景 | | --- | --- | --- | | `EK-FR-01` 至 `EK-FR-05` | 实例状态、保存后测试、目录与手工 ID | `EK-US-A1`、`EK-US-A2`、`EK-US-B1`、`EK-US-B2` | | `EK-FR-06` 至 `EK-FR-10` | 对象关系、创建编辑、配置与优先级 | `EK-US-B3`、`EK-US-B4`、`EK-US-D1`、`EK-US-D3` | | `EK-FR-11` 至 `EK-FR-14` | 范围、可用性、结果与失败、引用 | `EK-US-C1` 至 `EK-US-C4`、`EK-US-D2`、`EK-US-D4` | | `EK-FR-15`、`EK-FR-16` | 只读生命周期、凭据与请求边界 | `EK-US-A3`、`EK-US-C2` | 三家真实 Provider 的生产服务、本机 HTTP MCP、生产 IPC `always` 短答案,以及 Dify `auto` 与完整 App 输入区回答、引用已有证据,详见[实施进度](./progress.md)。`auto` 本轮 使用进程内工具调用,不覆盖远程 ACP。FastGPT/RAGFlow 完整桌面、复杂问答和远程 forced-preflight 仍待完成;远程已确认网络可达,但已有 SSH 凭据在验证程序内无法解密。 后续 Portal 与控件修正后的聚焦测试、全量回归及相关检查已通过,精确结果由实施进度维护。 原场景中的保存前分项测试、按地址 传输确认、取消交互、版本能力状态和完整诊断尚不等同当前实现,不能把对应 User Story 全部标为通过。是否调整这些验收要求由功能 owner 确认,本轮不增加新的状态框架。