# KnowHub 代码审查反馈 目的是帮助改进,不是批评。 --- ## 1. 数据库设计:用 PostgreSQL 但不用关系型特性 **现象**:所有实体间关系都存为 JSONB 数组(`capabilities: ["CAP-001", "CAP-002"]`),同一关系在两张表各存一份,由应用层维护双向一致性。 **问题**: - 这是文档数据库(MongoDB)的做法,放弃了 PostgreSQL 最核心的能力:引用完整性和 JOIN - 删一个 Capability,Tool 表里的 JSONB 数组还挂着已删除的 ID,变成悬空引用,数据库不会报错 - 想查"CAP-001 关联了哪些 Tool",要遍历 Tool 表所有行做 `@>` 匹配 - 两边各存一份的结果是:一边更新了另一边忘了 → 数据不一致,而且这种不一致很难发现 **应该怎么做**:多对多关系用关联表(junction table),关系只存一份,用外键约束和 `ON DELETE CASCADE`。这是关系型数据库的基本功。 **已解决**:迁移到新库 `knowhub`,所有关系改为关联表(8 张 junction table)。详见 `schema.md` 和 `schema-migration-plan.md`。 --- ## 2. 字段命名不统一 **现象**: - Requirement 指向 Capability 的字段叫 `atomics` - Knowledge 指向 Capability 的字段叫 `support_capability`(单数) - Capability 指向 Knowledge 的字段叫 `source_knowledge` - Tool 按知识类型拆成三个字段:`tool_knowledge` / `case_knowledge` / `process_knowledge` - Capability.implements 的 key 用 tool name 而非 tool id **问题**: - 看到 `atomics` 不知道它指向哪张表,必须查文档 - 单复数混用(`support_capability` vs `capabilities`) - Tool 的三个 knowledge 字段是冗余的——Knowledge 自己有 types 字段区分类型,不需要 Tool 侧按类型拆存 - implements 的 key 用 name 而非 id,和所有其他关联字段(都用 id)不一致 **原则**:字段名应该自解释指向什么。如果需要一个额外的"关联到"列来解释每个字段指向哪张表,说明命名有问题。 **已解决**:统一为 `{entity}_ids` 命名(`capability_ids`, `tool_ids`, `knowledge_ids`),Tool 的三个 knowledge 字段合并为一个 `knowledge_ids`。详见 `schema.md`。 --- ## 3. 文档与代码严重脱节 **现象**: - `knowhub/README.md` 描述的是 SQLite + LIKE 搜索的老架构,实际代码是 PostgreSQL + pgvector - `knowhub/docs/knowledge-management.md` 写着"Milvus Lite 单一存储架构",实际没有 Milvus - README 写"Server 无 LLM 调用、无 embedding、无向量数据库",实际全都有 **问题**:新人看文档会被误导,基于错误理解做设计决策。文档不如没有——错误的文档比没有文档更有害。 **应该怎么做**: - 文档是代码的快照,代码变了文档必须同步更新 - 如果没时间更新文档,至少在文档顶部标注"⚠️ 可能过时,以代码为准" - 设计方案和代码快照用不同文件名区分(我们现在用 `*-plan.md` 后缀标识未实现的方案) --- ## 4. 代码组织:一次性脚本和核心代码混在一起 **现象**:`knowhub_db/` 目录下 28 个 .py 文件,其中 5 个是核心 store,16 个是已跑完的一次性迁移脚本,7 个是调试脚本。 **问题**:打开目录看到 28 个文件,完全不知道哪些是重要的、哪些是垃圾。新人会花时间去读 `fix_embedding_migration.py` 试图理解系统,结果那只是修了一个一次性的 bug。 **应该怎么做**: - 核心代码和一次性脚本物理分开(`migrations/`、`scripts/`) - 或者跑完的迁移脚本直接删掉(git 历史里还有) 已整理:核心 5 个 store 留根目录,迁移脚本移到 `migrations/`,调试脚本移到 `scripts/`。 --- ## 5. Prompt 撰写:像写系统文档而非行为指令 **现象**:Librarian Agent 的 prompt 中包含大量实现细节和无关信息: - "你是 Knowledge Manager,作为后台服务运行,通过 IM 消息与调研 Agent 协作" - "你是事件驱动的:收到消息时立即处理并回复" - 大段描述"调研 Agent 如何发送调研结果" **问题**: - LLM 不需要知道自己通过什么协议被调用、运行在什么架构上、调用方是谁 - 这些信息只消耗 token,不改善行为 - "Knowledge Manager"、"Librarian Agent" 这类标签不激活任何有用的行为模式 - 数据库 schema 介绍和操作策略混在一起,LLM 需要行动时去翻 schema 定义 **应该怎么做**: - 角色定位描述具体能力("擅长从碎片信息中识别关联、去重"),不给标签 - 去掉所有实现细节(协议、框架、调用方) - Schema(世界是什么样的)和策略(遇到 X 做 Y)分层写 - 给判断标准而非流程步骤 **规范已建立**:`agent/docs/prompt-guidelines.md` --- ## 6. 知识库的"知识图谱"定位不准确 **现象**:文档和 prompt 中多处使用"知识图谱"一词。 **问题**:知识图谱(Knowledge Graph)通常指实体-关系-实体的三元组结构,通过图数据库存储和图遍历查询。KnowHub 是关系型数据库 + 向量检索,实体间通过 ID 列表关联。这不是图结构。 在 prompt 中用"知识图谱"会误导 LLM 去做图推理相关的行为(比如试图做多跳遍历),实际系统不支持。 **应该怎么做**:使用准确的术语。KnowHub 是"分层分类索引"或直接叫"知识库"。 --- ## 7. 上传流程中的概念混淆 **现象**:Librarian Agent prompt 中描述实体关系为"需求 → 拆解为能力 → 由工具实现 → 产生知识"的单向链。 **问题**: - 能力(Capability)是不可分割的原子能力,来自对工具的分析(可能参考需求),不是仅根据需求就能"拆解"出来的 - 知识(Knowledge)来自其他 Agent 的调研和汇报,不是 Librarian 自己创造的 - 单向链的描述暗示了错误的因果关系和创建流程 **应该怎么做**:各实体独立描述来源和用途,不要用暗示因果关系的箭头链。 --- ## 做得好的地方 值得肯定: - **上传编排的去重规则**写得很好:先查后写、新建 Capability 的三个条件(有工具+有用例+具体可操作),这是实战中总结出的判断标准 - **tree_matcher 工具**的设计不错,将需求匹配到内容分类树,多维度(实质/形式/意图)搜索 - **upload 的 buffer + batch 机制**是合理的:先缓存、批量合并、再由 Agent 处理,避免频繁小请求 - **KnowledgeProcessor 的去重流水线**(向量召回→LLM 关系判断→双向写关系)设计完整