# 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 关系判断→双向写关系）设计完整
