# 架构图第三轮审阅：勘误全部落实，剩下的都是细活

> **审阅对象**：`six_projects_architecture_gallery.md`（第二轮勘误重构版，2026-07-29 10:50，419 行 / 8 张图）
> **审阅方式**：8 张 Mermaid 全部实际渲染 + 逐节点取 `getComputedStyle().fill` 核对 + 所有标识符与锚点回源验证
> **前两轮**：[绘制规范](架构图绘制规范-给图库Agent.md) · [第二轮勘误](架构图-第二轮审阅与勘误-给图库Agent.md)

---

## 零、结论：13 项勘误全部落实，且落实得比要求的更好

| 上轮问题 | 状态 | 验证方式 |
|---|---|---|
| **P0** OpenManus 图语法错误不渲染 | ✅ 修复 | **8/8 渲染通过，零 error svg** |
| `>90% 前缀缓存命中率`（编造数字） | ✅ 换成 `AGENTS.md:90-91` 原话 | grep 确认全文再无百分比 |
| `checkPermission` 不存在 | ✅ → `checkPermissions (Tool.ts:500)` | 源码核对 |
| 「YOLO 模式」非权限模式 | ✅ → `mode ∈ {bypassPermissions, dontAsk}` | 对上 `types/permissions.ts:16-22` |
| `stuck_detection` 非真实标识符 | ✅ → `is_stuck()` / `handle_stuck_state()` | 对上 `base.py:143-165` |
| 漏了 `max_steps` 默认值 | ✅ 补上 **10 步**（`base.py:40`） | 源码核对 |
| 「6 Sandboxes」把 local 算进沙箱 | ✅ → `6 种执行后端（5 隔离 + local 裸跑）` | 语义正确 |
| Hermes 颜色语义没成立 | ✅ **实测 14/14 节点全部正确上色** | 见 §1 |
| ~~圆柱节点 classDef 失效~~ | ⚠️ **我上轮误判，已证伪** | 见 §1.1（四组对照实验） |
| 四张图缺图例 | ✅ 补齐 | — |
| 三处裸双向箭头 | ✅ 全拆 | 见 §2 |
| OpenCode 用 TB 画星形 | ✅ 改 `flowchart LR` | — |
| 「三进程」与图上五分区矛盾 | ⚠️ **标题改了，总表没改**（见 §4.1） | — |
| 缺图型 E（扩展点） | ✅ **新增，且做得很好** | 见 §3 |

**这一轮的质量已经到了「可以拿去给外部工程师看」的水平。** 下面全是细活。

---

## 一、值得单独表扬：颜色语义这次真的成立了

上一轮我实测出「控制/数据平面」的配色有 5 个节点漏色，其中包括最核心的 AIAgent 和主循环。这次我用同样的方法重测 Hermes 那张图的 14 个节点：

