在构建企业级 RAG(检索增强生成)系统的初期,绝大多数开发者都会倾向于采用一种直觉化的数据模型:Dataset -> Document -> Segment

这是一个典型的以检索为中心(Retrieval-Centric)的设计。在这个模型里,Dataset(数据集)是系统绝对的“一等公民”。它就像一个大箩筐,不仅装载着所有的向量切片,还承载着 Embedding 模型的配置、切块大小(Chunk Size)的规则,甚至还要被迫承担起给文档分类、打标签的杂活。

Knowledge Hub (以下简称 KH) 研发的雏形阶段,我也曾为了追求开发速度(毕竟当时只有我一个人配合 AI 结对)而短暂采用了这种扁平模型。但随着业务深入到 Git 仓库的多级目录同步、细粒度的角色权限控制(ACL)以及 RAPTOR(层级递归摘要)时,这个模型展现出了极其严重的扩展性天花板。

今天,我想通过对 internal/projectinternal/dataset 源码演进的复盘,聊聊为什么在复杂的知识治理场景下,我们必须强行在“检索”之上,建立一层显式的“内容意图”。

一、架构的阵痛:当 Dataset 沦为逻辑泥潭

在真实的业务场景下,企业知识的组织方式从来不是扁平的。

1.1 场景还原:无法还原的目录语义

当时,我需要让前端在 UI 上完美还原一个深达六层的 Git 仓库目录树(例如 docs/api/v1/internal/auth/oauth2.md)。在早期的扁平模型下,我只能在 Document 记录上存一个 path 字符串。

这引发了一系列连锁反应:

  • 查询低效:当我想查出 /docs/api/ 目录下的所有子节点时,数据库必须进行全文模糊匹配,性能极差。
  • 权限断层:我想给 /docs/internal/ 目录设为“仅管理员可见”,由于没有真实的目录节点实体,我必须递归地去修改该目录下成千上万份文档的权限字段。
  • 状态不一致:一旦 Git 端发生了目录重命名,系统需要对底层成千上万个切片的元数据进行全量更新,这在并发环境下简直是运维噩梦。

1.2 AI 给出的“诱人陷阱”

当我把这个痛点抛给 AI 时,它最初给出的重构方案是:在 Dataset 模型里加一个 directory_tree 的 JSONB 字段,用来缓存整棵树。

从代码实现上看,这个方案写起来最快,能迅速解决前端展示问题。但在做最后裁决时,我感知到了一种强烈的架构不适感。
如果把代表“人类组织意图”的目录树,强行塞进代表“机器检索物理”的 Dataset 里,Dataset 就会沦为一个无所不包的 God Object(上帝对象)。未来,无论我是想优化向量算法,还是想调整目录逻辑,都会在这个泥潭里牵一发而动全身。

工程师的直觉告诉我:这债,我不能欠。 于是我选择了另一条更厚重、但也更稳健的路——引入 Project 层。

二、架构升维:Project(意图)与 Dataset(物理)的分离

internal/model/project.godataset.go 中,我确立了一套全新的分层契约。这本质上是对 Spec-Driven Development (SDD, 规范驱动开发) 理念的深度实践。

2.1 Project:内容主权的回归

Project 代表了人类视角的“内容世界”。
project.go 源码中,我们定义了它负责的所有非检索事务:

  • SourceType:明确知识的来源(是受控的 Git,还是原生的 Managed)。
  • ProjectNode (VFS):维护真实的、具有物理 ID 路径的目录树。
  • AccessPolicy:存储复杂的组织权限策略。

在 SDD 的语境下,Project 就是一份声明式的“意图代码”。它只定义知识应该长成什么样,而不关心它是如何被计算成向量的。

2.2 Dataset:检索物理的归位

Dataset 退回到它最擅长的角色——底层检索引擎。
dataset.go 中,它现在只关心:

  • EmbeddingProvider:用哪个模型去编码。
  • RetrievalStrategy:是语义检索、混合检索还是关键词检索。
  • 调优参数:TopK、ScoreThreshold、VectorWeight。

