Command Palette

Search for a command to run...

0

把 275 份文档喂给 AI Agent:知识库结构设计实战

给 AI Agent 准备知识库 ≠ 把文档扔进向量数据库。270+ 文档、6 个 bundle、16 种类型——我如何设计一套 AI 自己能导航的知识体系,以及过程中学到的组织原则。

一篇续篇:格式转完了,然后呢?275 份文档、6 个 bundle、16 种类型——一切从"agent 根本不会全文搜索"这个残酷事实开始。


一切从一句 prompt 开始

事情是这样的。

上个月把 270 份知识文档全部转成了 OKF 格式,写完了那篇《给 Agent 知识库换个标准格式:OKF 转换实战》。自我感觉良好——格式合规、frontmatter 齐全、INDEX 自动生成,我觉得我已经给 agent 准备了一份豪华大餐。

然后我让 compound-system 读了一下知识库,想知道它学到了什么。

我得到的回复是这样的模糊匹配结果——相关性极低,还在 275 份文档里漏掉了一大半。

当时我就愣住了。

不对啊,我明明把文档组织好了,内容都有了,为什么 agent 表现像在图书馆里闭着眼睛翻书?

因为 agent 根本不会全文搜索。

至少,不是你以为的那种"扔进向量数据库,问什么就匹配什么"的全文搜索。大模型在 275 份文档的向量空间里做 embedding 相似度匹配,效果像在大海里用吸管捞鱼——偶尔能捞到一条,但你永远不知道错过了什么。

这不是 embedding 技术的问题。是我组织知识的方式错了。

我一直在用"人类图书馆"的思维组织知识。分类、归整、打标签,觉得目录够清晰就行了。但 agent 的阅读方式和人完全不同。

人看目录:扫一眼标题,跳到自己感兴趣的部分。 Agent 看目录:它真的会逐字读每一行的 title 和 type。

人搜索:输入关键词,扫标题摘要,选一个点进去。 Agent 搜索:给一个 query,embedding 找最近邻,top-K 截断,然后祈祷你想要的那个片段恰好被匹配到。

这是完全不同的两套导航系统。

我花了两周重新设计知识库结构。结果验证了我的猜想:当知识库结构对了,agent 的理解准确率会上一个台阶。

这篇文章就是那次重构的全记录。

Bundle 设计哲学:谁用,谁负责

先给你看看最终产出的样子。

275 篇文档划分成 6 个 bundle:

Bundle文档数主要类型
AI Agent Wiki12Knowledge、Reference
Compound System 知识库62Bug、Knowledge、Pattern
项目文档51AgentInstruction、Documentation
计划与报告22Documentation、Plan、Report
Skills 引用82HermesSkill、Reference
Skills 双形态46HermesSkill、SourcePattern

看到这个划分,你可能觉得:这不就是按"来源"分了个类吗?

一开始我也这么想。后来才发现——出发点反了。

最开始的版本是按来源分的。AI Agent Wiki 来自一个 wiki 项目、Compound System 来自自进化管线、Skills 来自技能库……看起来挺合理对吧?

但用起来就是不对劲。因为 agent 面对一个任务时,它不会想"哦,这个问题应该去 wiki bundle 里找"。它会想"我现在在写代码,我要找相关的参考实现"。

Bundle 不应该按"文档从哪里来"划分,而应该按"谁会消费它"划分。

这是一个认知上的转折点。

举个例子。Compound System 知识库里的 45 份 Knowledge 文档,内容涵盖了自进化管线的架构决策、调试经历、理论推导——全是工程实践中的血泪教训。这些文档如果按来源分类,应该和 AI Agent Wiki 放在一起(都是 compound-system 相关的)。但 AI Agent Wiki 的消费方是"想了解 agent 是什么的人",而 Compound System 知识库的消费方是"运行中的自进化管线 agent"。

消费场景完全不同。

结果就是前者只有 12 份文档(精炼的概览),后者有 62 份(细颗粒度的工程记录)。同样一份 Pattern 文档,放在 Compound System bundle 里,agent 会在调试管线时自动翻出来参考;放在 Wiki 里,agent 根本不知道它和当前任务有关。

Bundle 即消费场景。 我最后敲定的划分逻辑很简单:说出这个 bundle 的"主要用户"是谁,如果说不清,就说明划分有问题。

索引优先于内容

这里有一个让我很意外的发现。

你猜 agent 是怎么使用一个 275 份文档的知识库的?我一开始觉得,它会把所有文档 embedding,存到向量数据库,然后用 query 召回。

错了。

我追踪了 compound-system 实际的调用模式。绝大多数情况下,agent 做的第一件事是:读 INDEX.md。

对,就是那个自动生成的目录文件。它会一行一行地读索引,理解整体结构,然后根据索引标题决定下一步去哪里翻。

这和人其实挺像的——但有一个关键区别。

人读索引是"找重点",扫到感兴趣的就停下来。Agent 读索引是"构建导航地图",它要把整个知识库的结构加载到自己的上下文里,然后逐层下钻。

