如何真正理解一个复杂系统 —— 从学习 Hermes 到复刻最小版本
问题从哪里来
最近花了一周学习 Hermes—— 一个功能丰富的 Agent 开发框架。
它的架构包含 Gateway 路由、多平台适配、MCP 集成、浏览器自动化、语音与视觉、自进化机制等多个模块。
打开代码仓库的第一感觉是:东西太多,从哪里开始。
常见的学习方式是顺着文档和代码一路读下去。
但这种方法的问题:读完了不等于理解了。你可能知道每个模块叫什么,但问」这个系统为什么这样设计」,答不上来。
真正卡住的是哪一步
学习复杂系统时,真正卡住的不是」代码看不懂」,而是 没有一个检验理解程度的标准。
读了一遍 Gateway 的代码,你以为懂了。但换个平台运行呢?去掉一个模块看看哪里会坏呢?动手写一个最简版本呢?
你会发现很多」以为懂」的地方经不起推敲。
静态阅读给的是幻觉,动态交互给的是真相。
我在 Windows 上跑 Hermes,终端功能直接挂了。原因有两个:
- 代码默认启动 WSL 下的 bash,但我的环境里 WSL 不可用。
- 临时文件路径硬编码为
/tmp/...,Windows 下 Python 和 bash 指向不同位置。
这些问题文档里不会写,代码 review 也难看出来。只有实际运行才暴露。
我最后怎么处理
整个过程分三步:解剖、修复、复刻。
flowchart LR
A[解剖] --> B[修复]
B --> C[复刻最小版本]
C --> D[验证理解]解剖:按系统分模块阅读。不是逐行读代码,先理清模块边界和职责。
例如 Gateway 负责统一入口和路由,平台适配器抹平 OS 差异,自进化体系由技能创作闭环、生命周期 Hook、对话轨迹记录三块组成。每个模块问一个问题 ——「如果没有这个模块,系统会怎么样?」
修复:在 Windows 上把系统跑起来,遇到报错不跳过,追到底。
写了 LocalBackend._find_bash() 静态方法定位 Git Bash 路径;把硬编码临时目录改成 tempfile.gettempdir() 动态获取。修 bug 的过程,逼你理解系统对运行环境的假设。
复刻:最关键的一步。写一个 Agent 项目,复刻 learn-hermes 的基本功能。
先只跑通对话 loop,其他高级功能用 mock 占位。这不是偷懒 —— 最小可用版本让你在不受细节干扰的前提下,验证对核心架构的理解。骨架跑稳了,再逐个接真实功能。
同时在课程设计项目里实践了 Skill 驱动开发:先写 PRD 定边界,再做 Tech Plan(只规划文件名,不写实现),再用 TDD 推进编码,大里程碑之间做审计。
这件事留下的经验
第一,跑起来比读完更重要。
文档和代码随时能查。但不跑一次,你永远不知道漏掉什么。Windows 下的两个 bug 很小,背后反映的是系统对 POSIX 环境的隐含假设 —— 只有动手才能获得这个认知。
第二,复刻最小版本是终极检验。
你说你理解了某个系统,那你应该能写出它的最简版本。不需要功能完整,核心骨架要能跑。mock 是合法的设计选择 —— 让你聚焦架构而非细节。
第三,遇到 bug 是好事。
在理想环境正常工作的系统,和你能在各种环境让它正常工作的系统,是两种理解层次。环境兼容性问题往往暴露了设计中」未经审视的假设」。
第四,计划阶段克制细节冲动。
Tech Plan 只写文件名不写实现内容。这个策略在保护你:强制把注意力放在模块划分和职责边界上,而不是某个函数的实现。后者是 TDD 阶段的事。