CHAPTER 14 · STAGE 03 · 理解可靠性与扩展
面对大型仓库时怎样阅读源码
“我想理解整个仓库”不是可执行问题。把它缩小为:用户消息从哪里进入;Tool Call 谁解析;Session 怎样派生模型历史;Bash Provider 怎样启动进程;一个插件卸载时怎样清理。先确定问题,不要先打开最大的文件
“我想理解整个仓库”不是可执行问题。把它缩小为:用户消息从哪里进入;Tool Call 谁解析;Session 怎样派生模型历史;Bash Provider 怎样启动进程;一个插件卸载时怎样清理。
每个问题都应该有一个可观察的起点和终点。阅读范围由数据流决定,而不是由目录大小决定。
三文件法
面对一个新包,第一次只选三个文件:README、主要 src/index.ts 或 types、一个代表性测试。
README 给出包级职责与配置;入口源码展示公开 API 和注册;测试展示重要行为和失败方式。如果三者仍不能回答问题,再沿一个精确调用继续深入。
不要同时打开十几个内部 helper。没有包级心智模型时,helper 只会增加局部名词。
生产者—记录—消费者法
看到一个事件或数据类型时,依次找:谁创建它;它是否写入 Session 或 Registry;谁读取它。
例如 tool/result:AgentLoop/Tools 生产并追加;Session 保存;deriveMessages、UI、Persistence、Telemetry 等消费。这样比只阅读类型定义更快理解存在理由。
Service 三角色法
看到 ctx.<key> 时画三列:
| Definition | Provider | Consumer |
|---|---|---|
| 谁声明 API | 谁实现或注册 | 谁调用或暴露给模型 |
如果只能找到 Definition,可能 Provider 在叶节点配置;如果 Tool 自己做全部实现,可能它不是可替换 seam;如果存在多个 Provider,继续找路由 key。
事件域判断法
先判断事件属于 Session、Agent 还是 Capability。
需要重启后重建的是 SessionEvent;携带 live Agent 并拦截 Turn/Step 的是 agent event;属于工具、文件、遥测等能力策略的是 capability event。
判断错误会导致功能挂在不恰当扩展点。例如只影响工具权限的逻辑不应写进 AgentLoop;模型可见消息也不能只通过 live event 临时注入。
配置反向追踪法
运行时找不到一个 Service 时,不要只查 import。按下面顺序追踪:
- 入口选择哪个 Profile。
- Profile 叠加哪些 Bundle。
- Bundle 或组合包挂载哪个插件。
- 插件是否声明 inject,依赖是否实际存在。
- patch 是否替换或禁用该行。
--dump-config的最终树是什么。
源码存在但配置未挂载,是插件系统中最常见的认知误差之一。
从测试反向读实现
测试给出输入、操作与预期结果。先读 describe/it 名称和断言,跳过 fixture 细节;确认行为后再进入 helper 与实现。
遇到复杂 race test,先画参与者时间线:测试线程、AgentLoop、Tool Promise、取消 signal、dispose。再读 deferred promise 怎样控制顺序。
不要把测试实现方式误认为公开 contract。README、JSDoc 与类型决定消费者依赖的行为;测试只是证明手段。
使用搜索的具体方法
先搜索独特标识符,例如事件名、ctx key、class 名或错误码:
rg -n "agent/pre-step" packages docs
rg -n "class SessionStore" packages/core/session
rg -n "NO_ADAPTER" packages
搜索结果按生产者、消费者、测试和文档分类。不要逐条打开;先确定哪一类能回答当前问题。
查文件使用:
rg --files packages/core/agent-loop
这比在文件树中反复展开目录更快建立包的规模感。
阅读大函数的方法
第一遍只标记阶段、持久事件和外部 Service 调用。第二遍跟一个正常路径。第三遍才研究错误、取消和并发分支。
例如 AgentLoop 主函数第一遍只找 turn/start、step/start、llm、executeToolCalls、step/end、turn/end。正常路径清楚后,再研究 reject、retry、cancel、dispose。
如果第一遍就逐行理解所有 catch 和 rollback,很容易失去主线。
阅读类型密集代码的方法
把类型分成四类:领域数据、运行时 handle、跨边界 JSON、注册表/事件映射。
领域数据例如 Message;运行时 handle 例如 AgentHandle;跨边界 JSON 例如 SessionEvent data;映射类型例如 EventMap。不同类别的验证与生命周期完全不同。
先确定类别,再理解泛型细节。一个 Readonly SessionEvent 与一个可 dispose AgentHandle 不能用同一套思路理解。
什么时候读 Agent Note
当你已经知道当前机制,却不理解“为什么不用另一个明显方案”时读 Agent Note。它记录设计取舍、放弃的替代方案和验证要求。
不要用 Agent Note 替代当前源码与正式文档。Implemented Note 解释决策,package README 和 subsystem reference 描述当前公开行为。Archived Note 是冻结历史,不是当前权威。
什么时候读 vendor
只有当问题明确属于 Cordis 本身,并且 Primer、Tutorial、Cordis API reference 不能回答时,才进入 vendor。
学习 Harness 插件时通常不需要理解 Cordis Loader 的全部内部实现。先使用公开 Context、Service、event 和 effect API。
一次 45 分钟阅读会话模板
前 5 分钟:写下一个问题和预期终点。
接下来 10 分钟:读包 README 与架构链接,画角色图。
接下来 15 分钟:读入口源码和一个正常路径。
接下来 10 分钟:读一个测试并运行聚焦命令。
最后 5 分钟:用自己的例子复述,记录一个仍不明白的问题。
迷路时的恢复方法
停止继续点文件,回到最近一次你能解释的对象。写出它的输入、输出和 owner,然后只沿一个未解释的边继续。
如果同时出现五个未知概念,把问题降级为术语查阅;如果只剩一个未知方法,搜索它的生产者和测试;如果运行时行为与源码猜测不同,检查最终配置树。
本篇练习
- 选择
ctx.tools,用三文件法写一页笔记。 - 搜索
session/flush,把结果分成声明、生产、消费、测试和文档。 - 选择一个 Tool 包,画 Definition/Provider/Consumer 三列。
- 给自己设置 45 分钟计时,只追踪一个正常 Tool Call,不研究取消分支。
验收标准:你能主动控制阅读范围,并在 45 分钟结束时产出一张有起点、终点和源码证据的图,而不是留下二十个打开的文件。