那结果呢?

索引的信息密度决定了 agent 能感知到的知识范围。

如果 INDEX.md 只写了文件名列表,agent 读完只能知道"有哪些文件"。它不知道哪些文件是它现在需要的,也不知道哪些 bundle 和当前任务相关。

我现在的 INDEX 生成逻辑包含三层信息:

  1. 结构层:bundle 划分和每个 bundle 的文档数
  2. 语义层:每个 bundle 的主题摘要(自动从文档 frontmatter 提取)
  3. 导航层:每个文档的 title + type + 一句话摘要

当 agent 读到:

## Compound System 知识库 (62 篇)
- Bug: compression session split 修复
- Knowledge: 自进化管线 Executor-Curator 分离架构
- Pattern: Trajectory Mining Pipeline 模板

它立刻能判断"这个 bundle 和当前任务相关",然后只读取这个 bundle 的详细索引,再决定打开哪篇具体文档。

前端截断,后端全量。 INDEX 只给目录,内容在各自文档。Agent 从索引开始,觉得有需要才深入。

这个模式的效果超出预期。改造之前,agent 平均会打开 8-10 篇文档才能找到需要的信息。改造之后,降到了 2-3 篇。

类型导航:给 agent 编一套导航语言

16 种类型。这是另一个让我纠结很久的设计决策。

刚开始只有 5 种类型:Knowledge、Bug、Pattern、Documentation、Reference。够用吗?够。但面对 275 份文档,agent 需要一个更精细的筛选维度。

最终的类型清单是这样的:

类型定义例子
Knowledge长期有效的事实/概念"什么是 Embedding"
Bug已修复的常见陷阱"Agent 循环调用自身导致上下文爆炸"
Pattern可复用的方案"Executor-Curator 分离模式"
Reference外部链接/规范引用"OKF SPEC v0.1"
AgentInstruction给 agent 的行为指令"Code Review 流程"
Documentation项目文档/架构描述"自进化管线架构"
Plan计划和路线图"2026 Q3 优化计划"
Report执行报告/分析"7 月性能评估报告"
HermesSkillHermes agent skill 定义"verification-loop"
SourcePatternPi agent config 模式"coding-agents 配置"

— 以及另外 6 个子类型,在特定 bundle 内部使用。

加这么多的动机很简单:类型标签就是 agent 的导航语言。

当 agent 在调试一个自进化管线的问题时,它需要快速过滤出 Bug 类型的文档,而不是花时间去读 Pattern 或 Knowledge。当它要参考一个现有的 skill 实现时,直接在 HermesSkill 类型里找。

类型设计的原则是:agent 的筛选意图决定粒度。

如果 agent 经常需要"找到和 X 相关的所有 bug 记录"——那就把 Bug 抽出来作为独立类型。如果 agent 从来不区分 Reference 和 Documentation——那就合并它们。

听起来简单。但实际操作中,你会发现有些文档天然跨类型。一篇关于"修复了 compression 导致 session 分裂"的文档,它既是一个 Bug(记录了问题修复),也是一个 Knowledge(解释了 compression 和 session 的关系)。

怎么处理?

我试了两种方案。方案 A:允许一个文档有多个 type。方案 B:规定一个文档只能有一个主 type,跨类型信息用 tags 补充。

我选了方案 B。

为什么选 B 不选 A? 因为我追踪了 agent 的实际行为。Agent 在筛选时用的是精确匹配——它会问"给我所有 type=Bug 的文档"。如果一篇文档有 3 个 type,它在 3 个筛选结果里都会出现,既造成重复,又稀释了筛选的含义。

一致性很重要——但不必追求完美。Agent 能处理一定程度的不一致。一篇标记为 Knowledge 但包含 Bug 信息的文档,agent 读了之后不会出问题。它只是会在筛 Bug 时漏掉这篇。但 tags 可以补上这个缺口。

所以我的规则是:type 决定主分类,tags 覆盖跨类型信息。

双形态存储的代价

这个决定让我犹豫最久。

同一个知识库中,有 46 篇文档同时以两种形态存在:

  • 作为 Hermes Skill(放在 ~/.hermes/skills/ 下)
  • 作为 Source Pattern(放在 Pi agent 的配置目录下)

内容一样,只是 frontmatter 不同。Hermes Skill 的 frontmatter 需要 type, description, tags;Pi agent 的 Pattern 格式需要 trigger, context, workflow

为什么要这么折腾?直接让两个系统读同一份文件不行吗?

我试过。不行。

Hermes 和 Pi 是两个完全不同的 agent 系统。Hermes 读取 skill 的方式是加载整个 SKILL.md,解析 YAML frontmatter。Pi 读取 pattern 的方式是匹配 trigger 条件,注入 context。它们对 frontmatter 的 schema 要求完全不同——强行用同一份文件,意味着要么牺牲一方,要么搞一个超复杂的兼容层。

双形态存储解决了一个很现实的问题:同一份知识,能被两个 agent 系统以各自的方式消费。

代价呢?

