# 技能归纳、WeKnora 普通知识库与助手知识库:实现设计 本文档用于指导大模型或开发者实现知识库体系改造。整体包含两条线:一条是“将一个或多个技能归纳到一个 WeKnora Wiki 普通知识库”;另一条是新增自研助手知识库体系。助手知识库像普通知识库一样可以创建多个库,同时系统内置一个全局大库,用于统计全库数据、承接所有 WeKnora 导入沉淀、做全局实体对齐、Wiki 展示和导入审计。 本文档中,技能归纳目标只能是 WeKnora Wiki 普通知识库;助手知识库不接收技能直接归纳,也不接收任意本地文件。普通知识库负责向量化、检索、Wiki 和图谱;助手知识库负责自研存储、WeKnora 导入、实体对齐、Wiki 展示、搜索和导入审计。无论 WeKnora 数据导入到哪个助手知识库,最终都必须沉淀一份到系统全局大库。 > 最终约束:如果本文历史章节与本节冲突,以本节和“2. 当前边界”中的约束为准。 ## 1. 背景与目标 当前系统已有: - 技能管理:后台可管理运行中的技能,技能文件位于 `skills/public`、`skills/custom`。 - 技能地址管理:已经在管理员技能管理页中以标签页形式接入,支持扫描技能内 `.md`、`.py` 中的地址并批量替换。 - WeKnora / LLM Wiki 接入:前后端已有知识库、文档、Wiki 页面、Wiki 图谱等 API。 本次要新增一条管理员工作流: 管理员在“技能管理”页面选择多个技能,再选择一个目标 WeKnora Wiki 普通知识库,点击归纳。系统将每个技能作为独立来源读取、解析、抽取和归纳,把结果写入对应 WeKnora 知识库,并在技能列表和技能详情中展示“该技能已被哪些知识库归纳”。如果需要归纳到多个普通知识库,通过多次操作完成。 用户已确认的业务约束: - 一次操作可以选择多个技能,但只能选择一个目标 WeKnora Wiki 普通知识库。 - 同一个技能可以在不同时间归纳到多个知识库,所以最终关系是技能与知识库多对多。 - 多知识库归纳通过多次操作完成;每次操作形成一批 `技能 x 单目标知识库` 任务。 - V1 只支持归纳到可写 WeKnora Wiki 普通知识库;不支持非 Wiki、FAQ、系统会话存档、只读知识库作为技能归纳目标。 - 技能归纳时只抽取数据、业务说明、实体、概念、对象、事件、关系等知识;Python、TypeScript、Shell 等代码和脚本类文件完全排除,不读取注释、不抽取函数、不上传、不进入 prompt。 - 技能里的 Markdown、业务数据文件、表格、PDF、Office、图片等要纳入处理范围。 - 需要同步三类内容:原始资料、归纳后的 Wiki、实体与关系。 - 关系来源包括 Markdown 自然语言描述、目录和文件引用、模型自动推断。 - 允许模型推断关系,但前端不提供逐条实体/关系预览审核。 - 支持管理员手动点击“重新归纳”;不新增自动文件监听、定时后台同步或技能变更自动远程写入。 - 解除关联时弹窗让用户选择:保留知识库内容,或删除该技能贡献的内容。 - 权限仅管理员。 - 总技能数约 200 个,需要支持批量进度、后台执行、失败重试。 - 同一目标知识库内,不同技能抽取出的同一实体需要合并。 - 重归纳时先生成新版本,成功后再替换旧版本;失败时旧版本继续可用。 - 新增知识库类型区分:普通知识库是 WeKnora,助手知识库是自研存储。 - 助手知识库支持创建多个库;其中有且只有一个系统全局大库,建议命名为“知识梳理总库”,所有用户可见。 - 助手知识库只提供 Wiki 页面展示、搜索和数据导入;不落到 WeKnora。 - 助手知识库数据来源只能是 WeKnora 导出包或从某个 WeKnora 知识库触发导入。 - WeKnora 已向量化和处理完成的 Wiki、文档、实体、关系、图谱数据默认沉淀到助手知识库;如果导入的是某个自建助手库,也必须同步沉淀到全局大库。 - 普通知识库需要提供导出或“导入到助手知识库”的功能,把 WeKnora 里已经处理好的数据导入指定助手知识库;导出数据需要包含 Wiki、文档、chunk、实体、关系、图谱。 - 即使没有配置 WeKnora 地址,也要默认展示助手知识库列表和创建入口,而不是展示空的普通知识库页面。 - 管理员可切换知识库产品版本:`v1` / `v2`,默认 `v1`。 - 助手知识库普通用户只读,管理员可导入、编辑、删除、查看版本和回滚 Wiki 页面。 ## 2. 当前边界 以下能力明确不做,开发实现和验收必须按这些边界收敛: - 不支持一次选择多个目标知识库。多知识库归纳通过多次操作完成。 - 不支持非 Wiki 知识库、FAQ 知识库、系统会话存档知识库、只读知识库作为技能归纳目标。 - 不直接写 WeKnora 的 Neo4j 或底层图数据库。 - 不新增自动文件监听、定时后台同步或技能变更自动远程写入。 - 不在前端提供逐条实体/关系预览审核。 - 不把远端 WeKnora 内部 ID 暴露为前端业务主键。 - 不支持技能直接归纳到助手知识库。技能先进入 WeKnora 普通知识库,再由 WeKnora 导出沉淀到助手知识库。 - 不支持管理员向助手知识库导入任意本地文件;手动导入也必须是 WeKnora 导出包或从某个 WeKnora 知识库触发。 - 助手知识库 V1 不做页面版本管理,只保留当前合并后的 Wiki 状态和导入审计记录。 ## 3. 术语 | 术语 | 含义 | | --- | --- | | 技能 | DeerFlow 技能目录,通常包含 `SKILL.md`、代码、配置、示例、文档、数据文件等。 | | 目标知识库 | 管理员选择的一个 WeKnora Wiki 普通知识库。助手知识库不是技能归纳目标。 | | 普通知识库 | WeKnora 知识库,负责向量化、Wiki、文档检索和图谱能力。 | | 助手知识库 | DeerFlow 自研知识库类型,支持创建多个库,用于 Wiki 展示、搜索、WeKnora 导入和实体对齐。 | | 知识梳理总库 | 助手知识库体系中的系统全局大库,有且只有一个,自动统计和沉淀所有助手库与普通知识库导入数据,做全局实体对齐。 | | 自建助手库 | 管理员创建的普通助手知识库,类似普通知识库列表中的一个库,可导入 WeKnora 普通知识库导出数据。 | | 产品版本 | 知识库产品形态开关。`v1` 默认为普通知识库体验,`v2` 默认为助手知识库体验。 | | WeKnora 导出包 | 从普通知识库导出的已处理知识集合,包含 Wiki 页面、文档、chunk、实体、关系、图谱等。 | | 绑定 | 一个技能与一个目标知识库之间的持久关联。`skill_name + target_type + target_id` 唯一。 | | 快照 | 某次扫描技能目录得到的内容版本,包含文件清单、摘要、抽取结果和源 digest。 | | 贡献 | 某个技能快照对目标知识库内实体、关系、Wiki 页面、原始资料的增量内容。 | | 规范实体 | 在同一个目标知识库内合并后的实体。多个技能可以贡献到同一个实体。 | | 规范关系 | 在同一个目标知识库内合并后的关系。多个技能可以给同一关系提供证据。 | | 归纳任务 | 一次管理员发起的批量操作,可包含多个目标知识库和多个技能 item。 | | item | 归纳任务中的单个技能处理单元。 | ## 4. 总体方案 整体采用“双知识库形态 + 本地持久绑定 + 后台任务 + WeKnora 投影 + 助手底库实体对齐”的架构。 ```mermaid flowchart LR A[管理员选择多个技能] --> B[选择一个 WeKnora Wiki 目标知识库] B --> C[创建归纳任务] C --> D[逐技能扫描与解析] D --> E[生成技能快照] E --> F[LLM 归纳 Wiki 与关系抽取] F --> G[按目标 WeKnora Wiki 合并实体/关系] G --> H[写入 WeKnora 原始资料与 Wiki] G --> I[写入关系投影文档] H --> K[校验远端写入] I --> K K --> L[本地原子激活新版本] L --> M[技能页展示已归纳知识库] K --> N[WeKnora 导出已处理数据] N --> O[导入指定助手知识库] O --> P[同步沉淀到知识梳理总库] P --> Q[全局实体对齐] Q --> S[总库 Wiki 展示与搜索] ``` 关键原则: - DeerFlow 本地数据库保存权威绑定关系、版本状态、来源贡献和远端 artifact 映射。 - WeKnora 保存可检索知识内容,包括原文、Wiki 页面、FAQ、会话存档、关系投影文档和图谱。 - 不直接写 WeKnora 底层图数据库;关系以 WeKnora API 能力和关系投影文档承接。 - 重归纳使用 staged artifact,远端写入和校验成功后,本地才激活新版本。 - 删除或解除关联只影响该技能在目标知识库中的贡献,不误删其他技能共享的实体和关系。 - 助手知识库可以创建多个库;系统全局大库只有一个,负责汇总全库数据和做跨库实体对齐。 - WeKnora 的已处理结果可以导入指定助手知识库;无论是否指定自建助手库,都必须同步沉淀到知识梳理总库。 - 未配置 WeKnora 地址时,默认进入助手知识库页面,展示助手知识库列表、创建入口和知识梳理总库;与 WeKnora 相关的导出导入动作置灰或隐藏。 ## 5. 现有代码落点 实现前必须先读仓库说明: - `D:/cmzs-clean/deerflow-code/AGENTS.md` - `D:/cmzs-clean/deerflow-code/offline-backend-20260512/backend/AGENTS.md` - `D:/cmzs-clean/deerflow-code/offline-backend-20260512/backend/CLAUDE.md` 建议改动位置: | 模块 | 建议文件 | | --- | --- | | 后端 router | `offline-backend-20260512/backend/app/gateway/routers/skill_knowledge.py` | | 后台任务调度 | `offline-backend-20260512/backend/app/gateway/skill_knowledge_job_dispatcher.py` | | 后台任务执行 | `offline-backend-20260512/backend/app/gateway/skill_knowledge_job_executor.py` | | 技能归纳核心逻辑 | `offline-backend-20260512/backend/packages/harness/deerflow/skill_knowledge/` | | 持久化模型 | `offline-backend-20260512/backend/packages/harness/deerflow/persistence/skill_knowledge/` | | WeKnora 客户端扩展 | `offline-backend-20260512/backend/packages/harness/deerflow/integrations/weknora/client.py` | | 助手知识库核心逻辑 | `offline-backend-20260512/backend/packages/harness/deerflow/assistant_knowledge/` | | 助手知识库持久化 | `offline-backend-20260512/backend/packages/harness/deerflow/persistence/assistant_knowledge/` | | 助手知识库 Gateway | `offline-backend-20260512/backend/app/gateway/routers/assistant_knowledge.py` | | WeKnora 导出到助手库任务 | `offline-backend-20260512/backend/app/gateway/assistant_knowledge_import_executor.py` | | 前端 API | `frontend-web/src/strategy-components/api/skill-knowledge.ts` | | 助手知识库前端 API | `frontend-web/src/strategy-components/api/assistant-knowledge.ts` | | 管理员技能页 | `frontend-web/src/pages/AdminSkillsPage.tsx` | | 技能行组件 | `frontend-web/src/components/workspace/settings/skill-settings-page.tsx` | | WeKnora API hooks | `frontend-web/src/strategy-components/api/llmwiki.ts` | | 知识库产品版本设置 | `frontend-web/src/core/system-settings/api.ts` | 注意边界: - `packages/harness/deerflow/*` 不能 import `app.*`。 - 路由、鉴权、FastAPI app state 等放在 `app/gateway/*`。 - 纯扫描、解析、抽取、合并、投影计划生成放在 `deerflow.*`。 - 文件 IO、Office/PDF 转换、CPU 密集处理必须通过 `asyncio.to_thread`,避免阻塞事件循环。 ## 6. WeKnora 能力使用策略 现有 WeKnora 客户端已支持: - 创建知识库。 - 上传文件。 - 导入 URL。 - 创建手动文档。 - 创建、更新、删除、查询 Wiki 页面。 - 获取 Wiki 图谱。 V1 需要新增或补齐: - 目标知识库能力探测:是否启用 Wiki,是否可写,是否支持 GraphRAG / extract_config。 - 目标知识库分类和能力标识:Wiki、非 Wiki、FAQ、会话存档、只读、图谱可写。 - 导出已处理数据的能力:Wiki 页面、文档、chunk、实体、关系、图谱。 - WeKnora 处理完成后的自动沉淀触发点:在文件向量化、Wiki 生成和图谱抽取完成后,创建“导入助手知识库”的后台任务。 - 如果 WeKnora 当前实例 Swagger 暴露了图谱相关配置更新接口,则在目标知识库启用 `graph_enabled` 与 `extract_config`。 - 如果未暴露稳定的实体/关系写入 API,优先使用“关系投影文档 + Wiki 关系页 + Wiki 链接”让 WeKnora 自己抽取图谱;在配置显式开启时,可通过受控 Neo4j direct writer 写入底层图数据库。 参考官方资料: - [WeKnora Knowledge Graph 文档](https://github.com/Tencent/WeKnora/blob/main/docs/KnowledgeGraph.md) - [WeKnora knowledge-base API 文档](https://github.com/Tencent/WeKnora/blob/main/docs/api/knowledge-base.md) - [WeKnora QA 文档](https://github.com/Tencent/WeKnora/blob/main/docs/QA.md) - [WeKnora API 文档入口](https://github.com/Tencent/WeKnora/blob/main/docs/api/README.md) 实现要求: - 以部署实例的 Swagger 为准,不要硬编码未确认的远端 API。 - 可以封装 `probe_capabilities()`,返回 `wiki_enabled`、`graph_enabled_supported`、`extract_config_supported`、`wiki_page_upsert_supported`、`file_upload_supported` 等能力。 - GraphRAG 失败不能导致 Wiki 归纳整体回滚。Wiki 与原始资料成功、GraphRAG 写入失败时,绑定状态为 `partial_failed`,错误信息明确写“图谱投影失败,可重新归纳重试”。 - 如果 WeKnora 不支持直接导出向量、chunk 或图谱原始结构,导出层应退化为读取 Wiki 页面、文档状态、Wiki graph 和可查询的图谱摘要;助手知识库不强依赖复用 WeKnora embedding 向量。 ### 6.1 目标知识库适配策略 一次归纳任务允许选择多个技能和多个目标知识库。后端不要把它当成一个大事务,而是展开为矩阵: ```text items = selected_skills x selected_targets ``` 每个 item 对应一个 `binding`,一个技能归纳到三个目标知识库,就生成三个 binding。父 job 负责展示整体进度,item 独立成功、失败、取消和重试。 当目标是自建助手库时,item 写入顺序是: ```text 写入目标助手库 -> 生成目标库 Wiki/实体/关系 -> 创建总库沉淀任务 -> 总库实体对齐和统计 ``` 总库沉淀失败不能让目标助手库写入结果消失,但 item 必须标记为 `partial_failed`,并允许单独重试“沉淀到总库”。 目标知识库需要通过 adapter 写入: | 目标类型 | 写入策略 | Wiki | 关系/图谱 | | --- | --- | --- | --- | | WeKnora Wiki 知识库 | 上传原始资料、upsert Wiki 页面、写 graph projection | 写 WeKnora Wiki | WeKnora GraphRAG + 可选 Neo4j direct writer | | WeKnora 非 Wiki 知识库 | 上传原始资料和归纳文档,不强制生成 Wiki | 不写 WeKnora Wiki,可写本地 sidecar Wiki | graph projection 文档 + 可选 Neo4j direct writer | | WeKnora FAQ 知识库 | 把归纳结果转换为 FAQ 条目、来源文档和关系摘要 | 不写或只写 sidecar Wiki | 关系作为 FAQ metadata、projection 文档和图谱边 | | 系统会话存档知识库 | 把技能归纳结果作为系统会话归档条目写入 | sidecar Wiki 展示 | 关系作为归档 metadata 和图谱边 | | 只读 WeKnora 知识库 | 不修改远端原库;写本地 sidecar artifact,并生成待推送状态 | sidecar Wiki 展示 | sidecar graph + 待推送 Neo4j plan | | 助手知识库 | 写入指定助手库、实体对齐、生成 Wiki 页面版本;同时异步沉淀到知识梳理总库 | 写自研 Wiki | 写自研实体/关系表;总库做跨库实体对齐 | 只读目标说明: - “支持只读知识库作为目标”不代表绕过远端权限强行写入。 - 对只读 WeKnora,系统创建本地 sidecar 投影:归纳结果与该远端知识库绑定展示,但远端写入状态为 `pending_remote_write`。 - 当管理员后续授予可写权限时,可执行“推送到远端”,把 sidecar staged artifact 写入 WeKnora。 - 前端必须清楚展示“只读目标:已本地归纳,未写入远端”。 ### 6.2 Neo4j 直接写入设计 需求确认需要支持直接写 WeKnora 的 Neo4j 或底层图数据库。该能力风险较高,必须作为受控能力实现。 必须满足: - 通过配置显式开启,例如 `weknora.graph.direct_write_enabled = true`。 - 只允许管理员触发。 - Neo4j 连接串、账号、库名只允许后端配置,不允许前端传入。 - LLM 不得生成任意 Cypher 并直接执行。 - graph writer 只能执行代码中预定义的参数化模板。 - 所有写入使用事务。 - 每个节点和边都带稳定业务 key、来源 binding、snapshot、job、confidence 和 evidence。 - 支持 dry-run plan、写入审计、失败重试、回滚计划。 节点 key 建议: ```text provider = deerflow scope = target_type + target_id entity_key = entity_type + normalized_name 或 external_id ``` 边 key 建议: ```text source_entity_key + predicate + target_entity_key + contribution_scope ``` Neo4j 写入只作为图谱层加速和展示,不替代本地 contribution 表。本地 contribution 表仍是删除、回滚、重建、审计的权威来源。 ### 6.3 实体/关系预览审核 前端必须提供逐条实体/关系预览审核能力。审核发生在“抽取完成、写入目标前”。 审核对象: - 实体。 - 关系。 - 实体对齐候选。 - 关系合并候选。 - Wiki 页面草稿。 管理员操作: - 批准。 - 拒绝。 - 编辑名称、类型、谓词、别名、摘要、confidence。 - 批量批准高置信结果。 - 批量拒绝低置信结果。 审核模式建议: | 模式 | 说明 | | --- | --- | | `required` | 所有实体/关系必须审核后才能写入。 | | `auto_high_confidence` | 高置信自动通过,低置信进入审核队列。 | | `off` | 不阻塞写入,但仍保留预览页面和事后审计。 | 默认建议 `auto_high_confidence`。如果用户希望“必须逐条审核”,可在系统设置中改为 `required`。 审核记录必须入库: - 操作人。 - 操作时间。 - 原始抽取值。 - 修改后值。 - 通过或拒绝原因。 - 对应 job/item/binding/snapshot。 ### 6.4 自动同步设计 需要支持三类自动同步: - 文件监听:技能知识文件变化后自动创建归纳任务。 - 定时同步:按管理员配置的周期重新扫描并归纳。 - 技能变更自动远程写入:技能保存、启停、导入后触发相关 binding 同步。 自动同步仍需遵守: - 只处理非代码知识文件。 - 代码文件变化不触发归纳。 - 自动任务必须 debounced,避免保存过程中连续触发。 - 自动任务必须走 staged 版本,成功后激活,失败保留旧版本。 - 同一 binding 同一时间只能有一个 active item。 - 审核模式为 `required` 时,自动任务停在 `awaiting_review`,不写远端。 建议新增配置: ```json { "skill_knowledge_auto_sync": { "watch_enabled": true, "schedule_enabled": true, "schedule_cron": "0 */6 * * *", "debounce_seconds": 120, "write_remote_enabled": true } } ``` ### 6.5 远端 ID 暴露与业务 key 需求确认前端需要暴露 WeKnora 远端 ID,并可作为跨系统业务 key 使用。 实现要求: - API 返回 `remote_refs`,包含 provider、remote kind、remote id、slug、url。 - 前端可以展示、复制、筛选远端 ID。 - 与外部系统交互时,可使用 `provider + remote_id` 作为业务 key。 - 本地数据库仍保留 `id` 作为 surrogate primary key,避免远端 ID 变更导致本地关系断裂。 建议响应结构: ```json { "id": "local-artifact-id", "business_key": "weknora:wiki_page:remote-page-id", "remote_refs": [ { "provider": "weknora", "remote_kind": "wiki_page", "remote_id": "remote-page-id", "slug": "skills/example/overview", "url": "/llmwiki/kb/xxx/wiki/skills/example/overview" } ] } ``` 不要把远端 ID 当作唯一存储来源;本地仍需要维护 mapping、审计和状态。 ## 7. 数据处理范围 归纳一个技能时,扫描技能根目录下所有有效的非代码知识文件。代码类文件不作为知识源,也不影响知识 digest。 必须处理: | 类型 | 策略 | | --- | --- | | `.md` / `.mdx` / `.txt` | 直接读取,保留标题、段落、链接、表格和自然语言说明;代码块内容默认跳过,只保留“此处存在示例代码”的弱提示。 | | `.json` / `.yaml` / `.yml` / `.toml` / `.ini` | 仅当它们承载业务数据、数据字典、枚举、模板或知识配置时处理;运行配置、依赖配置、工具入口、工程配置不入库。 | | `.csv` / `.tsv` | 结构化读取,抽样概要 + 全量原始资料上传。 | | `.xlsx` / `.xls` | 使用已有文件转换或表格解析能力,保留 sheet、列名、样例和统计。 | | `.pdf` / `.docx` / `.pptx` | 复用现有 `deerflow.utils.file_conversion` 或 MarkItDown 路径转文本,同时上传原始文件。 | | 图片 | 上传原始文件;如果已有 VLM/OCR 能力则抽取图片描述,否则记录为二进制附件。 | | 压缩包 | 安全解压到临时目录后递归处理,必须限制大小、文件数、路径穿越、符号链接。 | | 其他二进制 | 记录 manifest 并尝试作为原始附件上传;无法解析时不生成文本摘要。 | 必须排除: - `.py` - `.ts` - `.tsx` - `.js` - `.jsx` - `.sh` - `.bat` - `.ps1` - 其他脚本、源码、构建脚本、依赖锁文件 - `.git` - `node_modules` - `__pycache__` - `.pytest_cache` - `dist` - `build` - 临时缓存目录 - 明确生成物和大体积模型文件 必须阻断: - `.env` - 私钥、公钥私钥对、证书私钥 - token、password、secret、access key 等高置信密钥文件 阻断规则是安全底线:发现疑似密钥时,该文件不能上传到 WeKnora,也不能进入 LLM prompt。任务 item 标记为 `partial_failed` 或 `failed`,前端展示“因安全策略跳过敏感文件”,并给出文件路径,不输出密钥内容。 代码排除规则: - 不读取源码内容。 - 不抽取源码注释。 - 不抽取函数、类、import、依赖、入口、调用链。 - 不上传源码文件。 - 不把源码路径作为 Wiki 知识展示;最多在管理员任务详情中显示“已跳过代码类文件数量”。 - 计算 `source_digest` 时只包含知识文件;纯代码变更不应导致技能归纳状态变为 stale。 ## 8. 中间表示 IR 扫描和抽取后生成统一 IR,后续 Wiki 生成、实体合并、远端投影都基于 IR,而不是直接从文件内容临时拼装。 建议结构: ```json { "skill": { "name": "example-skill", "display_name": "示例技能", "description": "从 SKILL.md 和元数据提取", "source_root": "skills/public/example-skill", "source_digest": "sha256:..." }, "files": [ { "path": "SKILL.md", "kind": "markdown", "sha256": "...", "size": 12345, "text_digest": "sha256:...", "title": "Example Skill", "summary": "文件摘要", "anchors": [ { "id": "heading-install", "line_start": 12, "line_end": 36, "text_digest": "sha256:..." } ], "references": [ { "target_path": "data/schema.yaml", "evidence": "Markdown link or import", "confidence": 1.0 } ], "safety": { "status": "allowed", "warnings": [] } } ], "entities": [ { "local_id": "ent_001", "type": "tool", "name": "web_search", "aliases": ["网络搜索"], "description": "用于检索外部信息的工具", "attributes": { "entrypoint": "config.yaml tools.web_search" }, "evidence": [ { "file_path": "config.yaml", "line_start": 10, "line_end": 20, "quote": "短证据,不包含敏感信息" } ], "confidence": 0.94 } ], "relations": [ { "local_id": "rel_001", "source_local_id": "ent_001", "predicate": "used_by", "target_local_id": "ent_002", "description": "web_search 被示例技能用于首步检索", "relation_source": "llm_inferred", "evidence": [ { "file_path": "SKILL.md", "line_start": 22, "line_end": 28, "quote": "短证据" } ], "confidence": 0.88 } ], "wiki": { "skill_page": { "slug": "skills/example-skill/overview", "title": "示例技能", "content": "Markdown Wiki 内容" }, "pages": [] } } ``` IR 需要版本化: - `extractor_version`:扫描、解析、提示词和抽取 schema 的整体版本。 - `prompt_hash`:LLM 抽取提示词 hash。 - `model_name`:抽取模型名。 - `source_digest`:技能目录内容 digest。 只要 `source_digest + extractor_version + prompt_hash + model_name` 相同,就可以复用已有 snapshot,避免 200 个技能批量时反复抽取。 ## 9. 关系抽取规则 关系来源分三类: | 来源 | 示例 | 置信度策略 | | --- | --- | --- | | 确定性结构关系 | 目录包含文件、Markdown 链接、数据文件引用、业务配置引用 | `confidence = 1.0` | | 显式文本关系 | Markdown 中写明“本技能依赖 A”、“输出 B”、“调用接口 C” | LLM 抽取,但要求证据;通常 `0.75` 以上 | | 模型推断关系 | 从上下文推断某工具服务于某步骤、某配置控制某行为 | 必须有证据片段和解释;无证据不得入库 | 无人工审核的前提下,必须设置阈值: - `confidence >= 0.75`:写入 Wiki 关系页和图谱投影文档。 - `0.50 <= confidence < 0.75`:只写入技能 Wiki 的“可能关系”小节,不进入规范关系。 - `confidence < 0.50`:丢弃。 每条关系都必须包含: - `source` - `predicate` - `target` - `confidence` - `relation_source` - `evidence` 如果没有证据路径和证据文本,不允许写入规范关系。 建议谓词归一化: | 原始表达 | 规范 predicate | | --- | --- | | 依赖、需要、requires | `depends_on` | | 调用、使用、uses | `uses` | | 生成、输出、produces | `produces` | | 读取、输入、consumes | `consumes` | | 配置、控制 | `configures` | | 包含、目录下有 | `contains` | | 引用、链接到 | `references` | | 替换、迁移到 | `replaces` | | 属于 | `belongs_to` | 实现可以先内置有限谓词表,再允许保留 `custom_predicate`,但 UI 与关系页要展示规范谓词。 ## 10. 同一知识库内的实体合并 实体合并范围只在一个目标知识库内生效,不跨知识库合并。 规范实体 key: ```text kb_mapping_id + entity_type + normalized_name ``` `normalized_name` 规则: - Unicode NFKC 归一化。 - trim。 - 多空白折叠成单空格。 - ASCII 字母转小写。 - 中英文标点归一化。 - 去除无意义包裹符号,例如反引号、引号。 如果实体带有明确外部 ID,可优先使用: ```text kb_mapping_id + entity_type + external_id_type + external_id ``` 合并规则: - `normalized_name` 相同且 `entity_type` 兼容:合并。 - 名称相同但类型不兼容:不合并,例如 `config:web_search` 和 `tool:web_search` 可保持两个实体,必要时用 `configures` 关系连接。 - LLM 判断别名合并:只有 `confidence >= 0.90` 且有明确证据时允许。 - 属性冲突不做最后写入覆盖,必须按来源保留 provenance。 实体贡献示例: ```json { "canonical_entity_id": "ce_123", "binding_id": "bind_001", "snapshot_id": "snap_001", "local_entity_id": "ent_001", "attributes": { "entrypoint": { "value": "config.yaml tools.web_search", "evidence": "config.yaml:10" } }, "confidence": 0.94 } ``` 删除贡献时: - 只删除该 binding 的贡献。 - 如果规范实体仍有其他贡献,保留实体页并重新生成聚合内容。 - 如果规范实体贡献数为 0,删除或标记归档该规范实体及其 Wiki 页面。 ## 11. 关系合并 规范关系 key: ```text kb_mapping_id + source_canonical_entity_id + normalized_predicate + target_canonical_entity_id ``` 合并规则: - 同一 key 的关系合并为一条规范关系。 - 每个技能的证据作为 relation contribution 追加。 - `confidence` 可存储最大值、平均值和证据数,但展示优先显示最高置信证据。 - 如果来源实体或目标实体被删除到无贡献,相关关系也需要重新计算。 关系 Wiki 页面内容必须保留: - 关系三元组。 - 关系说明。 - 贡献技能列表。 - 证据路径。 - 置信度。 - 最近更新时间。 ## 12. WeKnora 内容投影 每个技能归纳到一个目标知识库后,按目标 adapter 写入三类内容。目标是 WeKnora 时写入 WeKnora;目标是助手知识库或只读 sidecar 时写入 DeerFlow 自研存储。 ### 12.1 原始资料 原始资料分两种: - 可直接上传的文件:走 WeKnora 文件上传。 - 纯文本或需要展开的内容:创建手动文档,保留路径和 fenced code。 原始资料命名建议: ```text skills/{skill_name}/source/{relative_path} ``` 文档 metadata 建议: ```json { "source_type": "deerflow_skill", "skill_name": "example-skill", "binding_id": "bind_001", "snapshot_id": "snap_001", "source_digest": "sha256:...", "relative_path": "SKILL.md", "artifact_kind": "raw_source" } ``` 如果 WeKnora 不支持自定义 metadata,则本地 artifact 表必须保存远端 ID、slug、digest、kind 和来源关系。 ### 12.2 技能 Wiki 页面 每个技能至少生成: ```text skills/{skill_name}/overview skills/{skill_name}/files skills/{skill_name}/entities skills/{skill_name}/relations skills/{skill_name}/evidence ``` 内容要求: - `overview`:技能用途、输入、输出、依赖、使用方式、风险提示。 - `files`:重要文件清单、每个文件摘要、关键入口。 - `entities`:该技能贡献的实体,使用 Wiki 链接指向规范实体页。 - `relations`:确定关系和高置信推断关系,使用 Wiki 链接指向规范关系页。 - `evidence`:证据目录,只包含短摘录和位置,不包含敏感内容。 ### 12.3 规范实体 Wiki 页面 规范实体页 slug: ```text entities/{entity_type}/{stable_entity_id}-{slugified_name} ``` 页面内容: - 实体名称和类型。 - 别名。 - 归并来源技能。 - 聚合描述。 - 属性表,冲突属性按来源列出。 - 相关关系列表。 - 证据索引。 ### 12.4 规范关系 Wiki 页面 规范关系页 slug: ```text relations/{predicate}/{stable_relation_id} ``` 页面内容: - `[[source_entity]] --predicate--> [[target_entity]]` - 中文说明。 - 贡献技能。 - 证据。 - 置信度。 - 更新时间。 说明:即便 WeKnora Wiki graph 只能展示无类型链接,关系类型也不能丢。类型和证据由关系页保存,Wiki 链接负责把实体页和关系页连接起来。 ### 12.5 图谱投影文档 为提高 WeKnora GraphRAG 抽取成功率,每个 binding 生成一份机器可读关系文档: ```text skills/{skill_name}/graph-projection.jsonl ``` 每行一个关系: ```json { "source": "web_search", "source_type": "tool", "predicate": "used_by", "target": "example-skill", "target_type": "skill", "description": "web_search 被 example-skill 用于首步检索", "confidence": 0.88, "evidence": "SKILL.md:22-28" } ``` 同时生成一份人类可读 Markdown: ```text skills/{skill_name}/graph-projection.md ``` 该文档使用短句和固定模板,降低 WeKnora 抽取歧义: ```markdown # 技能关系投影:example-skill - 实体 `web_search`(tool)与实体 `example-skill`(skill)存在 `used_by` 关系。 - 说明:web_search 被 example-skill 用于首步检索。 - 证据:SKILL.md:22-28 - 置信度:0.88 ``` GraphRAG 可用时上传这些投影文档;Neo4j direct writer 开启时,同时把通过审核的规范实体和关系写入底层图数据库;两者都不可用时仍保留 Wiki 关系页和本地关系表。 ## 13. 重归纳一致性 重归纳必须满足“成功后替换,失败不影响旧版本”。 流程: 1. 读取当前 binding 和 active snapshot。 2. 重新扫描技能目录,生成新 snapshot。 3. 如果 digest 未变化且未强制重归纳,可以直接返回 `synced`。 4. 在本地创建 staged artifact 记录。 5. 写入 WeKnora 新版本原始资料、Wiki 页面、关系投影文档。 6. 校验远端可读取或状态完成。 7. 在一个数据库事务中: - 将旧 artifact 标记为 `retired`。 - 将 staged artifact 标记为 `active`。 - 将 binding 的 `current_snapshot_id` 指向新 snapshot。 - 更新实体和关系贡献。 - 设置 binding 状态为 `synced` 或 `partial_failed`。 8. 对旧远端文件执行 best-effort 清理。 9. 清理失败只记录 warning,不回滚已经激活的新版本。 如果第 5 或第 6 步失败: - binding 仍指向旧 snapshot。 - 旧 WeKnora 内容继续有效。 - staged artifact 标记为 `failed`。 - item 状态为 `failed` 或 `partial_failed`。 - 前端显示错误,允许 retry / 重新归纳。 Wiki 页面建议采用稳定 slug upsert。原始资料如果 WeKnora 没有 update API,则创建新远端文档,确认成功后再删除旧远端文档。 ## 14. 解除关联 解除一个技能与一个知识库的绑定时,前端必须弹窗让管理员选择: | 选项 | 行为 | | --- | --- | | 保留知识库内容 | 绑定标记为 `detached_keep`。WeKnora 远端内容、助手库 Wiki revision 或 sidecar 内容保留为冻结知识,不再随技能重归纳更新。技能页不再显示为“当前归纳”,但可在历史中显示“已解除,内容保留”。 | | 删除该技能贡献的内容 | 删除该 binding 的原始资料、技能 Wiki 页、关系投影文档、助手库 Wiki revision contribution、sidecar artifact 和实体/关系贡献;共享实体/关系只减少该技能贡献,仍有其他贡献则重建聚合页,没有贡献才删除。 | 如果删除远端内容部分失败: - 本地记录 artifact `delete_failed`。 - binding 状态为 `detached_delete_pending_cleanup`。 - 提供“重试清理”动作。 不要因为某个共享实体由该技能参与过,就删除其他技能还在使用的实体页。 ## 15. 状态机 绑定状态建议: | 状态 | 含义 | | --- | --- | | `pending` | 已创建绑定,但尚未开始处理。 | | `queued` | 已进入任务队列。 | | `running` | 正在处理。 | | `awaiting_review` | 抽取完成,等待管理员审核实体、关系或 Wiki 草稿。 | | `synced` | 当前版本已成功归纳到目标知识库。 | | `pending_remote_write` | 只读目标已完成本地 sidecar 归纳,等待远端具备写权限后推送。 | | `stale` | 技能源文件已变化,但尚未手动重新归纳。 | | `partial_failed` | 核心 Wiki/原始资料成功,但部分文件、图谱或清理失败。 | | `failed` | 当前归纳失败,旧版本若存在则继续有效。 | | `detached_keep` | 已解除关联,远端内容保留。 | | `detached_delete_pending_cleanup` | 已解除关联,但删除远端内容未完全成功。 | | `detached_deleted` | 已解除关联,远端贡献已清理。 | item 阶段建议: ```text scanning parsing summarizing extracting awaiting_review merging uploading_sources writing_wiki writing_graph_projection verifying activating cleanup completed ``` 前端展示时,父任务进度由 item 数量和阶段加权计算,不要只显示一个不可解释的 spinner。 ## 16. 持久化设计 下面是建议表结构,字段命名可按项目现有风格调整。 ### 16.1 `skill_knowledge_bindings` 保存技能与知识库的多对多关系。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `skill_name` | 技能名。 | | `target_type` | `weknora` / `assistant`。 | | `target_mode` | `wiki` / `document` / `faq` / `conversation_archive` / `readonly_sidecar`。 | | `target_id` | 目标知识库 ID。WeKnora 使用本地 mapping ID,助手库使用全局 base ID。 | | `remote_business_key` | 暴露给前端和外部系统的远端业务 key,例如 `weknora:kb:xxx`,可为空。 | | `status` | 绑定状态。 | | `active` | 是否当前有效绑定。 | | `current_snapshot_id` | 当前激活的技能快照。 | | `source_digest` | 最近扫描到的技能源 digest。 | | `synced_digest` | 最近成功归纳的 digest。 | | `last_job_id` | 最近一次父任务。 | | `last_item_id` | 最近一次 item。 | | `last_synced_at` | 最近成功归纳时间。 | | `last_error_code` | 最近错误码。 | | `last_error_message` | 最近错误摘要。 | | `created_by` | 管理员用户。 | | `created_at` / `updated_at` | 时间戳。 | 约束: ```text unique(skill_name, target_type, target_id) ``` ### 16.2 `skill_knowledge_snapshots` 保存技能扫描和抽取结果。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `skill_name` | 技能名。 | | `source_digest` | 技能目录 digest。 | | `extractor_version` | 抽取器版本。 | | `prompt_hash` | 提示词 hash。 | | `model_name` | 模型名。 | | `manifest_json` | 文件清单、hash、安全状态。 | | `ir_json` | 抽取后的 IR;较大时可存 blob 引用。 | | `status` | `ready` / `failed`。 | | `counts_json` | 文件数、实体数、关系数、跳过数。 | | `created_at` | 创建时间。 | 约束: ```text unique(skill_name, source_digest, extractor_version, prompt_hash, model_name) ``` ### 16.3 `skill_knowledge_sync_jobs` 保存一次批量归纳任务。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `target_count` | 本次目标知识库数量。 | | `targets_json` | 目标知识库列表,每项包含 `target_type`、`target_mode`、`target_id`、展示名和远端业务 key。 | | `status` | `queued` / `running` / `completed` / `partial_failed` / `failed` / `canceled`。 | | `total_count` | 技能数。 | | `success_count` | 成功数。 | | `partial_failed_count` | 部分失败数。 | | `failed_count` | 失败数。 | | `cancel_requested` | 是否请求取消。 | | `created_by` | 管理员用户。 | | `created_at` / `started_at` / `completed_at` | 时间戳。 | ### 16.4 `skill_knowledge_sync_items` 保存每个技能的任务项。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `job_id` | 父任务。 | | `binding_id` | 绑定。 | | `target_type` | 目标类型。 | | `target_mode` | 目标模式。 | | `target_id` | 目标 ID。 | | `skill_name` | 技能名。 | | `snapshot_id` | 本次输入快照。 | | `status` | item 状态。 | | `phase` | 当前阶段。 | | `progress` | 0-100。 | | `active_dedupe_key` | 活跃任务去重 key。 | | `lease_owner` | 执行器标识。 | | `lease_until` | 租约过期时间。 | | `attempts` | 尝试次数。 | | `last_error_code` | 错误码。 | | `last_error_message` | 错误摘要。 | | `created_at` / `updated_at` / `started_at` / `completed_at` | 时间戳。 | 活跃去重 key: ```text skill_name + target_type + target_id ``` 同一绑定同一时间只能有一个 active item。 ### 16.5 `skill_knowledge_artifacts` 保存本地到 WeKnora 的远端 artifact 映射。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `target_type` | 目标类型。 | | `target_mode` | 目标模式。 | | `target_id` | 目标 ID。 | | `binding_id` | 绑定,可为空;规范实体/关系聚合页可只属于知识库。 | | `snapshot_id` | 来源快照。 | | `scope` | `binding` / `kb_aggregate`。 | | `kind` | `raw_source` / `skill_wiki` / `entity_wiki` / `relation_wiki` / `graph_projection`。 | | `stable_key` | 本地稳定 key。 | | `remote_kind` | `file` / `manual_document` / `wiki_page`。 | | `remote_id` | 远端 ID。后端保存完整映射,前端可按权限展示和复制。 | | `wiki_slug` | Wiki 页面 slug。 | | `content_hash` | 写入内容 hash。 | | `version` | 版本号。 | | `status` | `staged` / `active` / `retired` / `failed` / `delete_failed`。 | | `created_at` / `updated_at` | 时间戳。 | ### 16.6 `skill_knowledge_entities` 规范实体表。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `target_type` | 目标类型。 | | `target_mode` | 目标模式。 | | `target_id` | 目标 ID。 | | `entity_type` | 实体类型。 | | `normalized_name` | 归一化名称。 | | `display_name` | 展示名称。 | | `aliases_json` | 别名。 | | `description` | 聚合描述。 | | `stable_slug` | Wiki slug。 | | `status` | `active` / `retired`。 | | `created_at` / `updated_at` | 时间戳。 | 约束: ```text unique(target_type, target_id, entity_type, normalized_name) ``` ### 16.7 `skill_knowledge_entity_contributions` 规范实体的技能贡献。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `canonical_entity_id` | 规范实体 ID。 | | `binding_id` | 绑定。 | | `snapshot_id` | 快照。 | | `local_entity_id` | IR 中的实体 ID。 | | `attributes_json` | 该技能贡献的属性。 | | `evidence_json` | 证据。 | | `confidence` | 置信度。 | | `status` | `active` / `retired`。 | | `created_at` / `updated_at` | 时间戳。 | ### 16.8 `skill_knowledge_relations` 规范关系表。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `target_type` | 目标类型。 | | `target_mode` | 目标模式。 | | `target_id` | 目标 ID。 | | `source_entity_id` | 源规范实体。 | | `predicate` | 规范谓词。 | | `target_entity_id` | 目标规范实体。 | | `description` | 聚合说明。 | | `stable_slug` | Wiki slug。 | | `status` | `active` / `retired`。 | | `created_at` / `updated_at` | 时间戳。 | 约束: ```text unique(target_type, target_id, source_entity_id, predicate, target_entity_id) ``` ### 16.9 `skill_knowledge_relation_contributions` 规范关系的技能贡献。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `canonical_relation_id` | 规范关系 ID。 | | `binding_id` | 绑定。 | | `snapshot_id` | 快照。 | | `local_relation_id` | IR 中的关系 ID。 | | `relation_source` | `deterministic` / `explicit_text` / `llm_inferred`。 | | `evidence_json` | 证据。 | | `confidence` | 置信度。 | | `status` | `active` / `retired`。 | | `created_at` / `updated_at` | 时间戳。 | ## 17. 后台任务设计 参考现有 deep research job 的持久化后台任务模式: - 任务入库后返回 `202 Accepted`。 - 执行器通过 DB 原子条件 claim item。 - 每个 item 有 `lease_owner`、`lease_until`。 - 执行中定期 heartbeat。 - 服务重启后可恢复过期 lease。 - cancel 使用 `cancel_requested`,任务在阶段边界检查并安全停止。 并发建议: - 同时处理技能数:2 到 4。 - 同时调用 WeKnora 写入:2 到 3。 - 同时 LLM 抽取:按系统现有限流,建议默认 2。 200 个技能批量时: - 父 job 只保存聚合进度,不串行阻塞 HTTP 请求。 - 每个技能 item 独立失败、独立重试。 - 前端按 job polling,不为每个技能单独发 N 个请求。 - 获取技能列表时批量查 binding summary,避免 N+1。 - 快照缓存可复用,跨知识库只重新投影,不重复解析同一技能内容。 ## 18. 后端 API 设计 新增 router: ```text /api/skill-knowledge ``` 所有写接口仅管理员可用。 ### 18.1 获取技能绑定摘要 ```http GET /api/skill-knowledge/bindings?skill_names=a,b,c ``` 返回: ```json { "items": [ { "skill_name": "example-skill", "knowledge_bases": [ { "binding_id": "bind_001", "target_type": "weknora", "target_mode": "wiki", "target_id": "local-kb-id", "knowledge_base_name": "作战知识库", "remote_business_key": "weknora:kb:remote-kb-id", "status": "synced", "last_synced_at": "2026-08-31T10:00:00Z", "source_digest": "sha256:...", "synced_digest": "sha256:...", "stale": false } ] } ] } ``` ### 18.2 创建归纳任务 ```http POST /api/skill-knowledge/sync-jobs ``` 请求: ```json { "skill_names": ["skill-a", "skill-b"], "targets": [ { "target_type": "weknora", "target_mode": "wiki", "target_id": "local-kb-id" }, { "target_type": "assistant", "target_mode": "wiki", "target_id": "assistant-global-base" } ], "force": false } ``` 行为: - 校验管理员。 - 校验技能存在。 - 校验目标知识库存在,并解析目标 adapter。 - 对只读目标创建 sidecar binding,不强行写远端。 - 对每个 `skill x target` upsert binding。 - 创建父 job 和 item。 - 如果同一 binding 已有 active item,返回当前 item,不重复创建。 - 返回 `202`。 返回: ```json { "job_id": "job_001", "status": "queued", "skill_count": 2, "target_count": 2, "total_count": 4, "items": [ { "item_id": "item_001", "skill_name": "skill-a", "target_type": "weknora", "target_mode": "wiki", "target_id": "local-kb-id", "binding_id": "bind_001", "status": "queued" } ] } ``` ### 18.3 查询任务 ```http GET /api/skill-knowledge/sync-jobs/{job_id} ``` 返回父 job、item 列表、每个 item 当前阶段、错误摘要、绑定结果。 ### 18.4 取消任务 ```http POST /api/skill-knowledge/sync-jobs/{job_id}/cancel ``` 设置 `cancel_requested = true`。已激活成功的 item 不回滚;未完成 item 在阶段边界停止。 ### 18.5 重试 item ```http POST /api/skill-knowledge/sync-items/{item_id}/retry ``` 只允许失败或部分失败 item。重试复用原 binding。 ### 18.6 对单个绑定重新归纳 ```http POST /api/skill-knowledge/bindings/{binding_id}/resync ``` 请求: ```json { "force": true } ``` 行为:创建一个只包含该 binding 的新 job。 ### 18.7 解除关联 建议使用显式 POST,避免 DELETE body 兼容性问题: ```http POST /api/skill-knowledge/bindings/{binding_id}/detach ``` 请求: ```json { "remote_content_action": "keep" } ``` 或: ```json { "remote_content_action": "delete" } ``` 返回解除结果、清理状态和仍被共享的实体/关系数量。 ### 18.8 知识库反查技能 ```http GET /api/skill-knowledge/knowledge-bases/{knowledge_base_id}/skills ``` 用于知识库详情页展示“该知识库由哪些技能归纳而来”。 ### 18.9 可选:源状态扫描 ```http POST /api/skill-knowledge/source-status ``` 请求: ```json { "skill_names": ["skill-a", "skill-b"], "knowledge_base_id": "local-kb-id" } ``` 只做本地快速 fingerprint 比对,标记是否可能 stale,不写 WeKnora。 ### 18.10 审核队列 ```http GET /api/skill-knowledge/reviews?job_id=xxx POST /api/skill-knowledge/reviews/{review_id}/approve POST /api/skill-knowledge/reviews/{review_id}/reject PUT /api/skill-knowledge/reviews/{review_id} POST /api/skill-knowledge/reviews/bulk-approve POST /api/skill-knowledge/reviews/bulk-reject ``` 审核通过后 item 才能进入写入阶段。审核模式为 `off` 时,接口仍保留预览和审计能力。 ### 18.11 自动同步配置 ```http GET /api/skill-knowledge/auto-sync-settings PUT /api/skill-knowledge/auto-sync-settings POST /api/skill-knowledge/bindings/{binding_id}/enable-auto-sync POST /api/skill-knowledge/bindings/{binding_id}/disable-auto-sync ``` 自动同步设置只允许管理员修改。 ## 19. 前端交互设计 ### 19.1 技能管理页 在 `AdminSkillsPage.tsx` 的“技能列表”页签增加: - 技能行 checkbox。 - 顶部批量操作按钮:“归纳到知识库”。 - 支持全选当前页、清空选择。 - 选择状态跨前端分页保留。 - 技能较多时保持现有搜索/过滤能力。 点击“归纳到知识库”打开弹窗: - 显示已选技能数量。 - 多选目标知识库。 - 按类型分组展示:WeKnora Wiki、WeKnora 非 Wiki、FAQ、系统会话存档、只读 sidecar、助手知识库。 - 只读目标要展示“本地归纳,暂不写远端”的状态说明。 - 助手知识库作为目标时,提示“直接写入知识梳理底库,不经过 WeKnora”。 - 二次确认文案说明:“将生成原始资料、Wiki 页面和关系内容;抽取完成后进入实体/关系预览审核。” 提交后: - 创建后台 job。 - 跳转或打开“归纳任务”抽屉/页签。 - 展示总进度、成功、失败、部分失败。 - 任务运行中每 2-3 秒 polling。 - item 到达 `awaiting_review` 时,显示“待审核”并提供审核入口。 ### 19.1.1 实体/关系审核页 审核页按 job 聚合展示: - 左侧筛选:技能、目标知识库、类型、置信度、来源。 - 中间列表:实体、关系、实体对齐候选、关系合并候选、Wiki 草稿。 - 右侧详情:原始值、模型解释、证据、目标写入位置。 管理员可以: - 单条批准、拒绝、编辑。 - 批量批准高置信结果。 - 批量拒绝低置信结果。 - 修改实体类型、名称、别名。 - 修改关系 predicate、说明、confidence。 - 查看远端 ID 或 sidecar ID。 审核完成后,任务继续写入目标知识库。 ### 19.2 技能行提示 `SkillRow` 增加归纳状态区域: - 未归纳:不显示或显示弱提示“未归纳”。 - 已归纳:显示 `已归纳至 N 个知识库`。 - 展开或悬浮可看到知识库 chips: - 知识库名称 - 状态 - 最近归纳时间 - stale 标识 每个绑定提供动作: - 查看知识库。 - 重新归纳。 - 解除关联。 - 失败时重试。 状态建议: | 状态 | UI 文案 | | --- | --- | | `synced` | 已归纳 | | `stale` | 待重新归纳 | | `running` | 归纳中 | | `partial_failed` | 部分完成 | | `failed` | 失败 | | `detached_keep` | 已解除,内容保留 | ### 19.3 解除关联弹窗 弹窗必须让管理员明确选择: - 保留知识库内容。 - 删除该技能贡献的内容。 弹窗文案应提醒: - 删除只删除该技能贡献,不删除其他技能共享的实体与关系。 - 若删除远端内容失败,可稍后重试清理。 ### 19.4 归纳任务页签 建议在管理员技能管理页新增第三个页签: ```text 技能列表 | 技能地址管理 | 归纳任务 ``` 任务列表显示: - 创建时间。 - 目标知识库。 - 操作人。 - 总技能数。 - 成功 / 部分失败 / 失败。 - 状态。 - 操作:查看详情、取消、重试失败项。 详情显示 item: - 技能名。 - 目标知识库。 - 目标类型。 - 当前阶段。 - 进度。 - 错误摘要。 - 绑定状态。 ### 19.6 自动同步设置 管理员可在技能归纳设置中配置: - 是否开启文件监听。 - 是否开启定时同步。 - 同步周期。 - 自动写远端开关。 - 审核模式。 - 单个 binding 是否参与自动同步。 ### 19.5 知识库详情反向展示 如果现有 WeKnora / LLM Wiki 页面有知识库详情入口,增加“来源技能”区域: - 技能名。 - 归纳状态。 - 最近归纳时间。 - 查看技能。 - 重新归纳。 ## 20. stale 检测与自动同步 系统既要支持手动重新归纳,也要支持自动同步。stale 检测负责判断“源知识文件是否变化”;自动同步负责在配置允许时创建后台任务并远程写入。 本地标识 stale: - 技能编辑、启停、删除等已知写操作完成后,查找相关 binding 并标记 `stale`。 - 技能列表加载时可做轻量 fingerprint:路径、大小、mtime。 - 真正归纳时计算完整 SHA digest。 - 如果归纳过程中源文件变化,最终激活前再次校验 expected digest;不一致则失败并标记 stale,避免一个版本混入两次源内容。 - 代码文件和脚本文件不参与 fingerprint,也不触发 stale。 自动同步: - 当 `watch_enabled` 开启时,知识文件变化后自动创建相关 binding 的归纳任务。 - 当 `schedule_enabled` 开启时,scheduler 按周期扫描并归纳 stale binding。 - 当技能保存、导入、删除或启停时,可触发相关 binding 的自动任务。 - 自动任务必须遵循审核模式;如果需要审核,任务停在 `awaiting_review`。 - 自动任务必须遵循 staged 激活;失败不覆盖旧版本。 ## 21. 权限与安全 权限: - 所有归纳、重归纳、解除关联、取消、重试接口仅管理员。 - 读接口如果包含后台任务错误或源文件路径,也建议仅管理员。 文件安全: - 技能根必须限制在 `skills/public` 或 `skills/custom`。 - 禁止路径穿越。 - 默认不跟随 symlink;如果要支持 symlink,必须确认 resolved path 仍在技能根内。 - 压缩包解压必须限制总大小、文件数量、单文件大小、目录层级。 - 不执行技能代码。 - 不读取或上传密钥文件。 - 日志和错误信息不得包含文件全文、token、secret。 远端安全: - 不新增外部 URL 抓取能力。 - 如果技能内容中包含 URL,只作为文本关系或知识内容处理,不主动访问。 - WeKnora 调用失败时,记录错误摘要和 request id,不记录认证头。 ## 22. LLM 抽取设计 抽取可分两级: - 小技能:一次性把文本摘要和结构信息送入模型。 - 大技能:先按文件或章节生成局部摘要与候选实体关系,再合并。 提示词需要明确: - 只基于提供的内容抽取,不凭空编造。 - 允许推断,但推断必须有 evidence。 - 输出必须符合 JSON schema。 - 不输出敏感信息。 - 关系必须有 confidence。 - 低置信关系要标记,不进入规范关系。 建议 schema: ```json { "summary": { "purpose": "技能用途", "inputs": ["输入"], "outputs": ["输出"], "usage": "使用方式", "risks": ["风险"] }, "entities": [ { "id": "ent_001", "type": "skill|tool|api|file|config|dataset|concept|workflow|external_system|output", "name": "实体名", "aliases": [], "description": "描述", "attributes": {}, "evidence": [ { "file_path": "SKILL.md", "line_start": 1, "line_end": 3, "quote": "短证据" } ], "confidence": 0.9 } ], "relations": [ { "id": "rel_001", "source_entity_id": "ent_001", "predicate": "uses", "target_entity_id": "ent_002", "description": "关系描述", "relation_source": "explicit_text|llm_inferred", "evidence": [], "confidence": 0.86 } ] } ``` 模型输出后必须做程序校验: - JSON schema 校验。 - entity id 存在性校验。 - relation source/target 必须存在。 - evidence path 必须来自本次 manifest。 - confidence 必须在 0 到 1。 - 敏感内容二次扫描。 ## 23. 后端实现步骤 建议按以下顺序实现。 ### 23.1 持久化层 新增 `deerflow.persistence.skill_knowledge`: - SQL model。 - memory store,用于测试或无 DB 环境。 - repository API。 - migration 或 `create_all` 接入。 Repository API 建议: ```python class SkillKnowledgeStore: async def upsert_binding(...) async def list_bindings(...) async def create_job(...) async def create_items(...) async def claim_next_item(...) async def heartbeat_item(...) async def complete_item(...) async def fail_item(...) async def create_or_get_snapshot(...) async def stage_artifacts(...) async def activate_snapshot(...) async def detach_binding(...) ``` ### 23.2 扫描与解析 新增: ```text deerflow/skill_knowledge/scanner.py deerflow/skill_knowledge/parsers.py deerflow/skill_knowledge/safety.py deerflow/skill_knowledge/digest.py ``` 输出 manifest 和初始文本块。 ### 23.3 抽取与归纳 新增: ```text deerflow/skill_knowledge/extractor.py deerflow/skill_knowledge/wiki_builder.py ``` 职责: - 构造 prompt。 - 调用项目已有模型封装。 - 解析 JSON。 - 校验 schema。 - 构建技能 Wiki 页面内容。 ### 23.4 实体关系合并 新增: ```text deerflow/skill_knowledge/merge.py ``` 职责: - 名称归一化。 - 实体 upsert。 - 关系 upsert。 - 贡献增删。 - 共享实体/关系重建计划。 ### 23.5 WeKnora 投影 新增: ```text deerflow/skill_knowledge/projector.py deerflow/skill_knowledge/target_adapters.py deerflow/skill_knowledge/graph_writer.py ``` 职责: - 根据 IR 和聚合结果生成 artifact plan。 - 按目标知识库类型选择 adapter。 - 调用 WeKnora client 上传原文、FAQ、会话归档或 Wiki 页面。 - 为只读目标写本地 sidecar artifact。 - 上传 graph projection docs。 - 在配置允许时通过受控 graph writer 写 Neo4j。 - 校验远端状态。 ### 23.6 Gateway 新增: ```text app/gateway/routers/skill_knowledge.py app/gateway/skill_knowledge_job_dispatcher.py app/gateway/skill_knowledge_job_executor.py app/gateway/skill_knowledge_watcher.py app/gateway/skill_knowledge_scheduler.py ``` 在 app 初始化中注册: - store。 - dispatcher。 - watcher。 - scheduler。 - router。 ### 23.7 前端 新增: ```text frontend-web/src/strategy-components/api/skill-knowledge.ts frontend-web/src/components/workspace/skills/skill-knowledge-sync-dialog.tsx frontend-web/src/components/workspace/skills/skill-knowledge-bindings.tsx frontend-web/src/components/workspace/skills/skill-knowledge-jobs-panel.tsx ``` 修改: - `AdminSkillsPage.tsx`:技能列表批量选择、归纳弹窗、归纳任务页签。 - `skill-settings-page.tsx`:`SkillRow` 展示归纳知识库 chips 和操作。 - 知识库详情页:展示来源技能。 ### 23.8 助手知识库 新增: ```text offline-backend-20260512/backend/packages/harness/deerflow/assistant_knowledge/ offline-backend-20260512/backend/packages/harness/deerflow/persistence/assistant_knowledge/ offline-backend-20260512/backend/app/gateway/routers/assistant_knowledge.py offline-backend-20260512/backend/app/gateway/assistant_knowledge_import_executor.py frontend-web/src/strategy-components/api/assistant-knowledge.ts frontend-web/src/pages/AssistantKnowledgePage.tsx ``` 职责: - 初始化唯一全局助手知识库“知识梳理”。 - 从 WeKnora 导出包导入 Wiki、文档、chunk、实体、关系、图谱。 - 做全局实体对齐和关系合并。 - 生成助手库 Wiki 页面。 - 建立搜索索引。 - 支持普通用户只读、管理员导入和维护。 - 支持知识库产品版本 `v1` / `v2` 切换。 ### 23.9 审核队列 新增: ```text offline-backend-20260512/backend/packages/harness/deerflow/persistence/skill_knowledge/review_model.py frontend-web/src/components/workspace/skills/skill-knowledge-review-panel.tsx ``` 职责: - 保存实体、关系、对齐候选、Wiki 草稿的审核项。 - 支持批准、拒绝、编辑、批量处理。 - 审核完成后唤醒对应 item 继续写入。 - 保留完整审计。 ## 24. 测试计划 后端测试: | 测试 | 覆盖点 | | --- | --- | | `test_skill_knowledge_scanner.py` | 文件扫描、排除目录、敏感文件阻断、压缩包安全。 | | `test_skill_knowledge_extractor.py` | LLM 输出 schema 校验、低置信关系过滤、证据路径校验。 | | `test_skill_knowledge_entity_merge.py` | 同知识库实体合并、跨知识库不合并、属性冲突 provenance。 | | `test_skill_knowledge_repository.py` | 绑定唯一、多对多、状态机、detach keep/delete。 | | `test_skill_knowledge_jobs.py` | job/item claim、lease、重试、取消、崩溃恢复、200 item 聚合进度。 | | `test_skill_knowledge_weknora_projection.py` | Wiki slug upsert、artifact staged/active、GraphRAG 不可用 partial_failed。 | | `test_skill_knowledge_api.py` | 管理员鉴权、目标知识库校验、创建任务、查询任务、解除关联。 | | `test_skill_knowledge_target_adapters.py` | Wiki、非 Wiki、FAQ、会话存档、只读 sidecar、助手库目标写入。 | | `test_skill_knowledge_review.py` | 实体/关系逐条审核、编辑、批量批准、审核阻塞写入。 | | `test_skill_knowledge_auto_sync.py` | 文件监听、定时同步、代码文件变化不触发、staged 自动写入。 | | `test_skill_knowledge_graph_writer.py` | Neo4j 参数化写入、事务、审计、回滚计划、禁用任意 Cypher。 | | `test_assistant_knowledge_repository.py` | 唯一全局底库、来源、导入任务、实体关系贡献。 | | `test_assistant_knowledge_import.py` | WeKnora 导出包导入、实体对齐、关系合并、搜索索引。 | | `test_assistant_knowledge_api.py` | 普通用户只读、管理员写入、未配置 WeKnora 场景。 | 前端测试: - 管理员技能页可以多选技能并打开知识库选择弹窗。 - 知识库选择弹窗支持多选目标,包含 Wiki、非 Wiki、FAQ、会话存档、只读 sidecar 和助手知识库。 - 创建 job 后显示进度并 polling。 - 抽取后进入实体/关系审核页,审核通过后继续写入。 - 技能行展示已归纳知识库 chips。 - 重归纳按钮调用正确接口。 - 解除关联弹窗必须选择 keep/delete。 - 200 个技能绑定摘要使用批量接口,不触发 N+1。 - 未配置 WeKnora 时仍能展示助手知识库初始化入口。 - 普通知识库详情页可触发导入到助手知识库。 - 助手知识库搜索、Wiki 页面、来源列表按权限展示。 建议命令: ```bash cd offline-backend-20260512/backend PYTHONPATH=. uv run pytest tests/test_skill_knowledge_*.py -v PYTHONPATH=. uv run pytest tests/test_harness_boundary.py -v ``` ```bash cd frontend-web pnpm typecheck pnpm test -- skill-knowledge ``` ## 25. 验收场景 必须通过: - 管理员选择 3 个技能归纳到 2 个目标知识库,生成 1 个 job、6 个 item、6 个 binding。 - 同一个技能随后归纳到另一个知识库,新增第二个 binding,技能行显示已归纳至 2 个知识库。 - 技能可以直接归纳到助手知识库,并生成实体对齐、Wiki 页面版本和搜索索引。 - 同一个技能同一个 digest 重复归纳,不产生重复 Wiki 页面或重复实体关系。 - 两个技能在同一知识库中抽取出同名同类型实体,只生成一个规范实体页,并列出两个贡献技能。 - 解除其中一个技能并选择删除内容后,共享实体仍保留另一个技能贡献。 - 关系必须带 evidence 和 confidence;低于 0.75 的推断关系不进入规范关系。 - 重归纳成功后,新版本替换旧版本;旧 artifact 进入 retired。 - 重归纳失败时,旧版本继续显示为当前版本。 - 解除关联选择保留内容后,远端 Wiki 和文档保留,但 binding 不再参与后续重归纳。 - 非管理员访问写接口返回 403。 - 非 Wiki、FAQ、系统会话存档知识库可作为目标,并通过对应 adapter 写入。 - 只读知识库可作为目标,结果写入本地 sidecar,远端状态显示 `pending_remote_write`。 - 直接 Neo4j 写入只在配置开启时执行,且只使用参数化 graph writer。 - 自动文件监听和定时同步可以创建后台任务,代码文件变化不会触发同步。 - 前端可逐条审核实体/关系,也可按策略批量处理。 - 前端展示 WeKnora 远端 ID,并可复制业务 key。 - WeKnora GraphRAG 不可用时,原始资料和 Wiki 成功,状态为 `partial_failed`,可重试图谱投影。 - 200 个技能批量归纳时,页面显示整体进度,失败技能可单独重试,后端没有 N+1 查询。 - 技能中出现 `.env` 或私钥时,该文件不上传、不进 prompt,页面显示安全跳过。 - 技能中的 `.py`、`.ts`、`.sh` 等代码文件不读取、不上传、不摘要,代码文件变化不触发知识 stale。 - 未配置 WeKnora 时,助手知识库仍可初始化和查看。 - 管理员可从普通知识库触发导入到助手知识库。 - 管理员可向助手知识库导入本地文件,安全扫描通过后生成 Wiki 页面版本。 - WeKnora 导出包中的 Wiki、文档、chunk、实体、关系、图谱都能进入助手底库。 - 助手底库把多个来源中的同一人或同一对象合并为一个实体 Wiki 页。 - 助手 Wiki 支持历史版本、对比和回滚。 - 助手知识库普通用户只读,管理员可导入、重导、删除来源。 ## 26. 分阶段交付 第一阶段:数据模型与 API 骨架 - 建表和 store。 - 绑定列表、创建 job、查询 job、detach API。 - 管理员鉴权。 - 前端技能行能展示空绑定状态。 第二阶段:扫描、快照与后台任务 - 技能目录扫描。 - 文件 digest 和 manifest。 - job/item 执行器、lease、heartbeat、失败重试。 - 200 技能批量进度。 第三阶段:Wiki 归纳与原始资料上传 - LLM 摘要和 Wiki 页面生成。 - WeKnora 原文上传。 - Wiki page upsert。 - staged/active 替换。 第四阶段:实体关系合并与关系投影 - 规范实体和关系表。 - contribution 机制。 - 关系 Wiki 页面。 - graph projection docs。 - GraphRAG 能力探测和 partial_failed。 第五阶段:完整前端体验 - 多选技能归纳弹窗。 - 归纳任务页签。 - 技能行 chips、详情、重归纳、解除关联。 - 知识库详情来源技能反查。 第六阶段:安全、测试、文档收口 - 敏感文件阻断。 - 压缩包安全。 - 崩溃恢复。 - README / CLAUDE 更新。 - 后端和前端测试。 第七阶段:助手知识库与产品版本 - 新增唯一全局助手知识库“知识梳理”。 - 新增 WeKnora 导出到助手库导入任务。 - 新增技能直接导入助手知识库。 - 新增管理员本地文件导入助手知识库。 - 新增助手库实体对齐、关系合并和 Wiki 展示。 - 新增助手库搜索。 - 新增助手库 Wiki 页面版本管理、对比和回滚。 - 新增知识库产品版本 `v1` / `v2` 切换。 - 处理未配置 WeKnora 时的助手库默认展示。 第八阶段:多目标、审核、自动同步和底层图写入 - 支持一次选择多个目标知识库。 - 支持非 Wiki、FAQ、系统会话存档和只读目标 adapter。 - 支持实体/关系逐条审核。 - 支持文件监听、定时同步、技能变更自动远程写入。 - 支持受控 Neo4j direct writer。 - 支持远端 WeKnora ID 作为业务 key 暴露给前端。 ## 27. 给实现大模型的硬性指令 实现时必须遵守: - 先完整阅读根目录 `AGENTS.md`、后端 `AGENTS.md`、后端 `CLAUDE.md`。 - 不要回滚用户已有改动。 - 支持直接写 WeKnora 的 Neo4j,但必须通过受控 graph writer、显式配置、参数化模板、事务和审计。 - 支持把远端 WeKnora ID 作为前端业务 key 暴露,但本地仍保留 surrogate id。 - 不要让 `deerflow.*` import `app.*`。 - 不要执行技能里的代码。 - 不要读取、上传、摘要或展示技能里的代码文件、脚本文件和代码块内容。 - 不要上传或提示词输入密钥文件。 - 技能可以直接归纳到助手知识库,必须走同样的安全扫描、审核和实体对齐。 - 不要创建多个助手知识库;助手知识库只有一个全局“知识梳理”底库。 - 自动 watcher、定时同步和技能变更远程写入必须使用 staged 激活、去重、审核和失败保留旧版本。 - 重归纳必须 staged 写入,成功后再激活。 - 删除绑定贡献时必须按 contribution refcount 处理共享实体和关系。 - 助手知识库需要 Wiki 页面版本管理、版本对比、回滚和导入任务审计。 - 直接写 Neo4j 必须通过受控 graph writer 和参数化模板,不允许执行 LLM 生成的任意 Cypher。 - 远端 WeKnora ID 可以作为前端业务 key 暴露,但本地仍要保留 surrogate id 和 mapping。 - 后端代码变更后按仓库要求更新相关 README / CLAUDE。 - 涉及文件编辑使用 `apply_patch` 或项目正常格式化工具,不做无关重构。 ## 28. 实现任务清单 - [ ] 新增 `skill_knowledge` 持久化模型和 store。 - [ ] 新增绑定、job、item、artifact、entity、relation、contribution 表。 - [ ] 新增管理员 router `/api/skill-knowledge`。 - [ ] 接入知识库能力校验,支持 Wiki、非 Wiki、FAQ、会话存档、只读 sidecar 和助手知识库。 - [ ] 支持一次选择多个目标知识库,并按 `技能 x 目标` 生成 item。 - [ ] 新增技能扫描器,支持全部非代码知识文件、安全排除和 digest。 - [ ] 技能扫描器完全排除代码文件、脚本文件和 Markdown 代码块内容。 - [ ] 新增 snapshot 缓存。 - [ ] 新增 LLM 抽取器,输出 schema 校验。 - [ ] 新增 Wiki 页面生成器。 - [ ] 新增实体归一化与合并逻辑。 - [ ] 新增关系归一化与合并逻辑。 - [ ] 新增 WeKnora artifact projector。 - [ ] 新增 target adapters。 - [ ] 新增 graph projection 文档生成和上传。 - [ ] 新增受控 Neo4j direct graph writer。 - [ ] 新增实体/关系审核队列。 - [ ] 新增后台 job dispatcher / executor。 - [ ] 新增文件 watcher、定时同步 scheduler、技能变更自动写入触发。 - [ ] 实现 resync 成功替换、失败保留旧版本。 - [ ] 实现 detach keep/delete。 - [ ] 技能变更后标记 binding stale。 - [ ] 前端新增 `skill-knowledge.ts` API。 - [ ] 技能管理页新增多选归纳入口。 - [ ] 新增选择知识库弹窗。 - [ ] 新增归纳任务页签。 - [ ] 技能行展示已归纳知识库状态。 - [ ] 实现重归纳、重试、取消、解除关联 UI。 - [ ] 知识库详情展示来源技能。 - [ ] 新增唯一全局助手知识库“知识梳理”。 - [ ] 新增助手知识库持久化模型、来源、导入任务、Wiki、实体、关系、贡献表。 - [ ] 新增 WeKnora 导出到助手知识库任务。 - [ ] 支持技能直接导入助手知识库。 - [ ] 支持管理员本地文件导入助手知识库。 - [ ] 导出范围覆盖 Wiki、文档、chunk、实体、关系、图谱。 - [ ] 助手知识库实现全局实体对齐和关系合并。 - [ ] 助手知识库生成 Wiki 页面并支持搜索。 - [ ] 助手知识库支持 Wiki 页面版本管理、对比、发布和回滚。 - [ ] 新增知识库产品版本 `v1` / `v2` 设置,默认 `v1`。 - [ ] 未配置 WeKnora 时仍展示助手知识库创建或初始化入口。 - [ ] 助手知识库普通用户只读,管理员可导入、重导、删除。 - [ ] 后端测试覆盖核心状态机和安全边界。 - [ ] 前端测试覆盖主要交互。 - [ ] 更新后端 README / CLAUDE 与必要前端文档。 ## 29. 风险与处理 | 风险 | 处理 | | --- | --- | | WeKnora 图谱 API 在部署版本不稳定 | 以 Swagger 能力探测为准;Wiki 关系页和 graph projection 文档作为稳定方案。 | | LLM 推断关系误判 | 强制 evidence、confidence 阈值、低置信不进入规范关系。 | | 200 技能批量耗时长 | 后台 job、item 并发、snapshot 缓存、前端批量查询。 | | 重归纳中断导致新旧混杂 | staged artifact + 本地事务激活 + 失败保留旧版本。 | | 删除共享实体误删其他技能知识 | contribution refcount,删除只移除该 binding 贡献。 | | 敏感信息进入知识库 | 文件级安全阻断、prompt 前二次扫描、日志脱敏。 | | 技能目录处理阻塞 Gateway | 文件 IO 和转换统一 `asyncio.to_thread`。 | | 技能源文件归纳中变化 | expected digest 校验,不一致则失败并标记 stale。 | ## 30. 新增:普通知识库与助手知识库双体系 本节是基于新需求的补充设计。后续实现以本节为准;如果本节与前文旧的 WeKnora-only 表述冲突,优先采用本节。 ### 30.1 产品形态 系统需要区分两类知识库: | 类型 | 存储 | 用途 | 可见性 | 写入来源 | | --- | --- | --- | --- | --- | | 普通知识库 | WeKnora | 文档向量化、Wiki、检索、图谱、技能归纳目标 | 按现有 WeKnora 逻辑 | 技能归纳、现有 WeKnora 导入 | | 助手知识库 | DeerFlow 自研库 | 全局知识梳理、实体对齐、Wiki 展示、搜索、版本管理 | 所有人可见,普通用户只读 | WeKnora 导出、WeKnora 自动沉淀、技能直接归纳、管理员本地文件导入 | 产品版本开关: - `v1`:默认值,优先展示现有普通知识库体验。 - `v2`:优先展示助手知识库体验。 - 管理员可在系统设置中切换版本。 - 如果未配置 WeKnora,即使当前版本默认是 `v1`,页面也应展示助手知识库创建/初始化入口;WeKnora 相关能力隐藏或置灰,并提示“未配置普通知识库服务”。 建议新增系统设置项: ```json { "knowledge_base_product_version": "v1" } ``` ### 30.2 助手知识库定位 助手知识库只有一个全局底库,建议默认名: ```text 知识梳理 ``` 它不是 WeKnora 的另一个知识库,也不向 WeKnora 写数据。它是 DeerFlow 自己维护的结构化知识底库。 助手知识库对用户展示: - Wiki 页面列表。 - Wiki 页面详情。 - Wiki 页面版本、对比和回滚。 - 实体页。 - 关系页。 - 搜索。 - 数据导入记录。 助手知识库内部可以存实体、关系、贡献、来源、搜索索引,但前端主体验以 Wiki 为中心,不做复杂图数据库管理页。 权限: - 普通用户:可查看、搜索。 - 管理员:可初始化助手知识库、从 WeKnora 导入、重新导入、删除导入来源、编辑或删除 Wiki 页面。 ### 30.3 数据来源 助手知识库的数据来源包括 WeKnora、技能归纳和管理员本地文件。 支持四种触发方式: | 方式 | 说明 | | --- | --- | | 自动沉淀 | WeKnora 普通知识库完成文档向量化、Wiki 生成和图谱抽取后,默认创建一个导入任务,把处理结果沉淀到助手知识库。 | | 手动导入 | 管理员在普通知识库页面点击“导入到助手知识库”或“导出到知识梳理”,本质仍是导出 WeKnora 数据后导入助手库。 | | 技能直接归纳 | 管理员在技能管理页选择助手知识库作为目标,系统直接写入自研助手底库。 | | 本地文件导入 | 管理员在助手知识库页面上传本地文件,系统安全扫描后抽取实体、关系和 Wiki 页面。 | 限制: - 普通用户不能写入助手知识库。 - 本地文件导入必须走敏感信息扫描、文件类型白名单、大小限制和导入审计。 - 技能直接归纳仍然完全排除代码文件、脚本文件和 Markdown 代码块内容。 ### 30.4 WeKnora 导出范围 从 WeKnora 导入助手知识库时,需要尽可能导出全部已处理数据: - 知识库元数据。 - Wiki 页面。 - Wiki 页面之间的链接。 - 已上传文档信息。 - 已向量化完成的 chunk 文本、chunk metadata、来源文档映射。 - WeKnora 可读取的实体。 - WeKnora 可读取的关系。 - WeKnora 可读取的图谱节点和边。 - 技能归纳产生的 source metadata,例如 `skill_name`、`binding_id`、`snapshot_id`。 说明: - 助手知识库不强依赖复用 WeKnora embedding 向量。若 WeKnora 不暴露 embedding 原始向量,导入 chunk 文本和 metadata 即可。 - 如果 WeKnora 不暴露稳定的图谱导出 API,则使用现有 Wiki graph、关系投影文档、Wiki 关系页和可查询接口重建实体关系。 - 导入过程要记录能力缺失:例如“已导入 Wiki 与文档,图谱 API 不可用,关系由 Wiki 关系页重建”。 导出包建议: ```json { "export_id": "wk_export_001", "source": { "type": "weknora", "knowledge_base_id": "local-kb-id", "knowledge_base_name": "作战知识库", "exported_at": "2026-09-01T10:00:00Z" }, "wiki_pages": [], "documents": [], "chunks": [], "entities": [], "relations": [], "graph": { "nodes": [], "edges": [] } } ``` ### 30.5 助手库实体对齐 助手知识库的核心价值是把来自不同 WeKnora 普通知识库、不同技能来源、不同 Wiki 页中的同一实体合并。 实体对齐范围是全局助手库: ```text assistant_base_id + entity_type + normalized_name ``` 如果存在更强 ID,则优先: ```text assistant_base_id + entity_type + external_id_type + external_id ``` 对齐规则: - 确定性同一:证件号、统一社会信用代码、装备编号、系统唯一 ID 等完全一致,直接合并。 - 高置信名称同一:同类型实体 normalized name 一致,合并。 - 模型推断同一:允许模型判断“张三”“张三同志”“Zhang San”是否同一人,但必须给出 evidence 和 confidence。 - `confidence >= 0.90` 的模型对齐可以自动合并。 - `0.70 <= confidence < 0.90` 的模型对齐不自动合并,可记录为候选别名或候选关联。 - `confidence < 0.70` 丢弃。 合并后必须保留来源贡献: - 来自哪个 WeKnora 知识库。 - 来自哪个 Wiki 页面或 chunk。 - 来自哪个技能 binding。 - 证据文本。 - 导入任务 ID。 属性冲突不覆盖: - 不同来源给出不同姓名、职务、时间、地点、数值时,按来源分组展示。 - Wiki 页面中展示“多来源信息”而不是最后写入覆盖。 ### 30.6 助手库关系对齐 关系 key: ```text assistant_base_id + source_assistant_entity_id + normalized_predicate + target_assistant_entity_id ``` 关系来源: - WeKnora 图谱边。 - WeKnora Wiki graph 链接。 - 技能归纳时写入 WeKnora 的关系页。 - graph projection 文档。 - 模型从 WeKnora 导出包中推断的高置信关系。 关系合并后保留多个证据贡献。 关系 Wiki 页面建议: ```text assistant/relations/{predicate}/{stable_relation_id} ``` 关系页内容: - 源实体。 - 关系类型。 - 目标实体。 - 关系描述。 - 来源知识库。 - 来源 Wiki 页。 - 证据和置信度。 ### 30.7 助手库 Wiki 生成 助手库最终以 Wiki 页面展示。 页面类型: | 页面 | slug 示例 | 说明 | | --- | --- | --- | | 首页 | `assistant/home` | 知识梳理总览、实体数量、来源数量、最近导入。 | | 来源页 | `assistant/sources/{source_id}` | 某个 WeKnora 知识库导入结果。 | | 实体页 | `assistant/entities/{type}/{entity_id}-{name}` | 对齐后的全局实体页面。 | | 关系页 | `assistant/relations/{predicate}/{relation_id}` | 对齐后的全局关系页面。 | | 主题页 | `assistant/topics/{topic}` | 从 WeKnora Wiki 分类或目录生成。 | 实体页结构: - 标题:实体名。 - 类型:人物、组织、地点、装备、事件、概念、数据对象等。 - 摘要:模型基于多来源融合生成。 - 别名。 - 多来源属性。 - 相关实体。 - 相关关系。 - 来源 Wiki 页面。 - 证据。 助手库 Wiki 页面需要版本管理。每次 WeKnora 导入、技能直接归纳、管理员本地文件导入、实体对齐重建或人工编辑,都生成新 revision。新 revision 先处于 draft 或 staged 状态,发布成功后成为 current。 版本能力: - 查看历史版本。 - 对比两个版本。 - 回滚到历史版本。 - 查看版本来源:导入任务、技能归纳任务、人工编辑。 - 查看版本发布人和发布时间。 ### 30.8 助手库搜索 助手知识库需要支持搜索。 V1 建议实现: - Wiki 标题搜索。 - 实体名称和别名搜索。 - Wiki 正文全文搜索。 - 来源知识库过滤。 - 实体类型过滤。 实现选择: - SQLite 可使用 FTS5。 - PostgreSQL 可使用 tsvector 或 trigram。 - 如果现有持久化层不方便,先实现数据库 LIKE + normalized name 索引,后续再升级全文索引。 搜索结果只展示 Wiki 页面,不直接展示底层 chunk。 ### 30.9 助手库持久化表 建议新增: ```text deerflow.persistence.assistant_knowledge ``` 核心表: #### `assistant_knowledge_base` 只允许一条 active 记录。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `name` | 默认“知识梳理”。 | | `status` | `active` / `disabled`。 | | `created_by` | 初始化管理员。 | | `created_at` / `updated_at` | 时间戳。 | #### `assistant_knowledge_sources` 记录每个来源。来源可以是 WeKnora 知识库、技能归纳或管理员本地导入。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `base_id` | 助手库 ID。 | | `source_type` | `weknora` / `skill` / `local_file`。 | | `knowledge_base_mapping_id` | DeerFlow 本地 WeKnora 映射 ID,可为空。 | | `skill_name` | 技能名,可为空。 | | `upload_batch_id` | 本地文件导入批次,可为空。 | | `source_name` | 来源名称。 | | `last_import_job_id` | 最近导入任务。 | | `status` | `active` / `import_failed` / `deleted`。 | | `created_at` / `updated_at` | 时间戳。 | #### `assistant_knowledge_import_jobs` | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `base_id` | 助手库 ID。 | | `source_id` | 来源 ID。 | | `trigger` | `auto_after_weknora_indexed` / `manual_from_weknora` / `skill_direct` / `manual_file_upload` / `scheduled` / `watcher`。 | | `status` | `queued` / `running` / `completed` / `partial_failed` / `failed`。 | | `phase` | `exporting` / `scanning` / `parsing` / `awaiting_review` / `aligning_entities` / `aligning_relations` / `writing_wiki` / `indexing_search` / `completed`。 | | `counts_json` | 导入页面、实体、关系、chunk 数。 | | `last_error_message` | 错误摘要。 | | `created_by` | 操作人;自动任务可为空或 system。 | | `created_at` / `completed_at` | 时间戳。 | #### `assistant_knowledge_wiki_pages` | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `base_id` | 助手库 ID。 | | `slug` | 全局唯一 slug。 | | `title` | 标题。 | | `page_type` | `home` / `source` / `entity` / `relation` / `topic`。 | | `current_revision_id` | 当前发布版本。 | | `content_markdown` | 当前版本冗余内容,便于列表和搜索。 | | `summary` | 摘要。 | | `status` | `active` / `deleted`。 | | `search_text` | 搜索文本。 | | `updated_at` | 更新时间。 | #### `assistant_knowledge_wiki_revisions` 保存 Wiki 页面版本。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `page_id` | Wiki 页面。 | | `revision_no` | 递增版本号。 | | `content_markdown` | 版本内容。 | | `summary` | 版本摘要。 | | `source_id` | 来源,可为空。 | | `import_job_id` | 导入任务,可为空。 | | `change_type` | `import` / `skill_sync` / `manual_edit` / `entity_rebuild` / `rollback`。 | | `status` | `draft` / `staged` / `published` / `superseded`。 | | `created_by` | 创建人。 | | `created_at` / `published_at` | 时间戳。 | #### `assistant_knowledge_entities` 保存全局对齐后的实体。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `base_id` | 助手库 ID。 | | `entity_type` | 实体类型。 | | `normalized_name` | 归一化名称。 | | `display_name` | 展示名称。 | | `aliases_json` | 别名。 | | `attributes_json` | 聚合属性。 | | `wiki_page_id` | 实体 Wiki 页。 | | `status` | `active` / `deleted`。 | #### `assistant_knowledge_entity_contributions` | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `entity_id` | 全局实体。 | | `source_id` | WeKnora 来源。 | | `import_job_id` | 导入任务。 | | `source_wiki_slug` | 来源 Wiki 页。 | | `source_chunk_id` | 来源 chunk,可为空。 | | `source_skill_name` | 来源技能,可为空。 | | `evidence_json` | 证据。 | | `confidence` | 置信度。 | #### `assistant_knowledge_relations` 保存全局对齐后的关系。 | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `base_id` | 助手库 ID。 | | `source_entity_id` | 源实体。 | | `predicate` | 关系类型。 | | `target_entity_id` | 目标实体。 | | `description` | 聚合描述。 | | `wiki_page_id` | 关系 Wiki 页。 | | `status` | `active` / `deleted`。 | #### `assistant_knowledge_relation_contributions` | 字段 | 说明 | | --- | --- | | `id` | UUID。 | | `relation_id` | 全局关系。 | | `source_id` | WeKnora 来源。 | | `import_job_id` | 导入任务。 | | `source_wiki_slug` | 来源 Wiki 页。 | | `graph_edge_id` | WeKnora 图谱边 ID,可为空。 | | `evidence_json` | 证据。 | | `confidence` | 置信度。 | ### 30.10 助手库 API 新增 router: ```text /api/assistant-knowledge ``` 建议接口: ```http GET /api/assistant-knowledge/base POST /api/assistant-knowledge/base/initialize GET /api/assistant-knowledge/wiki-pages GET /api/assistant-knowledge/wiki-pages/{slug} GET /api/assistant-knowledge/wiki-pages/{slug}/revisions GET /api/assistant-knowledge/wiki-pages/{slug}/revisions/{revision_id} POST /api/assistant-knowledge/wiki-pages/{slug}/revisions/{revision_id}/rollback GET /api/assistant-knowledge/search?q=xxx&type=entity&source_id=xxx GET /api/assistant-knowledge/entities/{id} GET /api/assistant-knowledge/relations/{id} GET /api/assistant-knowledge/sources GET /api/assistant-knowledge/import-jobs GET /api/assistant-knowledge/import-jobs/{id} POST /api/assistant-knowledge/sources/{source_id}/reimport POST /api/assistant-knowledge/imports/files POST /api/assistant-knowledge/imports/skills DELETE /api/assistant-knowledge/sources/{source_id} ``` 新增 WeKnora 普通知识库导出或导入触发接口: ```http POST /api/llmwiki/knowledge-bases/{knowledge_base_id}/export-to-assistant ``` 行为: - 校验管理员。 - 校验助手知识库已初始化;未初始化则自动创建“知识梳理”或提示管理员确认创建。 - 校验 WeKnora 知识库已完成处理。 - 创建 `assistant_knowledge_import_jobs`。 - 后台导出 WeKnora 数据并导入助手库。 本地文件导入接口: ```http POST /api/assistant-knowledge/imports/files ``` 使用 multipart 上传文件。后端必须先完成安全扫描和敏感信息阻断,再进入解析、抽取、审核、实体对齐和 Wiki 版本生成。 技能直接导入接口: ```http POST /api/assistant-knowledge/imports/skills ``` 请求: ```json { "skill_names": ["skill-a", "skill-b"], "force": false } ``` 该接口也可以由 `/api/skill-knowledge/sync-jobs` 在目标类型为 `assistant` 时内部复用。 系统设置接口需要支持: ```http GET /api/system-settings/knowledge-base-product-version PUT /api/system-settings/knowledge-base-product-version ``` ### 30.11 前端页面 知识库入口需要支持产品版本切换。 建议: - 管理员设置页提供“知识库版本”切换:`v1 普通知识库` / `v2 助手知识库`,默认 `v1`。 - 知识库页面顶部用 Tabs 或 Segmented Control 区分“普通知识库”和“助手知识库”。 - 未配置 WeKnora 时,普通知识库 tab 隐藏或禁用;助手知识库 tab 正常显示。 - 助手知识库未初始化时,展示“创建知识梳理库”按钮。 - 创建后展示 Wiki 首页、搜索框、来源列表、最近导入和页面版本入口。 - 普通知识库详情页增加按钮:“导入到助手知识库”。 - 普通知识库列表中可显示“已沉淀到知识梳理 / 待导入 / 导入失败”状态。 - 助手知识库页面为管理员提供“导入本地文件”和“导入技能”入口。 助手知识库页面建议: ```text 知识梳理 ├── 搜索 ├── Wiki 首页 ├── 实体 ├── 关系 ├── 来源知识库 ├── 导入记录 └── 页面版本 ``` 普通用户只看到搜索、Wiki、实体、关系、来源和只读版本历史。管理员额外看到导入、重新导入、删除来源、版本对比和回滚。 ### 30.12 自动沉淀时机 WeKnora 普通知识库完成以下任意流程后,应触发或排队助手库导入: - 管理员从技能归纳到 WeKnora 成功。 - WeKnora 文档上传并完成向量化。 - WeKnora Wiki 页面生成或更新完成。 - WeKnora 图谱抽取完成。 - 管理员手动点击“导入到助手知识库”。 为了避免频繁重复导入: - 对每个 WeKnora 知识库维护 `last_export_digest`。 - 导出前计算 Wiki 页面、文档 chunk、图谱摘要的 digest。 - digest 未变化时跳过导入。 - 同一 WeKnora 知识库同一时间只允许一个 active 导入任务。 ### 30.13 与技能归纳的关系 技能归纳支持两条链路。 链路 A:先进入 WeKnora,再沉淀助手库。 ```text 技能知识文件 -> WeKnora 普通知识库 -> WeKnora 向量化 / Wiki / 图谱 -> 导出已处理数据 -> 助手知识库“知识梳理” -> 全局实体对齐 -> Wiki 展示与搜索 ``` 链路 B:直接进入助手知识库。 ```text 技能知识文件 -> 助手知识库“知识梳理” -> 实体/关系预览审核 -> 全局实体对齐 -> Wiki 页面版本发布 -> 搜索索引 ``` 代码文件在第一步就被排除,不进入后续任何知识库。 ### 30.14 新增测试与验收 新增后端测试: - 未配置 WeKnora 时,助手知识库 base 接口可用,普通知识库导入接口返回明确不可用。 - 初始化助手知识库后,只存在一个 active base。 - 普通用户可读助手 Wiki,写接口返回 403。 - 管理员可从 WeKnora 知识库触发导入任务。 - 管理员可把技能直接导入助手知识库。 - 管理员可上传本地文件导入助手知识库。 - WeKnora 导出包包含 Wiki、文档、chunk、实体、关系、图谱时,助手库全部导入。 - 同一个人或同一对象来自多个 WeKnora 来源时,在助手库合并为一个实体。 - 模型推断实体对齐 `confidence >= 0.90` 自动合并,低于阈值不合并。 - 删除某个来源时,只删除该来源贡献;其他来源贡献仍保留。 - 助手库搜索能命中 Wiki 标题、实体别名和正文。 - 助手 Wiki 页面支持 revision、对比和回滚。 - 技能归纳扫描不读取、不上传、不摘要代码文件,代码文件变化不触发 stale。 新增前端测试: - 知识库产品版本默认 `v1`。 - 管理员可切换 `v1` / `v2`。 - 未配置 WeKnora 时仍可看到“创建知识梳理库”。 - 普通知识库详情页有“导入到助手知识库”按钮。 - 助手知识库页面展示 Wiki、搜索、来源、导入记录。 - 助手知识库页面展示页面版本、对比和回滚入口。 - 普通用户看不到导入、删除、重新导入和回滚动作。