e5fa063d7d
WeKnora 的 wiki 图谱与页面同表(wiki_pages),旧的库级 DELETE + 灌包在共享真实 知识库上会把其他文档的页面与 agent 手写页一起抹掉(2026-10-08 生产演练现场)。 - 新增 internal/releaseadapter/v071/wiki_scope.go:按 source_refs 判定归属 (全部引用属于本包 = InScope;引用包外知识 = 外来页;无引用 = index/log/ agent 页永不触碰),目标库存在外来页时降级为只替换包内页集,外来页上仅摘掉 本包知识引用,包内库级页不写;包内会写入的页与外来页同 slug 时 fail-closed 拒绝发布且不落任何改动。 - Replace 与 ReplaceWiki(撤权/回滚)共用同一套判定,不再整库清空。 - 作用域以一行日志落盘:release wiki replace scope in_scope=… degraded=… - 测试:单测 7 条(纯函数判定)+ 真库集成 3 条(共享库保护 / slug 冲突拒绝 / 镜像库整库替换不变),集成用例只用带随机后缀的临时知识库并自清理。 - 文档:P6 新增「wiki 通道的归属门禁」,P8 补验收点与镜像前提。
5.4 KiB
5.4 KiB
P8 目标知识库可选(源库 → 目标库映射)开发与验收
缺口
发布链路把「测试库」与「生产库」绑成同一个 KB ID,全链路 fail-closed:
contract_inspector用route.knowledge_base_id读目标契约,且要求它等于发布项的测试库 ID;manifest.Validate/sidecar_publisher/verifier断言KnowledgeBaseID == item.TestKnowledgeBaseID;postgres_import.prepareSnapshot逐行断言knowledge_base_id == identity.KnowledgeBaseID;search_import.searchHitMatches断言文档knowledge_base_id == identity.KnowledgeBaseID。
因此只能发布到与测试库同 ID 的「镜像库」,无法选择目标端已有真实库(如「对内知识库-wiki」)。
目标
- 系统设置新增「生产知识库绑定(对内 / 对外)」,从目标(生产)WeKnora 实际列表里选目标库,保存时真校验。
- 发布执行时按绑定把 KB 身份从源库改写到目标库,三通道(PostgreSQL / Elasticsearch / 对象)与官方 API 验收一致收敛。
- 保持 fail-closed 语义:映射关系是显式声明并被冻结的,未绑定时退回同 ID 语义(目标库不存在则在契约检查阶段即失败)。
契约变更(唯一一处放宽)
原来比较 manifest.Contract.KnowledgeBaseID == target.KnowledgeBaseID;
现在比较声明的映射对(releasebundle.KnowledgeBaseMapping):
manifest.Contract.KnowledgeBaseID == mapping.SourceKnowledgeBaseID (源:测试库)
target.KnowledgeBaseID == mapping.TargetKnowledgeBaseID (目标:生产库)
其余字段(版本 / migration / tenant / 检索引擎 / 模型 / 维度 / ES mapping SHA)继续必须相等
映射来源:release_item_executions.target_knowledge_base_id(执行开始时按 production_kb_bindings 解析并落库冻结),审计可见、重试复用。已在生产发布过的条目保留原目标(重试/回滚回到同一处),未发布过的按当前绑定。
落点
| 层 | 改动 |
|---|---|
| 迁移 000044 | production_kb_bindings(单行 active,对内/对外目标库)+ release_item_executions.target_knowledge_base_id |
| 系统设置 | 新增「生产知识库绑定(对内 / 对外)」,从生产 WeKnora 实际列表选库;保存时真校验(不存在→400 拒收;生产端不可达→只提示并如实回报 production_connected=false) |
| API | GET/PUT /api/v1/production-kb-bindings、GET /api/v1/production-kb-bindings/options |
| 执行计划 | StartExecution 解析映射并冻结进 plan 与 release_item_executions;空目标直接拒收 |
| 快照改写 | DocumentBundle.RemapKnowledgeBase:5 张带 KB 列的表 + ES 文档 knowledge_base_id 改写为目标库,重算表/文档摘要并重新封装(Seal);其余字段按原字节透传(嵌入向量不动) |
| 目标身份 | targetIdentities / 契约检查器 / 验证器 / PlanItem.TargetBase() 全部走映射后的目标库 |
| 对象通道 | 不改:对象 key = weknora/<tenant>/<knowledge_id>/<asset>,无 KB 段(实测穷举) |
| Compose | compose.release-sim.yaml(数据面接线,无门禁)+ compose.release-gates.yaml(两道门禁,须人工授权叠加) |
改写面(实测穷举)
源库行里 KB ID 只出现在 knowledge_base_id 这一个字段(每行 1 处):
| 表 / 位置 | 是否有 KB 列 | 处理 |
|---|---|---|
knowledges / chunks / knowledge_tags / wiki_pages / wiki_folders |
有(各 1 处) | 改写为目标库 ID,并重算表摘要 |
resources / resource_bindings / knowledge_tag_relations |
无 | 不改(按 knowledge/handle 关联) |
Elasticsearch _source.knowledge_base_id |
有 | 改写为目标库 ID,并重算文档摘要 |
对象存储 locator storage://<backend>/oss://<bucket>/weknora/<tenant>/<knowledge>/<asset> |
无 KB 段 | 不改(仅按目标 backend/bucket 重写) |
验收点
go test ./...、go vet、前端tsc+vitest全绿;迁移 44 up/down 往返可用。- 单元/集成:映射生效后
knowledges/chunks/wiki_pages的 KB 列、ES 文档字段均为目标库;摘要随改写重算;未绑定退回同 ID。 - 界面:系统设置可选目标库(列真实库名),保存时对目标 WeKnora 真校验(无效 ID 拒绝);发布管理显示本次目标库。
- 真实跨环境:1 份文档发布到目标库 → 三通道读回一致 → 官方 API 检索命中 → 撤权后零命中 → 回滚恢复。
- wiki 归属门禁(2026-10-08 起):目标库存在引用包外知识的页时,发布自动降级为"只替换包内页集",
外来页 / agent 手写页 / 目标库自己的
index-log一行不动;包内会写入的页与外来页同 slug 时拒绝发布 且事务不留任何改动。真库集成用例:TestWikiReplaceOnSharedKnowledgeBaseKeepsForeignPages、TestWikiReplaceRefusesWhenPackageSlugBelongsToForeignPage、TestWikiReplaceOnFaithfulMirrorReplacesWholeLibrary(TEST_WEKNORA_V071_TARGET_DATABASE_URL指向 v0.7.1 库时运行,只使用带随机后缀的临时知识库并自清理)。 - 跨库对发布的前置条件:测试库与生产库承载同一批文档(镜像)时 wiki 才能保持"测试=生产"; 只做单文档发布时,共享页正文不保证两边一致(见 P6「wiki 通道的归属门禁」)。