五大 Agent 代码量全拆解
用 cloc 对 Claude Code、Hermes Agent、OpenClaw、Codex CLI 四个业界仓库 + 我们自己的 FFAI Agent 做逐文件统计,回答三个问题:一共多少代码?每个模块多少代码?多少行代码实现了什么功能?本页所有行数都来自工具实测(git 追踪文件、纯代码行、剔除空行和注释),不是估算。各篇架构解剖是本页的姊妹篇——那边讲「怎么实现的」,这边讲「花了多少代码实现」。
00五项目对比总览
同一个统计口径下,五个项目放在一张表里。先记住一件事:「仓库总行数」和「核心实现行数」是两个完全不同的数字——仓库里有测试、文档、生成物、移动端 App、还原出来的依赖库,直接比总数会得出错误结论。
| 项目 | 仓库总行数 | 核心实现口径 ★ | 文件数 | 主语言 | 一句话画像 |
|---|---|---|---|---|---|
| Claude Code | 734,210 | ≈390,592 restored-src 手写 TS(余为还原出的依赖+打包产物) | 4,419 | TypeScript | 单机终端 agent,UI 占核心的 1/4 强 |
| Hermes | 1,571,980 | ≈396,646 运行时核心 Python(余为测试 48万 + 网站 + skills 内容) | 5,930 | Python | Python 心脏 + 多张脸(TUI/桌面/30+平台) |
| OpenClaw | 5,495,675 | ≈1,063,216 src 实现(剔 143万测试;另有 151万 extensions) | 20,972 | TypeScript | 常驻 Gateway 消息网关,体量是其余三家之和 |
| Codex | 1,193,059 | ≤599,173 Rust 运行时上界(含无法剔除的内联测试) | 4,625 | Rust | 123 个 crate 的 Rust 工作区,沙箱做得最重 |
| FFAI(我们的) | — 宿主 = 整个 ffworkspace 企业平台仓库(agent 相关范围全口径 ≈71,265) | ≈48,807 后端模块 34,131 + Web UI 11,190 + 桌面壳 2,990 + 协议包 496(测试 20,466 另计) | 326 | TypeScript | 长在企业平台上的多租户模块:平台复用 + 后发临摹,体量最小 |
核心实现体量对比(万行)
同一个功能,各家花了多少行(跨项目抽样)
口径各有重叠与差异(详见各项目章节的口径说明),量级对比仍然成立。
| 功能子系统 | Claude Code | Hermes | OpenClaw(实现口径) | Codex(运行时口径) | FFAI(模块口径) |
|---|---|---|---|---|---|
| Agentic 主循环/引擎 | 3,755 | 12,626 | 52,535 | 15,340 | 3,483 |
| 工具系统 | 41,125 | 45,625 | 30,699 | 27,648 | ≈3,945 85 个 @AgentTool 挂业务方法,肉在业务模块 |
| 权限/安全/沙箱 | 23,041 | 4,758 | 22,066 | 49,214 沙箱 29,859 + 审批 19,355 | ≈8,076 沙箱 7,375 为主;权限复用平台 IAM 不计入 |
| 模型/API 层 | 11,365 | 14,531 | 47,747 | 20,011 | 4,736 5 家 provider + IR + 路由 + 配额 |
| 上下文压缩 | 4,626 | 5,846 | — 并入会话/记忆 182,782(全口径) | 5,679 | 220 后发临摹 CC 最终答案,一个文件 |
| 终端/主 UI | 106,624 | 63,942 | 165,808 Control UI(Web) | 153,715 | 14,180 浏览器 + Lark DS,渲染引擎白拿 |
| MCP | 17,297 | 4,388 | — 并入插件系统 | 16,479 | 375 仅 stdio/SSE 两 transport,无市场 UI |
CCClaude Code:73 万行仓库里,真正的源码是 39 万行
基于官方 npm 包 @anthropic-ai/claude-code@2.1.88 的 source map 还原仓库。本节所有行数均来自 cloc 统计的代码行(不含空行和注释),逐文件明细可复算,没有任何估算值。
1 · 总量与口径:先分清「源码」和「附带品」
如果直接对整个仓库跑统计,会得到一个吓人的数字:734,210 行代码、4,419 个文件。但这个数字有很大水分——这个仓库是用 source map(打包时留下的「源码地图」)从发布产物反向还原出来的,还原过程会把「Anthropic 工程师手写的代码」和「顺带被打包进去的第三方开源库」一起吐出来。给小白读者的第一课就是:看一个项目多大,先问统计口径。
| 仓库区域 | 代码行 | 文件数 | 占比 | 这是什么 |
|---|---|---|---|---|
restored-src/src/ | 390,592 | 1,886 | 53.2% | 真正的手写源码:还原出的 TypeScript/TSX,目录结构、变量名、注释都是原始的 |
restored-src/node_modules/ | 330,797 | 2,522 | 45.1% | 第三方依赖被 source map 一并还原(highlight.js、AWS SDK、zod……),不是 Anthropic 写的 |
package/ | 12,420 | 5 | 1.7% | npm 实际发布的打包产物:压缩后的 cli.js(11,273 行)+ 类型声明,是上面源码「编译打包后的样子」 |
restored-src/vendor/ + 根文件 | 401 | 6 | 0.05% | 少量内嵌第三方代码和 README |
| 合计 | 734,210 | 4,419 | 100% | — |
按文件类型拆:TypeScript 398,585 行、JavaScript 335,530 行(几乎全是 node_modules 和打包产物——手写源码里 JS 只有 5 行)、Markdown 仅 62 行、JSON 仅 33 行。几乎没有文档和数据文件,因为 source map 只还原代码,官方文档不在 npm 包里。
本节结论口径:Claude Code 的真实手写源码 ≈ 390,592 行代码(1,886 个 TS/TSX 文件;若把注释 86,830 行和空行 35,799 行也算上,原始文件约 51.3 万行)。下文所有百分比都以 390,592 为分母。另注:《源码架构全解剖》页引用的行号(如「query.ts 1729 行」)是文件的原始行号(含空行注释),本页统一用 cloc 代码行(query.ts = 1,282),两者不矛盾,是同一文件的两种数法。
2 · 逐模块代码量:钱都花在哪个目录
下表是 restored-src/src/ 下全部一级目录(模块职责取自《源码架构全解剖》;标「未核实」的是解剖页未覆盖、仅按源码文件名抽查判断的):
| 目录 | 代码行 | 文件 | 占比 | 一句话职责 |
|---|---|---|---|---|
utils/ | 127,128 | 564 | 32.5% | 横切工具箱:bash 解析、权限、hooks、插件、settings、消息序列化……最大杂物间(下文细拆) |
components/ | 74,351 | 389 | 19.0% | React 终端组件:权限对话框、消息流、输入框、各种菜单 |
services/ | 39,727 | 130 | 10.2% | 无 UI 服务层:MCP 客户端、API 请求、上下文压缩、工具执行编排 |
tools/ | 38,494 | 184 | 9.9% | 30+ 个内置工具本体(Bash/Edit/Read/Agent……) |
commands/ | 23,116 | 191 | 5.9% | 斜杠命令(/model /plugin /ide /insights……) |
hooks/ | 14,621 | 104 | 3.7% | React hooks(UI 交互逻辑——不是 Claude 的 Hooks 扩展功能,那个在 utils/hooks.ts) |
ink/ | 13,205 | 96 | 3.4% | fork 版 Ink:React → 终端 ANSI 的自研渲染引擎 |
cli/ | 9,463 | 19 | 2.4% | headless 模式(-p)执行器 + WebSocket/SSE 传输层 |
| src 根文件 | 8,983 | 18 | 2.3% | 心脏所在:main.tsx 入口、query.ts 主循环、QueryEngine.ts、Tool.ts 接口 |
bridge/ | 8,703 | 31 | 2.2% | 远程会话 / IDE 桥接(bridgeMain、replBridge、JWT 会话,未核实细节) |
screens/ | 4,447 | 3 | 1.1% | 主屏幕,几乎全是 REPL.tsx 一个巨型组件 |
entrypoints/ | 3,270 | 8 | 0.8% | 进程入口 cli.tsx(轻量引导层)+ SDK 控制协议 |
native-ts/ | 3,240 | 4 | 0.8% | 原生模块的纯 TS 替代实现:yoga-layout 排版、color-diff、file-index(未核实) |
skills/ | 3,171 | 20 | 0.8% | Skill 加载器 + 内置 skills(updateConfig、keybindings……) |
types/ | 2,458 | 11 | 0.6% | 共享类型定义 |
tasks/ | 2,403 | 12 | 0.6% | 后台任务(远程 agent 任务、cron) |
keybindings/ | 2,233 | 14 | 0.6% | 可自定义快捷键系统 |
constants/ | 1,877 | 21 | 0.5% | 常量 + system prompt 文案(prompts.ts) |
bootstrap/ | 1,235 | 1 | 0.3% | 启动引导(未核实) |
buddy/ | 1,192 | 6 | 0.3% | 终端桌宠动画(CompanionSprite 像素小人,未核实用途边界) |
vim/ | 1,148 | 5 | 0.3% | 内置 vim 编辑模式 |
memdir/ | 1,140 | 8 | 0.3% | 记忆目录(agent 长期记忆存取,未核实) |
| 其余 14 个小目录 | 4,987 | 57 | 1.3% | state / context / remote / query / migrations / server 等 |
其中最大的 utils/(12.7 万行)值得再切一刀,它内部藏着好几个完整的功能子系统:插件系统 13,772 行、bash 解析 8,646 行、权限判定 6,509 行、多 agent 协作(swarm)5,061 行、单文件的消息序列化 messages.ts 4,338 行、单文件的 Hooks 引擎 hooks.ts 3,905 行、会话落盘 sessionStorage.ts 3,532 行……所以「utils 占 1/3」不是代码乱堆,而是大量横切子系统按惯例都挂在这个名字下。
3 · 多少行代码实现了什么功能
这是最有信息量的一问。下面 12 个功能子系统的行数是按功能跨目录把相关文件逐个加总算出来的(一个功能的代码常散落在 tools/、utils/、components/、services/ 四处)。注意个别子系统之间有少量重叠(会标注),因此不能把 12 个数直接加成总数。
① Agentic 主循环 —— 3,755 行 / 11 文件
整个产品的心脏:query.ts(1,282)+ QueryEngine.ts(1,060)+ queryHelpers.ts(432)+ Stop hooks 与 token 预算(517)等。这 3,755 行干的事就一句话:while(true) { 调模型 → 有工具调用就执行 → 结果喂回去 → 再调 }。行数花在边界上:中断时补合成 tool_result、每轮的裁剪与压缩检查、maxTurns/预算熔断。
② 工具系统 —— 41,125 行 / 188 文件
tools/ 全部(38,494)+ services/tools/ 执行编排(2,631)。头部:BashTool 8,524、PowerShellTool 5,913、AgentTool 5,351、LSPTool 1,577、FileEditTool 1,432、FileReadTool 1,308。每个工具 = 执行逻辑 + 权限判断 + 给模型的说明书 + 终端 UI 渲染,四合一全塞一个目录,所以单个工具动辄上千行。
③ Bash 命令安全解析 —— 19,384 行 / 51 文件
为了敢让 AI 执行 shell 命令,Anthropic 写了一个自己的 bash 语法解析器:bashParser.ts(3,846)+ AST(1,504),再加权限匹配 bashPermissions.ts(1,725)、注入检测 bashSecurity.ts(1,580)、三份「只读命令」白名单校验(合计 3,903)。复合命令拆开逐个查、环境变量前缀剥离但 LD_PRELOAD 绝不剥——全是防绕过。(其中 8,524 行属 BashTool,与②重叠。)
④ 权限系统 —— 23,041 行 / 92 文件
「模型想做 X,允许吗?」的完整判定链。判定核心 permissions.ts(1,169)、规则初始化(1,068)、文件系统边界(982)、自动分类器 yoloClassifier.ts(1,133);而 UI 占了一半——components/permissions/ 有 11,531 行,51 个文件全是各种「允许吗」对话框。安全功能一半的代码其实是在把问题问清楚。(含 ③ 的 bashPermissions 等约 2,700 行重叠。)
⑤ API 层 + 认证 + 成本 —— 11,365 行 / 33 文件
services/api/(8,092:请求组装与 SSE 流式消费 claude.ts 2,713、错误分类 963、重试降级 withRetry 624、缓存断点检测 574……)+ OAuth/API key 认证(2,268)+ 缓存断点策略 utils/api.ts(543)+ 价目表与成本累计(462)。四条认证路径、90 秒无响应看门狗、429/529/401 各有一套重试策略,全在这里。
⑥ 上下文压缩 —— 4,626 行 / 27 文件
「对话太长塞不下」的三级续命机制:auto-compact 全量摘要(compact.ts 1,313 + 触发判定 251 + 摘要 prompt 264)、microcompact 微清理旧工具输出(349)、token 估算(376)。行数不大但极精巧:摘要后还要重注入读过的文件、复用缓存前缀省钱。
⑦ 终端 UI —— 106,624 行 / 592 文件(占 27.3%)
全项目最大的功能块 = components/(74,351)+ ink/ 自研渲染引擎(13,205)+ screens/REPL.tsx 等(4,447)+ React hooks(14,621)。把 React 渲染到终端:虚拟 DOM → Yoga flexbox 排版 → 前后帧逐格 diff → 最小 ANSI 补丁。单是「消息怎么显示」(components/messages)就 5,710 行,「输入框」(PromptInput)4,354 行。
⑧ 斜杠命令 + 输入分流 —— 25,118 行 / 196 文件
commands/ 全部 + processUserInput/ 判定「/ 开头走命令、! 开头直跑 bash、其余是普通消息」。最大单命令是 /insights(2,686 行,使用统计报告);/plugin 一家的管理界面(浏览市场、安装、设置)就超过 5,500 行——斜杠命令的大头其实是「命令背后的整套 UI」。
⑨ Hooks + Skills —— 12,372 行 / 52 文件
Hooks(生命周期钩子,6,791 行)核心是一个 3,905 行的巨型文件 utils/hooks.ts:27 种事件 × 4 种执行后端(shell/LLM/HTTP/子 agent)× exit code 语义。Skills(5,581 行)本质是「把 SKILL.md 贴进对话」,行数花在加载器(798)、SkillTool(1,180)和权限确认 UI 上。
⑩ MCP 外部工具协议 —— 17,297 行 / 64 文件
连接外部工具服务器的完整客户端:services/mcp/client.ts(2,614,5 种传输方式 + 动态把远程工具包装成本地 Tool 对象)、OAuth step-up 认证(1,892)、服务器配置管理(1,151),再加 3,691 行的管理与审批 UI。一个「外部协议」比上下文压缩贵 3.7 倍。
⑪ 插件系统 —— 15,000 行 / 49 文件
一个插件 = commands/agents/skills/hooks/MCP 的打包目录。加载器 pluginLoader.ts(2,329)、市场 marketplaceManager.ts(1,716)、配置 schema 校验(1,087),加上 /plugin 命令的整套市场浏览 UI。分发生态的基础设施。
⑫ 子 agent 系统 —— 16,456 行 / 72 文件
AgentTool(5,351:子 agent 就是递归调用同一个 query(),行数花在隔离——独立权限、独立中断、结束时清理连接和后台任务)+ 后台/远程任务管理(6,174)+ agents 管理 UI(4,376)。另有独立的 swarm 多 agent 协作层 6,246 行(tmux 分屏跑一队 agent,未计入本数)。
还有两个没单列卡片但值得知道的数:会话持久化与消息序列化(messages.ts + sessionStorage.ts + attachments.ts + history.ts)合计 11,211 行——「把对话记下来、恢复回来」比主循环本身贵 3 倍;IDE/远程桥接(bridge/ + ide 相关文件)约 1.2 万行。
4 · 五个由数字推出的洞察
- 最大单文件是
cli/print.ts(4,421 行代码)——headless 模式(claude -p)的完整执行器。因为-p模式不挂 Ink UI,交互模式里散在几十个组件里的逻辑(输出格式化、流式转发、权限自动决策)必须在一个进程线性流程里全部重写一遍。第二名utils/messages.ts(4,338)、第三名utils/hooks.ts(3,905)——整个 Hooks 功能几乎是一个文件。 - 「画界面」是「想问题」的 28 倍:终端 UI 106,624 行(27.3%),而 agentic 主循环只有 3,755 行(<1%)。决定 Claude Code 智能程度的核心逻辑小得惊人——智能主要来自模型,代码的大头花在「让人用得舒服」。
- 安全防御 ≈ 10 个主循环:权限系统 23,041 + bash 命令安全 19,384(去掉重叠后合计约 3.97 万行),约为主循环的 10.6 倍。「让 AI 安全地执行命令」比「调用 AI」难一个数量级,这是所有 agent 产品的真实成本结构。
- 注释率 18.2%:源码里有 86,830 行注释(注释 ÷(注释+代码)= 18.2%)。最舍得写注释的是会话落盘 sessionStorage.ts(1,174 行注释配 3,532 行代码,比率 25%)和 REPL.tsx(1,257 行注释)——越是状态复杂、容易改坏的地方注释越密。
- 被打包进来的依赖比源码本身还夸张:node_modules 还原出 330,797 行,其中一个语法高亮库 highlight.js 就 39,194 行——比 Claude Code 全部 30+ 个工具(38,494 行)还大。而官方发布的
package/cli.js打包产物只有 11,273 行——39 万行源码压成一个文件,这就是「打包」。
5 · 小白学习路径:只读 5,000 行,读这 8 个文件
按《一条消息的一生》的顺序读,合计 4,994 行代码,正好覆盖「输入 → 循环 → 工具 → 压缩」主动脉:
| 顺序 | 文件 | 代码行 | 你会学到 |
|---|---|---|---|
| 1 | Tool.ts | 456 | 「工具」的接口契约:一个工具由哪 7 组成员构成,为什么默认值全部 fail-closed |
| 2 | utils/processUserInput/processUserInput.ts | 485 | 你敲的字怎么被分流:斜杠命令 / ! bash 模式 / 普通消息 |
| 3 | query.ts | 1,282 | 核心中的核心:agentic while(true),「有 tool_use 就再来一轮」的全部秘密 |
| 4 | services/tools/toolExecution.ts | 1,486 | 工具怎么被安全执行:Zod 校验 → hook 拦截 → 权限门禁 → 错误包成 tool_result 让模型自己重试 |
| 5 | services/tools/toolOrchestration.ts | 170 | 只读工具并发 10 个、写工具串行——一页纸的调度器 |
| 6 | tools/FileEditTool/FileEditTool.ts | 515 | 「Edit 前必须 Read」「文件被人改过就拒绝」这些规则的真实实现 |
| 7 | services/compact/autoCompact.ts | 251 | 上下文快满时怎么触发压缩(阈值 = 窗口 − 20000 − 13000) |
| 8 | services/compact/microCompact.ts | 349 | 更聪明的省钱术:趁缓存过期把旧工具输出清掉 |
读完这 5,000 行你会得到一个反直觉的结论:Claude Code 没有任何「计划器」或「智能调度」,它只是一个循环、一张工具表、一套权限门禁——其余 38 万行都在为这三件事做安全、界面和生态。
HermesHermes Agent:157 万行的「常驻个人 agent」全拆解
基于 NousResearch/hermes-agent v0.18.0(commit 7426c09,2026-07-06)git 追踪文件的 cloc 统计。本节所有行数均为 cloc「纯代码行」口径——空行和注释已剔除;这与源码解剖页引用的「文件总行数」不同(例如 cli.py 文件共 16183 行,剔除空行注释后是 10766 行代码,下文一律用后者)。
H.1 一共多少代码?——157 万行,但要分四种「东西」看
cloc 给出的总数是 1,571,980 行 / 5,930 个文件。对编程小白来说,这个数字最容易被误读——它不是「157 万行程序」,而是四类完全不同的东西加在一起:
| 口径 | 行数 | 占比 | 这是什么 |
|---|---|---|---|
| 纯代码 | 1,163,893 | 74.0% | Python / TypeScript / JavaScript / Rust / Shell 等真正会被执行的程序 |
| Markdown 文档 | 328,713 | 20.9% | 官方文档网站的文章 + 174 个「技能」的说明书(skill 本质是给 AI 看的 Markdown) |
| JSON / YAML / XSD 数据 | 78,831 | 5.0% | 配置、语言包、schema;其中两个 package-lock.json(依赖清单,机器生成)就占 43,836 行 |
| 其他杂项 | ~543 | <0.1% | Dockerfile 等零散格式 |
纯代码再按语言拆(前 8 名,其余合计约 1.4 万行):
| 语言 | 行数 | 占总仓比例 | 主要用在哪 |
|---|---|---|---|
| Python | 906,124 | 57.6% | 引擎、工具、网关、CLI、测试——一切核心逻辑 |
| Markdown | 328,713 | 20.9% | 文档网站 + 技能说明书 |
| TypeScript | 204,975 | 13.0% | Ink TUI、桌面 App、Web dashboard——全是「脸」 |
| JSON | 49,836 | 3.2% | lockfile、配置样例 |
| JavaScript | 22,229 | 1.4% | Electron 主进程、插件 dashboard 的打包产物 |
| XSD | 19,654 | 1.3% | 几乎全是 PowerPoint 技能带的 ISO-IEC 29500 Office 文档 schema |
| TeX | 12,200 | 0.8% | 论文写作技能的 LaTeX 模板 |
| YAML | 9,341 | 0.6% | 插件清单、CI、配置 |
那「真正的 Hermes」到底多大?把测试(483,128 行)、文档网站(196,225 行)、独立子项目 apps/(132,016 行,桌面 App)和技能内容库(约 21.8 万行,以 Markdown 为主)都剥掉,运行时核心 Python 是 396,646 行 / 758 个文件——这才是「你敲 hermes 回车后真正在跑的程序」。再收窄到引擎心脏(主循环 + 工具 + 模型层 + 安全 + 记忆 + 压缩,见 H.3),大约 10.8 万行;其余近 29 万行是 CLI、网关、TUI 桥这些「壳与管线」。
H.2 每个模块多少代码?——逐目录账本
按仓库顶层目录(个别拆到二级)列账。占比条以全仓 1,571,980 行为分母。
| 目录 | 行数 | 文件 | 占比 | 一句话职责 |
|---|---|---|---|---|
tests/ | 483,128 | 1,954 | 30.7% | Python 测试(483,019 行),按被测对象分子目录:gateway 11.9 万、hermes_cli 10.1 万、tools 8.2 万 |
website/ | 196,225 | 699 | 12.5% | Docusaurus 文档网站(独立子项目):英文文档 9 万行 + 简中翻译 7.8 万行 |
apps/desktop/ | 127,684 | 805 | 8.1% | Electron 桌面 App(独立子项目,React 前端 + hermes serve 后端),含 4 语言 i18n 包 12,438 行 |
hermes_cli/ | 105,981 | 199 | 6.7% | 命令行入口:40+ 子命令、setup 向导、认证、看板、内置 web server |
optional-skills/ | 108,799 | 495 | 6.9% | 按需安装的技能库;仅 mlops 训练类就 5.8 万行(大半是抓来的参考文档) |
skills/ | 104,743 | 440 | 6.7% | 内置 18 类技能:creative 4.3 万、productivity 2.5 万(PowerPoint 技能自带 Office XSD schema) |
plugins/ | 85,711 | 278 | 5.5% | 一等功能插件化:20 个消息平台适配器(4.5 万)、30+ 模型厂商、记忆后端、看板、可观测 |
tools/ | 58,235 | 112 | 3.7% | 80+ 个工具的注册表与实现:终端 6 后端、浏览器、文件、MCP、审批与安全扫描 |
agent/ | 57,569 | 149 | 3.7% | 引擎腹地:主循环、system prompt、模型适配器、错误分类、压缩、curator——「god-file 拆解运动」的目的地 |
ui-tui/ | 52,613 | 379 | 3.3% | Node/Ink 图形化 TUI(React 19 + 原版 Ink 6 + 补丁包 hermes-ink 1.8 万行) |
gateway/ | 50,696 | 72 | 3.2% | 常驻网关进程:平台路由、鉴权配对、scale-to-zero、重启熔断;run.py 一个文件 13,644 行 |
| 仓库根散文件 | 47,966 | 37 | 3.1% | 历史遗留的巨型文件:cli.py 10,766、run_agent.py 3,798、hermes_state.py 3,416、lockfile 19,690 |
web/ | 39,444 | 118 | 2.5% | Vite + React 的网关管理 dashboard(xterm.js 投影终端) |
scripts/ | 15,930 | 41 | 1.0% | 构建/发布脚本 + 一个 JavaScript 的 whatsapp-bridge(4,207 行) |
tui_gateway/ | 11,329 | 10 | 0.7% | TUI 的 Python 侧 RPC 服务;server.py 一个文件 10,032 行(文件总行 13873) |
locales/ | 5,754 | 16 | 0.4% | CLI 界面多语言 JSON |
cron/ | 4,492 | 10 | 0.3% | 「会思考的定时任务」:60 秒 tick 调度器 + agent/脚本两种 job |
apps/bootstrap-installer/ | 3,883 | 32 | 0.25% | Tauri(Rust)首装引导器——仓库里仅有的 2,628 行 Rust 大多在此 |
acp_adapter/ | 4,025 | 11 | 0.26% | ACP 协议适配层(把 Hermes 接进 Zed 等编辑器,职责据目录名与测试推断,未核实细节) |
docs/ + plans/ + 其余 | ~6,700 | ~50 | 0.4% | 开发者文档、设计计划、Docker/Nix 打包等 |
点名巨型文件(cloc 代码行 / 文件总行):gateway/run.py 13,644、cli.py 10,766/16,183、hermes_cli/web_server.py 10,600、tui_gateway/server.py 10,032/13,873、hermes_cli/main.py 9,650、plugins/platforms/telegram/adapter.py 6,062。解剖页说的「god-file 拆解运动」正是冲着这批文件去的——注意它们全是「壳与管线」,引擎本体最大的 conversation_loop.py 只有 3,694 行代码。
H.3 多少行实现了什么功能?——12 个子系统的精确账
功能不等于目录:同一个功能的代码常横跨 agent/、tools/、根目录和 plugins/。下面每个子系统的行数都是把相关文件从逐文件 CSV 里挑出来求和得到的(cloc 代码行口径),并列出最大的几个文件。
① Agentic 主循环与引擎 — 12,626 行 / 12 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
run_agent.py | 3,798 | AIAgent 类:所有状态与回调的容器,工具批执行入口 |
agent/conversation_loop.py | 3,694 | 心脏:while 循环「调模型 → 执行工具 → 再调模型」 |
agent/prompt_builder.py | 1,456 | 找 HERMES.md/AGENTS.md/CLAUDE.md 项目说明并注入 |
agent/tool_executor.py | 1,323 | 工具的并发(8 线程)/串行执行 + 420 秒看门狗 |
agent/moa_loop.py | 576 | MoA 多模型合议模式 |
这 1.2 万行是「agent 之所以是 agent」的全部:组消息、发请求、解析 tool_calls、把结果喂回去。其中约 2,800 行花在 system prompt 的三层组装与字节级稳定性上(prompt_builder + system_prompt.py 280 行 + coding_context.py 494 行)——只为让厂商的 prompt cache 永远命中。
② 工具系统 — 45,625 行 / 100 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
tools/browser_tool.py | 2,990 | Playwright 浏览器自动化 |
tools/delegate_tool.py | 2,291 | 子 agent:new 一个 AIAgent 后台跑 |
tools/tts_tool.py | 1,914 | 语音合成(个人 agent 特色) |
tools/terminal_tool.py | 1,868 | 终端工具门面(6 种执行后端) |
tools/environments/*(6 文件) | 3,778 | local/docker/ssh/modal/daytona/singularity 后端实现 |
tools/registry.py | 438 | 注册表:schema + handler + 可用性探针 + dispatch |
80+ 个工具(不含下文单列的安全、MCP、记忆、技能类)共 4.6 万行。注意配比:注册表本身只有 438 行,「一个工具怎么被登记和调用」极薄;钱都花在具体工具的脏活上——光「让命令能跑在 6 种地方」就 5,646 行(terminal + environments),比主循环的一半还多。
③ 权限与安全 — 4,758 行 / 12 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
tools/approval.py | 1,725 | manual/smart/YOLO 审批 + 12 条硬底线 + 47 条危险命令模式 |
tools/skills_guard.py | 764 | 技能安装门:信任分级 × 危险等级矩阵 + 注入扫描 |
tools/tirith_security.py | 520 | Rust 二进制内容威胁扫描(SHA-256 + cosign 验签下载) |
agent/tool_guardrails.py | 376 | 循环检测等运行时护栏,可强停整轮 |
agent/file_safety.py | 346 | 写黑名单(.ssh/.env/sudoers…) |
tools/url_safety.py + osv_check.py + 其余 | ~1,027 | SSRF 阻断、恶意包预检、MCP 配置 IOC 扫描、secret 剥离 |
不到 5 千行守住整个系统的边界——是 12 个子系统里「行数最少、职责最重」的一个。它没有操作系统级沙箱,全部力气花在正则风险库、审批流和供应链验证上。
④ Provider / 模型层 — 14,531 行 / 86 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
agent/chat_completion_helpers.py | 1,997 | 发流式请求、逐 chunk 消费、fallback 链切换 |
agent/credential_pool.py | 1,817 | 多凭据池:4 种轮换策略、429 冷却、跨进程 OAuth 同步 |
agent/anthropic_adapter.py | 1,565 | Anthropic Messages API 方言翻译 |
agent/error_classifier.py | 931 | 30 家厂商的报错归成约 20 类 FailoverReason |
codex_responses / gemini_native / bedrock 适配器 | 2,531 | 另三种消息方言 |
plugins/model-providers/*(30+ 目录) | ~4,200 | 每家厂商一个声明式 ProviderProfile 插件 |
「一颗引擎适配 30+ 厂商」的代价:1.45 万行里约 40% 花在「厂商方言翻译」(4 个适配器 + 30 个 profile),30% 花在「出错之后怎么办」(错误分类、重试退避、fallback、凭据轮换)——跟任意模型打交道,一半工程量是收拾残局。
⑤ 上下文压缩与会话持久化 — 5,846 行 / 5 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
hermes_state.py | 3,416 | SQLite(WAL)会话库 + FTS5 全文索引 + 压缩 fork 链 resume |
agent/context_compressor.py | 1,621 | 四相压缩:零成本裁剪 → 定边界 → LLM 摘要 → fork 会话 |
agent/conversation_compression.py | 713 | 跨进程压缩锁(CLI 和网关别同时压一个会话) |
「一次会话装不下怎么办」+「关机重启后还记得吗」两个问题共 5,846 行。持久化比压缩更贵(3,416 vs 2,401)——因为 Hermes 是常驻 agent,崩溃恢复、会话 fork 链、全文检索都压在 hermes_state.py 一个文件上。
⑥ 记忆与学习循环 — 19,853 行 / 47 个文件(Hermes 的招牌)
| 文件 | 行数 | 干什么 |
|---|---|---|
plugins/memory/*(openviking/honcho/hindsight…) | 14,323 | 外部记忆后端插件,同时只挂一个 |
agent/curator.py | 1,334 | 后台「园丁」:闲时用便宜模型整理技能库 |
tools/skill_manager_tool.py | 1,048 | agent 自己写 SKILL.md 的工具 |
tools/memory_tool.py | 711 | MEMORY.md / USER.md 双文件记忆(2200/1375 字符上限) |
tools/session_search_tool.py | 688 | FTS5 跨会话全文检索 |
有意思的配比:内置记忆机制本身极简(memory_tool.py 仅 711 行),学习循环四件套(复盘触发 + 自建技能 + curator + 会话检索)合计约 4,300 行;而可插拔的外部记忆后端插件反而占了 1.4 万行——「自带的够用、想要更强随便换」。
⑦ Ink TUI + tui_gateway — 63,942 行 / 389 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
tui_gateway/server.py | 10,032 | Python 侧 JSON-RPC 服务:76 类 RPC(会话/配置/计费/setup 全 RPC 化) |
ui-tui/packages/hermes-ink/ | 18,232 | Ink 6 补丁包(含 vendored yoga-layout 1,999 行) |
ui-tui/src/(React 组件 + 状态) | 33,700 | 渲染与输入;其中 __tests__ 9,601 行 |
为了一个「更好看的终端界面」花了 6.4 万行——比引擎本体(①+④+⑤ 合计 3.3 万)还多。三段式进程链(Python → Node 渲染 → Python 引擎)的桥接税真实存在:光 RPC server 就 1 万行。
⑧ Gateway 与 30+ 消息平台 — 96,143 行 / 132 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
gateway/run.py | 13,644 | 全仓最大代码文件:asyncio 网关主进程、会话路由、AIAgent 缓存 |
gateway/platforms/base.py | 3,130 | 平台适配器基类(消息收发、审批异步化的公共骨架) |
plugins/platforms/telegram/adapter.py | 6,062 | 最重的平台适配器 |
discord / feishu / matrix / slack 适配器 | 16,793 | 每接一个聊天软件 ≈ 3,000–5,500 行 |
gateway/authz_mixin.py + pairing.py | 762 | 默认拒绝 + 配对码鉴权 |
Hermes 最独特的子系统,占运行时代码的四分之一。9.6 万行几乎全是「适配税」:每个聊天平台的消息格式、长度上限、按钮交互、webhook 怪癖都要单独伺候——「从任何聊天软件召唤 agent」这个卖点的真实价格。
⑨ CLI 层 — 116,747 行 / 200 个文件
| 文件 | 行数 | 干什么 |
|---|---|---|
cli.py | 10,766 | 经典 prompt_toolkit REPL + 斜杠命令(正被逐步拆解) |
hermes_cli/web_server.py | 10,600 | 内置 HTTP 服务(dashboard 后端) |
hermes_cli/main.py | 9,650 | argparse 入口 + 40 余个子命令分发 |
hermes_cli/auth.py | 5,999 | 各家 OAuth / device-code 登录流 |
hermes_cli/setup.py + model_setup_flows.py | 4,901 | 首次运行向导 + 14 家厂商专属配置流程 |
hermes_cli/kanban_* | ~7,800 | 看板多 agent 编排(swarm:分解任务图、派发子进程) |
最大的运行时子系统。11.7 万行里没有一行 agent 智能——全是入口、向导、配置、认证、进程管理这类「让普通人装得上、跑得起」的工程。
⑩ Skills 内容生态 — 217,630 行 / 937 个文件
内置 skills/ + 按需 optional-skills/ + 工具侧的 skills_tool.py(1,169)/skills_hub.py(2,919,对接 8 个分发市场)。成分:Markdown 说明书 150,623 行 + 配套 Python 脚本 25,121 行 + XSD/JSON/YAML 21,491 行。这不是「程序」,是给 AI 看的操作手册库——最大单篇是 unsloth 训练指南 11,895 行。
⑪ MCP 双向集成 — 4,388 行 / 4 个文件
tools/mcp_tool.py 3,131(client:三种 transport、并行连接、动态刷新)+ mcp_serve.py 640(把 Hermes 自己暴露成 MCP server)+ OAuth 509 + 安全扫描 108。四千多行换来「既能用别人的工具,也能被 Claude Code 当工具用」。
⑫ Cron 定时任务 — 5,251 行 / 11 个文件
cron/scheduler.py 2,133(60 秒 tick + 文件锁)+ jobs.py 1,195 + agent 自管的 cronjob_tools.py 759。「每天八点给我发日报」一句话背后的五千行。
H.4 趣味洞察
「代码行冠军」不是 cli.py
按文件总行数 cli.py(16,183)最大,但剔除空行注释后 gateway/run.py(13,644 行代码)才是冠军——cli.py 只剩 10,766,说明它有 1/3 是空行和注释。四个万行文件(run.py / cli.py / web_server.py / server.py)全是壳层,引擎心脏 conversation_loop 反而只有 3,694 行。
测试 : 运行时代码 = 1.22 : 1
483,019 行 Python 测试 vs 396,646 行运行时 Python——测的比写的多。测试重心与风险一致:最难测的 gateway(异步、多平台)拿了 11.9 万行测试,是其实现代码(9.6 万)的 1.24 倍。
Python : TypeScript ≈ 4.4 : 1
90.6 万 vs 20.5 万。TS 几乎 100% 花在「脸」上(TUI 5.3 万、桌面 App、Web dashboard),Python 独占引擎——印证解剖页「一颗 Python 心脏、多张脸」:换掉任何一张脸都不伤引擎。
Python 注释率 20.0%
90.6 万行代码配 226,738 行注释,每 5 行代码 1 行注释——对一个快速迭代的 AI 项目来说相当高,与源码里大量「为什么这么写」的辩护性长注释(审批冻结、缓存不变量)的观感一致。
一张 PPT schema 比整个 MCP 子系统还大
PowerPoint 技能自带的 ISO-IEC 29500 XSD 有 19,654 行——比 MCP 双向集成(4,388 行)+ 权限安全(4,758 行)+ cron(5,251 行)加起来还多;两个机器生成的 package-lock.json(43,836 行)则占掉「数据类」口径的 56%。行数 ≠ 工作量,在这两处最直观。
H.5 小白学习路径:只读 5,000 行,读哪些?
顺着解剖页「一条消息的一生」的 12 站走,按依赖顺序读这 9 个文件(约 5,000 行代码),可以完整理解 Hermes 的骨架:
| 顺序 | 文件 | 行数 | 你会学到 |
|---|---|---|---|
| 1 | hermes_bootstrap.py | 52 | 进程启动前的环境垫片——全仓最短的核心文件,热身 |
| 2 | tools/registry.py | 438 | 「工具」是什么:JSON Schema + handler + dispatch,一切扩展的地基 |
| 3 | agent/system_prompt.py | 280 | 模型「人格」怎么组装:三层结构 + 字节稳定不变量 |
| 4 | agent/turn_context.py | 353 | 一个回合开始前要准备什么(清洗输入、预检压缩) |
| 5 | agent/conversation_loop.py(前半 + 循环体,选读约 1,500 行) | ~1,500 | 心脏本体:while 循环怎么把「模型要工具」变成自主行动 |
| 6 | agent/tool_dispatch_helpers.py + agent/tool_executor.py(并发判定段,选读约 600 行) | ~600 | 哪些工具能并行、guardrail 和审批插在哪 |
| 7 | tools/memory_tool.py | 711 | 跨会话记忆:为什么是两个 Markdown 文件 + 冻结快照 |
| 8 | agent/turn_finalizer.py | 301 | 回合收尾:落盘、诊断、学习循环的触发点 |
| 9 | tools/clarify_tool.py + agent/iteration_budget.py | ~250 | 两个小而美的设计:「问用户」也是工具、预算防失控 |
读完这约 4,500–5,000 行,再回头看那些万行巨文件(cli.py、gateway/run.py)就会发现:它们只是把同一个 run_conversation() 包了一层又一层的壳。
OCOpenClaw:549 万行的「个人 AI 服务器」,但别被吓到
四个项目里体量最大的一个。cloc 统计(git 跟踪文件、纯代码行、不含空行注释,2026-07-06 源码):5,495,675 行代码、20,972 个文件。但这 549 万里有将近一半是测试、还有 70 万行是翻译 JSON 和文档——真正的实现代码约 302 万行,而你要读懂它的核心链路,5000 行就够。
OC.1 一共多少代码?——先把口径拆干净
「一个项目多少行代码」这个问题,诚实的回答必须先分口径。549 万行里混着五种完全不同性质的东西:
| 口径 | 行数 | 占比 | 说明 |
|---|---|---|---|
| 全仓总量 | 5,495,675 | 100% | cloc 纯代码行(已剔除空行和注释),20,972 个文件 |
| 其中 TypeScript | 4,408,373 | 80.2% | 主语言,17,196 个 .ts 文件(含大量测试) |
| 其中 Swift + Kotlin | 228,205 | 4.2% | iOS/macOS 节点 App(Swift 165,675)+ Android App(Kotlin 62,530) |
| 其中 JSON + YAML 数据 | 467,484 | 8.5% | 大头是移动 App 的 21 份翻译文件(apps/.i18n 共 304,687 行) |
| 其中 Markdown 文档 | 183,673 | 3.3% | docs/ 文档站 + 各目录 README + 14,372 行的 CHANGELOG |
| 测试代码(全仓) | 2,473,981 | 45.0% | 按文件名 .test/.spec/__tests__/test 目录等规则从 CSV 剥出 |
| 实现代码(全仓去测试) | 3,021,694 | 55.0% | 「这个项目实际写了多少产品代码」的最接近答案 |
再往核心聚焦一层:src/ 是 Gateway 主程序所在地,共 2,493,166 行、9,260 个文件——但其中 1,429,950 行(4,129 个文件)是和实现放在同一目录下的 *.test.ts。剥掉之后,Gateway 核心实现约 106.3 万行(1,063,216 行、5,131 个文件)。如果只算「一条消息从聊天软件走到 agent 再走回去」的主链路(gateway + 渠道路由/auto-reply + agents + agent-core/ai/gateway-protocol 三个核心包,均去测试),约 49 万行。
为什么测试快占一半?OpenClaw 是「跑在你家里、7×24 无人值守」的服务器软件,消息丢一条用户可能根本不知道。所以它的测试密度远超普通 CLI 工具:src/ 内测试 : 实现 ≈ 1.34 : 1——测试比实现还多 37 万行。这不是浪费,是这类「异步 + 多渠道 + 常驻」软件的生存必需。
OC.2 每个模块多少代码?——仓库地图(按目录,含测试)
先看一级目录的大盘(口径:cloc 代码行,含该目录内的测试):
| 目录 | 行数 | 文件 | 占比 | 占比条 | 一句话职责 |
|---|---|---|---|---|---|
src/ | 2,493,166 | 9,260 | 45.4% | Gateway 主程序:控制平面、渠道、agent、CLI 全在这 | |
extensions/ | 1,511,739 | 6,702 | 27.5% | 141 个 bundled 插件:聊天渠道、模型 provider、能力扩展 | |
apps/ | 553,270 | 1,160 | 10.1% | iOS / Android / macOS 原生节点 App + 共享库 + 21 语言翻译 | |
ui/ | 230,987 | 544 | 4.2% | Control UI:Vite + Lit 的网页控制台,Gateway 直接托管 | |
scripts/ | 204,608 | 814 | 3.7% | 构建 / 发布 / e2e 脚本(含 43,008 行 e2e) | |
test/ | 147,659 | 654 | 2.7% | 仓库级集成测试(src/ 之外的另一批) | |
docs/ | 143,097 | 731 | 2.6% | 文档站源码(docs.openclaw.ai) | |
packages/ | 83,430 | 546 | 1.5% | 21 个 workspace 包:agent-core、ai、gateway-protocol… | |
| 根文件 | 46,108 | 32 | 0.8% | pnpm-lock(11,019)、CHANGELOG(14,372)、启动器 openclaw.mjs(709) | |
.github/ 等其余 | 81,611 | 529 | 1.5% | CI workflows、qa 场景、.agents skills、安全文档等 |
src/ 内部:Gateway 主程序的二级分解
src/ 的 249 万行再切一刀(口径同上,含同目录测试;「实现」列已剥测试):
| 模块 | 总行数 | 实现 | 文件 | 占 src 比 | 职责 |
|---|---|---|---|---|---|
src/agents | 620,830 | 253,074 | 1,868 | agent 装配与运行:工具表、system prompt、模型选择、嵌入式 runner | |
src/gateway | 285,902 | 106,893 | 788 | 常驻控制平面:WS 服务器、握手鉴权、server-methods、热重载 | |
src/infra | 209,312 | 89,322 | 819 | 基础设施:状态迁移、出站投递(outbound 38,507)、进程/端口工具 | |
src/plugins | 207,909 | — | 643 | 插件运行时:发现、加载、生命周期、能力注册(30+ 种注册点) | |
src/commands | 196,856 | — | 708 | 60+ 个 CLI 子命令的实现体(doctor 一家就 45,404 行) | |
src/auto-reply | 195,088 | 73,070 | 576 | 回复编排:并发控制(steer/排队/打断)、block 流水线、分块发送 | |
src/cli | 131,916 | — | 474 | Commander 命令树、懒注册、onboarding 向导、update 命令 | |
src/config | 97,695 | 49,591 | 406 | JSON5 配置:$include、${ENV} 替换、校验、会话存储(sessions 28,627) | |
src/plugin-sdk | 62,079 | — | 594 | 给插件作者用的 SDK 类型与工具面 | |
src/channels | 57,491 | 30,405 | 380 | 渠道抽象层 + turn 内核(五阶段准入) | |
src/cron | 52,369 | 18,152 | 228 | 定时任务:调度器 + isolated-agent(18,940)独立会话跑任务 | |
src/skills + hooks | 43,157 | 20,450 | 190 | SKILL.md 技能加载 + 生命周期 hooks | |
src/secrets | 27,468 | — | 127 | 凭据存取与脱敏 | |
src/acp | 22,998 | 10,247 | 99 | ACP(Agent Client Protocol)接入,对接 IDE 类客户端 | |
src/tui | 20,413 | 9,846 | 69 | 终端聊天界面(基于外部库 pi-tui) | |
src/logging | 19,702 | 10,369 | 76 | 结构化日志与诊断 | |
src/daemon | 18,636 | 8,566 | 72 | 装成系统服务:launchd / systemd / schtasks 三平台 | |
src/tasks | 18,399 | — | 61 | 后台任务队列 | |
src/media-understanding + media | 27,659 | 11,717 | 129 | 图片/语音/视频理解的编排(具体模型调用在扩展里) | |
src/security + pairing | 19,687 | — | 134 | DM 配对策略、白名单、审批闸门 |
extensions/:141 个插件,逐个渠道看行数
渠道插件(你在哪些聊天软件里能用它)是 OpenClaw 的门面。每个渠道就是一个完整的小工程——收发、鉴权、格式转换、分块、测试全套(口径:该扩展目录全部代码行 / 其中剥出的实现行):
| 渠道扩展 | 总行数 | 实现 | 规模条 | 说明 |
|---|---|---|---|---|
discord | 110,894 | 55,832 | 545 个文件;服务器/角色路由最复杂的渠道 | |
telegram | 110,091 | 45,704 | 解剖页 14 站主线用的就是它;grammY + spool 落盘 | |
matrix | 80,315 | 37,872 | 去中心化协议,带端到端加密(E2EE)适配 | |
slack | 65,799 | 28,009 | Bolt SDK、workspace 级安装 | |
feishu | 54,996 | 27,561 | 飞书/Lark,国内渠道里最大 | |
whatsapp | 47,870 | 23,789 | 走 Baileys 逆向 Web 协议,登录态维护是难点 | |
msteams | 37,636 | 17,905 | Microsoft Teams(Bot Framework) | |
qqbot | 33,404 | 21,378 | QQ 官方机器人 | |
voice-call | 29,902 | 14,675 | 语音通话渠道(电话进 agent) | |
imessage | 29,207 | 13,444 | iMessage(借 macOS 节点收发) | |
mattermost | 22,649 | 10,457 | 开源版 Slack | |
google-meet | 20,633 | 12,717 | 会议机器人 | |
signal | 18,037 | 8,629 | Signal(signal-cli) | |
line | 14,878 | 7,972 | LINE | |
googlechat | 12,461 | 6,174 | Google Chat | |
zalouser + zalo | 19,158 | 11,533 | 越南 Zalo(用户号 + 官方号两套) | |
nostr / tlon / irc / twitch / nextcloud-talk / synology-chat / sms… | 各 4~9 千 | — | 长尾渠道,每个 4,026~8,989 行 |
渠道之外,extensions/ 里还有三类大块头:编码 agent 集成——codex(122,449 行,全仓最大扩展)、copilot(17,849)、github-copilot(4,820);QA 基础设施——qa-lab(102,851,真渠道联调实验室)+ qa-matrix(31,482,契约矩阵跑批);记忆系统——memory-core(60,178)+ memory-wiki(22,047)+ active-memory(9,403)+ memory-lancedb(6,495),合计约 9.8 万行。再加上 ~60 个模型 provider 小扩展(openai 20,788、google 20,642、ollama 14,174、xai 12,137、anthropic 5,681……大多每个几百到几千行)。
OC.3 多少行实现了什么功能?——14 个子系统
这是本章核心:把「549 万行」翻译成「每个功能花了多少行」。所有数字由逐文件 CSV 按路径求和得出;「总」含同目录测试,「实现」已剥测试。
① Gateway 控制平面 296,272 行 · 实现 115,042
口径:src/gateway(285,902)+ 协议包 packages/gateway-protocol(10,370)。常驻 WS 服务器、req/res/event 三帧协议(v4)、挑战-签名握手、预认证预算、慢消费者熔断、配置热重载。最大文件 server-methods/chat.ts(5,691 行)和 server.impl.ts(1,759 行)——后者是启动 12 步的编排者。测试占比高达 62.6%:控制平面是全仓测得最狠的地方。
② 渠道路由与 turn 内核 256,532 行 · 实现 105,076
口径:src/channels + src/routing + src/auto-reply。消息进来后「归哪个 agent、哪个会话、现在能不能跑」全在这层:8 级 binding 路由(resolve-route.ts 750 行)、session key 生成(323 行)、五阶段 turn 准入(kernel.ts 785 行)、同会话并发四策略(steer/排队/打断/丢弃),以及把模型输出切成语义 block 保序发回聊天软件的流水线。auto-reply/reply 一个目录就 170,885 行。
③ agent 主循环与运行时 112,022 行 · 实现 52,535
口径:packages/agent-core(9,178,心脏 agent-loop.ts 仅 982 行)+ src/agents/embedded-agent-runner(102,844)。真正的「调模型 → 执行工具 → 回填 → 再调」while 循环只有不到千行(源自 pi-mono,与 Claude Code 的 query.ts 同构);外面包的 10 万行 runner 负责组装 system prompt、注入工作区文件、订阅流式事件、压缩重试。单文件冠军 run/attempt.ts 5,644 行。
④ 工具系统 74,734 行 · 实现 30,699
口径:src/agents/tools/ + src/agents/bash-tools*。工具表装配与策略过滤(agent-tools.ts 1,065 行)、exec shell 工具(bash-tools.exec.ts 1,974 行,10 秒未完自动转后台、危险命令两阶段审批)、文件读写、子 agent 派生。约 3 万行实现撑起了 agent 的全部「手」。
⑤ Provider / 模型接入 106,565 行 · 实现 47,747
口径:packages/ai(21,675,按 API 协议族注册的流式适配器)+ src/agents 中文件名含 model / auth-profile / provider 的文件(84,890,含 openai-transport-stream.ts 4,266 行、五级 failover 链、认证 profile 轮换)。这还不算 extensions/ 里 60 来个 provider 插件(合计约 15 万行)。
⑥ 配对与安全 47,155 行 · 实现 22,066
口径:src/security(16,192)+ src/secrets(27,468)+ src/pairing(3,495)。陌生人私聊 8 位配对码、群 allowlist、DM 策略、凭据脱敏。「谁能跟你的 AI 说话」这一件事花了 2.2 万行实现。
⑦ 会话、压缩与记忆 ≈182,782 行
口径:src/config/sessions(28,627,JSONL 转录存储)+ src/agents/sessions(25,517)+ src/ 内文件名含 compact 的压缩相关文件(30,515,含 compaction-safeguard 等)+ 记忆类扩展四件套(98,123)。上下文满了怎么压、压完怎么不丢关键信息、长期记忆存哪,是常驻 agent 特有的重活。
⑧ Control UI 230,987 行 · 实现 165,808
口径:ui/ 整目录。Vite + Lit 单页应用,由 Gateway 直接托管:pages 99,366 行(聊天、会话、配置、日志、workboard 各页)、i18n 53,868 行、样式 23,110 行。一个「附赠的管理后台」体量已是中型前端项目。
⑨ TUI 终端界面 20,413 行 · 实现 9,846
口径:src/tui。敲 openclaw 不带参数时连上 Gateway 的终端聊天界面,渲染层用外部库 pi-tui。不到 1 万行实现——因为重活都在 Gateway 侧。
⑩ 节点体系(手机/Mac App)561,136 行 · 原生代码 246,771
口径:apps/(553,270)+ src/node-host 等节点宿主(7,866)。三个原生 App 把手机/Mac 变成 agent 的「外设」(拍照、定位、发 iMessage):iOS 63,532 行 + Android 66,276 行 + macOS 63,610 行 + 共享 Swift 库 OpenClawKit 53,353 行。剩下 304,687 行是 apps/.i18n 的 21 语言翻译 JSON——纯数据。
⑪ 插件系统 270,315 行 · 实现 117,648
口径:src/plugins(207,909,运行时:发现/加载/能力注册/热 reload)+ src/plugin-sdk(62,079)+ packages/plugin-sdk(327)。141 个扩展能长出来,靠的就是这 11.7 万行实现的地基。
⑫ cron 定时任务 52,369 行 · 实现 18,152
口径:src/cron。「每天早上 8 点让 agent 给我发新闻摘要」这类需求:调度器 + isolated-agent(18,940 行)在独立会话里跑任务再把结果投回渠道。
⑬ CLI 命令树 328,772 行 · 实现 140,424
口径:src/cli + src/commands。23 个核心命令 + 39 个子 CLI(gateway/daemon/nodes/skills/security…)全部懒注册;光 doctor(自检修复)一族就 45,404 行、update 命令 4,237 行。CLI 比很多项目整个仓库都大。
⑭ infra 基础设施 209,312 行 · 实现 89,322
口径:src/infra。全仓最大单文件在这:state-migrations.ts(5,738 行,状态目录逐版本迁移);outbound(38,507 行)管所有出站投递的重试与限速。
OC.4 五个趣味洞察(全部由 CSV 推导)
- 接一个 WhatsApp 要 4.8 万行。
extensions/whatsapp共 47,870 行(实现 23,789 + 测试 24,081)。而这已经是「借力 Baileys 库」之后的量——一个渠道插件的体量抵得上很多创业公司的整个后端。最贵的渠道是 Discord(110,894 行)。 - 测试比实现多的仓库。src/ 内测试 : 实现 = 1,429,950 : 1,063,216 ≈ 1.34 : 1;全仓测试占 45.0%(2,473,981 行)。四个项目里 OpenClaw 的测试纪律最重——无人值守软件的代价。
- 翻译 JSON 比三个手机 App 加起来还多。apps/.i18n 的 21 份语言包共 304,687 行(每份约 13,482 行),超过 iOS + Android + macOS 三个 App 的原生代码总和(193,418 行)。「支持 21 种语言」在代码量上是一个隐形巨无霸。
- 全仓最大扩展不是任何聊天渠道,而是
codex(122,449 行)。OpenClaw 内嵌了一整套 OpenAI Codex 编码 agent 的集成(app-server 协议对接、run-attempt 3,801 行),比 Telegram 渠道还大——「聊天助手顺便帮你写代码」这件事的成本可见一斑。 - 心脏只有 982 行。整个 549 万行仓库真正「智能」所在的 agentic while 循环 packages/agent-core/src/agent-loop.ts 只有 982 行代码——占全仓 0.018%。其余 99.98% 都在解决工程问题:不丢消息、不重复副作用、不被陌生人使唤、不刷爆平台限速。
OC.5 小白 5000 行学习路径
如果只允许读 5000 行就要看懂 OpenClaw,按《OpenClaw 源码架构全解剖》的「一条 Telegram 消息 14 站」主线,推荐这个顺序(共 4,785 行,全部为 cloc 代码行):
| 顺序 | 文件 | 行数 | 你会看懂什么 |
|---|---|---|---|
| 1 | packages/gateway-protocol/src/schema/frames.ts | 182 | 全系统只说三种话:req / res / event。协议是理解一切的钥匙 |
| 2 | src/routing/session-key.ts | 323 | 「会话」到底是什么:agent × 渠道 × 聊天对象怎么折叠成一个 key |
| 3 | src/routing/resolve-route.ts | 750 | 一条消息归哪个 agent:8 级优先匹配(14 站中的第 7 站) |
| 4 | src/channels/turn/kernel.ts | 785 | 五阶段准入:ingest → classify → preflight → resolveTurn → dispatch(第 8 站) |
| 5 | packages/agent-core/src/agent-loop.ts | 982 | 心脏:调模型 → 执行工具 → 回填 → 再调的双层 while 循环(第 10 站) |
| 6 | src/agents/agent-tools.ts | 1,065 | 工具表怎么装配、怎么被策略过滤(第 12 站) |
| 7 | extensions/telegram/src/telegram-ingress-spool.ts | 370 | 「先落盘再确认」:渠道可靠性的第一课(第 1 站) |
| 8 | src/auto-reply/reply/block-reply-pipeline.ts | 328 | 为什么对外不发 token 只发 block:保序流水线(第 11 站) |
别从大文件入手:bot-handlers.runtime.ts(3,416 行)、embedded-agent-runner/run.ts(4,114 行)、server-methods/chat.ts(5,691 行)都是「编排型」巨文件,适合带着问题查阅,不适合线性阅读。先用上面 8 个小文件建骨架,再回头看解剖页第 1 章的 14 站图对号入座。
CCodex CLI:123 个 crate 的 Rust 巨兽
基于 openai/codex 主干快照(commit 9c5be7e1,2026-07-06)。行数全部来自 cloc 对 git 跟踪文件的统计(纯代码行,不含空行和注释)。
C.1 这个项目一共多少代码?
整个仓库共 1,193,059 行代码、4,625 个文件——四大 agent 里体量最大的一个。但这个数字要拆开看:真正的产品是 codex-rs/ 这个 Rust workspace(1,141,344 行,占 95.7%),其余是 Python/TypeScript SDK(sdk/ 20,665 行)、CI 配置(github/ 10,967 行)和一层只有 702 行的 npm 安装壳(codex-cli/)。
| 语言 | 行数 | 占比 | 主要是什么 |
|---|---|---|---|
| Rust | 978,171 | 82.0% | 全部核心实现:引擎、TUI、沙箱、协议层 |
| JSON | 127,895 | 10.7% | 其中 111,650 行是 app-server 协议自动生成的 JSON Schema(不是人写的) |
| Python | 28,863 | 2.4% | Python SDK(16,936 行)+ CI 脚本 |
| Markdown | 12,519 | 1.0% | 文档(大部分已外迁官网,剩重定向占位) |
| YAML | 11,267 | 0.9% | CI workflow |
| TypeScript + JS | 5,577 | 0.5% | TS SDK(2,447)+ codex-rs 内工具脚本 + npm 壳 |
| C | 4,362 | 0.4% | vendor 进仓库的 bubblewrap 沙箱源码(编译进自己的二进制) |
| 其他(TOML/Shell/Starlark…) | 24,405 | 2.0% | 构建配置、execpolicy 示例规则等 |
测试有多少?按文件名口径统计(tests/ 目录 + *tests.rs + test_* 文件),Rust 测试代码 378,998 行、721 个文件,占全部 Rust 的 38.7%。也就是说:
结论:运行时核心 Rust 约 60 万行(978,171 − 378,998 ≈ 599,173)。口径局限要说清楚:Rust 惯例允许把单元测试用 #[cfg(test)] 写在实现文件内部(内联测试),cloc 按文件统计、分不出这部分——所以「60 万行运行时」是上界,真实值还要再低一些。反过来,比起 Claude Code 那种「一个混淆后的 bundle」,Codex 的测试是实打实和源码同仓可读的。
C.2 每个模块多少代码?——逐 crate 拆解
Codex 最大的结构特色:codex-rs/Cargo.toml 里列了 123 个成员 crate(crate ≈ npm 包,一个仓库统一构建)。下表按 codex-rs/ 下的顶层目录聚合(个别目录如 utils/、ext/ 内含多个子 crate,所以目录数 100 < crate 数 123)。行数含该 crate 自带的测试。职责一句话取自《Codex CLI 源码架构全解剖》。
| crate(目录) | 行数 | 文件 | 占 codex-rs | 一句话职责 |
|---|---|---|---|---|
| core | 247,606 | 526 | 21.7% | 引擎心脏:ThreadManager → Session → Task → run_turn 主循环、工具运行时、审批编排 |
| tui | 199,993 | 732 | 17.5% | ratatui 终端界面:inline 视口、流式 Markdown、审批弹窗、自研输入框 |
| app-server-protocol | 136,075 | 906 | 11.9% | JSON-RPC 协议类型定义 + 111,650 行自动生成的 JSON Schema |
| app-server | 105,228 | 185 | 9.2% | 协议服务器:把 JSON-RPC 翻译成引擎 SQ/EQ 操作,TUI/IDE/SDK 共用 |
| core-plugins | 29,041 | 48 | 2.5% | 插件系统:codex plugin add/list + marketplace(npm 分发源) |
| exec-server | 27,443 | 95 | 2.4% | 子进程执行的 JSON-RPC 服务:经 PTY spawn/控制进程、文件系统 handler |
| cli | 20,672 | 49 | 1.8% | 命令行入口:clap 解析 28 个子命令,分发到各模式 |
| state | 17,214 | 77 | 1.5% | SQLite 状态库:会话/记忆/日志/目标的元数据镜像 |
| config | 17,109 | 61 | 1.5% | config.toml 七层分层加载(MDM→系统→企业→用户→项目→CLI→legacy) |
| windows-sandbox-rs | 16,699 | 61 | 1.5% | Windows 沙箱:Restricted Token + ACL + WFP 网络过滤 |
| protocol | 16,299 | 37 | 1.4% | 引擎内部协议类型:Op(提交)/ EventMsg(事件)枚举、审批与沙箱策略 |
| app-server-transport | 14,967 | 26 | 1.3% | app-server 的传输层(stdio / socket 监听) |
| ext | 14,617 | 116 | 1.3% | 扩展 API 子 crate 群:connectors/goal/guardian/mcp/skills 等接口面 |
| utils | 12,893 | 124 | 1.1% | 23 个工具子 crate:PTY、模糊匹配、字符串截断、路径处理… |
| network-proxy | 12,719 | 27 | 1.1% | 受管网络代理:沙箱内出网统一裁决、按 host 审批放行 |
| login | 11,922 | 36 | 1.0% | ChatGPT OAuth(PKCE + localhost 回调)/ API key 认证、token 刷新 |
| rollout-trace | 11,302 | 37 | 1.0% | 会话 trace bundle 格式:写入器 + 语义回放 reducer |
| codex-api | 11,261 | 44 | 1.0% | Responses API 的 HTTP/SSE/WebSocket 传输与事件解析 |
| hooks | 11,186 | 48 | 1.0% | 10 种生命周期钩子(PreToolUse/Stop…)的类型与配置 |
| rollout | 10,779 | 21 | 0.9% | 会话落盘:每行一条 JSON 追加到 sessions/*.jsonl,resume 数据源 |
| analytics | 10,374 | 11 | 0.9% | 使用遥测:事件采集、reducer、上报客户端 |
| code-mode | 10,049 | 35 | 0.9% | 实验能力:模型写 JS 在内嵌 V8(deno_core)里编排工具调用 |
| rmcp-client | 9,584 | 32 | 0.8% | MCP 客户端(官方 rmcp SDK 封装):OAuth、elicitation、重试 |
| codex-mcp | 8,563 | 22 | 0.7% | MCP 连接管理:多 server 生命周期与工具聚合 |
| thread-store | 8,518 | 23 | 0.7% | 存储中立的会话线程持久化接口(本地 rollout 文件 / RPC 后端) |
| core-skills | 8,357 | 22 | 0.7% | Skills 体系:SKILL.md 发现(5 类根目录)、渲染进上下文 |
| exec | 8,284 | 32 | 0.7% | codex exec 无头模式:JSONL 事件流输出,为 CI 而生 |
| vendor | 7,405 | 40 | 0.6% | bubblewrap 的 C 源码(Linux 沙箱,直接编译进二进制) |
| linux-sandbox | 7,066 | 18 | 0.6% | Linux 沙箱:bwrap 文件系统视图 + seccomp 网络/系统调用过滤 |
| tools | 7,058 | 41 | 0.6% | 工具契约层:ToolExecutor trait、四态曝光、并行读写锁 |
| 其余 70 个目录合计 | 111,061 | — | 9.7% | otel / skills / memories / sandboxing / apply-patch / execpolicy / cloud-tasks / git-utils / features / connectors… |
core/src/ 内部再下钻一层(155,359 行,crate 内另有 tests/ 85,498 行)
| 子目录 | 行数 | 装着什么 |
|---|---|---|
| (根散文件) | 44,914 | client.rs(2,113,模型请求组装)、mcp_tool_call.rs、thread_manager.rs、exec.rs、hook_runtime.rs、compact*.rs 等约 90 个顶层文件 |
| tools/ | 34,871 | 工具运行时:spec_plan(按模型现算工具表)、orchestrator(审批+沙箱编排)、handlers/(shell、apply_patch、multi_agents…) |
| session/ | 24,415 | 主循环本体:mod.rs(3,619)、turn.rs(2,203,run_turn)、handlers.rs、rollout 重建;其中 session/tests.rs 一个文件就 10,102 行 |
| config/ | 21,864 | 配置合并与校验(mod.rs 3,544) |
| guardian/ | 7,316 | AI 审 AI:用一个专门的 guardian 会话自动评估审批请求,减少弹窗 |
| agent/ + tasks/ | 8,908 | 子 agent 编排 + 四种任务(Regular/Compact/Review/UserShell) |
| unified_exec/ | 3,975 | PTY 持久会话(exec_command/write_stdin,模型操作 REPL/vim 的能力) |
| context/ + context_manager/ | 6,627 | 对话历史持有、发请求前归一化清洗、环境上下文 |
C.3 多少行代码实现了什么功能?
下面按功能子系统横切(跨 crate 归组,与上面 crate 表口径不同;个别文件同时属于两个子系统,如 TUI 里的审批弹窗,所以子系统行数不能直接相加)。每项给「总行数(运行时 / 测试)」,测试口径同 C.1。
① 主循环与 session —— 30,372 行(运行时 15,340 / 测试 15,032)
整个 agent 的「心跳」:core/src/session/ + tasks/ + thread_manager。top 文件:session/mod.rs(3,619)、session/turn.rs(2,203)、thread_manager.rs(1,688)。这 1.5 万行运行时花在:SQ/EQ 双队列协议、TurnContext 快照、steer(干活时补话不打断)、优雅中断(先协作取消再硬杀)、以及「有 tool call 就再来一轮」的 run_turn 循环。值得玩味的是测试和实现几乎 1:1——核心循环是全仓测得最狠的地方。
② 工具系统 —— 48,099 行(27,648 / 20,451)
tools crate(契约)+ core/src/tools/(运行时)+ unified_exec + exec.rs。花在哪:ToolExecutor trait 与四态曝光(Direct/Deferred/Hidden…)、按模型现算工具表(spec_plan.rs 1,027)、读写锁并行调度、shell 的升权重试链(unix_escalation.rs 1,048)、PTY 会话管理(process_manager.rs 1,344)。Codex 没有独立的 Read/Edit 工具——读文件走 shell、改文件走 apply_patch,所以工具面比 Claude Code 小但每个都更重。
③ 三平台沙箱(重头戏)—— 36,949 行(29,859 / 7,090)
Codex 与其他三家最大的差异化投入:操作系统级沙箱是默认防线,审批弹窗只是逃生口。三个平台的成本差极其悬殊:
| 平台 | 行数 | 实现方式 |
|---|---|---|
| Windows | 16,699(windows-sandbox-rs)+ 散落各 crate 约 1,900 | CreateRestrictedToken + ACL + WFP 网络过滤——全手搓,最贵;默认还是关闭的 |
| Linux | 14,625 = linux-sandbox 7,066 + vendor 的 bubblewrap C 源码 7,387 + bwrap 编译壳 172 | 把 bubblewrap 整个 C 项目 vendor 进仓库编译进自己的二进制 + seccomp 封网/封 ptrace/io_uring;Landlock 降为 legacy 后备 |
| macOS | 约 2,107 = seatbelt.rs 687 + CLI 调试 96 + 测试 1,324 | 最便宜:委托给系统自带的 sandbox-exec,Codex 只负责生成 SBPL 策略文本 |
| 公共层 | 5,356(sandboxing crate) | 统一 SandboxType 入口、策略变换、沙箱拒绝识别 |
一句话读懂这张表:操作系统给你多少安全原语,决定你要写多少代码。macOS 有现成的 Seatbelt(800 行搞定),Linux 有 bubblewrap 可借(1.5 万行,一半是抄来的 C),Windows 什么都得自己搭(1.7 万行还只敢默认关闭)。
④ 审批与 execpolicy —— 31,789 行(19,355 / 12,434)
「什么时候问人」这一个问题写了 3 万行:shell 命令拆解与规范化(parse_command.rs 2,285)、Starlark 规则引擎(execpolicy,用户可写 prefix_rule(pattern=["rm"], decision="forbidden"))、known-safe 白名单(含 Windows 专用危险/安全命令名单各 600 行)、TUI 审批弹窗(approval_overlay.rs 2,221),以及最有意思的 guardian 子系统(7,316 行)——用一个专门的 AI 会话自动评审「这条命令该不该放行」,替人挡掉大部分弹窗。
⑤ 模型 API 层 —— 24,659 行(20,011 / 4,648)
core/src/client.rs(2,113)+ codex-api + codex-client + model-provider + models-manager + backend-client。只支持 OpenAI Responses API(Chat Completions 已物理删除),但传输花样多:WebSocket 优先、HTTP SSE 兜底、单向降级棘轮、三层重试(HTTP 退避 / 流级重连 / 传输降级),还有 realtime 语音通道(realtime_websocket/methods.rs 2,235 是本子系统最大文件)。
⑥ TUI 终端界面 —— 199,993 行(153,715 / 46,278)
全仓第二大 crate,占近 1/6。花在哪:自研输入框 chat_composer.rs(9,430 行——含 Vim 模式、粘贴突发检测、@文件补全)、resume 会话选择器(5,745)、键位映射(keymap.rs 2,540)、Markdown 流式渲染(按整行提交进终端原生回滚缓冲)、diff 渲染(2,024)。还有 360 个动画帧文件(5,553 行)和一个 3,355 行的「终端宠物」彩蛋目录 pets/。ratatui 是 immediate-mode(每帧重画),所以「界面长什么样」全是代码算出来的,没有模板可省。
⑦ app-server 协议层 —— 266,369 行(其中生成的 JSON Schema 111,650;Rust 154,719 行,内含测试 78,675)
Codex 特有、也是账面最大的子系统:app-server 六件套(server/protocol/transport/daemon/client/test-client)。TUI、codex exec、IDE 插件、SDK 全部走同一套 JSON-RPC 驱动引擎。数字要拆开读:11 万行是从 Rust 类型自动生成的 JSON Schema(906 个文件里 856 个在 schema/ 目录),真正手写的 Rust 约 15.5 万行,其中一半是集成测试(app-server/tests/ 62,250 行)——协议层是契约面,测试密度全仓最高之一。
⑧ MCP(既当客户端又当服务器)—— 26,451 行(16,479 / 9,972)
rmcp-client(官方 Rust SDK 封装 + OAuth 登录流 1,191 行)、codex-mcp(多 server 连接管理)、mcp-server(把 Codex 自己暴露成 MCP 工具给别的 agent 调)、core 里的 mcp_tool_call 路由(2,062)。对比:这一个「插件协议」子系统比整个 apply_patch + 登录认证 + exec 无头加起来还大。
⑨ 配置系统 —— 39,194 行(20,767 / 18,427)
config crate + core/src/config/。为什么一个「读配置文件」要 4 万行:七层来源合并(MDM 托管→系统→企业→用户→项目→命令行→legacy)、requirements 校验(config_requirements.rs 3,460)、在线编辑回写(edit.rs)、profile 叠加。企业管控(MDM/托管配置)是 Codex 面向公司部署的卖点,成本都在这。
⑩ rollout 录制与回放 —— 24,262 行(14,644 / 9,618)
每条对话事件时间戳落盘成 JSONL(recorder.rs 1,765)、会话列表扫描(list.rs 1,350)、压缩存档、trace bundle 语义回放 reducer(rollout-trace 11,302 行)。codex resume 的「从新到旧逆向重放重建 history」算法也在这。
⑪ 登录认证 —— 11,922 行(6,335 / 5,587)
OAuth PKCE、localhost:1455 回调服务器(server.rs 1,185)、token 刷新状态机(manager.rs 2,263,过期前 5 分钟或 8 天强刷)、keyring 优先的凭据存储。测试近半——认证状态机的边界(过期/竞态刷新/降级)全部有集成测试锁着。
⑫ 上下文管理与压缩 —— 9,479 行(5,679 / 3,800)
history 归一化(孤儿工具调用合成 aborted 输出)、90% 水位触发、四种 compact 实现(开新窗口/远端 v2/远端 v1/本地摘要兜底)。比想象中小——因为 Responses API 是有状态协议,压缩的重活大半交给了服务端。
⑬ apply_patch 补丁 DSL —— 4,543 行(3,945 / 598)
Codex 改文件的唯一入口:自定义 patch 文法解析(parser.rs 538 + streaming_parser.rs 881)、shell heredoc 拦截双入口(invocation.rs 829)、三级容错匹配(seek_sequence.rs 119——精确→忽略行尾空白→trim)。一个能让模型「宽松地」改代码的完整解析器,不到 4 千行运行时。
⑭ exec 无头模式 + hooks/skills/plugins 生态 —— 8,284 行 + 55,898 行
codex exec(CI 场景 JSONL 输出,3,537 行运行时——薄,因为它只是 app-server 的另一个客户端);生态三件套反而厚:hooks(11,186)+ skills(core-skills 8,357 + skills 6,476)+ plugins(core-plugins 29,041,含 marketplace)合计 5.6 万行——Codex 在「可扩展性」上的投入超过它的沙箱。
C.4 趣味洞察
一个 textarea:9,430 行
tui 的 chat_composer.rs(自研输入框:Vim 模式、kill buffer、粘贴检测、@补全)一个文件 9,430 行——是整个 apply_patch crate(4,543 行)的两倍。终端 UI 的钱都花在「手感」上。
全仓 JSON 的 87% 是机器写的
127,895 行 JSON 里 111,650 行是 app-server-protocol 从 Rust 类型自动导出的 Schema。「代码即协议」:改一个 Rust struct,IDE/SDK 的 TypeScript 定义和 JSON Schema 自动跟上。
Windows 沙箱 = macOS 的 8 倍
macOS 借系统 sandbox-exec 约 800 行运行时搞定;Windows 手搓 Restricted Token + ACL + WFP 花了 16,699 行——还默认关闭。Linux 干脆把 bubblewrap 的 7,387 行 C 源码整个搬进仓库编译。
38.7% 的 Rust 是测试文件
379k 行测试里最大单文件是 core/src/session/tests.rs(10,102 行)——主循环一个文件的测试就超过 apply_patch + exec 无头两个子系统的全部代码。
3,355 行终端宠物
tui/src/pets/ 是一个完整的终端宠物系统(模型、图片协议、ambient 动画、选择器),外加 frames/ 目录 360 个动画帧文件 5,553 行。比整个 execpolicy 规则引擎(2,572 行)还大。
和 Claude Code 的 Ink TUI 对比着感受:Claude Code 用 React/Ink 声明式写 UI,组件树描述「长什么样」;Codex 的 ratatui 是 immediate-mode,20 万行 Rust 每帧手算布局、手写流式 Markdown 逐行提交进终端回滚缓冲。同样的产品体验,Rust 路线的 UI 代码量是数倍级的——这是「原生性能 + 单二进制分发」的直接代价。
C.5 小白学习路径:只读 5,000 行的话读什么
顺着《源码解剖》的「一条消息的一生」14 站主线走,按依赖顺序读这 10 段(合计约 4,800 行,路径相对仓库根):
npm 壳:程序怎么被启动的(~160 行)
看懂「JS 层零业务」:按平台挑原生二进制、透传信号。
codex-cli/bin/codex.js
入口与 busybox 技巧(~400 行)
cli/src/main.rs 的子命令定义(94-212 行附近)+ arg0/src/lib.rs 的「同一个二进制按名字变身」。
codex-rs/cli/src/main.rs · codex-rs/arg0/src/lib.rs
协议词汇表:Op 与 EventMsg(~600 行)
protocol.rs 里挑 Op 枚举(515 行起)和 EventMsg 枚举(1267 行起)读——这是引擎的全部输入输出词汇。
codex-rs/protocol/src/protocol.rs
外层循环:RegularTask(~230 行)
全文最短也最重要的循环:跑一个 turn,有 steer 输入就再跑一个。
codex-rs/core/src/tasks/regular.rs
内层循环:run_turn(~500 行)
agentic 循环本体:组 prompt → 采样 → 执行工具 → 回填 → needs_follow_up 决定是否再来一轮。读 turn.rs:142-459 附近即可。
codex-rs/core/src/session/turn.rs
安全编排:orchestrator(~450 行)
一条命令的四道关:execpolicy 规则 → 审批 → 沙箱执行 → 升权重试。文件头注释就是流程自述。
codex-rs/core/src/tools/orchestrator.rs
模型请求怎么组装(~800 行)
client.rs 挑 build_responses_request(812 行起)和 stream()(1734 行起):字段一个个对着 Responses API 文档看。
codex-rs/core/src/client.rs
一个完整的解析器:apply_patch(~660 行)
parser.rs + seek_sequence.rs:麻雀虽小五脏俱全的 DSL 解析 + 三级容错匹配,适合当第一个「从头读完」的 Rust 模块。
codex-rs/apply-patch/src/parser.rs · seek_sequence.rs
沙箱最易读的一块:Seatbelt(~690 行)
seatbelt.rs:怎么生成 SBPL 策略、怎么防路径注入。三平台里唯一能一次读完的。
codex-rs/sandboxing/src/seatbelt.rs
无头模式收尾(~400 行)
exec/src/lib.rs 挑主流程:验证你已经能把「事件流 → JSONL 输出 → 退出码」串起来。至此 5,000 行预算用完,主线闭环。
codex-rs/exec/src/lib.rs
口径备注:本节所有行数 = cloc 统计的纯代码行(git 跟踪文件,剔除空行/注释);「运行时 / 测试」按文件名划分(tests/ 目录 + *tests.rs + test_* 文件),无法剔除实现文件内的 #[cfg(test)] 内联测试;功能子系统为跨 crate 归组,存在少量文件重叠,不可加总。crate 职责描述来自《Codex CLI 源码架构全解剖》及源码头注释核对;exec-server、state、thread-store 等职责摘自各 crate 的 lib.rs/README 自述。
FFFFAI Agent:4.9 万行核心——「长在平台上的模块」和「独立产品」不是一个量级
我们自己的 agent(ffworkspace 仓库 backend/src/modules/agent/ + 前端/桌面/协议包),cloc 同口径统计(git 追踪文件、纯代码行、剔除空行注释和 lockfile),2026-07-07 实测于 develop。姊妹篇《FFAI Agent 实现架构全解剖·对照 Claude Code》讲怎么实现的,本节讲花了多少代码。
1 · 总量与口径:先声明一个天生的不公平
把 FFAI 放进这张对比表,必须先讲清口径差:其余四家是独立产品,UI 渲染、权限体系、存储、配置、更新机制全要自带;FFAI 是长在 ffworkspace 企业平台上的一个模块——IAM/RBAC、DataScope、Prisma、统一存储、通知、i18n 这些地基归平台,不算 agent 的行数。所以下面的数字天生偏小,比的是「agent 本体」而不是「跑起来需要的一切」。
| 范围 | 代码行 | 文件数 | 这是什么 |
|---|---|---|---|
backend/src/modules/agent/ | 34,131 | 254 | 后端核心:主循环、四轨工具、沙箱、5 家 provider + IR、路由、记忆、审计链、Teams/桌面通道 |
前端(app/agent + components/agent + 散件) | 11,190 | 31 | Web 工作台:对话页、Canvas、会话侧栏、admin 三页(cost/routing/memories) |
desktop/ | 2,990 | 26 | Electron 壳 + HostBridge 本地强制层(scope/realpath/授权弹窗) |
packages/agent-protocol + agent-renderer-lark | 496 | 15 | 跨端类型契约 + 渲染器包(骨架期) |
| 核心实现合计 ★ | ≈48,807 | 326 | — |
testing/ agent 测试 | 20,466 | 135 | 单测 6,023 + 集成 14,443(内真外假,L1 连真 PG/Docker) |
packages/agent-client | 1,830 | 42 | 独立 Tauri 客户端原型(实验品) |
agent-skills/ 内容 | 162 | 4 | 平台 skill 的 markdown 本体 |
| 全口径合计 | ≈71,265 | ≈507 | — |
本节结论口径:FFAI Agent 核心实现 ≈ 48,807 行代码(326 文件,几乎全 TypeScript)——约为 Claude Code 的 1/8、OpenClaw 的 1/22。测试 20,466 行另计(=核心的 42%;注意 CC 的 source map 还原仓库不含测试,此项四家口径本就不齐)。下文百分比以 34,131(后端模块)或 48,807(含前端)为分母,逐处标明。
2 · 逐模块代码量:钱都花在哪个目录
backend/src/modules/agent/ 全部 26 个子目录(占比以 34,131 为分母):
| 目录 | 代码行 | 文件 | 占比 | 一句话职责 |
|---|---|---|---|---|
sandbox/ | 7,375 | 60 | 21.6% | 最大单块:per-user 持久容器、cloud_code_exec、暖池/回收/GC、env 剥离——安全预算花在容器不是弹窗 |
services/ | 7,229 | 23 | 21.2% | 心脏:messages.service(主循环 2,402)、sessions、记忆三件套、MCP 管理、cron、取消广播 |
tools/ | 2,395 | 16 | 7.0% | ToolRegistry 治理收口 + cli_exec 白名单 + Docker 底座 + sql_read_query + IAM hook |
protocols/ | 2,232 | 18 | 6.5% | 厂商中立 IR + OpenAI/Anthropic 双编解码 + SSE 读流(45s stall watchdog)+ 重试 |
storage/ | 2,161 | 16 | 6.3% | 工作区 ~/ 命名空间、三层 binding(OneDrive/LOCAL)、文件版本、信封加密 |
controllers/ | 2,002 | 23 | 5.9% | HTTP/SSE 入口 ×23 个 controller(messages/sessions/workspace/trajectory/admin…) |
teams/ | 1,698 | 15 | 5.0% | Teams Bot:webhook 入站、Adaptive Card 投影、proactive 推送、SSO/OBO |
providers/ | 940 | 10 | 2.8% | OpenAI/Anthropic/Gemini/Qwen/Mock 五家 + capability 声明 + 出网合规门 |
quota/ | 939 | 5 | 2.8% | org/user 双 scope 月度配额(含容器机时)+ 成本累计 + 价目表 + 预算告警 |
projects/ | 904 | 3 | 2.6% | 项目工作区(Gitea 集成)+ app_promote |
registry/ | 783 | 10 | 2.3% | @AgentTool 装饰器 + forFeature 收集器 + bootstrap 装配(业务模块一行接入的机关) |
hooks/ | 666 | 5 | 2.0% | 13 事件 HooksRegistry + DB 装配桥 + HTTP webhook 执行器(SSRF guard)+ 内置审计 hook |
router/ | 625 | 4 | 1.8% | ★ModelRouter:三层 scope 规则 + 轻 turn 降档 + region-aware + 决策落库 |
asr/ | 605 | 7 | 1.8% | 实时语音转写(doubao/qwen + WS gateway) |
artifact/ + skills/ + utils/ | 1,553 | 21 | 4.6% | Canvas artifact(13 类型+版本链)535 · skill 4 层加载器 533 · SSRF/bounded-fetch/脱敏等工具 485 |
subagent/ + engine/ + trajectory/ + devices/ + 其余 7 目录 | 1,577 | 17 | 4.6% | 子 agent 并发内核 315 · 压缩引擎 220 · HMAC 审计链 216 · 设备绑定 187 · scratchpad/queues/桥接等 |
| 模块根文件 | 347 | 2 | 1.0% | agent.module.ts 装配(依赖注入全家福) |
3 · 多少行代码实现了什么功能
与总览表同口径的功能子系统聚合(跨目录加总,个别有重叠会标注):
① Agentic 主循环 —— 3,483 行 / 4 文件
messages.service.ts(2,402,runTurnStream 主循环 + failover + 上下文组装)+ sessions.service.ts(761,含 delegate_task 子 agent spawn)+ 压缩引擎(220)+ 跨副本取消广播(~100)。和 CC 的 3,755 行几乎同量——五个项目再次验证:主循环本体就是几千行,agent 的智能在模型里。
② 工具系统 —— ≈3,945 行
tools/ 治理收口(2,395)+ registry/ 装饰器机关(783)+ client/ 桌面工具 descriptor(228)+ 业务模块独立工具出口(539)。远小于 CC 的 41,125——因为 85 个 @AgentTool 大多直接挂在业务 Service 方法上(合同/表单/审批/采购/M365),工具的「肉」在业务模块里本就存在,agent 侧只出「接口皮」。这正是 service 轨的意义:不搬运业务逻辑,只暴露它。
③ 沙箱 + 治理 + 审计 —— ≈8,076 行
sandbox/(7,375)+ 治理流水线 governance(298,与②少量重叠)+ HMAC 审计链(216)+ 设备绑定(187)。预算结构像 Codex 不像 CC:CC 的 23,041 行权限系统里一半是「允许吗」对话框(问机主);FFAI 多租户没有机主可问,钱全花在容器隔离(network=none/cgroup/env 剥离)和结构保证(收口点/fail-closed/哈希链)上。权限判定本体复用平台 IAM,不在此数内。
④ Provider + IR + 路由 + 配额 —— 4,736 行
protocols/(2,232)+ providers/(940)+ router/(625)+ quota/(939)。对比 CC 的 11,365:我们没有 OAuth/四条认证路径/订阅体系(企业统一 API key),省下的行数换来了 CC 没有的东西——5 家厂商、中立 IR、ModelRouter、双 scope 配额。OpenClaw 同类层花了 47,747 行(十几家 provider 散弹),我们用「2 个协议编解码器伺候 5 家」的收敛设计压到 1/10。
⑤ 上下文压缩 —— 220 行 / 1 文件
全场最悬殊的对比:CC 4,626 行,FFAI 220 行(21 倍差)。原因不是我们简陋——五层机制、同款阈值公式(window−20000−13000)、microcompact 白名单、熔断全都有——而是后发临摹的杠杆率:CC 的 4,626 行里大量是试错沉淀(三套策略并存、cache-editing 实验、被动压缩兜底),我们直接抄最终答案,一个文件写完。
⑥ UI(Web + 桌面壳)—— 14,180 行
Web 11,190 + Electron 2,990。CC 的 1/7.5(106,624)——不是我们 UI 简陋,是渲染引擎白拿:CC 要自研 fork Ink(React→Yoga→ANSI 帧 diff,1.3 万行)再写 592 个终端组件;我们跑在浏览器 + Lark 设计系统上,只写业务组件。这是「平台复用」价值最直观的一笔账。
⑦ MCP —— 375 行
CC 的 1/46(17,297)。差距来自功能面:CC 做了 5 种 transport + OAuth step-up + 服务器市场/审批 UI;我们只做 stdio/SSE 两种 transport + DB 管理 + admin API,工具包装后直接进统一治理流水线(脱敏/审计免费获得)。多租户产品的 MCP 必须窄——每多一种接入方式就多一个审计面。
⑧ 记忆 + 工作区 —— ≈3,212 行
记忆服务三件套(memory-file/snapshot-cache/curator + org 记忆,1,051)+ storage/ 工作区(2,161)。CC 的 memdir 只有 1,140 行——多出来的部分全是多租户税:per-user 命名空间隔离、三层 binding、信封加密 OAuth token、共享上下文三层隐私护栏、文件版本链。
⑨ Teams 通道 —— 1,698 行
四家全都没有的行当(CC/Codex 是终端、Hermes/OpenClaw 的 IM 接入是个人向)。webhook 入站 + 后台跑 turn + GFM→Adaptive Card 投影 + proactive 私聊推送 + SSO/OBO。企业 agent 的「入口在员工已经在的地方」,这 1,698 行买的就是这个。
⑩ Hooks + Skills —— 1,199 行
Hooks 666(13 事件 + DB 装配 + SSRF-guarded webhook 执行器)vs CC 的 6,791(27 事件 ×4 后端);Skills 533(4 层 scope 加载器)vs CC 的 5,581。行数差 = 口径收窄:多租户禁 command hook、禁 skill 内联 shell,砍掉的正是 CC 里最贵的执行后端。
4 · 五个由数字推出的洞察
- 4.9 万 vs 39 万:1/8 的差距由三个因素构成,缺一不可——① 平台复用(浏览器+Lark DS 省掉 CC 的 10.6 万行 UI 引擎;平台 IAM 省掉 2.3 万行权限系统);② 后发临摹(压缩 220 vs 4,626 是极端样本,阈值常量逐字对齐 CC,试错成本为零);③ 功能面收窄(无插件市场 1.5 万、无斜杠命令 2.5 万、无 vim/keybindings/桌宠)。不能只用其中一个因素解释,更不能反过来说明「我们效率是 CC 的 8 倍」。
- 沙箱是我们最大的单块(7,375 行,占后端模块 21.6%)——预算结构与 Codex 同型(Codex 沙箱 29,859)而与 CC 相反:CC 把安全预算花在「把问题问清楚」(权限 UI 1.15 万行),我们和 Codex 花在「让问题问不出来」(OS/容器级强制)。多租户无人值守场景下这是唯一解。
- 最大单文件在前端不在后端:
frontend/src/app/agent/page.tsx3,830 行 >messages.service.ts2,402 行。「巨型文件病」和 CC 一模一样(CC 是 REPL.tsx 4,447 + print.ts 4,421)——五个项目无一幸免:主界面和主循环永远是最难拆的两个上帝文件。 - 注释率 17.7%,与 CC 的 18.2% 几乎相同(后端模块 7,337 行注释 / 34,131 行代码)。两个毫无关联的团队在成熟 TypeScript 工程上收敛到同一个注释密度,这大概是行业常数。
- 测试 20,466 行 = 核心实现的 42%,且是「内真外假」的集成测试为主(连真 PG/Docker)。CC 的还原仓库不含测试无从比较;Hermes 测试 48 万行(120%)是另一个极端。42% 对一个两个月大的模块是健康水位。
5 · 学习路径:只读 ~4,700 行,读这 8 个文件
按《一条消息的一生》的顺序读(行数为 cloc 代码行):
| 顺序 | 文件 | 代码行 | 你会学到 |
|---|---|---|---|
| 1 | tools/tool.types.ts | 91 | 工具契约:descriptor 有哪些治理标志(writeAction/destructive/idempotent/exposure) |
| 2 | registry/agent-tool-bootstrap.service.ts | 148 | @AgentTool 装饰器怎么在启动时变成可调工具(业务模块一行接入的全部机关) |
| 3 | services/messages.service.ts(读 runTurnStream 段) | 2,402 | 核心中的核心:while(true) 主循环、failover 候选链、上下文八段组装 |
| 4 | tools/tool-registry.service.ts | 454 | 治理收口:IAM→确认→幂等→执行→脱敏→落盘,零绕过的结构保证 |
| 5 | engine/query-engine.service.ts | 220 | 压缩五层一个文件写完(对照 CC 的 4,626 行体会后发优势) |
| 6 | protocols/sse-line-reader.ts | 108 | 45s stall watchdog:为什么心跳「:」刻意不续命 |
| 7 | router/model-router.service.ts | 430 | ModelRouter:三层 scope 匹配、轻 turn 降档、决策落库——CC 没有的一层 |
| 8 | trajectory/trajectory.service.ts | 111 | HMAC 审计哈希链:111 行买到「DBA 也伪造不了审计」 |
读完的反直觉结论和 CC 篇同一句话:没有计划器,只有一个循环、一张工具表、一套治理门禁——只是 FFAI 把 CC 的「权限门禁」换成了多租户版的「治理门禁」,其余 4.4 万行都在为这三件事做安全、通道和生态。