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。按下面顺序追踪:

  1. 入口选择哪个 Profile。
  2. Profile 叠加哪些 Bundle。
  3. Bundle 或组合包挂载哪个插件。
  4. 插件是否声明 inject,依赖是否实际存在。
  5. patch 是否替换或禁用该行。
  6. --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,然后只沿一个未解释的边继续。

如果同时出现五个未知概念,把问题降级为术语查阅;如果只剩一个未知方法,搜索它的生产者和测试;如果运行时行为与源码猜测不同,检查最终配置树。

本篇练习

  1. 选择 ctx.tools,用三文件法写一页笔记。
  2. 搜索 session/flush,把结果分成声明、生产、消费、测试和文档。
  3. 选择一个 Tool 包,画 Definition/Provider/Consumer 三列。
  4. 给自己设置 45 分钟计时,只追踪一个正常 Tool Call,不研究取消分支。

验收标准:你能主动控制阅读范围,并在 45 分钟结束时产出一张有起点、终点和源码证据的图,而不是留下二十个打开的文件。