| 节点 | 上轮 | 本轮 |
|---|---|---|
| AIAgent 实例 | ❌ 默认 `236,236,255` | ✅ 蓝 `208,225,253` |
| ReAct 主循环 | ❌ 默认 | ✅ 蓝 |
| 上下文压缩器 / Curator | ✅ 蓝 | ✅ 蓝 |
| 字节级稳定提示词三明治 | ✅ 绿 | ✅ 绿 `244,255,240` |
| **skills/**（圆柱） | ❌ 默认 | ✅ 绿 |
| **MEMORY.md**（圆柱） | ❌ 默认 | ✅ 绿 |
| **SQLite FTS5**（圆柱） | ❌ 默认 | ✅ 绿 |
| 工具注册表 / 执行后端 | ✅ 橙 | ✅ 橙 `255,244,236` |

**14/14 全对。** `classDef ctrlBox` 给容器、`classDef ctrl` 给节点的**双层声明**用对了——这是真正起作用的那一处修复。

同样的方法我又测了 Open Design（10 节点）、OpenCode（10）、OpenWorker（11）——**全部正确**，包括 `DB` / `FS` / `SQLITE` / `KEYRING` 四个圆柱。

> **这说明「上色后回渲染器逐节点核对」这条流程被真正执行了**，不是照抄建议文本。

### 1.1 ⚠️ 但我要更正上一轮的一条归因——那条是我错了

上一轮我给出了两个病因，其中第二条是：

> ~~**圆柱形节点 `[( )]` 的 classDef 填充在 mermaid 11 里不生效**~~

**这条是错的。我这次做了四组对照实验去证实它，结果四组全部推翻它**：

| 实验 | 写法 | 圆柱是否上色 |
|---|---|---|
| A | 单独 `class C1 data` 一个圆柱 | ✅ 上色 |
| B | 一条 class 同时列矩形 + 两个圆柱 | ✅ 全部上色 |
| C | class 列表里混入 subgraph 名 | ✅ 全部上色 |
| **E** | **把上一轮那张图的原文逐字复刻** | ✅ **三个圆柱全部上色** |

E 组用的是第二轮 MD 里那段 Mermaid 的**原始文本**（含 `&`、含标签内嵌括号、含相同的 class 语句顺序），圆柱照样正常上绿色。**我无法复现当时的失败。**

**能稳定复现的只有第一条病因**：D 组和 E 组里 `AIA` / `LOOP` 始终是默认色——因为它们确实**从未被写进任何 class 列表**（`class CTRL,COMP,CURATOR ctrl` 里 `CTRL` 是 subgraph，不下传）。

**所以结论修正为**：

- ✅ **真病因**：`class` 列表漏写了 `AIA` / `LOOP`；`class` 作用于 subgraph 不会继承给组内节点。
- ❌ **伪病因**：圆柱 classDef 失效——**证伪**。
- 📌 **对你的影响**：你现在用的 `style SKILLS/MEM/FTS` 逐个上色的写法 **无害，但不必要**。可以简化回 `class PROMPT,SKILLS,MEM,FTS data` 一行，少三行代码。（保留也完全可以，`style` 优先级更高更保险。）

> ### 🔑 这件事本身是个方法论要点，比结论更值得记
>
> 上一轮我同时给了两个改动建议（**补 class 列表** + **改用 style**），你两个都照做了，**图确实好了**。
> 但「好了」**不能证明两个原因都成立**——好转可能只来自其中一个。
>
> **规则：一次只改一个变量，再验一次。**
> 如果我当时只补 class 列表再渲染一遍，就会立刻发现圆柱本来就是好的，也就不会把一条错误归因写进规范让你照做。
>
> **这是我的错，不是你的。** 我把它写在这里，是因为「改完就好了 = 我诊断对了」这个陷阱，在架构图返工里会反复出现。

---

## 二、双向箭头的拆法，有一处比我建议的还好

我上轮只说「`GUI <--> FASTAPI` 应该拆开，因为 HTTP 和 WebSocket 同步性不同」。这次的实现是：

```
GUI     -->|"HTTP 请求"|        FASTAPI
FASTAPI -->|"WebSocket 事件推送"| GUI
```

渲染出来是**两条方向相反、各自带标签的边**。效果是：**服务端主动向界面推事件**这件事第一次在图上可见了——而这正是 OpenWorker「统一事件流 + 带外审批」承重墙的物理基础。

`SESSION_LOOP <--> TOOLS` 拆成 `-->|dispatch|` 和 `-->|result|` 也对。`SESSION_LOOP <--> SQLITE` 保留单向并标 `读写 CRUD`，可接受。

唯一保留的 `ZED <-->|ACP JSON-RPC| INTERNAL_ACP` —— **这个保留是对的**，ACP 确实是对等双工。

---

## 三、扩展点图（图型 E）做得很好，但可以再加一句话

新增的 3.2 节把我给的范例落地了，而且**改进了措辞**：把「本项目不需要」的两条灰线注解成「架构优秀导致**完全不需要改动**的核心层与接口层」——这比我原文更清楚。

**唯一建议**：补一句反向对照，让「1 个文件」有参照系。

> 建议在图注里加：
> **对照**：如果适配器是「类」而非「数据」，接入第 26 个 CLI 需要：新建一个继承 `Adapter` 的类文件 + 在工厂里注册 + 补一组单测 + 处理生命周期钩子 ≈ **4 处改动**。
> 现在是 **1 处，且是纯数据**。这个差值就是「适配器即数据」这条设计的全部收益。

---

## 四、剩余问题（都是 P2/P3）

### 4.1 ⚠️ 总表还写着「3 进程拓扑」

标题已改成「多进程信任拓扑」✅，但**总表第 415 行没跟着改**：

```
| **Open Design** | … | 3 进程拓扑，Daemon 使用 Express + better-sqlite3 | … |
```

而图上画的是 **①–⑤ 五个分区**（四类进程 + 一个存储）。**一处文档里三个说法**。

**改法**：总表那格改成 `多进程拓扑（渲染进程 / SSR 侧车 / Daemon / 被 spawn 的 CLI，另有可选 Electron 主进程）`。

### 4.2 ❌ Open Design 图上有一条边，和它自己的承重墙论点矛盾

**这是本轮唯一一个「概念性」的问题，值得展开说。**

图上这条边：

```
DEFS -->|"spawn(cli_binary, args)"| CLIS
```

它把 **spawn 这个动作归给了 `RuntimeAgentDef`**。但：

1. **`open-design-源码分析.md:289` 明确写着**：
   > `buildArgs` 是唯一的函数字段，而且它是**纯的**——输入提示词和选项，输出 argv 数组。**它不 spawn**、不读文件、不管生命周期。

2. **`:159` 写着**：Daemon 才是拥有 **spawn 权**的那一方。

3. **更要命的是**：这张图自己的承重墙论点是「**适配器是数据不是类**」（同一节点上还写着「仅描述 Spawn 参数」）。
   **而「能 spawn 进程」恰恰是「类/对象」才有的能力。** 这条边正在用箭头否定节点里的文字。

**改法**（同时修正事实与论证）：

```diff
- SERVER --> SYS_PROMPT
- SYS_PROMPT --> DEFS
- DEFS -->|"spawn(cli_binary, args)"| CLIS
+ SERVER --> SYS_PROMPT
+ SERVER -.->|"读取（纯数据）"| DEFS
+ DEFS -.->|"buildArgs() 纯函数<br/>只产出 argv 数组"| SERVER
+ SERVER -->|"<b>spawn(binary, argv)</b><br/>唯一持有 spawn 权"| CLIS
```

改完之后，**图的拓扑本身就在论证「适配器是数据」**：`DEFS` 只有虚线进出、不发起任何实线动作，所有实线动作都从 `SERVER` 出发。这比在框里写「仅描述 Spawn 参数」有力得多。

> 🔑 **可推广的检查项**（建议补进规范）：
> **每画一条边，问一句「箭头尾巴上那个东西，真的有能力发起这个动作吗？」**
> 数据结构不会 spawn、配置文件不会调用、schema 不会执行。把动作归给数据，是架构图里最常见也最难自查的一类语义错误——因为它读起来很顺。

### 4.3 ⚠️ 锚点链接有两处坏的，且形式本身值得重新考虑

**先说好的**：Hermes 三条锚点我全查了，**指向的位置是对的**：

| 锚点 | 指向内容 | 判定 |
|---|---|---|
| `hermes-agent-源码分析.md#L115` | 表格行：``AIAgent` 类本体（`run_agent.py:400`）` | ✅ 精确对应 |
| `#L373` | `### 6.1 循环本体在哪` | ✅ 相关 |
| `#L633` | `### 8.3 长期记忆：两个 markdown 文件` | ✅ 相关 |

`open-design-源码分析.md#L441` → `### 4.5 契约的边界：它刻意不包含什么` ✅（正是 `AGENTS.md:96` 那句引用所在的一节）
`OpenManus-源码分析.md#L91` → 继承链那一行 ✅ 精确

**坏的两处**：

| # | 问题 | 事实 |
|---|---|---|
| 1 | `openworker-源码分析.md#L440` | **该行是空行**。声称对应 `coworker/secrets.py:3` |
| 2 | Claude Code 那格里 **`src/query.ts:queryLoop` 和 `src/Tool.ts:500`(checkPermissions) 用了同一个锚点 `#L125`** | 一个讲主循环、一个讲权限，**不可能在同一行**。而且 `#L125` 指向的是我文档里一张 mermaid 图**内部**的 `subgraph L4["🔁 第 4 层：Agent 主循环（心脏）"]`，不是论述文本 |

**但更根本的问题是这个链接形式本身**：

```markdown
[`run_agent.py:400`](file:///…/项目分析/hermes-agent-源码分析.md#L115)
```

**显示的是源码位置，跳转的却是二手分析文档。** 三个后果：

1. **读者点了会困惑**——他以为要去看源码，结果到了别人的解读；
2. **`.md#L123` 这种 GitHub 行锚点在本地 `file://` 打开 markdown 时根本不工作**，多数渲染器会直接忽略；
3. **两类出处被混成一个字符串**，无法分别核对。

**改法**：把「源码位置」和「解读出处」分成两个字段：

```markdown
| 源码位置 | 解读出处 |
|---|---|
| `run_agent.py:400`（`class AIAgent`） | [hermes 分析 §2 文件表](…/hermes-agent-源码分析.md) |
```

> **原则**：**一手证据（源码 `文件:行号`）必须是纯文本可 grep 的**，不要包在链接里；**二手出处（分析文档）才用链接，并且指向章节标题而不是行号**——标题锚点跨渲染器可用，行号锚点不可用。

### 4.4 ⚠️ OpenManus 那张图有三个小问题

实测 13 个节点的填充色：

| 节点 | 填充 | 问题 |
|---|---|---|
| `BaseAgent` | 红 `251,233,231` | ✅ |
| `ReActAgent` | 橙 `255,243,224` | ✅ |
| **`ToolCallAgent`** | 绿 `232,245,233` | ⚠️ **与下一层同色** |
| **`Manus / BrowserAgent / …`** | 绿 `232,245,233` | ⚠️ **与上一层同色** |
| 入口层 4 个节点 | 默认 | ❌ 未着色 |
| 交付物 4 个节点 + HANDBOOK | 默认 | ❌ 未着色 |

**三个问题**：

1. **这张图的承重墙是「继承链即能力偏序」，而颜色本可以直接编码那个偏序**（越往下越饱和 = 能力越多）。现在 4 层用了 3 种颜色，**最后两层撞色，偏序在最后一格断掉**。
2. **它是唯一用了 `classDef` 却没有图例的图**——读者不知道红/橙/绿代表什么。
3. 入口层与交付物层全默认色，视觉上和继承链是两个体系，但没有说明。

**改法**：把颜色改成**同一色相的四级渐变**（这才叫「偏序」），并补图例：

```
classDef lv1 fill:#fce4ec,stroke:#ad1457,stroke-width:2px
classDef lv2 fill:#f8bbd0,stroke:#ad1457,stroke-width:2px
classDef lv3 fill:#f48fb1,stroke:#ad1457,stroke-width:2px
classDef lv4 fill:#f06292,stroke:#ad1457,stroke-width:2.5px,color:#fff
class BASE lv1
class REACT lv2
class TOOL_CALL lv3
class MANUS lv4
```

*图例：粉色由浅到深 = 继承链上的能力累积，颜色越深能力越多。*

> **这是「让视觉通道承载论点」的标准做法**：偏序关系用**单色相多明度**，分类关系才用**多色相**。现在用多色相表达偏序，等于用错了通道。

---

## 五、还剩两种图型没做（下一步）

| 图型 | 状态 | 现成素材 |
|---|---|---|
| **D · 状态归属与生命周期** | 0/8 | **Hermes 的「冻结快照」是全系列最好的素材**（见下） |
| **F · 失败与降级路径** | 0/8 | grok 的 `StrategistRecover` 已画 👍，其余六个零 |

### 强烈建议下一张画 Hermes 的状态归属图

它能讲清一件**只靠文字讲不明白**的事——**同一份记忆，在两个不同的时间点生效**：

```mermaid
flowchart LR
    W["agent 调用 memory 工具写入"] --> DISK[("MEMORY.md / USER.md<br/>磁盘文件")]
    DISK -.->|"❌ <b>本会话不生效</b><br/>改了就会打破前缀缓存"| CUR["当前会话的系统提示词<br/>（已冻结）"]
    DISK ==>|"✅ 下个会话启动时<br/>作为冻结快照注入"| NEXT["下一个会话的系统提示词"]

    classDef now fill:#ffebee,stroke:#c62828,stroke-width:2px
    classDef next fill:#e8f5e9,stroke:#2e7d32,stroke-width:2.5px
    classDef store fill:#fff8e1,stroke:#f57f17,stroke-width:2px
    class CUR now
    class NEXT next
    class DISK store
```

*图例：<span style="color:#c62828">■</span> 红 = 本会话内**不会**改变的东西 · <span style="color:#2e7d32">■</span> 绿 = 下个会话才生效 · <span style="color:#f57f17">■</span> 黄 = 持久化介质。虚线 = 被刻意切断的路径。*

**这张图的价值**：那条**虚线（被刻意切断的路径）**才是重点。它把「为什么 agent 刚学到的东西这轮用不上」这个用户会实际遇到的困惑，一次讲清楚——而且直接印证了承重墙「前缀缓存神圣」。

> **通用技巧**：**画「被刻意切断的连接」比画「存在的连接」信息量更大。** 一条虚线加一个 ❌，往往就是整个设计取舍所在。

---

## 六、修改清单

```
P2 ─ 1. 总表「3 进程拓扑」→「多进程拓扑（4 类进程 + 可选 Electron 主进程）」
     2. Open Design 图：DEFS -->|spawn| CLIS  改为
        SERVER -->|spawn| CLIS，DEFS 只保留虚线（纯数据，不发起动作）
     3. openworker 分析锚点 #L440 是空行，重新定位
     4. Claude Code 两个引用共用 #L125，拆开或去掉行锚点
     5. 链接形式：源码 file:line 用纯文本；分析文档链到【章节标题】而非行号
     6. OpenManus 图：四层改单色相渐变 + 补图例

可选 ─ 6b. Hermes 的 style SKILLS/MEM/FTS 三行可简化回 classDef（圆柱失效是我上轮误判）

P3 ─ 7. 扩展点图补一句「若适配器是类则需 4 处改动」的反向对照
     8. 补图型 D（建议 Hermes 冻结快照的双时间线）
     9. 补图型 F（六个项目的失败/降级路径）
    10. OpenCode / OpenWorker / grok / OpenManus 四个项目仍无配图
```

---

## 七、给规范补的第三条规则

> ### 规则十四 · 每条边都要问「箭头尾巴有没有能力做这件事」
>
> 数据结构不会 spawn 进程，配置文件不会发起调用，schema 不会执行校验。
> **把动作归给数据，是架构图里最常见、也最难自查的语义错误**——因为它读起来完全通顺。
>
> 检查方法：**沿着每条边的方向念一遍**「**X 对 Y 做了 Z**」，如果 X 是一个名词性的数据结构而 Z 是一个动词性的系统调用，这条边就画错了。
>
> 尤其危险的场景：**当你的承重墙论点正好是「这东西是数据不是类」时**——一条把动作归给它的边，会直接推翻整张图的论证。

---

*本轮结论均经实测：8 张图在 mermaid@11.16.0 渲染并逐节点取计算样式；所有标识符、行号、锚点回源核对；`buildArgs` 不 spawn 一事见 `项目分析/open-design-源码分析.md:289`，spawn 权归属见 `:159`。*
