01|五分钟仓库地图:为什么这个仓库看起来像迷宫
先别从界面代码往里走
打开这个仓库的包目录,第一反应通常是数文件夹。几十个能力组,组下面还有包,光是"某个能力家族"这一类目录就能列出七八个子包。于是很多人开始按字母顺序逛,或者直接点进看起来最像"产品"的那个界面目录。
这条路是错的。
迷宫感不是因为仓库真的乱,是因为你用了错误的坐标系。这个仓库不是"一个应用加一堆工具库",它是三层东西叠在同一个工作区里:控制脊(决定循环、会话、工具怎么跑的那一小撮核心能力)、可替换能力(凑齐了定义、实现、消费者三个角色的能力家族)、产品外壳(怎么启动、界面怎么呈现)。目录名只是抽屉标签,不代表依赖顺序。五分钟够用的地图,只回答一个问题:接下来该打开哪扇门。
1. 顶层其实只有四类东西
先给判断:值得记住的不是几十个包名,是四种职责。
| 你看见的目录类别 | 它实际是什么 |
|---|---|
| 被完整纳入仓库的框架源码 | 底层插件框架 Cordis,被整份拉进仓库自己维护,不是普通的第三方依赖 |
| 能力包的集合 | 产品本身。每个能力家族是一个抽屉,抽屉里的每个包才是真正的发布单元 |
| 启动入口和示例配置 | 怎么把上面这些包拼起来跑起来,是叶子,不是骨架 |
| 架构文档、决策记录、校验脚本 | 地图、决策依据、门禁,持续检查代码有没有跑偏 |
底层插件框架被整份拉进仓库、可审计、可打补丁的原因很直接:他们想要完全拥有这一层,而不是依赖外部版本升级的节奏。这也意味着你在某个能力包里写"我依赖插件框架"的时候,解析到的永远是仓库自己这一份,不是外部随时可能变动的版本。
如果没有这层划分会怎样?你会把底层框架当成随手升级的第三方库,把示例配置当成产品入口去改循环,把某个界面包当成整个 harness。三件事都会让你在错误的层次上做看似合理的局部修改。
2. 一个容易踩的坑:目录抽屉名不等于包名
这个仓库有个统一规则:物理目录路径按"能力家族/具体包"两层组织,但发布出去的包名永远遵循同一个前缀约定,跟目录名不是一一对应。
举个例子:某个能力家族的目录叫"shell",家族下面又有一个具体包也叫"shell"——外面那层"shell"是能力家族的抽屉标签,里面那层"shell"才是真正定义这个能力该长什么样的那个包。
这个坑会让人做出两种低效的搜索:
- 按抽屉名字去找一个不存在的包——比如以为核心能力家族本身就是一个包,实际上这个抽屉下面是好几个各管一块的独立包:会话日志、工具注册表、Agent 接口、循环实现,各自是独立的发布单元。
- 看到某个能力家族里出现了"工具消费者"这样的包,就以为具体的执行逻辑住在"定义"那个包里——它故意不住在一起,见下一节。
活的抽屉分类清单会随项目演进变化,根目录里那份速记性质的架构树也会滞后于实际情况。以后认路,优先信每个能力家族自己的说明文档,不要全信根目录那张一次性画的树。
3. 三层走法:控制脊 → 可替换能力 → 产品外壳
这个仓库有一条比任何一张表都值钱的依赖纪律:扩展插件永远依赖"这个能力应该长什么样"的定义,绝不直接依赖某一个具体实现。默认的循环实现可以被整个换掉;界面、钩子、工具这些插件打的都是"Agent 该有什么接口"这份约定,不是某个具体司机。把这句话当成看地图时的图例。
第一层:控制脊,先认这几个核心能力
控制脊是一小撮包,各自拥有一件不能被别的包染指的事情:
| 核心能力 | 它拥有什么 |
|---|---|
| 按 Agent 分域的注册原语 | 谁能看见某个贡献、谁拥有它 |
| 会话日志 | 只增不改的事件流和内存里的存取接口 |
| Prompt 组装 | 把各插件贡献的提示词片段和工具schema拼成最终请求 |
| 工具注册表 | 有作用域的工具集合和带守卫的执行管道 |
| Agent 接口 | 对外的行为约定和相关的生命周期事件 |
| 默认循环实现 | 实现上面那份接口约定的默认司机 |
| 模型消息与流式协议 | 消息格式的公共词汇和模型适配器的接入点 |
这几个名字是后面每一章的坐标。这里有个容易混的地方:控制脊里的"会话日志"(内存里的事件流本身)和更外层的"会话持久化家族"(负责落盘、投影、生成标题、上报遥测的那一整套数据面)不是一回事,前者是骨架,后者是围着骨架转的一整圈应用能力。第一次逛的人几乎必混,先记下这个区分,第 05 章会专门拆。
第二层:可替换能力,拿"执行命令"这个能力家族当尺子
前面第 00 章提到的"执行命令"能力家族,是整个仓库里最干净的标本:一个包定义"执行世界该长什么样",若干个包各自实现(本机执行、沙箱执行……),再有一个包把这个能力包装成模型能调用的工具。
看见某个能力家族里同时出现"不带修饰的抽象定义包"、"带具体环境后缀的实现包"、"带工具前缀的消费者包",那就说明你找到了一个完整的能力家族,不是一堆碰巧同名的工具。文件系统、子进程、持久终端、语言服务、网页访问、子代理,全部按这把尺子摆放。
有些能力家族目前还只是实验性的探索性实现,第一次路过可以先当它不存在,等你熟悉了标准样本之后再回头看。
第三层:产品外壳,启动器很薄,界面只是其中一种叠法
命令行启动器本身非常薄:它只负责按运行模式动态加载对应的启动逻辑,然后把解析好的参数丢给真正的启动流程。产品外壳的零件分三处:
- 所有启动入口共用的启动胶水;
- 一组分层的配置补丁包,负责把"基础层"、"带界面的 Web 层"、"无服务进程的一次性运行层"这几种产品形态拼出来;
- Web 界面的宿主端(负责对外服务)和浏览器端(负责渲染)。
真正组装起来的 Web 应用,是启动器先叠好基础层,再让宿主端去服务浏览器端。浏览器端本身只是控制脊的一个消费者——它从会话事件里渲染画面,不自己维护另一份状态。第 00 章说过"模型看见的必须能从日志重建",这里再补一句:人看见的画面,同样是从这条日志投影出来的,不是原创的第二份真相。
三层图里的箭头方向是"构建在……之上",不是调用方向的唯一表示——外壳消费能力层,能力层消费控制脊,反过来则不允许。
4. 五分钟只开这五扇门
按这个顺序,别加戏:
- 架构总览文档——整个仓库里唯一值得当"先读我"的一页,事件域、一次运行的完整节拍、新行为该往哪挂,都在这一页交代清楚。
- 控制脊的说明文档——记住那几个核心能力各自拥有什么,先不要急着钻进循环实现的具体代码,那是第 04 章的事。
- 一个标准能力家族——用"定义/实现/消费者"三件套去核对,这是后面理解文件系统、子代理这些能力家族时的参照物。
- 启动器——确认它有多薄,循环不在这里,界面也不在这里。
- 一份最小示例配置——看一份最简单的配置怎么把控制脊和一两个能力家族拼到一起。产品味更重的完整 Web 应用是同一套叠法再加一层界面配置,不是另一套系统。
五分钟到此结束。下面这些门现在先别开:
- 浏览器端的具体渲染代码和它的测试快照——那是表面,会把你按界面故事重新组织大脑;
- 各能力自动生成的事件签名目录——那是查阅用的参考手册,当小说读会累死人;
- 面向浏览器的静态文档站点——它只是内部文档的一份投影,不是第三份架构;
- 语言 SDK 和沙箱底层实现——主线走通再回来看。
文档本身也分层:架构图归架构总览文档,包契约归各自的说明文档,设计决策归内部笔记,自动生成的目录归对应的生成区域,操作步骤归操作手册。你在这本电子书里看到的判断,对应的"当时为什么这么选"通常住在内部决策笔记里。别在三个地方同时找同一句话。
5. 结语:带走一句话
这个仓库看起来像迷宫,是因为你按文件夹顺序走;按控制脊 → 可替换能力 → 产品外壳这条路走,它只剩五扇该先开的门,其余的都只是这三层之上的重复形状。