同步成本。 每次更新知识内容,需要在两个位置做同样的修改。我写了一个自动同步脚本(其实就是一个 curator agent 任务),但仍然偶尔出现一个更新了另一个没更新的情况。

但我觉得这个代价值得。

因为如果不做双形态,最大的受损方不是 agent,是我。每次我在 Hermes 里写了一个好 skill,想要在 Pi 里复用——就得重新写一遍。重写两遍的事情,我坚持不了一个月。

双形态存储其实就是:一次创作,多渠道分发。 代价是同步成本。收益是持续可用。

值不值?以我现在的文档量(46 篇双形态),我觉得值。超过 100 篇可能就需要更好的自动化了。

踩坑流水账

说几个货真价实的坑。不是"我们遇到了一个挑战然后优雅地解决了",而是"妈的又出错了"的那种。

自动分类不精确

我天真地写了一个自动分类脚本,基于文档的 frontmatter、文件名和关键词,自动分配 bundle。

结果呢?大约 20% 的文档被分到了错误的 bundle。

最离谱的一篇——一份关于"session 压缩算法优化"的技术笔记,被分类到了 Skills 引用 bundle。追下去看才发现,分类脚本看到"compression"这个词,又看到文档里提到了一个 skill 的用法,就以为它属于 Skills。

分类不精确的本质是:cross-bundle 文档是常态,不是异常。

我现在不做全自动分类了。采用"预分类 + 人工确认"的流程:自动脚本给出建议的 bundle,我每周花 15 分钟过一遍确认。

INDEX 膨胀

自动生成的 INDEX 有一个天然的问题:它越生成越详细。

最开始,INDEX 只包含文件名和类型。然后我加了简介。然后加了 tags。然后加了一句话摘要。然后——INDEX 文件变得比任何一篇内容文档都长。

285 行的 INDEX 文件。

Agent 读完它需要消耗相当多的上下文 token。而且内容太详细之后,agent 反而失去了"导航"的功能——它直接在 INDEX 里看到了所有内容,就不再深入读具体文档了。

解决方式是人工控制 INDEX 的粒度。 只有 title + type + 一句话摘要。详细信息留在各文档。超出三句话的摘要自动截断。

迁移时 frontmatter 残缺

1% 的旧文档没有 frontmatter。

对,就是那些在最早期写的、还没形成格式规范时的产物。没有 type、没有 tags、没有 timestamp

这导致 INDEX 生成脚本报错,因为 parser 找不到必填字段。

我的做法不是批量修复——因为有些文档的准确 type 我已经不确定了,乱填一个比不填更有害。

加了一个 fallback 逻辑:如果缺少 frontmatter,自动赋予一个 type: Unknownstatus: needs-review。INDEX 里会单独列出这些"待分类"文档。慢慢补。

把知识库设计成查询语言

做到这一步,我有一个越来越强烈的感受:

给 AI 准备知识库,本质是在设计一套查询语言。

你看,传统的知识库设计思维是"如何把信息组织好"——分类、索引、标签、全文搜索。这是人类图书管理员的思维。

但 agent 不需要"组织好"的信息。它需要的是能被高效定位和消费的信息。

区别在哪里?

人类知识库的查询路径是:浏览 → 找到 → 阅读。 Agent 知识库的查询路径是:索引定位 → 类型筛选 → 定向读取。

所以你的知识库结构就是 agent 的查询语言:

  • Bundle 是"命名空间"(namespace)
  • Type 是"类型系统"(type system)
  • Tags 是"查询标签"(query tags)
  • INDEX 是"查询计划"(query plan)

听起来像什么?像一种编程语言的类型系统。

对。知识库结构就是 agent 的 DSL。

这件事让我想通了一个更大的问题:为什么很多 RAG 系统在生产环境里表现不佳?不是因为 embedding 不够好,不是因为 chunk size 没调对——而是因为知识库本身的结构没有被设计成可查询的。

你有一个 10000 份文档的知识库,但没有 bundle,没有类型系统,没有索引导航——那你有的只是一个文件列表。agent 用起来,和一个没有 schema 的数据库一样痛苦。

如果你从一开始就把知识库设计成"agent 的查询语言",你会在所有细节上做出不同的决策:

  • 加新文档时会想:这个文档的 type 应该是什么?它会被谁在什么场景下消费?
  • 划分 bundle 时会想:这个 bundle 的查询意图是什么?agent 会在什么任务里导航到这里?
  • 写摘要时会想:agent 读到这个摘要能不能判断要不要深入?

这就是我现在做知识库的方式。

275 份文档。6 个 bundle。16 种类型。

不是最多的,也不是最牛的。但它是我见过第一个真正被 agent 好好用起来的知识库。


这篇文章是知识库结构设计的上篇。下篇会聊:如何让 agent 在知识库里做"横向迁移"——从一个 bundle 学到的东西,怎么在另一个 bundle 的场景里被复用。如果你想第一时间收到,关注博客更新就好。

项目相关代码和配置:知识库结构在 compound-system 仓库 里,OKF 转换工具链在独立 CLI 中。