mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-06 22:55:56 +08:00
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com> Signed-off-by: eeee0717 <chentao020717Work@outlook.com>
319 lines
10 KiB
Markdown
319 lines
10 KiB
Markdown
# 知识库后端当前实现说明
|
||
|
||
本文档只记录当前分支中 `src/main/services/knowledge` 已经落地的后端分层、调用边界和 workflow 行为。
|
||
|
||
`v2-refactor-temp` 下的文档是临时工作笔记。当前 canonical 文档是:
|
||
|
||
- [Knowledge Service](../../../docs/references/knowledge/knowledge-service.md)
|
||
- [Knowledge Workflow Architecture](../../../docs/references/knowledge/workflow-architecture.md)
|
||
- [Knowledge Operation Guards](../../../docs/references/knowledge/operation-guards.md)
|
||
|
||
本文不覆盖旧的 `src/main/knowledge` / `knowledge-base:*` 通道。
|
||
|
||
## 1. 当前架构图
|
||
|
||
```text
|
||
UI / preload IPC / main-side workflow
|
||
-> KnowledgeService
|
||
-> KnowledgeWorkflowService
|
||
-> JobManager
|
||
-> knowledge.prepare-root
|
||
-> knowledge.index-documents
|
||
-> knowledge.delete-subtree
|
||
-> knowledge.reindex-subtree
|
||
-> KnowledgeLockManager
|
||
-> KnowledgeBaseService / KnowledgeItemService
|
||
-> KnowledgeVectorStoreService / FileManager
|
||
|
||
UI Data API reads / patch
|
||
-> Data API knowledge handlers
|
||
-> KnowledgeBaseService / KnowledgeItemService
|
||
```
|
||
|
||
当前没有 `KnowledgeRuntimeService`,也没有 Knowledge 自己维护的 in-memory queue。持久化调度、retry、timeout、cancel 和 startup recovery 由 `JobManager` 负责。
|
||
|
||
## 2. 当前职责边界
|
||
|
||
`KnowledgeBaseService` / `KnowledgeItemService`:
|
||
|
||
1. 负责 SQLite 业务表读写。
|
||
2. 负责 `knowledge_item.status` / `error` 的持久化更新。
|
||
3. 负责 `knowledge_item.data` 与 `type` 的一致性校验。
|
||
4. 负责 container item 的子项状态向上聚合。
|
||
5. 不负责 reader、embedding、向量库写入、JobManager 调度或 caller-facing IPC。
|
||
|
||
Data API knowledge handlers:
|
||
|
||
1. 只暴露数据库可直接满足的读和 base metadata/config 更新。
|
||
2. 不负责 runtime mutation,不创建或删除 vector store artifacts。
|
||
|
||
`KnowledgeService`:
|
||
|
||
1. 负责 caller-facing `knowledge-runtime:*` IPC。
|
||
2. 负责 create/delete/restore base workflow。
|
||
3. 注册 Knowledge JobManager handlers。
|
||
4. 持有 `KnowledgeWorkflowService` 和 `KnowledgeLockManager`。
|
||
5. 对 delete / reindex / chunk 操作做入口 guard。
|
||
6. 不直接执行 reader / chunk / embed / vector write。
|
||
|
||
`KnowledgeWorkflowService`:
|
||
|
||
1. 负责 `addItems` / `deleteItems` / `reindexItems` 的 workflow 分支。
|
||
2. 负责 `scheduleItem(baseId, itemId)`。
|
||
3. 将 `directory` 分派为 `knowledge.prepare-root`。
|
||
4. 将 `file` / `note` / `url` 分派为 `knowledge.index-documents`。
|
||
5. 负责 add/reindex 调度失败后的状态补偿。
|
||
|
||
`KnowledgeLockManager`:
|
||
|
||
1. 负责同一 base 下的进程内 mutation 串行化。
|
||
2. 保护 vector replace/delete、FileRef cleanup、item status writes 和 destructive cleanup/reset。
|
||
3. 不能替代 `DbService.withWriteTx`;主 SQLite 写事务仍必须走 `DbService.withWriteTx`。
|
||
|
||
## 3. 当前调用边界与调用方契约
|
||
|
||
UI / preload 当前调用模型:
|
||
|
||
```text
|
||
UI
|
||
|
|
||
+--> Data API
|
||
| -> list/get knowledge bases
|
||
| -> patch base metadata/config
|
||
| -> list/get knowledge items
|
||
|
|
||
\--> preload knowledgeRuntime IPC
|
||
-> create/delete/restore base
|
||
-> add/delete/reindex items
|
||
-> search
|
||
-> list/delete chunks
|
||
```
|
||
|
||
添加 file / url / note / directory 时,调用方直接走:
|
||
|
||
```text
|
||
caller
|
||
-> preload IPC add-items(item payloads)
|
||
```
|
||
|
||
调用方不应先通过 Data API 创建 item,再把 created item ids 传给 runtime `addItems`。
|
||
|
||
Leaf item 当前链路:
|
||
|
||
```text
|
||
add-items(leaf payloads)
|
||
-> create leaf item rows
|
||
-> status = processing
|
||
-> enqueue knowledge.index-documents
|
||
```
|
||
|
||
Container item 当前链路:
|
||
|
||
```text
|
||
add-items(directory payloads)
|
||
-> create root item rows
|
||
-> status = preparing
|
||
-> enqueue knowledge.prepare-root
|
||
-> prepare-root expands owner
|
||
-> prepare-root creates child rows
|
||
-> workflowService.scheduleItem(child)
|
||
```
|
||
|
||
`prepare-root` 创建出的 child 可以继续是 `directory`,由 workflow service 再次分派为 `knowledge.prepare-root`。递归展开不由 reader 或 leaf indexing 分支处理。
|
||
|
||
## 4. JobManager 模型
|
||
|
||
当前 Knowledge job types:
|
||
|
||
1. `knowledge.prepare-root`
|
||
2. `knowledge.index-documents`
|
||
3. `knowledge.delete-subtree`
|
||
4. `knowledge.reindex-subtree`
|
||
|
||
每个 base 使用独立队列:
|
||
|
||
```text
|
||
base.${baseId}
|
||
```
|
||
|
||
JobManager 负责:
|
||
|
||
1. job 持久化。
|
||
2. dispatch。
|
||
3. retry / timeout。
|
||
4. cancel。
|
||
5. startup recovery。
|
||
|
||
Knowledge 不再维护 `entries` map、`controller`、`runPromise`、`interruptError` 或其他 in-memory queue 状态。
|
||
|
||
## 5. 当前索引执行链路
|
||
|
||
`knowledge.index-documents` 当前流程:
|
||
|
||
```text
|
||
handler.execute
|
||
-> load base and item
|
||
-> skip missing / deleting / already completed item
|
||
-> under base mutation lock:
|
||
rebuild source file refs
|
||
status = reading
|
||
-> read documents
|
||
-> chunk documents
|
||
-> under base mutation lock:
|
||
status = embedding
|
||
-> embed chunks
|
||
-> under base mutation lock:
|
||
re-read item
|
||
skip vector write if item is deleting
|
||
vectorStore.replaceByExternalId(itemId, nodes)
|
||
status = completed
|
||
```
|
||
|
||
如果 reader 返回空 documents,或 chunk 后没有可索引 chunks,`index-documents` 仍视为成功:写入 `replaceByExternalId(itemId, [])` 清空该 item 的旧 chunks,然后把 item 标记为 `completed`。
|
||
|
||
非中断错误由 JobManager retry。Retry 耗尽或 job cancel 时,handler `onSettled` 把对应 item 标记为 `failed`,但如果 item 已经是 `deleting` 则跳过失败回写。
|
||
|
||
## 6. Delete / Reindex 当前链路
|
||
|
||
`delete-items` 当前流程:
|
||
|
||
```text
|
||
delete-items(baseId, itemIds)
|
||
-> collapse to top-level roots
|
||
-> under base mutation lock:
|
||
mark selected root subtrees deleting
|
||
-> enqueue knowledge.delete-subtree
|
||
```
|
||
|
||
`knowledge.delete-subtree`:
|
||
|
||
```text
|
||
-> resolve still-deleting subtree
|
||
-> cancel active jobs touching subtree
|
||
-> under base mutation lock:
|
||
delete vectors for leaf items
|
||
clear Knowledge FileRef rows for full subtree
|
||
delete knowledge_item rows
|
||
```
|
||
|
||
`file_ref.sourceId` 是 polymorphic,没有 FK 指向 `knowledge_item`。因此最终 hard delete 必须先展开完整 subtree 清理 Knowledge FileRef,再删除 rows;仅删除显式 root id 对应 refs 会留下 descendant orphan refs。
|
||
|
||
`reindex-items` 当前流程:
|
||
|
||
```text
|
||
reindex-items(baseId, itemIds)
|
||
-> collapse to top-level roots
|
||
-> reject unless every selected subtree item is completed or failed
|
||
-> enqueue knowledge.reindex-subtree
|
||
```
|
||
|
||
`knowledge.reindex-subtree`:
|
||
|
||
```text
|
||
-> skip if delete already marked any subtree item deleting
|
||
-> under base mutation lock:
|
||
re-check deleting guard
|
||
delete old vectors
|
||
delete expanded descendants for selected container roots
|
||
reset selected roots to preparing / processing
|
||
-> workflowService.scheduleItem(root)
|
||
```
|
||
|
||
Reindex 不是 cancellation primitive。Active subtree 只能 delete,不能 reindex。
|
||
|
||
## 7. `knowledge_item.status` 当前边界
|
||
|
||
当前 `status` 表达业务生命周期和粗粒度运行进度:
|
||
|
||
1. `idle`
|
||
2. `preparing`
|
||
3. `processing`
|
||
4. `reading`
|
||
5. `embedding`
|
||
6. `completed`
|
||
7. `failed`
|
||
8. `deleting`
|
||
|
||
当前不再保留单独 `phase` 字段。`status` 是持久业务状态,不由 JobManager progress 反推。
|
||
|
||
状态语义:
|
||
|
||
1. `preparing`:`directory` 正在 expand / create children。
|
||
2. `processing`:leaf 已接受但尚未进入 reading,或 container 仍有 active children。
|
||
3. `reading`:leaf 正在读取 source documents。
|
||
4. `embedding`:leaf 正在 embedding。
|
||
5. `completed`:leaf indexing 完成,或 container 没有 active children。
|
||
6. `failed`:index/preparation/scheduling compensation 失败。
|
||
7. `deleting`:用户不可见,等待后台 cleanup。
|
||
|
||
## 8. Base workflow
|
||
|
||
`createBase(dto)` 当前流程:
|
||
|
||
```text
|
||
IPC create-base(CreateKnowledgeBaseDto)
|
||
-> KnowledgeBaseService.create(dto)
|
||
-> KnowledgeVectorStoreService.createStore(base)
|
||
-> return created base
|
||
```
|
||
|
||
如果 vector store 初始化失败,orchestration 会调用 `KnowledgeBaseService.delete(base.id)` 回滚刚创建的 SQLite base,然后把原始错误抛给调用方。
|
||
|
||
`deleteBase(baseId)` 当前流程:
|
||
|
||
```text
|
||
IPC delete-base(baseId)
|
||
-> cancel active Knowledge jobs in base queue
|
||
-> under base mutation lock:
|
||
KnowledgeVectorStoreService.deleteStore(baseId)
|
||
KnowledgeBaseService.delete(baseId)
|
||
```
|
||
|
||
Artifact 删除失败时 SQLite 行保留,用户可从 UI 重试。SQLite 删除失败时已删除 artifacts 不会恢复,orchestration 抛出 `invalidOperation`。
|
||
|
||
`restoreBase(dto)` 当前流程:
|
||
|
||
```text
|
||
IPC restore-base(sourceBaseId, embeddingModelId, dimensions)
|
||
-> load source base
|
||
-> load source root items
|
||
-> create new base with source config and requested embedding contract
|
||
-> add source root item payloads to restored base
|
||
```
|
||
|
||
Restore 允许 failed base,也允许 completed base;即使 completed source base 的 `embeddingModelId` 和 `dimensions` 未变化,也允许创建同配置 clone/rebuild。
|
||
|
||
## 9. Search / Chunk 当前边界
|
||
|
||
`search(baseId, query)` 当前流程:
|
||
|
||
1. 拒绝 failed base。
|
||
2. 拒绝没有 searchable token 的 query。
|
||
3. 使用 base embedding model 生成 query embedding。
|
||
4. 查询 sqlite-vec vector store。
|
||
5. 过滤 missing / other-base / deleting source item 的结果。
|
||
6. 如果配置了 rerank model,执行 rerank。
|
||
7. 仅对 `scoreKind = relevance` 的结果应用 relevance threshold;BM25 / hybrid 的 ranking 分数不做 threshold 过滤,然后写入 rank。
|
||
|
||
`list-item-chunks` / `delete-item-chunk` 当前规则:
|
||
|
||
1. 拒绝 failed base。
|
||
2. 要求目标 item 自身为 `completed`。
|
||
3. 对 completed `directory` list 请求,如果 subtree 仍含 `deleting` descendant,则拒绝。
|
||
|
||
## 10. 当前明确不做的内容
|
||
|
||
1. 不使用旧 `KnowledgeRuntimeService`。
|
||
2. 不维护 Knowledge 自己的 in-memory queue。
|
||
3. 不使用 `index-leaf` job type;当前 leaf indexing job 是 `knowledge.index-documents`。
|
||
4. 不保留单独 `phase` 字段。
|
||
5. 不把 restore same embedding config 视为 no-op 并拒绝。
|
||
6. 不主动 permanent-delete detached `FileEntry` rows。
|
||
7. Round 1 不接入 FileProcessing;`knowledge_base.fileProcessorId` 当前对 indexing inert。
|
||
|
||
## 11. 后续更新本文档时的原则
|
||
|
||
1. 如果 canonical 文档已经覆盖事实,优先更新 canonical 文档,再在本文保留短摘要或链接。
|
||
2. 不再新增大段逐实现复制,避免 `v2-refactor-temp` 与 canonical docs 漂移。
|
||
3. 如果当前实现与 RFC 目标不同,必须明确写“当前实现”还是“目标设计”。
|