From 6e455d37be0e5b064907520200cd174c31631e4a Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 22 Jul 2026 15:43:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(kb):=20=E6=B3=A8=E5=85=A5=E5=A5=91=E7=BA=A6?= =?UTF-8?q?=E6=94=B9=E4=B8=BB=E5=8A=A8=E6=9F=A5=E9=98=85=E2=80=94=E2=80=94?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=8D=B3=E5=85=88=E8=AF=BB=E6=97=A0=E9=9C=80?= =?UTF-8?q?=E7=82=B9=E5=90=8D,=E5=8E=BB=E6=8E=89=20document=5Fsearch=20?= =?UTF-8?q?=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _KB_CONTRACT 第一条换成主动性规矩:INDEX 条目与问题相关就先 read 正文再答, 用户无需说"从我的知识库里查";此前契约只写"怎么用"没写"何时主动用", flash 容易把 INDEX 当背景略过 - 去掉契约里的 document_search 路由条(契约只管 .kb 自己怎么用,路由靠各自 工具描述自然分流,少一层耦合) - DESIGN §3.8 两处描述同步收窄 Co-Authored-By: Claude Fable 5 --- DESIGN.md | 4 ++-- core/kb.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index da9919f..aa19e67 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -87,11 +87,11 @@ Session = 消息列表,ORM 直写 PG `messages`(append-only,jsonb 存 LiteLLM ### 3.8 个人知识库(`core/kb.py` + `core/kb_ingest.py`,✅ 2026-07-22) -用户自建资料(规范/报告/标准/内部文档)的长期查阅层,与 §3.7 记忆同范式:**纯文件 + prompt 注入契约,无向量无 DB**(判据同"真实文件为准":个人库几十~百余文件,agentic search 足够;索引若引入只能是可重建派生缓存)。**两层格局**:zcbot 内建 `.kb/` 私有小库(本节)+ 院检索服务共享大库(document_search,zcbot 只当客户端)——分工标准 = 文件数 × 查询频次,路由规则写进注入契约(学科文献→院库 / 自建资料→`.kb`)。 +用户自建资料(规范/报告/标准/内部文档)的长期查阅层,与 §3.7 记忆同范式:**纯文件 + prompt 注入契约,无向量无 DB**(判据同"真实文件为准":个人库几十~百余文件,agentic search 足够;索引若引入只能是可重建派生缓存)。**两层格局**:zcbot 内建 `.kb/` 私有小库(本节)+ 院检索服务共享大库(document_search,zcbot 只当客户端)——分工标准 = 文件数 × 查询频次,路由靠各自工具/契约描述自然分流,不在 kb 契约里点名 document_search(2026-07-22 收窄:契约只管自己怎么用,少一层耦合)。 - **做成机制而非 skill**(判据:有独立于会话的持久状态需用户管理 → 机制):落盘 `user_root/.kb/<库名>/`(INDEX.md + docs/ 转换后 md + sources/ 原件)。**"已入库"判据 = INDEX.md 有条目**,sources 有而 INDEX 无 = 待入库 → 入库幂等、崩溃可恢复、零 migration。dotfile 命名同 `.memory` 双向防呆,GET /v1/files 天然隐藏。 - **入库管线**(`core/kb_ingest.py`,上传即触发 + 手动兜底):markitdown Python API 转 md → 扫描件 PDF(文本近零)走方舟文档理解 OCR 兜底(§8.13 同通道)→ deepseek flash 单次 chat 写 标题/摘要/关键词(失败降级文件名+正文开头,不阻塞)→ 追加 INDEX 行。编排照定时执行器:create_task + to_thread + per-(user,库) 内存锁去重;进度存内存供前端轮询,崩了靠 FS 判据续跑。 -- **agent 侧零新工具**:`kb_block`(照 memory_block)把 INDEX 全文 + 契约(路由规则 / INDEX 行格式 / 答题标来源)注 prompt,**用户有库才注入**;fs 工具在 user_root 内可读写、docker 沙箱整 user_root bind → `.kb` 天然可达。INDEX 行格式是对话内手动入库与后台产出的同一契约。 +- **agent 侧零新工具**:`kb_block`(照 memory_block)把 INDEX 全文 + 契约(主动查阅无需点名 / INDEX 行格式 / 答题标来源)注 prompt,**用户有库才注入**;fs 工具在 user_root 内可读写、docker 沙箱整 user_root bind → `.kb` 天然可达。INDEX 行格式是对话内手动入库与后台产出的同一契约。 - **API 薄壳**(`/v1/kb*` 8 端点):列/建/删库、详情(带入库进度)、上传即入库、手动 ingest、看/删单篇。**不设 HTTP 检索端点**——检索是 agent 的事。前端两栏 modal(kb.js)管上传/删除,查询全走对话。 - **记账**:`usage_events` kind="kb_ingest"(OCR 那笔走 kind="vision"),无 task 上下文 → 0022 放宽 task_id 可 NULL,溯源靠 units JSONB `{"kb", "source"}`。 diff --git a/core/kb.py b/core/kb.py index 2776af1..58fd1a5 100644 --- a/core/kb.py +++ b/core/kb.py @@ -241,9 +241,9 @@ def save_source(workspace_dir: Path, user_id: UUID, name: str, filename: str, da _KB_CONTRACT = """\ 用法规矩: -- **路由**:找**材料学科学术文献**优先 `document_search`(院内共享大库);查**用户自建 - 资料**(个人上传的规范 / 报告 / 标准 / 内部文档)用下面的知识库 —— 先看各库 INDEX - 条目定位,再 `read` 对应 `docs/*.md` 正文,量大时 `grep` 先缩范围。 +- **主动查阅**:回答前先扫一眼下方各库条目,标题 / 摘要 / 关键词与用户问题相关就先 + `read` 对应 `docs/*.md` 正文再作答(量大时 `grep` 先缩范围)。**无需用户点名 + "知识库"** —— 用户资料里可能已有的内容,宁可多查一次,不要凭空回答。 - **答题标来源**:引用了哪个库哪篇就在回答里注明(标题或文件名)。 - **对话内入库**:用户在对话里给了值得长期留的资料时,可直接写入:原件放 `sources/`(没有原件就跳过)、正文转成 markdown 写 `docs/.md`、再往该库