这两者之间,仅仅通过一个 BoundDatasetID 建立 1:1 的物理映射。这种“逻辑在前,物理在后”的解耦,让 KH 获得了极强的工程免疫力。

三、后端的降维打击:物化路径 (Materialized Path) 与性能

在实现 ProjectNode 这个 VFS 实体时,我再次与 AI 在方案选型上产生了深度博弈。

AI 惯性地推荐了基于 parent_id 的递归查找方案。在处理前端组件时,这种方案很直观,但在后端数据库层,递归查询是性能的死穴。尤其是在一个人负责所有运维工作的单兵环境下,我必须确保系统的查询效率是可预测的。

我强令系统引入了 Materialized Path(物化路径) 方案。
每个节点都有一个形如 /{root_id}/{mid_id}/{current_id}/ 的路径字段。
这个决策带来了质的飞跃:

  • $O(1)$ 级目录查询:我们只需要执行一条 LIKE '{current_path}%' 配合前缀索引,就能在常数时间内拉出整棵子树。
  • 权限继承的毫秒化:当计算目录穿透权限时,物化路径让我们能用极简的 SQL 逻辑实现大范围的隔离。

这种基于后端索引优势对应用层逻辑的“降维打击”,正是架构解耦带来的红利。

四、RAPTOR:为什么它必须建立在 Project 的骨架上?

引入 Project 层最大的惊喜,在于它为 KH 实现 Hierarchical RAG (RAPTOR) 提供了天然的土壤。

传统的 RAG 检索存在一个致命弱点:它能给你砖头(切片),但它没法给你大厦的蓝图。当你问“这个项目的权限体系是怎么演进的?”时,AI 往往只能根据相似度抓取几个碎片,拼凑出的答案通常断章取义。

市面上常见的 RAPTOR 实现,是让算法去算“语义聚类”。但那种机器算出来的层级,人类根本看不懂,也没法审计。

在 KH 的架构下,因为有了 ProjectNode 维护的真实目录,我们直接利用用户规划的业务目录作为摘要树的骨架。

  • 每个目录节点(如 /internal/rag/raptor)都可以自动汇总其子节点的知识,生成高层级的 SummaryEmbedding
  • 检索时,系统会先匹配高层目录摘要,锁定“知识林区”,再向下探测“知识树叶”。

这种“业务逻辑驱动”的层级检索,保证了 AI 给出的回答始终具备极强的人类可解释性。 如果没有当初强行把 Project 抽离出来的决策,这一切高级检索特性都将成为无法落地的空中楼阁。

五、Hermes 闭环:系统自省的起点

作为这个项目的唯一开发者,我意识到系统必须具备一定的“自我进化”能力,我称之为 Hermes 闭环

在重构 Project 层的期间,我让 AI 承担了一个角色:架构自体检员
每当我们在 DEV_LOG.md 记录下一次重构阵痛后,我会指挥 AI 去“对齐”现有的源码实现与 spec.md
AI 曾在审计过程中主动向我反馈:

“由于我们现在采用了 materialized_path 管理 VFS,internal/dataset 里的某些旧元数据标记已经出现了语义冗余。建议将其彻底清理,以维持架构的纯粹性。”

这种基于 Specs 和 Logs 发现设计缺陷、并主动提议重构的模式,让我深刻感受到:架构师的价值不在于码字,而在于建立这种能让系统不断优化的规则闭环。

结语:在 AI 时代,守住“解耦”的底线

回看 Knowledge Hub 把 Project 放到前面的重构历程,我感触最深的是:AI 给我们带来的执行力越强,我们对架构洁癖的坚持就越重要。

AI 可以在一分钟内写完几千行 CRUD,但它无法替你做出“解耦”这种违背短期效率、但决定长远生死的决定。在这个时代,程序员的真正壁垒,在于你能否在面对看似便捷的“打补丁”方案时,坚定地拔出架构的手术刀;在于你能否通过定义清晰的逻辑边界,让系统在复杂的业务冲刷下,依然保持那种数学般纯粹的优雅。

Knowledge Hub 的 Project 架构,并不是为了炫技,它是我们向“通用工业级知识治理”迈出的、最冷静也最坚决的一步。