# 十二大 AI Agent 横向对比与总结评价（Claude Code / opencode / hermes-agent / Raven / CodeWhale / goose / nanobot / grok-build / OpenWorker / **Open Design** / **MiMo Code** / **MiroFish**）

> **本文是什么**：这是"AI Agent 源码分析系列"的横向综合篇。写作依据是本仓库已完成的九份单项目分析——《Claude-Code-工作原理科普-深入版》与《项目分析/》下的 opencode、hermes-agent、Raven、CodeWhale、goose、nanobot、grok-build、OpenWorker 八份源码分析。文中所有事实均以那九份文档为准，关键机制转引它们标注的 `文件:行号`，你可以顺藤摸瓜回到单项目文档、再回到源码核对。特别说明：**nanobot（港大 HKUDS）是 Raven 的上游**——Raven 的 Python 运行时 fork 自 nanobot `v0.1.5.post3`，再叠 EverOS / Curator / Spine 等能力；读 Raven 时对照 nanobot，能看清"出厂设置"与"后加改造"。**grok-build 是 xAI（马斯克）官方 CLI**，Rust 60+ crate monorepo，最大特色是 Goal Mode 五件套显式状态机与 ACP 协议。**OpenWorker 是吴恩达团队的开源"AI 同事"**——注意它是**另一个物种**：前八家都是"编程 Agent"，OpenWorker 是本地优先的**通用任务同事**（接 Slack、按 cron 定时、动手前先问、交付成品），引擎建在吴恩达团队的 **aisuite** 之上。把它并进来，恰恰能照出"编程 Agent"这一类隐含的共同假设——所以本文保留"编程 Agent 为主"的比较框架，但在每个维度标出 OpenWorker 这个"跨界样本"的不同答案。
>
> **🆕 第十个样本 Open Design（`nexu-io/open-design`，commit `c893b60`）是又一个物种，而且比 OpenWorker 更极端**：它**一行 Agent 主循环都没写**，把前九家里的 `claude` / `codex` / `cursor-agent` / `opencode` / `hermes` 等 **25 个本地 CLI 当成自己的执行引擎**。也就是说，它不是这张桌子上的第十个竞争者，而是**这张桌子本身**。因此第一到第六章仍以九家为比较框架，**新增第七章专门处理这个"宿主型样本"**——它在权限、上下文、质量保证三个维度给出的答案，和前九家完全不在一个坐标系里。详见 [`open-design-源码分析.md`](./open-design-源码分析.md)。
>
> **🆕 第十一个样本 MiMo Code（`XiaomiMiMo/MiMo-Code`，commit `076b790`）又是一种关系**：它是**本文第二个分析对象 opencode 的深度 fork**（核心包至今就叫 `packages/opencode`，`LICENSE` 里两行版权并列）。所以它和其余十家的可比性最高——**同一副骨架，看小米往上焊了什么**。答案很集中：**六道「别让 Agent 骗自己」的装置**（独立裁判 / 四道死循环闸门 / Try-Best 检测 / 产物验证）+ **20 份一模型一份的提示词** + **给 Codex 家族专用的 4 件工具 ABI**。**新增第八章**处理它。详见 [`MiMo-Code-源码分析.md`](./MiMo-Code-源码分析.md)。
>
> **适合读者**：已经读过至少一份单项目分析的初学者。本文不再解释"什么是 Agent 循环"这类基础概念（九家的心跳是同一个：**模型说话 → 软件替它动手 → 把结果喂回模型 → 直到模型不再要工具**），而是回答一个更有意思的问题：**同一颗心跳，九家为什么长出了九副完全不同的身体？**
>
> **本文结构（读起来像剥洋葱，一层比一层深）**：第一章速览总表建立全局印象；第二章按十个产品维度并列对比"做了什么"；**第三章往下钻一层，比"代码到底怎么写的"——语言选型、流式管线、持久化、并发取消、错误处理五个技术深水区（偏 technical，含二次源码核对的行号）**；**第四章反过来问"为什么"——从团队基因、商业逻辑、治理形态推导技术形态的因果**；第五章挑五个功能点做"思路对决"；第六章给初学者选型建议。想快速了解看一二章，想读工程细节看三四章。

---

## 一、速览总表

先把九个项目摊在同一张桌子上。这张表建议横屏看，每一列都是一个完整项目的"体检报告"（最后一列 OpenWorker 是"通用任务同事"跨界样本）。

| 维度 | Claude Code | opencode | hermes-agent | Raven | CodeWhale | goose | nanobot | grok-build | OpenWorker |
|---|---|---|---|---|---|---|---|---|---|
| **背后团队** | Anthropic 官方（闭源产品） | Anomaly（做 SST 的团队），MIT 开源 | Nous Research（开放权重模型公司），MIT 开源 | EverMind-AI（记忆技术生态），Apache-2.0，pre-alpha | 社区项目（前身 deepseek-tui），MIT 开源 | Block 发起，现归 Linux 基金会 AAIF | 港大 HKUDS（Xubin Ren），MIT 开源；**Raven 上游** | xAI 官方（马斯克），Apache-2.0，内部 monorepo 快照，**不接受外部 PR** | **吴恩达团队（aisuite 作者）**，MIT 开源，Beta |
| **技术栈** | TypeScript 单体，React/Ink 终端 UI（魔改版 `src/ink/`） | TypeScript on Bun，Effect 框架；TUI 用 SolidJS + OpenTUI；SQLite 持久化 | Python 3.11+ 内核（大脑）+ TypeScript TUI/桌面/Web（脸） | Python 3.12 内核 + Node TUI（界面 fork 自 hermes-agent） | Rust workspace 18 crate，约 65 万行，ratatui 终端 UI，双二进制 | Rust 12 crate 核心 + Electron 桌面 + 实验性 Ink TUI | Python≥3.11+asyncio；**直连 openai/anthropic SDK（无 LiteLLM）**；React+Vite WebUI；dulwich git 记忆 | **Rust 60+ crate**；DotSlash 锁工具链；Tokio 异步；Ratatui TUI；tree-sitter bash AST；JSONL 持久化 | **Python 后端（建在 aisuite）** + React GUI + **Tauri(Rust) 桌面壳** + Rust STT；FastAPI+uvicorn 服务 |
| **架构形态** | 单体 TUI：五层全在同一进程 | **服务器即本体**：TUI 与同进程服务器也走完整 HTTP/RPC | Python 后端 + 多平台网关（CLI/TUI/桌面/约 20 个消息平台） | 双进程（TCP-loopback JSON-RPC）+ **Spine 调度脊骨** | Rust 单二进制，UI⇄引擎靠 Op/Event **消息管道** | **引擎即服务器**：`goose serve` = ACP over HTTP，GUI 只是客户端 | Channels/WebUI → **MessageBus** → AgentLoop（显式状态机）→ AgentRunner → tools/providers | Rust 单二进制；**Goal Mode** 五件套（GoalTracker/Planner/Worker/Classifier/Strategist）；**ACP 协议**（Zed 集成）；WorkflowManager | **本地优先桌面 App**：Tauri 壳监管 Python 服务侧车；界面/TUI/Slack 都是**同一条 Event 流**的消费者 |
| **模型策略** | **锁定 Anthropic**（订阅 OAuth / API key / Bedrock / Vertex / Foundry） | provider 无关：models.dev 目录几十家 + 自研原生运行时 + 订阅登录插件 | 33 个 provider 插件 + Nous Portal（300+ 模型）+ 本地模型一等公民 | LiteLLM 统一适配多家 + fallback 链 | 36 个 ProviderKind 枚举（含 vLLM/SGLang/Ollama 免 key） | 15+ 手写 provider + 37 个声明式 JSON + **订阅复用** + 本地 llama.cpp | **~40 provider 自研注册表** + fallback（显式路径，不外包 LiteLLM） | **锁定 xAI Grok**；多媒体工具深绑 xAI 独有能力；AgentDefinition.model 可覆盖 | **provider 无关（BYO key，建在 aisuite）**：OpenAI/Anthropic/Gemini 原生 + 十几家 + **Ollama 本地**；精选"验证过工具调用"清单 |
| **工具数量级** | 约 40 个内置 + MCP | 约 20 个内置 + 插件/MCP | **74 个**内置，按 toolset 分组开关 | 约 25 个内置 + MCP/插件工具 | **60+** 个内置 + MCP | **0 个内置工具**——一切皆扩展，内置能力也是扩展提供 | 约 **20+**，`pkgutil` 发现 | 约 40+ 内置 + MCP；**独有**：ImageGenTool / ImageEditTool / ImageToVideoTool / SchedulerCreateTool | 核心工具（文件/shell/git/搜索/web）+ **40 个连接器**（GitHub/Slack/Jira/Notion/Gmail…）+ MCP |
| **扩展机制** | 插件市场 + 技能（SKILL.md）+ MCP（6 种传输）+ hooks（20 事件点） | 插件 Hooks（工具/provider/权限全可挂）+ 技能 + MCP + 自定义命令/工具目录 | 一切皆插件（连平台、provider 都是插件）+ 技能（agentskills.io 标准）+ MCP 双向 | 插件（仅记忆后端/工具两类贡献点）+ Skill Hub 远程市场 + MCP | 技能目录兼容六家 + MCP（可运行时拉起）+ hooks（11 点）+ Markdown 自定义命令 | 一切皆 MCP 扩展（六种形态）+ **Recipe 配方**（可分享的工作流 YAML）+ 技能 | MCP + `entry_points` 工具插件 + channel 插件 + SKILL.md/**ClawHub** | **Plugin Marketplace**（Git/GitHub/本地；allowlist；InstallRegistry commit hash）；Hooks（xai-grok-hooks crate）；ACP 协议开放 | **persona 市场**（目录/git 安装，**同意闸门**）+ 技能（渐进披露）+ MCP(OAuth) + 40 个连接器 |
| **一句话定位** | 为 Claude 打造的最佳终端编程 agent | 为所有模型、所有客户端打造的开放 agent 平台 | 住在消息软件里、会自我学习闭环的个人 agent | 围绕记忆/上下文/主动性搭建的自我改进 agent 底座 | 把"安全"做成可验证机制的 Rust 编程 agent | 模型中立、协议开放、"可被任何东西骑"的本地 agent 引擎 | **超轻量、可完全自持**的个人 AI Agent；住在聊天软件里 | xAI 官方 CLI；**Goal Mode 数小时级自主执行**；Rust 单二进制 + ACP 协议是核心壁垒 | 能交付成品、能**无人值守**、动手前**先问**的**本地 AI 同事**（接 Slack、按 cron 定时） |

这张表里有三件事值得先记住：

**第一，九家项目的架构形态仍然可以收成六七种大类。** Claude Code 和 grok-build 证明"一个进程内做到极致"依然成立（grok-build 用 Rust 单二进制 + Goal Mode 五件套走出了自主执行的新高度）；opencode 和 goose 则把 agent 做成了"本地服务器"，连自家界面都只是客户端——agent 正在从"一个 App"变成"一种本机基础设施"；nanobot / hermes / Raven 同属"消息网关 + 大脑"一系，但调度从朴素 MessageBus 到 Spine 调度脊骨，复杂度阶梯清晰；**OpenWorker 又添一种：本地优先的桌面 App（Tauri 壳）把 Python agent 服务当侧车监管，界面/终端/Slack 共享同一条事件流**。架构选择没有标准答案，只有"你的产品想被谁使用"的答案。

**第二，模型策略是最深的一道分水岭。** 现在有两家锁定单一厂商：Claude Code（锁定 Anthropic）和 grok-build（锁定 xAI Grok），各自把本家模型的独特能力用到了极致；其余七家全部 provider 无关——但"宽"的程度差别巨大：从 Raven 的 LiteLLM 适配、nanobot 的 ~40 家自研注册表、OpenWorker 直接建在 aisuite 上，到 CodeWhale 的 36 个枚举、goose 的 52+ 个 provider、hermes 的 33 个插件加本地模型。锁定做深还是无关做宽，直接决定了后面九章里几乎每个设计。

**第三，工具数量与产品强弱毫无关系。** hermes 有 74 个内置工具，goose 却一个内置工具都没有（连读写文件都是扩展），nanobot 刻意压在约 20+、靠边缘扩展，grok-build 独有多媒体生成与定时任务工具——这不是谁先进谁落后，而是各自自洽的哲学：hermes/nanobot 奉行"核心窄腰、能力长在边缘"，goose 奉行"一切皆 MCP、只有一条流水线"，grok-build 奉行"平台深度绑定换来独有能力"。

```mermaid
quadrantChart
    title 九大 Agent 定位象限（依据九份源码分析归纳）
    x-axis 锁定单一厂商 --> provider 无关
    y-axis 终端程序 --> 本地基础设施
    quadrant-1 开放平台型
    quadrant-2 深度绑定的基础设施
    quadrant-3 单一厂商的极致工具
    quadrant-4 开放的终端工具
    "Claude Code": [0.12, 0.18]
    "grok-build": [0.10, 0.25]
    "CodeWhale": [0.72, 0.22]
    "opencode": [0.85, 0.68]
    "hermes-agent": [0.80, 0.78]
    "Raven": [0.68, 0.82]
    "nanobot": [0.75, 0.72]
    "goose": [0.90, 0.88]
    "OpenWorker": [0.82, 0.58]
```

> 注：OpenWorker 落在"开放平台型"象限——provider 无关（建在 aisuite）、本地服务侧车化，但它是**桌面 App 优先**、面向通用任务而非编程，与 opencode/goose 的"服务器即本体"同区不同种。

---

## 二、逐项详细横向对比

这一章把十个关键维度逐一拆开。每项一张对比表，表后附一段"各家的想法"分析——表格回答"是什么"，分析回答"为什么"。

### 2.1 架构形态：从"一个终端程序"到"一套本地基础设施"

| 项目 | 形态 | 界面与核心的关系 | 关键机制（转引行号） |
|---|---|---|---|
| Claude Code | 单体 TUI | React/Ink 渲染树与主循环同进程，五层（界面/分流/组装/循环/通信）直接函数调用 | 五层架构（深入版第二章）；`src/query.ts` 与 `src/screens/REPL.tsx` 同进程 |
| opencode | **服务器即本体** | 服务器跑在同进程的 Bun Worker 线程里，TUI 经 RPC 包装成假 `fetch("http://opencode.internal")`，与远程模式走完全相同的 HTTP 协议 | `cli/cmd/tui.ts:210-247`（Worker + RPC + 内存 fetch） |
| hermes-agent | Python 大脑 + N 张脸 | TUI（React/Ink）经 stdio 上的换行分隔 JSON-RPC 调用 Python 进程；消息网关挂约 20 个平台 | `ui-tui/README.md`："TypeScript owns the screen. Python owns sessions, tools, model calls." |
| Raven | 双进程 + 调度脊骨 | Python 内核监听 TCP-loopback 端口，Node TUI 连接后讲 JSON-RPC 2.0；**前端永不 import 后端内部** | `raven/tui_rpc/`（协议模型 911 行）；`CONTEXT-MAP.md:15` |
| CodeWhale | Rust 单二进制（实际双二进制） | `codewhale` 调度器 + `codewhale-tui` 运行时；UI 与引擎之间是 **Op/Event 枚举消息管道**而非函数调用 | `core/ops.rs:87`、`core/events.rs`；`crates/cli/src/lib.rs:4215` 定位兄弟二进制 |
| goose | **引擎即服务器** | 桌面 GUI spawn `goose serve` 子进程（ACP over HTTP/WebSocket，带 `GOOSE_SERVER__SECRET_KEY` 鉴权），前端经 WebSocket 用 ACP 对话 | `crates/goose-cli/src/cli.rs:844`；`ui/desktop/src/gooseServe.ts:369,336` |
| nanobot | **MessageBus + 显式状态机** | Channels/WebUI/CLI → inbound/outbound 双队列 → AgentLoop（turn 状态机）→ AgentRunner（模型迭代）；单进程 asyncio，WebUI 走 WebSocket | `bus/queue.py`；`agent/loop.py:1351`；`agent/runner.py:354` |
| grok-build | **Rust 单二进制 + Goal Mode 五件套** | Ratatui TUI + 引擎全在同一进程；Goal Mode：GoalOrchestrator 协调 GoalTracker / GoalPlanner / GoalWorker / GoalClassifier / GoalStrategist；ACP 协议对外暴露（Zed 集成）；WorkflowManager 最多 4 并发 | `goal/orchestrator.rs`；`goal/planner.rs`；`goal/worker.rs`；`extensions/acp.rs` |
| OpenWorker | **本地优先桌面 App + 事件流** | Tauri(Rust) 壳把 Python 服务当侧车监管；界面/TUI/Slack 都是**同一条 Event 流**的消费者，由 SessionManager 编排 | `server/run.py`（侧车 + 孤儿守卫）；`engine.py` yield `Event`；`events.py` |

**各家的想法**：架构形态的本质是回答"**界面和大脑之间隔着什么**"。Claude Code 的答案是"什么都不隔"——没有协议开销、状态共享最直接，所以迭代最快、终端体验最细；代价是"被集成"能力弱（后来靠 SDK、ACP、bridge 补上）。opencode 和 goose 最激进：**隔着一套公开协议，哪怕在同一进程里**——本地单机和远程团队是同一份代码路径，Zed、GitHub Action、第三方 SDK 零成本接入；代价是终身维护协议与进程管理。hermes、Raven、nanobot 隔的是消息/进程边界：TUI 崩了不会带走内核，nanobot 更用两条 asyncio 队列把门市与后厂彻底解耦，代价是多出一套消息模型要维护。CodeWhale 则证明折中成立：**消息管道在单进程内也能用**，同一引擎同时服务 TUI、`exec` 无头模式和 web 客户端——"无头不是阉割版，只是没有界面"。

```mermaid
flowchart TB
    subgraph CC["Claude Code：单体 TUI"]
        A1["React/Ink 界面"] --> A2["五层全在同一进程<br/>src/query.ts 主循环"]
    end
    subgraph OC["opencode：服务器即本体"]
        B1["TUI (SolidJS)"] -->|"Worker RPC → 内存 fetch<br/>http://opencode.internal"| B2["HTTP 服务器<br/>session/prompt.ts"]
    end
    subgraph GS["goose：引擎即服务器"]
        C1["Desktop GUI / Zed / 第三方"] -->|"ACP over HTTP/WebSocket"| C2["goose serve<br/>Rust 核心 crates/goose"]
    end
    subgraph HM["hermes：Python 大脑 + 多平台网关"]
        D1["TUI / 桌面 / 约20个IM平台"] -->|"JSON-RPC over stdio 或网关"| D2["Python 内核<br/>conversation_loop.py"]
    end
    subgraph RV["Raven：Spine 调度脊骨"]
        E1["TUI / 12个IM平台"] --> E2["Spine：submit 进 → Lane 串行 → emit 出"]
    end
    subgraph CW["CodeWhale：单二进制消息管道"]
        F1["ratatui 界面"] -->|"Op / Event 枚举"| F2["Rust 引擎<br/>turn_loop.rs"]
    end
    subgraph NB["nanobot：MessageBus + 状态机"]
        G1["Channels / WebUI / CLI"] -->|"inbound / outbound 双队列"| G2["AgentLoop 状态机<br/>→ AgentRunner"]
    end
```

### 2.2 Agent 主循环实现：同一颗心跳，六种体质

| 项目 | 循环本体 | 语言形态 | 轮次预算 | 中断机制 | 最具特色的边界防御 |
|---|---|---|---|---|---|
| Claude Code | `queryLoop`（`src/query.ts:241`），`while(true)`（`:307`）+ 跨轮 `State` 结构体 | async generator（由递归重构而来） | 由调用方 `maxTurns` 决定 | ESC：`signal` 直达 HTTP 层真掐断连接；自动为"孤儿 tool_use"补假回信（`query.ts:123-149`） | 输出截断恢复最多 3 次；token 预算快尽时注入催促 |
| opencode | `runLoop`（`session/prompt.ts:1081`），`while(true)`（`:1088`） | Effect 生成器；**每轮重读数据库** | `agent.steps` 默认 Infinity | ESC 双击确认；Effect 结构化中断，cleanup 把未完工具标记为 interrupted | **doom_loop**：连续 3 次相同工具调用就弹权限询问（`processor.ts:356-380`） |
| hermes-agent | `run_conversation()`（`conversation_loop.py:669`），while 条件在 `:822` | Python while，单函数近 5500 行 | 默认 90 次 + **IterationBudget**（可退还、有 grace call） | 协作式：每轮开头查 `_interrupt_requested` 标志 | **先持久化再执行工具**（`:5361-5366`）——防破坏性工具杀掉进程后"失忆" |
| Raven | `_run_agent_loop`（`agent/loop/main.py:1424`），`while iteration < 40`（`:1476`） | Python asyncio while | 默认 40 次 | Spine Lane 取消（`asyncio.Task.cancel`）；**只有 USER 来源能插队打断** | 紧急收缩后 `iteration -= 1`——"溢出的调用没干活，不占预算"（`:1570`） |
| CodeWhale | `handle_deepseek_turn`（`turn_loop.rs:286`），外层 `loop`（`:342`）+ 内层流式 `loop`（`:785`） | Rust tokio 双层循环 | `max_steps` 默认 1000 + token 预算 | 取消令牌贯穿三层；drop 掉 `FuturesUnordered` 即硬取消整个并行池 | 睡眠唤醒检测：`Instant` 与 `SystemTime` 双轨对表（issue #2990） |
| goose | `reply_internal`（`agents/agent.rs:1846`），`loop`（`:1948`） | Rust tokio loop | 默认 1000（`DEFAULT_MAX_TURNS`） | `CancellationToken` 贯穿主循环/流式/工具并发 `select!` | **goal/grind**：想收工就塞一条用户不可见的催促消息，直到目标达成 |
| nanobot | AgentLoop 状态机（`loop.py:1351`）+ AgentRunner 模型迭代（`runner.py:354`） | Python asyncio；显式 `_TRANSITIONS` 状态表 | 默认 **200**（`max_tool_iterations`） | `/stop` 优先命令穿透 → `asyncio.Task.cancel`；取消后仍 checkpoint 落盘 | 提前落盘用户消息 + runtime_checkpoint；空闲会话 AutoCompact |
| grok-build | **Goal Mode 三状态机**：普通对话走 `agent_loop.rs`；触发 `/goal` 后 GoalOrchestrator 接管，GoalPhase（Inactive/Planning/Working）× GoalStatus（8 态）双轴驱动 | Rust tokio；GoalStopDetector 9 类正则匹配最后一段决定是否完成 | GoalWorker 单轮无显式上限；GoalStrategist fail-open（不阻塞） | 取消令牌贯穿；GoalPlanner fail-closed（必须写非空 plan.md 且返回字面 "Done"） | UpdateGoalTool 是完成信号（必须调用）；GoalClassifier 验证每轮输出 |
| OpenWorker | `TurnEngine._loop`（`engine.py:294`）：异步 `while` + 阻塞调用包进 `to_thread` | Python asyncio；provider 阻塞流经**线程+队列**桥回主循环、与 Stop 赛跑 | 默认 **150**（`config.py:31`；引擎默认 12、explorer 10） | 任意状态可中断且**不留孤儿 tool_call**（`request_interrupt:120`）；steering 插话不打断 | **持久恢复**：挂在收件箱的回合重启后 `resume` 不双跑（幂等 by tool_call_id） |

**各家的想法**：八家的循环在教科书层面完全一样——请求模型、有 tool_use 就执行并追加、再请求，"模型什么时候不用工具了，答案就出来了"。真正的差距在**边界情况的处理密度**：opencode 每轮重读数据库，"排队消息被自然接管、多客户端并发安全"都是免费得到的；hermes 把"先落盘、再动手"定为铁序——工具可能执行 `hermes restart` 自杀，这是用血泪换来的顺序；Raven 把"错误也是上下文"贯彻到底，工具错误返回字符串还附赠"换个方法"的小抄；nanobot 用显式状态机把 turn 生命周期拆清，默认预算 200 配合长任务；CodeWhale 的循环里埋着每个历史 bug 的 `#issue` 注释，堪称带编号的错题本。另一个趋同进化是**插话机制**：agent 干活时用户再说话，各家给出排队（Claude Code、opencode）和 steer 中途注入（hermes、Raven、CodeWhale、goose）两类答案——长任务时代的标配。

```mermaid
sequenceDiagram
    participant L as 主循环（七家同款骨架）
    participant M as 模型 API
    participant T as 工具层
    loop 直到模型不再要工具
        L->>L: 家务：drain插话 / 压缩检查 / 预算检查 / 取消检查
        L->>M: 流式请求（全部历史 + 工具清单）
        M-->>L: 文本 + tool_use（边收边解析，参数收齐即执行）
        L->>T: 权限裁决后执行（只读并行 / 写串行）
        T-->>L: tool_result 追加进历史
    end
    L-->>L: 正常出口：无 tool_use 的那次回复
```

### 2.3 工具系统设计：数量不重要，纪律才重要

| 项目 | 数量与分组 | 安全元数据 | 工具太多怎么办 | 独特机制 |
|---|---|---|---|---|
| Claude Code | 约 40 个内置，11 个分组；`getAllBaseTools()`（`tools.ts:193`）唯一事实来源 | **三标记**：`isReadOnly` / `isConcurrencySafe` / `isDestructive`，默认全 false（`Tool.ts:750-761`），且可以是函数按入参动态判定 | **ToolSearch 延迟加载**：平时只发工具名清单，模型查到才递完整 schema；26 个内置 + 全部 MCP 工具被延迟 | deny 规则在模型看到清单前就剔除（`tools.ts:262-269`）；内置工具必须排成连续前缀（保 prompt cache） |
| opencode | 约 20 个内置（`tool/registry.ts:226-244`） | 无标记体系，权限按"名字 + 通配符"外置管理 | 输出统一截断 2000 行/50KB 落临时文件 | bash 用 **tree-sitter 语法树**解析命令；修不好的工具调用塞进 `invalid` 工具兜底 |
| hermes-agent | **74 个**内置，按 toolset 分组开关（`toolsets.py:96`） | `check_fn` 服务门控：环境不具备的工具根本不出现 | 会话启动时**冻结工具集**（换集会摧毁前缀缓存）；`tool_search` 三桥接工具（10% 阈值） | "Footprint Ladder"：新能力优先做技能/插件，万不得已才加核心工具（`AGENTS.md:24-27`） |
| Raven | 约 25 个内置（`main.py:571-700`） | `timeout_seconds` 强制超时；`blocking_interaction` 标记等人工具 | `tool_search` + `tool_call` 渐进披露（opt-in） | 媒体/Skill Hub 工具按配置条件注册；`cast_params` 宽容类型转换 |
| CodeWhale | **60+** 个内置，builder 模式分族注册 | **能力标记推导审批级别**：`ExecutesCode`→必批，`WritesFiles`→建议批，只读→自动放行（`spec.rs:1181-1190`） | **一个正名 + action 参数 + 隐藏兼容别名**（`File` 合并读写改列四个动作） | **探测式注册**：本机没有 pandoc/OCR 后端就根本不注册，从源头消灭幻觉调用 |
| goose | **0 个内置工具**，一切皆扩展 | 依赖 MCP 工具的 `readOnlyHint` 注解 | 官方建议上限：**5 个扩展 / 50 个工具**（`prompt_manager.rs:19-20`） | 工具命名 `扩展名__工具名`；agent 能自己搜索、启用新扩展 |
| nanobot | 约 **20+**，`pkgutil` 扫描 `agent/tools/` 自动发现 | 无逐次审批元数据；靠黑名单/工作区/SSRF/沙箱前置 | 核心窄腰：新能力优先 channel/skill/MCP，少动 `loop.py`/`runner.py` | `entry_points(group="nanobot.tools")` 外挂工具；内置工具稳定排序保 cache |
| grok-build | 约 40+ 内置 + MCP；**独有三类**：多媒体（ImageGenTool / ImageEditTool / ImageToVideoTool / ReferenceToVideoTool）、定时任务（SchedulerCreateTool / SchedulerDeleteTool / SchedulerListTool）、目标模式（UpdateGoalTool） | ToolBridge 统一工具接口（内置 + MCP 同一流水线）；tree-sitter bash AST 解析权限 | ToolBridge.tool_definitions() 工具顺序稳定保 cache | **UpdateGoalTool 是 Goal Mode 的完成信号**——不调用等于任务未完成（fail-closed 设计） |
| OpenWorker | 核心工具（文件/shell/git/搜索/web）+ 40 个连接器 + MCP；**全部基于 aisuite** `ai.tool` / `ai.toolkits` | `ai.ToolMetadata`（category / risk_level / requires_approval）——主循环并发裁定与权限判决都读它 | 按 **persona 家族**分装（code 给 explorer；knowledge 给调度/self-wake）；技能渐进披露 | **`_display` 隐私旁路**：agent 看不到被隐私过滤器藏掉的条数（防模型试探） |

**各家的想法**：工具系统最重要的共识是——**每个工具的 schema 每轮请求都要随包发送，是持续付费的**（hermes 的 `AGENTS.md` 把这句话写成了军规："Every model tool we add is sent on every API call, so the bar for a new core tool is high"）。围绕这笔账，各家给出了三类节流方案：Claude Code 的 ToolSearch 是"先报菜名后递菜单"（书架先只放书名，用到哪本抽哪本）；hermes/Raven 的 tool_search 是"按需取货架"；CodeWhale 是"合并同类项"（一个 `File` 工具吃掉四个动作，目录变短、模型选错的机会也变少）；nanobot 则直接把核心工具压在约 20+，用 pkgutil/entry_points/技能把能力推到边缘。安全元数据方面则有两条路线：Claude Code 和 CodeWhale 把安全属性做成**工具自身的声明**（三标记/能力标记，CodeWhale 甚至让审批级别从声明自动推导——这就是"safe by construction"的字面意思），而 opencode、goose 把安全判断**外置**到权限规则或 MCP 注解里。前者的好处是安全与工具同生共死，后者的好处是工具实现更薄。最后请注意一个反复出现的小智慧：**"不给模型看用不了的工具"**——deny 提前剔除、探测式注册、check_fn 门控，本质上都是同一件事：模型看不见，就不会乱调。

### 2.4 权限与安全：四条截然不同的路线

| 项目 | 裁决主线 | 命令解析与防线 | 沙箱 | 被拒后模型收到什么 |
|---|---|---|---|---|
| Claude Code | **五关裁决**：deny→ask→工具自查→权限模式→alwaysAllow→兜底弹窗（`permissions.ts:1158`）；六档权限模式 | Bash 解析成 AST，复合命令逐条拆分匹配（`BashTool.tsx:447-465`）；危险模式清单 + 投机分类器 | 有沙箱开关（`/sandbox`） | 拒绝原因作为工具结果回喂，模型通常自我纠正换方案 |
| opencode | **三态规则**：ask/allow/deny + 通配符，`findLast` 后写优先（`permission/index.ts:28-38`） | tree-sitter 真语法树解析命令（`tool/shell.ts:91-117`） | 无（文档未见沙箱机制） | **可教导拒绝**：reject 可附一句自然语言反馈（`CorrectedError`），"教"模型改正 |
| hermes-agent | 危险模式检测 + 人工审批（单次/会话/永久白名单三级） | `DANGEROUS_PATTERNS` + Tirith 预执行扫描（形似字符钓鱼、pipe 到解释器） | **六种执行后端**任选：local/docker/ssh/singularity/modal/daytona | 拒绝并告知模型 |
| Raven | **无逐工具审批**：正则黑名单 + 工作区围栏 + 确认往返 | 7 类危险命令正则（`shell.py:32-42`）；`../` 越界拒绝 | **boxlite microVM 真隔离**（启用时黑名单反而关闭）；绝不把 `os.environ` 传进 VM | 错误字符串 + "换个方法"小抄 |
| CodeWhale | **真值表**：`resolve_tool_permission()` 输出 Allow/Prompt/Deny（`authority.rs:289`） | **四层防线**：execpolicy 前缀策略 → 真值表 → `constitution.json` 写保护（只能加锁不能放权）→ OS 沙箱 | Seatbelt（macOS，探测成功才声明）/ bubblewrap（Linux，显式开启）；Windows 如实报告"没有" | 精确参数指纹记拒绝、元数感知指纹记允许，防"拒绝被泛化" |
| goose | **GooseMode 四模式**（Auto/Approve/SmartApprove/Chat）+ 五级裁决 | SmartApprove 靠 `readOnlyHint` 注解 + **LLM 裁判**判断只读/写；装扩展永远强制问 | 无（文档未见 OS 沙箱） | 拒绝结果回喂；LLM 裁判的判定会被缓存 |
| nanobot | **无逐次审批**：黑名单 + 工作区围栏 + SSRF + bwrap | 危险命令正则拦截；工作区越界拒绝；exec 不传整份 `os.environ` | **bwrap**（bubblewrap）沙箱；无 bwrap 时仅应用级工作区保护 | 错误字符串回喂；前置防线挡住的不进模型循环 |
| grok-build | **六档 PermissionMode** + **LLM 分类器（PermissionClassifier）**：Auto 模式用 LLM 实时分类风险，3 次连续 / 20 次总量自动拒绝后强制弹窗；Ask 模式逐次询问；YOLO 模式 | tree-sitter bash AST 解析（`permission/manager.rs:8759`行）；8 类危险模式正则；沙箱探测 | 有沙箱开关（与 CC 相似） | 拒绝原因作为工具结果回喂；Auto 拒绝上限触发后 PermissionMode 自动降级到 Ask |
| OpenWorker | **五档模式**（discuss/plan/interactive/auto/custom）+ **带外审批**：needs_user 交注入的 approver（有人值守内联卡 / 无人值守进收件箱挂起） | shell **含操作符即拒白名单**（防 `git status && rm -rf ~`）+ argv 精确前缀匹配（`permissions.py:216`）；写必须落**可写根** | 无 OS 沙箱（本地优先桌面）：靠路径围栏 + 审批 + **§25 标准规则**（仅外部风险按精确 target，shell 永远要问） | 拒绝原因回喂；`_display` 隐私计数永不入模型 |

**各家的想法**：权限是八家分歧最大的地方，可以分成四条路线。**弹窗询问派**（Claude Code）相信"人在回路"：五关裁决层层过滤，拿不准就弹窗，"始终允许"还会沉淀为规则——安全靠"每次都给人否决的机会"。**规则前置派**（opencode、hermes）相信"先立法后执法"：事先写好规则，运行时照章办事少打扰；opencode 的"可教导拒绝"是个妙笔——拒绝时附一句话（"不许跑 rm -rf，请用更安全的方式"）回给模型，拒绝瞬间变成教学现场。**结构安全派**（CodeWhale 最极致）相信"根本没有越权的工具"：Plan 模式直接不注册写工具；`constitution.json` 连全权模式都绕不过；`UserInputProvenance` 区分"真人敲的"和"子 agent 交接的"，非真人来源不能继承自动批准——这是对"间接提示注入提权"这一新威胁的源码级防御。**沙箱派**（nanobot / Raven）干脆放弃逐次审批：常驻 IM 的场景里没人按 y，用"黑名单 + 沙箱 + 工作区围栏"的前置防御代替人工闸门——nanobot 押 bwrap，Raven fork 后升级 boxlite microVM。诚实也值得记住：黑名单只是"Best-effort"，真正的兜底是沙箱。

```mermaid
flowchart LR
    subgraph P1["① 弹窗询问派（人在回路）"]
        A["Claude Code<br/>五关裁决，兜底弹窗<br/>'始终允许'沉淀为规则"]
    end
    subgraph P2["② 规则前置派（先立法后执法）"]
        B1["opencode：三态通配符<br/>拒绝可附教学反馈"]
        B2["hermes：审批三级 + YOLO 导入即冻结<br/>防提示注入偷开免审批"]
    end
    subgraph P3["③ 结构安全派（没有越权的工具）"]
        C1["CodeWhale：Plan 不注册写工具<br/>constitution.json 只加锁不放权<br/>provenance 降级"]
        C2["goose：Chat 模式禁工具<br/>SmartApprove 靠注解 + LLM 裁判"]
    end
    subgraph P4["④ 沙箱派（隔离代替审批）"]
        D0["nanobot：黑名单 + 工作区 + SSRF + bwrap<br/>Raven 上游出厂设置"]
        D1["Raven：黑名单 + microVM<br/>无人值守场景的答案"]
        D2["hermes：docker/modal 后端<br/>按任务选隔离级别"]
    end
    subgraph P5["⑤ LLM 分类器派（智能分类）"]
        E1["grok-build：PermissionClassifier 实时判断<br/>Auto 模式 3/20 上限后强制弹窗<br/>把'该不该'也交给模型"]
    end
```

### 2.5 上下文工程与压缩：摘要丢弃派 vs 无损资产管理派

| 项目 | 触发 | 手段分层 | 保真哲学 |
|---|---|---|---|
| Claude Code | 触发线 = 有效窗口 − 13000（约 16.7 万 token 提前动手）；熔断：连续失败 3 次放弃 | **五层防线**：工具结果落盘 → snip → microcompact → context collapse → autocompact | 摘要丢弃：派 fork 分身（`maxTurns:1`）写 9 小节结构化摘要，边界前细节只留要点；413 时还有 reactiveCompact 被动抢救 |
| opencode | 用量 ≥ 可用上限（上限 − 20000 余量） | **三层**：auto compaction（隐藏无权限 agent 写摘要）+ 工具输出 prune（40k 保护线/20k 释放线）+ 单次截断 | 摘要丢弃，但尾部保留很精细：最近 2 个用户回合 + 25% 上下文预算（2k–8k），不够时把回合从中间切开；溢出型压缩会**重放最后一条真实用户消息** |
| hermes-agent | 窗口的 **50%** 即触发（最早的一家）；单轮最多 3 次 | 压缩前先做一次工具输出剪枝预扫 | 摘要丢弃：辅助模型总结**中段**，保护头部（原始指令）和尾部（工作现场）；压缩是**唯一被允许重建系统提示词**的场景 |
| Raven | 三尺度：本轮紧急收缩 / 会话内 Curator / 跨会话 Consolidator | 紧急收缩：老工具结果→占位符，零 LLM 调用 | **无损归档**：Curator 让小模型看 Manifest 索引、出 ContextPlan，**Archive 原文逐字落盘 + 留引用**，再以 Working State（目标/未决/已决）蒸馏注回系统提示——"事实离开了窗口，但没有离开模型的视野"；出错有确定性 Fail-Safe 兜底 |
| CodeWhale | **纯 token 信号**（v0.8.11 删掉了消息条数分支），默认阈值 80 万 token（1M 窗口的 80%） | **先免费后付费**：先机械修剪重复工具输出（零成本），仍超标才调 LLM 摘要 | 摘要丢弃但极度克制：保留最近 4 条原文；**pin 住工作集 top 24 个路径**相关消息；摘要输入截成头 14000 + 尾 6000 字符；失败宁可不压缩（"never corrupt state"） |
| goose | token 用量 ≥ 可用窗口的 80% | compaction + **工具调用对摘要**（每批 10 对，后台进行） | 摘要丢弃但"很体面"：旧消息标记为"对用户可见、对模型不可见"（**不删历史**），摘要仅模型可见，还追加一段"衔接台词"教模型别声张、接着干活 |
| nanobot | token 预算触发 Consolidator；空闲 TTL 触发 AutoCompact | **微压缩**（老工具结果占位）+ **有损 LLM 摘要**；跨会话 **Dream** 反思 | 摘要丢弃派：秒级确定性微压缩 + 会话级有损归档到 `history.jsonl`；Dream 夜间提炼进 SOUL/USER/MEMORY |
| grok-build | 85% 上下文阈值触发 CompactionPolicy；`wall_clock_budget_secs=300` 防超时 | **两轮压缩**：Pass1 推测性后台预压缩 + Pass2 正式压缩（两轮均可选）；Goal Mode 小更新走 fire-and-forget 不落 JSONL | 摘要丢弃派；Goal Mode GoalOrchestrator 频繁状态更新与压缩解耦，防状态抖动污染系统前缀 |
| OpenWorker | **不自动压缩上下文**；回合由 max_iterations（150）有界 | 每回合临时 `<system-context>` 挂最后一条用户消息（不持久化）；`_outbound_messages` 剥旁路 + 按当前模型能力适配 PDF/视觉 | **显式记忆派（非摘要丢弃）**：靠 SQLite 显式事实 + 持久化历史，"可预测优先于省 token" |

**各家的想法**：这是八家哲学分歧最漂亮的一个战场。**摘要丢弃派**（含 nanobot）的思路是"压缩 = 浓缩"：派分身或便宜模型把旧历史写成摘要，之后只看摘要和最近几轮，功夫全下在细节里——保留多少尾部、压缩前先免费瘦身（机械修剪/剪枝预扫/微压缩）、失败了怎么兜底（熔断、宁可不压）；nanobot 还用 Dream 把有价值的东西跨会话写回 Markdown。**无损资产管理派**只有 Raven 一家：它认为"上下文不是消耗品，是被管理的资产"，压缩不是"扔掉旧报纸"，而是"归档进图书馆、留下索书号和读书笔记"——Archive 一字不丢，Working State 保证模型始终记得目标与未决事项。代价也直白：多花一个小模型的钱和一套 Manifest/Plan/校验的复杂度，且 Raven 自己还留着有损的 Consolidator 作退路（那正是 fork 自 nanobot 的原路径）。

```mermaid
flowchart TD
    O["上下文要爆了"] --> P{"两派选择"}
    P --> Q["摘要丢弃派<br/>Claude Code / opencode / hermes / CodeWhale / goose / nanobot"]
    P --> R["无损资产管理派<br/>Raven Curator"]
    Q --> Q1["派分身/辅助模型写摘要<br/>旧历史离开窗口，只留要点<br/>功夫在：保留尾部 + 免费预剪 + 失败兜底"]
    R --> R1["Archive：原文逐字落盘 + 留引用<br/>Working State：蒸馏笔记注回系统提示<br/>确定性代码执行小模型的方案，Fail-Safe 兜底"]
```

### 2.6 记忆体系：从"一个公约文件"到"双轨长期记忆"

| 项目 | 项目级记忆文件 | 跨会话记忆 | 自动沉淀 | 检索方式 |
|---|---|---|---|---|
| Claude Code | **CLAUDE.md 五来源**逐级叠加（企业管控/用户全局/项目向上每层/--add-dir/自动记忆），每个文件带身份标签 | memdir 自动记忆：`MEMORY.md` 上限 200 行/25KB + 按相关性注入；Session Memory 后台提炼会话笔记 | 模型自主写入；你纠正它时会追加"考虑存到记忆"提示 | 相关性检索注入，条目带"新鲜度"前缀 |
| opencode | 兼容 AGENTS.md/CLAUDE.md；向上查找时**第一种匹配的文件名就停**（不叠加）；read 工具触发"就近发现" | **无跨会话长期记忆**（分析文档明确"未找到"） | 无（靠人工维护 AGENTS.md） | 无 |
| hermes-agent | AGENTS.md/CLAUDE.md/.cursorrules 三兼容，还会探测"项目指纹"（包管理器、验证命令） | `MEMORY.md`（它的笔记）+ `USER.md`（你的画像），**冻结快照**：会话内写入只落盘、不改提示词 | **闭环学习**：`skill_manage` 自己创建技能 → `curator` 空闲时后台评审归档 → `session_search` FTS5 搜历史会话 | 快照注入 + FTS5 全文搜索 + 可选 Honcho 用户建模 |
| Raven | `soul.md`（人格）/ `agent.md`（守则）/ `TOOLS.md`（工具笔记）三件套，拆自 CLAUDE.md 思想 | **EverOS 双轨**：用户轨（画像/事件）+ agent 轨（技能/案例）；`user.md` / `episodes.md` / Foresight 预判 | Consolidator 摘要成记忆笔记；after-turn 流水线把每轮索引进 EverOS 并回报技能使用情况 | **SkillForge 加权 RRF 融合**：本地文件 1.0 / EverOS 召回 0.9 / Skill Hub 0.85 |
| CodeWhale | AGENTS.md 为规范名、CLAUDE.md 兼容，向上回溯支持 monorepo；合并上限 500KB 防爆 | `handoff.md`（压缩/退出时的交接信）+ 用户记忆（legacy，正迁移到 Moraine MCP） | `#` 快捷记录；`remember` 工具（opt-in） | handoff 作为"continuity"片段注入；工作集 top 24 路径 pin |
| goose | `.goosehints` + AGENTS.md（文件名可配置），git 根向下逐层收集；`@文件`导入以 git 根为界、`.gitignore` 文件不读 | **memory 扩展**（分类 + 标签存取）+ chatrecall 扩展（搜历史会话） | 模型调 `remember_memory` 主动记 | 按分类取回；子目录 hints 动态加载（SubdirectoryHintTracker） |
| nanobot | AGENTS.md（项目）+ **SOUL.md / USER.md**（agent 工作区人格与画像） | 纯文件 **MEMORY.md** + `history.jsonl`；**dulwich** 对记忆文件做 git 追溯 | **Dream** 夜间受限 agent 改 SOUL/USER/MEMORY；可创建技能 | 文件全文注入 + Dream 后近期历史；提交信息以真实 diff 为准 |
| grok-build | AGENTS.md / CLAUDE.md 兼容；AgentDefinition.finalize_prompt 在 build 时用 MiniJinja 模板渲染并锁定系统提示 | JSONL 会话存储（`~/.config/grok-build/sessions/`）；Goal Mode plan.md 跨轮持久化 | 无自动学习；plan.md 是 Goal Mode 的跨轮"工作记忆"（GoalPlanner 必须写入、GoalWorker 读取） | plan.md 全文注入 Goal Mode 上下文；JSONL 按轮追加 |
| OpenWorker | AGENTS.md（工作区约定，注入系统提示） | **SQLite `remember`**：全局 / 工作区双 scope（`memory/sqlite_store.py`） | 无夜间自动学习；模型按"何时记"引导主动 `remember` / `memory_update` / `memory_forget` | 按 scope 全量注入系统提示；存前查已知记忆去重 |

**各家的想法**：八家在记忆上有三个共识——**记忆即 Markdown 文件**（人能读、能手改）、**项目公约进系统提示词**、**兼容对手的格式是基本礼仪**（opencode/hermes/CodeWhale/goose 都读 CLAUDE.md）。分野在"自动学习"这条轴上：opencode 最克制，明确不做跨会话记忆，把"记得用户"交给配置而非算法；Claude Code 和 CodeWhale 居中，有自动记忆但保持文件形态；nanobot 的 Dream 反思是文件记忆路线的招牌——夜间整理、git 可审计；hermes 和 Raven 则把记忆做成了**闭环**——hermes 五件套环环相扣，Raven 双轨制把"你的事"和"agent 自己的本事"分开存储、分开召回。记忆是"产品性格"差异最大的维度：有人当配置，有人当资产，有人当护城河。

### 2.7 子 agent 与多 agent：分身术的三代进化

| 项目 | 派生方式 | 父子的关系 | 结果怎么回来 | 防失控设计 |
|---|---|---|---|---|
| Claude Code | `Agent` 工具，**子 agent 跑的就是主循环本身**——同一个 `query()` 换三样东西：自己的系统提示、缩水的工具箱、身份标记（`runAgent.ts:248,748`） | 前台共享取消令牌（ESC 父子一起停）；后台独立令牌（ESC 故意杀不死它） | 前台：阻塞等结果作 tool_result；后台：完成后往 messageQueueManager 塞通知 | 工具箱缩水；coordinator 模式下主 agent 只剩派活收活三个工具 |
| opencode | `task` 工具创建 `parentID` 指向父会话的**子会话** | 权限由父会话派生；派生前先问用户 | 前台等待，最后文本包进 `<task>` 标签返回；后台（实验开关）完成后通知主 agent | **深度默认 1 层**（子代理默认不能再生孙子）；后台并发藏在实验开关后 |
| hermes-agent | `delegate_task` 工具 | **顶层模型发起的委派一律后台**——"the model does not get to choose"；编排者子代理例外（同步） | 子代理完成后结果作为**一条新消息**回到父会话 | `DELEGATE_BLOCKED_TOOLS`：不许递归委派/找用户/写共享记忆/发消息/排定时任务；子代理默认**自动拒绝**一切危险审批（防 input 死锁） |
| Raven | `spawn` 工具 | 子代理独立循环（上限 15 次迭代）、独立沙箱 VM | **经 Spine 提交 `origin=SUBAGENT` 的新 TurnRequest**——父像收到普通消息一样被唤醒 | 并发闸（信号量最多 4 个）+ 频率闸（每会话每小时最多 30 个）；工具箱缩水：无 `message`、无 `spawn` |
| CodeWhale | `agent` 工具（start/status/peek/wait/cancel） | **不屏障父循环**（issue #3216）：父回合照常结束、保持响应 | **哨兵消息** `<codewhale:subagent.done>` 在后续回合汇入 | 深度"相加后钳制到天花板"（防重新报值绕过）；总数上限 64；子不继承父的会话级自动批准 |
| goose | `summon` 扩展的 `delegate` 工具；或 recipe 的 sub_recipes | 子代理独立会话、独立上下文、**可有独立模型配置** | 跑完只把结论文本带回来，中间过程不污染父上下文 | 上下文隔离即默认形态；orchestrator 扩展（多会话总控）默认关闭且对 UI 隐藏 |
| nanobot | `spawn` 工具 | 子代理独立循环、缩水工具箱（无 `message`/无 `spawn`） | **经 MessageBus 回灌**：`channel=system` + `injected_event=subagent_result` 进同一条 inbound 队列 | 默认并发 **1**；`/stop` 连带 `cancel_by_session` |
| grok-build | Goal Mode 内的 GoalWorker 本身即"大型子任务执行器"；WorkflowManager 支持最多 4 个并发 workflow | GoalWorker 受 GoalOrchestrator 统一调度；GoalStrategist fail-open 不阻塞主流 | GoalClassifier 验证每轮输出并回报 GoalOrchestrator；GoalStatus 8 态追踪进度 | GoalPlanner fail-closed（必须返回字面 "Done"）；GoalStopDetector 9 类正则防过早退出 |
| OpenWorker | `explore` 工具开子 `TurnEngine`（code 家族，`tools/subagent.py`） | 子引擎强制 **PLAN 只读**、无 approver、全新上下文 | 只回最终报告，中间文件读不进父上下文 | 强制 plan 硬拦写/shell + **不递归**（子无 `explore`）；另有 **self-wake** 自我挂起 + 定时/事件唤醒 |

**各家的想法**：子 agent 的难点从来不是"派出去"，而是"收回来"和"别发疯"。收回方式呈现三代进化：**第一代阻塞等待**——父 agent 原地干等，简单可靠但界面冻结；**第二代消息注入**——结果以通知/哨兵汇入后续回合，CodeWhale 的注释写得最透彻："Launching a sub-agent is not the same as joining it"；**第三代调度脊骨 / 消息总线**——nanobot 让结果变成带 `injected_event` 的 InboundMessage 从总线正门重新进入，Raven 在此基础上换成 Spine 的 `origin=SUBAGENT`。防失控手段高度趋同：限深度、限并发、工具箱缩水、权限不继承——因为大家都怕"**会自己花 token 的递归**"。而 Claude Code"子 agent 复用同一个 `query()`"是其中最美的设计：主 agent、子 agent、压缩分身全是同一台发动机的复用——这也是压缩分身能顺便命中 prompt cache 的原因。

```mermaid
sequenceDiagram
    participant P as 父 agent
    participant S as 子 agent
    Note over P,S: ① 阻塞等待（CC前台 / opencode默认 / hermes编排者）
    P->>S: 派活
    P-->>P: ⏸️ 原地等待
    S-->>P: 结果作为 tool_result
    Note over P,S: ② 消息注入（CC后台 / hermes / CodeWhale）
    P->>S: 派活（后台，立即返回）
    S-->>P: 完成 → 通知消息/哨兵汇入后续回合
    Note over P,S: ③ 消息总线 / 调度脊骨（nanobot → Raven）
    P->>S: spawn
    S-->>P: publish_inbound(injected_event)<br/>或 Spine TurnRequest(origin=SUBAGENT)
```

### 2.8 扩展生态：MCP 是地板，地板之上各显神通

| 项目 | 自有扩展体系 | 技能 | MCP 支持 | 命令体系 |
|---|---|---|---|---|
| Claude Code | **插件市场**：市场 = git 仓库 + marketplace.json，稀疏克隆只拉你装的插件 | SKILL.md 渐进披露；条件技能（paths 匹配才激活）；MCP 来源的技能禁用 shell 预执行（安全红线） | 6 种传输；`mcp__server__tool` 命名；annotations 映射成三标记；不可信仓库启动审批 | 60+ 命令，local/local-jsx/prompt 三类型；用户命令支持 `$ARGUMENTS` 与 shell 预执行 |
| opencode | **插件 Hooks 全门类**：config/event/tool/auth/provider/chat.params/permission.ask/tool.execute.before…；另有 `{tool,tools}/*.ts` 轻量目录 | 多位置发现，含 `~/.claude/skills` | 3 种传输 + OAuth；`server_tool` 命名；MCP 的 prompts 变命令 | TUI 应用命令 + 服务端模板命令（`$ARGUMENTS`、`` !`cmd` `` 预执行、`@file`） |
| hermes-agent | **一切皆插件**：约 20 个消息平台、33 个 provider、看板、记忆全是插件，且延迟加载 | agentskills.io 开放标准 + Skills Hub；**agent 能自己创建技能** | 既是客户端也是服务器（`mcp_serve.py` 反向暴露自己） | **83 个命令一张注册表**：CLI 帮助、网关分发、Telegram 菜单、Slack 子命令、自动补全全部自动生成 |
| Raven | 插件刻意收窄：仅"记忆后端"和"工具"两类贡献点（`raven-plugin.toml`） | 本地 SKILL.md + **Skill Hub 远程市场**（search 只拉元数据） | `mcp_<server>_<tool>` 包装进同一注册表 | CLI typer 命令树 + TUI 约 50 个斜杠命令，三层就近分流 |
| CodeWhale | 插件**只读发现**（"发现 ≠ 启用 ≠ 信任 ≠ 执行"）；hooks 11 个生命周期点 | **兼容六家目录**（.codewhale/.agents/.claude/.cursor/.opencode/.codex）——"能兼容就不另立标准" | 3 种传输；`start_mcp_server` 工具让**模型自己**在对话中拉起新 MCP server | 85+ 内置命令（含中文别名）+ `.codewhale/commands/*.md` 自定义（可限定工具集） |
| goose | **一切皆 MCP 扩展**（六种形态含 Builtin 进程内、Platform 纯 Rust）；**Recipe 配方**：指令+扩展+参数+输出 schema+重试打包成可分享的 YAML | `~/.agents/skills` 或项目 `.agents/skills` | rmcp 实现；agent 能自己搜索安装扩展（强制批准 + 恶意检查） | 内置命令 + 配方命令 + 技能命令三来源合并去重 |
| nanobot | channel 插件（pkgutil 发现）+ `entry_points` 工具插件 | 本地 SKILL.md 渐进披露 + **ClawHub** 远程技能市场 | 官方 mcp SDK；MCP 工具进同一注册表 | CLI typer + 斜杠命令（`/stop` 等优先命令可穿透忙会话） |
| grok-build | **Plugin Marketplace**（Git/GitHub/本地；allowlist 白名单；InstallRegistry commit hash 防篡改）；Hooks（xai-grok-hooks crate）；**ACP 协议**对外暴露 | AgentDefinition.skills 白名单 + discoverSkills 自动发现；`~/.claude/skills` 兼容 | MCP Dispatcher（50ms 滚动窗口合并 + last-write-wins + auto-restart）；xai-acp-lib 独立 crate | `/goal /compact /recap /model /effort /memory` session 命令；Ctrl+P 命令面板 |
| OpenWorker | **persona 市场**：目录/git 安装、快照进托管区、**默认禁用待同意**（`personas/registry.py:340`） | Anthropic 格式技能，渐进披露 + `load_skill`（工作区 `.coworker/skills`） | 自带异步层的 MCP 客户端：stdio + streamable-http + **OAuth**（`mcp/oauth.py`） | **40 个连接器**（GitHub/Slack/Jira/Notion/Gmail/GCal…）逐工具控制；`openworker-connectors` CLI |

**各家的想法**：MCP 已成为八家共同的地板——全部支持，且与内置工具一视同仁（同样的校验、同样的权限裁决）。竞争发生在地板之上：Claude Code 用**市场**做体验（稀疏克隆这种细节都抠），hermes 用**插件**做广度（连平台接入和模型供应商都是插件），goose 用**recipe**做流通（agent 工作流第一次可以像脚本一样被版本管理和分享），opencode 用 **Hooks 全门类**做开放（连"权限询问"都可被插件接管），CodeWhale 用**兼容**做迁移成本最低化（你为别家写的技能它直接用），nanobot 用 **ClawHub + entry_points + channel 插件**保持核心小、能力在边缘，Raven 用**收窄**表达 pre-alpha 的克制。另一个共识是 **SKILL.md + 渐进披露**（平时只给清单，用到才加载全文）几乎全有——这是跨项目收敛最快的实践之一。

### 2.9 模型与认证：订阅复用是开源社区的突围打法

| 项目 | provider 阵容 | 认证形态 | 本地模型 | fallback / 容错 |
|---|---|---|---|---|
| Claude Code | **仅 Anthropic 系**（含 Bedrock/Vertex/Foundry 企业渠道） | 订阅 OAuth（PKCE + 回环/手动双通道）/ API key / 企业云；token 存钥匙串，**带文件锁的静默刷新** | 无 | 529 超载重试 3 次后切 `--fallback-model`；Haiku 级小模型打杂（主题检测等） |
| opencode | models.dev 目录几十家；对 OpenAI/Anthropic/自家有**自研原生运行时**，其余走 Vercel AI SDK | api / oauth / wellknown 三种形态；**内置认证插件**：ChatGPT Plus（Codex）、GitHub Copilot、Claude 订阅都能登录 | 文档未作主打 | `SessionRetry.policy` 重试策略；文档未见跨模型 fallback 链 |
| hermes-agent | **33 个 provider 插件** + Nous Portal（300+ 模型） | Portal OAuth device code 一键登录 / API key / 外部密钥管理器（Bitwarden、1Password） | **一等公民**：custom provider + vLLM/SGLang/llama.cpp；ollama-cloud 插件；自家 Hermes 系列开放权重模型配套 | **fallback 链**（主模型挂了按序换备胎）+ MoA（同一问题问多个模型再综合） |
| Raven | LiteLLM 统一适配（OpenAI/Anthropic/Gemini/DeepSeek/Copilot/Codex OAuth/Azure/自定义端点） | `raven onboard` 向导配置 | 自定义 OpenAI 兼容端点 | `chat_with_retry` + `fallback_models`；**错误分类驱动**：限流/网络→重试降级，认证/欠费→致命，**上下文溢出→该压缩而不是降级** |
| CodeWhale | **36 个 ProviderKind 枚举**（含月之暗面、智谱、MiniMax、阶跃、小米 MiMo） | OS 钥匙串 / 环境变量 / OAuth 设备码；base URL 强制 HTTPS（回环除外，否则要显式自证） | **免 key 一等公民**：vLLM / SGLang / Ollama | `/model auto` **自动路由**：按任务内容选模型与思考档位，TUI 能看路由回执 |
| goose | 15+ 手写 + **37 个声明式 JSON** + ACP provider（把别的 agent 当模型后端） | API key / OAuth 设备码 / 系统钥匙串；**订阅复用五连**：ClaudeCodeProvider、ChatGptCodexProvider、GeminiCliProvider、KimiCodeProvider、CursorAgentProvider | Ollama；内置 llama.cpp/candle 运行时；`goose local-models` 从 HuggingFace 下载 GGUF/MLX | 错误分支专门化（余额不足/拒答/网络错误分别提示）；文档未见明确 fallback 链 |
| nanobot | **~40 provider 自研注册表**（直连 openai/anthropic SDK，**无 LiteLLM**） | `nanobot onboard` / `provider` 子命令；config 热加载 | OpenAI 兼容端点可接 | `fallback_provider`：可降级错误且未流出内容时按链换备胎；溢出→压缩而非降级 |
| grok-build | **锁定 xAI Grok**；多媒体工具深绑 xAI 独有能力；xai-grok-auth 专用 crate | xAI OAuth / API key / OIDC 企业 SSO / 外部自定义端点；AuthRetryMiddleware 自动刷新 token | 无（xAI 锁定） | 错误分级；AgentDefinition.model 字段可覆盖；Goal Mode 子 agent 可指定不同模型 |
| OpenWorker | **建在 aisuite 上**：OpenAI/Anthropic/Gemini 原生 + DeepSeek/GLM/Kimi/Qwen/MiniMax/Grok/Mistral + **Ollama** | BYO key 存**本地密钥库**；云端只撮合 OAuth；**不登录也能用**（手填凭证） | **Ollama 全本地一等公民** | ProviderRouter 按 `provider:` 前缀路由；能力矩阵记 vision/pdf；**换模型只改字段** + 一条降级 notice |

**各家的想法**：认证层面最精彩的暗战是"**订阅复用**"：官方产品希望你就用它的订阅，开源社区把这堵墙拆成了门——opencode 用插件登录你的 ChatGPT Plus 和 Copilot，goose 写了五个 provider 专门复用你已有的 Claude/ChatGPT/Gemini/Kimi/Cursor 订阅，hermes 检测到同类项目的配置目录还会主动提出一键迁移。fallback 设计暴露成熟度：nanobot / Raven 的错误分类最讲究——区分"该重试的"（限流）、"没救的"（欠费）和"**搞错药方的**"（上下文溢出时降级毫无意义，该做的是压缩）；Raven 换用 LiteLLM 外包适配，nanobot 坚持自研注册表"每条路径可追溯"。CodeWhale 的自动路由代表另一个方向：既然模型是零件，就让系统替你挑零件。

### 2.10 prompt cache 工程：七家里最鲜明的共同主题

| 项目 | 为缓存命中做的事（全部有行号可查） |
|---|---|
| Claude Code | 系统提示词分静态段/动态段，静态段对所有用户一致；工具清单**内置必须排成连续前缀**、deny 提前剔除；CLAUDE.md/git 状态 memoize 每轮重复发送相同内容；计费头拆出来标 `cacheScope: null` 不打翻缓存；**压缩分身刻意保持相同前缀**——源码注释记载：旧路径 98% 缓存未命中、每天浪费约 38B token；连 `Sleep` 工具的说明书都提醒模型"prompt cache 5 分钟过期" |
| opencode | 系统提示词按模型家族选底料、拼装顺序固定（七家中相对着墨最少的一家，未见专门机制章节） |
| hermes-agent | **"Per-conversation prompt caching is sacred"**（前缀缓存神圣，`AGENTS.md:19-23`）：系统提示词在一个会话内**逐字节稳定**；三层三明治 stable/context/volatile；记忆用冻结快照（宁可 agent 暂时不知道自己刚记住的东西）；工具集会话级冻结；压缩是唯一例外 |
| Raven | 环境信息（时间/渠道）**放在用户消息而非系统提示**，让同一份系统前缀跨轮复用；**TokenWise**：对 Anthropic ≤4 个 cache 断点做自适应摆放 + 用量记账 |
| CodeWhale | **volatile-content-last invariant**（易变内容靠后不变式）：块按"最稳定→最易变"排序追加；`PrefixStabilityManager` 运行时检测漂移并上报；`frozen_prefix` 三区前缀契约在首回合冻结基线、后续校验；`/cache` 命令看缓存遥测；压缩"晚做、少做"（因为它最伤缓存）；子 agent 可 `fork_context` **共享父的缓存前缀** |
| goose | 时间戳**只精确到小时**（同一小时内系统提示逐字节一致）；扩展信息按名字排序（注释原话："Stable tool ordering is important for multi session prompt caching"）；**动静分离**：系统提示纯静态，每轮变化的信息走 MOIM 便签插入最后一条用户消息 |
| nanobot | 对支持缓存的 provider 注入 **3 处 ephemeral `cache_control` 断点**（系统提示末 / 最后一条用户消息 / 工具定义若干处）；工具清单内置前、MCP 后、各自按名排序并缓存定义 |
| grok-build | AgentDefinition finalize_prompt **build 时渲染并锁定**（MiniJinja 模板引擎稳定渲染结果）；MCP Dispatcher 50ms 滚动窗口合并防状态抖动污染系统前缀；ToolBridge tool_definitions() 工具顺序稳定；Goal Mode GoalOrchestrator 频繁小更新走 fire-and-forget **不落 JSONL** |
| OpenWorker | 历史是**规范 OpenAI 形状**、每 provider 每次转换；**每回合临时上下文挂在最后一条用户消息**（而非系统前缀），静态系统前缀跨轮稳定——与 goose/Raven 同款缓存友好姿势；notice / 旁路（source/_display/ts）发送前剥离（`engine.py:878`） |

**各家的想法**：这轮分析最鲜明的共同主题是**对 prompt cache 的执念**——缓存命中部分不计费或少计费、响应更快，缓存命中就是真金白银。手段可归纳成五板斧（见下图）：静态内容前置、易变内容后置或外移到用户消息侧、工具清单逐字节稳定、压缩慎重触发、遥测可见。程度最深的三家各有口头禅：hermes 说缓存"神圣"，CodeWhale 说"省钱是工程，不是运气"，goose 把缓存当"工程化"对象；nanobot 则是朴素的"固定三断点 + 稳定排序"，Raven 在其上加了自适应 TokenWise。一个有趣的推论：**锁定单一厂商的 Claude Code 反而可以把缓存优化做到极致**（计费头、缓存作用域、断点位置都和 API 供应商"里应外合"），这是下一章"模型策略"对决的重要伏笔。

```mermaid
flowchart TD
    C["prompt cache 命中 = 省钱 + 提速<br/>本轮分析最鲜明的共同主题"] --> S1["① 静态内容前置<br/>CC 静态段 / hermes stable 层 / CodeWhale 恒定区"]
    C --> S2["② 易变内容后置或外移<br/>goose MOIM / Raven 环境信息 / CodeWhale 易变区"]
    C --> S3["③ 工具清单逐字节稳定<br/>CC 连续前缀 / hermes 冻结 toolset / goose 按名排序"]
    C --> S4["④ 压缩慎重触发<br/>CodeWhale 晚做少做 / CC 同前缀 fork 分身"]
    C --> S5["⑤ 遥测可见<br/>CodeWhale /cache + PrefixStabilityManager"]
```

---

## 三、技术实现的深层对比：六个工程决策的选择困境

前面第二章比的是"产品做了什么"，这一章往下钻一层，比"代码到底怎么写的"。同一个功能——比如"把模型吐出来的字显示在屏幕上"——七家的实现差异大到会让你怀疑他们做的不是同一类软件。这些差异不是随机的，每一处都能追到一个具体的**工程困境**：某个东西你只能三选一，选了 A 就注定要为 B、C 的缺失付代价。本章挑六个最能暴露"偏好逻辑"的技术轴逐一拆开。

> 阅读提示：本章的 `文件:行号` 主要来自对各仓库的二次源码核对（在第二章单项目分析的基础上补充挖掘），nanobot 行号转引自《nanobot-源码分析》；标注"未找到/不确定"的地方是真的没找到，不是省略。

### 3.1 语言与运行时：四种赌注，押在四个不同的痛点上

一个 AI agent 的第一行代码还没写，就得先回答一个问题：**用什么语言、跑在什么运行时上？** 这个决定会一路渗透到并发模型、分发方式、贡献者门槛，几乎不可逆。七家给出了四种答案。

| 项目 | 语言 / 运行时 | 关键选型证据 | 想解决的核心痛点 | 付出的代价 |
|---|---|---|---|---|
| Claude Code | TypeScript 单体 + **魔改 Ink** | `src/ink/` 自带 reconciler/DOM/Yoga/termio，不是 `import 'ink'`；`src/native-ts/yoga-layout/` 是纯 TS 重写的 Yoga 子集；构建期用 `bun:bundle` 的 `feature()` 做死代码消除（`src/entrypoints/cli.tsx:1`） | 把终端 UI 当成"小 React DOM"，让流式打字、鼠标选区、alt-screen 成为一等公民 | 维护一整套渲染器 + Yoga 子集，几乎无法再跟上游 Ink 合并 |
| opencode | TypeScript on **Bun** + **Effect** | `packageManager: bun@1.3.14`（`package.json:7`）；服务默认跑在 Bun **Worker 线程**（`cli/cmd/tui.ts:210`）；SQLite 走 `bun:sqlite`（`database/sqlite.bun.ts:1`）；服务注入用 `Layer.effect`、取消用 `Fiber.interrupt`（`effect/runner.ts:178`） | 用函数式 + 结构化并发换"中断/依赖注入/可测试性"的干净 | `yield*` 满屏、贡献门槛陡；Node 兼容要 `#db` 双轨（`db.bun.ts`/`db.node.ts`） |
| hermes-agent / Raven / **nanobot** | **Python 内核**（hermes/Raven 另加 TypeScript 界面） | hermes 主循环"entirely synchronous"（`AGENTS.md:352`）；Raven 全链路 asyncio；**nanobot**：Python≥3.11 全程 asyncio，**直连 openai/anthropic SDK（无 LiteLLM）**，React+Vite WebUI 同进程经 WebSocket | 复用 Python 的 AI/记忆/科研生态；nanobot 另押"核心可读、完全自持" | 双运行时（hermes/Raven）或浏览器前端边界（nanobot）；自研 provider 注册表要自己养 |
| CodeWhale / goose | **Rust + tokio** | CodeWhale：635 个 `.rs`、约 65 万行，release 开 LTO + `codegen-units=1` 但**刻意不开 `panic=abort`**（`Cargo.toml:67-73`），全局换 mimalloc；goose：12 crate，CLI 8MB 栈（`main.rs:38-46`） | 单二进制分发、内存安全、把"边流式边并行跑工具"这类并发写对 | 编译慢、异步 Rust 学习曲线筛掉贡献者；类型体操换来的严谨也是负担 |
| **OpenWorker** | **Python 后端（建在 aisuite）+ 三种语言** | `pyproject.toml` 依赖 aisuite（pin commit）/textual/fastapi；三命令入口（TUI/服务/连接器）；引擎全 asyncio；壳是 **Tauri(Rust)**、界面 React、语音 **Rust STT** | 复用 Python AI 生态 + aisuite 统一 provider 抽象；桌面壳给"本地优先"体验 + 自更新 | 三语言构建/打包复杂度（Python+Node+Rust，`packaging/` 一整套）；provider 覆盖依赖 aisuite |

**各家的想法**：这张表最有意思的地方是——**没有一家选语言是为了"跑得快"，都是为了"某个特定的难点不容易写错"**。

- Claude Code 的痛点是**终端体验的精度**。它要做到"ESC 一路急刹到网线"、字符级流式、鼠标选区，普通的 Ink 包装满足不了，于是干脆把 Ink 整个 vendored 进来改，连 Yoga 布局引擎都用 TS 重写了一个子集（`src/native-ts/yoga-layout/index.ts:1`）。这是"为了体验，宁可自己维护一个渲染器"。
- opencode 的痛点是**长期可维护性与并发正确性**。它赌 Effect——用 `Layer` 做依赖注入、用 `Fiber` 做结构化并发，换来的是中断清理路径极其干净、几乎不用 mock 就能测。代价写在它自己的贡献指南里：门槛高。这是典型的"长期主义豪赌"。
- hermes、Raven、nanobot 的痛点是**生态复用 + 可读核心**。记忆、多 provider、全文检索这些能力，Python 生态最成熟；nanobot 作为学术开源更把"small core / readable internals"写成铁律，并拒绝 LiteLLM 外包——**显式优于魔法**。有趣的是同步/异步分道：hermes 主循环坚持**全同步**；Raven / nanobot 则全 asyncio（nanobot 的 MessageBus 与状态机天生是并发的）。
- CodeWhale 和 goose 的痛点是**并发的正确性 + 分发的干净**。agent 主循环要同时处理 SSE 流、并行工具、子 agent 完成、用户插话——Rust 的所有权模型让这些不容易写错，`Drop` 语义还能白送一个"硬取消"（见 3.5）。单二进制分发对"要装在别人机器上"的工具是巨大加分。代价是 65 万行 Rust 的编译时间和贡献门槛，CodeWhale 的 `AGENTS.md` 甚至专门教新人怎么跑定向测试来绕开全量编译。

一个反直觉的观察：**语言选型和"团队能招到什么人"强相关**。Nous / HKUDS 偏 Python 科研生态，于是 hermes/Raven/nanobot 系是 Python 内核；Block 和 CodeWhale 社区偏系统工程，于是选 Rust；Anthropic 要极致的产品迭代速度，TypeScript 单体最快。技术选型从来不是纯技术决定。

### 3.2 流式管线：从字节流到屏幕，六条路的分岔点

模型的回答是**流式**吐出来的（一个 SSE 事件接一个），agent 必须一边收、一边解析、一边显示、一边在参数收齐时执行工具。这条"从 provider 的字节流到用户屏幕"的管线，是 agent 里最实时、最容易出 bug 的地方。七家的管线骨架一致，分岔点在三处。

**分岔点一：用 SDK 还是裸流？** Claude Code 的选择很硬核——**故意不用** Anthropic SDK 的 `BetaMessageStream`，而是直接 `beta.messages.create({stream:true})` 拿原始 SSE 自己攒（`src/services/api/claude.ts:1818`）。源码注释写明原因：SDK 对每个 `input_json_delta` 都做一次 partial JSON parse，是 O(n²) 的，大工具参数下会卡。这是"第一方最懂自家 API"的一个缩影。opencode 则相反，主路径走 Vercel AI SDK 的 `streamText().fullStream`，再归一化成自己的 `LLMEvent`（`session/llm.ts:373`）——用一层抽象换多 provider 通用。nanobot 介于两者之间：**直连 openai/anthropic 官方 SDK**，但自己维护 ~40 家注册表与 OpenAI 兼容适配层，拒绝 LiteLLM 魔法。

**分岔点二：部分工具参数怎么攒？** 这是全场最一致的设计：七家**全部**用"字符串累积 + 收齐再解析一次"。模型吐工具参数时是一段段 JSON 碎片（`{"pa` ... `th":"/foo"}`），谁都不会去解析半截 JSON：

| 项目 | 累积实现 | 证据 |
|---|---|---|
| Claude Code | `contentBlock.input += delta.partial_json`，`content_block_stop` 时 `normalizeContentFromAPI` 只解析一次 | `claude.ts:2087`、`messages.ts:2670` |
| opencode | `ToolStream.appendOrStart` 拼字符串，收齐 `parseToolInput`（空串当 `"{}"`） | `protocols/utils/tool-stream.ts:117` |
| hermes | `tool_calls_acc[idx]`，name 用赋值、arguments 用 `+=`（防 provider 重发整名） | `chat_completion_helpers.py:2845` |
| Raven | `_merge_tool_call_fragments` 按 `index` 开 slot，arguments append 再 `json.loads` | `agent/loop/main.py:2474` |
| CodeWhale | `Delta::InputJsonDelta{partial_json}` → `input_buffer.push_str`，块结束 final parse | `turn_loop.rs:1080` |
| goose | 首个 tool_calls delta 建 `HashMap`，`args.push_str(...)`，完整后 `parse_tool_arguments` | `formats/openai.rs:1090` |
| nanobot | AgentRunner 流式路径按 index 累积 arguments，收齐再解析（与 Raven 同源设计） | `agent/runner.py`（转引 nanobot 分析） |

这种高度收敛说明一件事：**这是被现实教育出来的最优解**。谁试图解析半截 JSON，谁就会在 provider 换行、字段乱序、并行多工具时崩掉。hermes 的注释甚至专门标了 MiniMax/Ollama 的特殊处理，Raven / nanobot 的注释记录了从"假设单工具有序"到"尊重 index 支持并行多工具"的演进——这些都是踩坑留下的疤。

**分岔点三：thinking / 推理块怎么办？** 这里 Claude Code 又暴露了第一方的深度：Anthropic 的**签名 thinking 块**（signed thinking）必须逐字节原样回传才能继续对话轨迹，所以它专门累积 `signature_delta` 且**明确不计入输出长度统计**（`messages.ts:3078`，避免思考动画把 token 计量冲高），换 API key 时还要 `stripSignatureBlocks` 剥掉签名（签名与 key 绑定，`messages.ts:5061`）。goose 遇到的是同一个问题的多 provider 版本：它把 thinking 块**挂到 tool-call 消息上**而不是独立成消息，注释说明动机是"避免独立 thinking 消息和 tool-call 合并后被 Anthropic 因重复 signed block 打 400"（`agents/agent.rs:2346`）。同一个坑，第一方用"里应外合"解决，provider 无关方用"迁就规则"解决——这正是后面第四章"第一方红利"的伏笔。

```mermaid
flowchart LR
    P["Provider SSE 字节流"] --> A{"分岔一<br/>SDK 还是裸流"}
    A -->|裸流·第一方最懂| CC["Claude Code<br/>自管状态机<br/>避开 O(n²) parse"]
    A -->|SDK·多provider通用| OC["opencode/goose/nanobot<br/>归一化或直连官方 SDK"]
    CC & OC --> B["分岔二：工具参数<br/>字符串累积 + 收齐解析一次<br/>（七家完全一致）"]
    B --> C{"分岔三<br/>thinking 块"}
    C -->|签名块里应外合| CC2["CC：不计输出长度<br/>换key剥签名"]
    C -->|迁就provider规则| GS["goose：挂到tool-call<br/>防重复signed block 400"]
```

### 3.3 "何时开火"：参数一收齐就执行，还是等这轮全部收完？

上一节说"参数收齐再解析"，但"解析完立刻执行工具"还是"等模型这一整轮说完再统一执行"，七家有分歧，这直接决定了**首个工具的启动延迟**。

Claude Code 最激进：`query.ts` 一收到带 `tool_use` 的 assistant 消息就立刻 `streamingToolExecutor.addTool`（`query.ts:837`），此时**流可能还在继续吐后面的内容**——工具已经跑起来了。这是把"首 token 到工具启动"的延迟压到最低。代价是复杂：如果流后续失败或要 fallback，得把整批执行器 `discard()` 掉、再给模型补合成的错误结果（`StreamingToolExecutor.ts:174`）。这套还藏在一个门控 `config.gates.streamingToolExecution` 后面，关掉就退回"收齐再 `runTools`"的老路（`query.ts:1380`）。

opencode 走 AI SDK 的调度：工具在参数完整后由 SDK 调 `execute`（`session/llm.ts:276` 注释"AI SDK owns tool dispatch"），是"参数完整即执行"，但不是"半截就执行"。goose、CodeWhale 类似——收齐该工具的参数就能进并行池。

**各家的想法**：这个选择的本质是**用复杂度换延迟**。Claude Code 作为要打磨极致体验的第一方产品，愿意为"点下回车后工具早零点几秒开跑"承担 `discard`/合成错误这套复杂机制；而 provider 无关的项目更倾向让 SDK 或统一管线管调度，牺牲一点延迟换实现的简单和跨 provider 的一致。**延迟是产品指标，复杂度是工程成本，天平往哪边倒取决于你是不是把"体验"当护城河。**

### 3.4 会话持久化与崩溃恢复：三种存储心智

agent 要能 `--resume` 接着上次聊，进程崩了不能失忆，所以必须把会话落盘。存成什么格式，暴露了各家对"会话是什么"的理解。

| 项目 | 存储格式 | 位置 | 崩溃恢复机制 |
|---|---|---|---|
| Claude Code | **JSONL**（多类型 Entry，带 parentUuid 链、mode/worktree/file-history 等元数据） | `~/.claude/projects/<cwd>/<sessionId>.jsonl` | 队列 + 100ms flush（`sessionStorage.ts:565`）；worktree 三态区分"干净退出 vs 崩溃"（`:541`）；**无 WAL**，崩溃窗口内 ≤100ms 未 flush 可能丢 |
| opencode | **SQLite + Drizzle**（session/message/part 表，事件投影落库） | `Global.Path.data/opencode.db`，`WAL` 模式 | 业务只 `events.publish`，projector 落 SQLite（`session/projector.ts:262`）；abort 后 cleanup 标工具 interrupted |
| hermes | **SQLite + FTS5** `state.db`（取代旧 JSONL） | `~/.hermes/state.db`，WAL | **先持久化 tool-call 再执行工具**（`conversation_loop.py:5361`，有契约测试）；gateway `resume_pending` 重启续聊 |
| Raven | **JSONL** append-only（为 LLM cache），Curator Archive 另存无损原文 | `workspace/sessions/{channel}/{chat_id}.jsonl` | load 时跳过崩溃留下的半行 JSON（`session/manager.py:303`）；**Spine Lane 不持久化**（纯内存） |
| CodeWhale | **每会话一个原子 JSON**（非 Codex 式 rollout）+ checkpoint | `{id}.json` 原子写；`checkpoints/<id>.json` | 回合中 checkpoint 原子写，完成即清；`utils.rs:371` 目录 fsync 防掉电丢 rename |
| goose | **SQLite + WAL** `sessions.db`（sessions/messages/usage_ledger 三表） | `Paths::data_dir()`（仍用 Block 品牌目录兼容老用户） | `BEGIN IMMEDIATE` + WAL；可导入 Claude Code/Codex/Pi 的会话 |
| nanobot | **JSONL** 会话 + 原子落盘；记忆另用 dulwich git | `session/manager.py`；agent 工作区 Markdown | 提前落盘用户消息 + runtime_checkpoint；`/stop` 取消后仍保住已跑工具结果 |

这里有个清晰的分野：**JSONL 派**（Claude Code、Raven、nanobot）图的是简单、人可读、append 天然利于 prompt cache（历史一字不改）；**SQLite 派**（opencode、hermes、goose）图的是可查询、有事务、能建全文索引和用量账本。CodeWhale 走了第三条——**原子 JSON 文件 + checkpoint**，既要 JSON 的简单，又用"原子写 + fsync"补上崩溃安全，代价是随机改历史贵（fork/undo 要整体重写）。

**最值得单独拎出来的是 hermes 的"先落盘再动手"**（`conversation_loop.py:5361`，还配了专门的契约测试 `test_tool_call_incremental_persistence.py`）。它的逻辑是：一个工具可能执行 `hermes restart` 把自己进程干掉，如果先执行后落盘，重启后就会"忘记自己刚发起过这个工具调用"，对话轨迹断裂。所以铁律是**先把 assistant 的 tool-call 消息写进 SQLite，再执行工具**。这是"要在别人机器上无人值守跑几个月"的软件用血泪换来的顺序——一个只在本地交互式用的工具，根本想不到要防这个。

**各家的想法**：注意 opencode 和 goose 的一个共同选择——**事件/投影分离**。业务代码不直接写库，而是发事件（`events.publish`），由一个 projector 把事件落成 SQLite 状态（opencode `projector.ts`）。goose 更进一步，给消息打了 `user_visible` / `agent_visible` 两个布尔（`conversation/message.rs:661`），压缩后原文标"用户可见、模型不可见"，摘要标"模型可见、UI 可隐藏"。这种"把状态变更当事件流"的思路，正是它们"服务器即本体"架构的自然延伸——因为多客户端要订阅同一份状态，事件总线是必需品。而 Claude Code 的单体架构不需要这层，直接写 JSONL 最快。**架构形态决定了持久化的心智模型。**

### 3.5 并发与取消：读并行写串行的六种实现，硬取消 vs 协作取消

agent 一轮里模型可能同时要调好几个工具。全并行会撞车（俩工具同时写一个文件），全串行又慢。七家的共识是**"只读工具可并行、写工具串行"**，但实现方式和"怎么把正在跑的东西喊停"差异巨大。

**并行调度：谁能并行，由什么决定？**

- Claude Code 有**两套**调度（一个容易被搞混的细节）：流式路径 `StreamingToolExecutor` 里并发安全的工具并行、非安全的独占，**没有硬编码上限**；而非流式的 `runToolsConcurrently` 才有默认上限 10（`toolOrchestration.ts:152`）。"是否并发安全"默认 fail-closed 为 false（`Tool.ts:750`），且可以是个按入参动态判定的函数。还有个巧思：只有 **Bash 出错**才触发兄弟工具取消（`siblingAbortController`，`StreamingToolExecutor.ts:356`），Read 失败不连坐——因为 Bash 常是"命令链"语义。
- CodeWhale 用 `FuturesUnordered` 做并行池（`turn_loop.rs:2214`），并行条件是工具声明了 `supports_parallel`（默认 false，`spec.rs:1217`），shell 另加信号量 `MAX_PARALLEL_SHELL_EXEC=4`。能并行的是只读 File/Grep/WebSearch/只读 shell 等。
- goose 用 `stream::select_all` 合并各工具 future 并行推进（`agent.rs:2266`），外层套 `select!` 夹 100ms sleep（`biased`）以便轮询取消。
- hermes 最精细，有**三态**：parallel / sequential / **segmented**（把一批工具里的安全子集并行、遇到写工具设屏障串行，`run_agent.py:6443`），路径重叠的 file 工具可并行但不可同 subtree。
- Raven / nanobot 反而**同一轮里工具是串行执行**的（`await tools.execute`），尽管模型可以并行发多个 tool_call。它们把并发让给了"跨会话"和"子代理"维度。

**取消：硬取消 vs 协作取消，是这一节最大的哲学分歧。**

```mermaid
flowchart TB
    subgraph HARD["硬取消派（能强杀正在跑的工具）"]
        R1["Rust 系：CancellationToken 三层贯穿<br/>CodeWhale drop(FuturesUnordered) 即杀掉所有在跑 future<br/>turn_loop.rs:2333 注释：不是等协作取消，是直接 drop"]
        R2["opencode：Fiber.interrupt ↔ AbortController<br/>Effect 结构化中断 + acquireRelease 清理"]
        R3["Claude Code：AbortSignal 直达 HTTP 层<br/>ESC 真掐断连接；后台 agent 独立 controller 不被连累"]
        R4["nanobot：/stop → asyncio.Task.cancel<br/>硬取消 + checkpoint 优雅落盘"]
    end
    subgraph SOFT["协作取消派（只能等它自己检查标志）"]
        S1["hermes：每轮开头查 _interrupt_requested<br/>检查点分布在 API 前/重试sleep中/退避循环<br/>卡死的工具不能强杀，只能靠超时"]
        S2["Raven：asyncio.Task.cancel 向上冒泡<br/>Lane 即取消单元；AgentLoop 内几乎不 catch CancelledError"]
    end
```

CodeWhale 的硬取消最典型：取消时直接 `drop(tool_tasks)`，注释写得很直白——"Dropping FuturesUnordered drops every still-active tool future ... instead of merely waiting for cooperative cancellation"（`turn_loop.rs:2333`）。这是 Rust `Drop` 语义白送的能力：丢掉 future 就等于取消，还配了 RAII guard 保证取消后终端不会卡在 cooked mode。

hermes 是另一极：**协作式中断**。主循环每轮开头查 `_interrupt_requested` 标志（`conversation_loop.py:837` 等多个检查点），工具跑到一半没有检查点就只能等它跑完或超时。hermes 自己在取舍清单里承认这个代价："卡死的工具不能强杀，只能靠超时"。但它换来一个好处：**不会杀出半个写坏的文件**——强杀一个正在写文件的工具，可能留下损坏的半个文件，协作式取消则保证工具要么没开始要么完整结束。

**各家的想法**：硬取消 vs 协作取消，本质是**"响应速度"和"状态完整性"的取舍**。Claude Code 要"ESC 一按立刻停"的爽快，所以 AbortSignal 直捅 HTTP 层真断连接；hermes 要"无人值守时不留下烂摊子"，所以宁可慢一点也要协作式。而这又和语言强相关：Rust 的 `Drop` 让硬取消几乎免费，Python 的协作式取消则要自己铺检查点。**语言的能力边界，悄悄塑造了产品的中断哲学。**

### 3.6 错误处理：分类器、回喂、错题本

模型会说胡话、provider 会限流、网络会断、上下文会溢出。怎么处理错误，是区分"玩具"和"生产级"的最硬指标。这里有三个层层递进的共识。

**共识一：错误要分类，不能一律重试。** 最成熟的是 Raven 和 goose 的错误分类器：

- Raven 的 `classify_error` 把错误分成三类动作：`retryable`（限流→重试）、`should_fallback`（换模型）、`should_compress`（`providers/base.py:16`）。最精彩的是它把**上下文溢出**单独拎出来——溢出时降级模型毫无意义，该做的是压缩。这个区分很多项目都没做对。
- goose 的 `ProviderError` 分支更细：`ContextLengthExceeded` 最多压缩 2 次再放弃（`agent.rs:2532`）、`CreditsExhausted` 弹充值链接、`Refusal` 直接终止禁止重试同一对话（`:2617`）。
- Claude Code 按**产品身份**分叉，这是第一方特色：**529** 前台重试、后台源立刻丢弃不放大级联；**429** 默认可重试，但 **Claude.ai 订阅用户默认不重试**（因为多半是额度窗口，盲重试无用，`withRetry.ts:765`）；**413** 还要拆两义——status 413 是"请求体字节过大"，而文案 `prompt is too long` 是"token 过长"，后者走压缩抢救（`errors.ts:560`）。

**共识二：工具错误回喂给模型，让它自己改。** 七家一致——工具失败不抛异常炸会话，而是把错误当成一条普通的 tool_result 喂回去。Raven 做得最有人情味：工具出错时统一追加一句 `[Analyze the error above and try a different approach.]`（`agent/tools/registry.py:48`），字面上"教"模型换个方法。Claude Code 用结构化 XML 标签 `<tool_use_error>...</tool_use_error>` + `is_error:true`（`toolExecution.ts:669`）。opencode 的"可教导拒绝"更进一步——权限拒绝时能附一句自然语言反馈 `CorrectedError`，把拒绝变成教学。

**共识三：把踩过的坑写进代码，防止重犯。** CodeWhale 把这个做成了文化——**每个历史 bug 都留下 `#issue` 编号注释**，堪称一本带编号的错题本：

| Issue | 踩的坑 | 修法 |
|---|---|---|
| `#4030` | `codewhale doctor \| head` 会触发 SIGPIPE panic | Unix 启动重置 SIGPIPE 为 SIG_DFL（`cli/src/main.rs:11`） |
| `#2990` | 笔记本合盖休眠后 SSE 假死 | 单调钟 vs 墙上钟双轨对表，丢弃半截流重发（`turn_loop.rs:337`） |
| `#3014` | Anthropic 签名 thinking 块必须原样回放 | 累积 `SignatureDelta` 并原样回传（`turn_loop.rs:1072`） |
| `#1542` | 非 DeepSeek 的 OpenAI 兼容端拒绝 `reasoning_content` | 通用兼容端丢弃该字段（`client/chat.rs`） |
| `#2264` | 提示词前缀漂移打翻 KV 缓存 | 三区前缀契约 `PrefixStabilityManager`（`prompt_zones.rs:1`） |

这套"错题本文化"配合它的 **"never corrupt state"** 哲学——会话原子写 + fsync（`session_manager.rs:372`）、畸形信任文件解析成空列表而非崩溃（`workspace_trust.rs:49`）、poisoned mutex 也 `into_inner` 继续跑（`engine.rs:903`）、release 保留 unwind 让单工具 panic 不拖死会话——共同构成了"生产级 Rust agent 该长什么样"的样本。Claude Code 也有一堆防御性配对修补（`ensureToolResultPairing` 补缺失 result、剥孤儿、跨消息去重 tool_use id，`messages.ts:5120`），只是它把这些藏在了代码里而没写成 issue 注释。

**各家的想法**：错误处理的成熟度和"运行时长"强相关。**跑得越久、越无人值守，错误处理就越重**。hermes、nanobot 和 Raven 要住在 IM 里长期在线，所以有最完整的 fallback 链和 cooldown；CodeWhale 要在各种奇怪机器上跑，所以攒了一本错题本；Claude Code 要服务海量用户的订阅和 API 两种计费，所以错误分类按产品身份分叉。**一个 agent 的错误处理代码量，约等于它被真实世界毒打的次数。**

---

## 四、产品逻辑如何决定技术形态：从团队基因到架构因果

前三章都在讲"怎么做"，这一章回答一个更根本的问题：**为什么七家会做出这么不同的选择？** 答案不在技术里，在**产品逻辑**里——谁出的钱、想卖什么、服务谁、靠什么活下去。技术形态是产品逻辑的下游。本章反过来推：从团队基因出发，看每一个"技术偏好"是怎么被"商业逻辑"倒逼出来的。

### 4.1 一张"基因表"：谁出钱、卖什么、于是怎么写代码

| 项目 | 团队基因 | 靠什么活下去（商业逻辑） | 于是技术上偏好 |
|---|---|---|---|
| Claude Code | Anthropic 官方（模型厂商） | 卖 Claude 订阅 / API tokens——**让你更多地用 Claude** | 锁定 Anthropic 把每个特性榨干（缓存计费头、签名 thinking、effort 档位）；闭源单体求极致迭代 |
| opencode | Anomaly（做 SST 的基础设施团队） | 开源 agent 引流，云端变现：**OpenCode Zen**（策展模型网关，宣称零加价）+ **OpenCode Go**（低价订阅）（`console/app/src/i18n/en.ts:196`；`sst.config.ts`） | 服务器即本体（基础设施思维）；provider 无关但自建一个模型网关做变现入口 |
| hermes-agent | Nous Research（开放权重模型公司） | 卖 **Nous Portal 订阅**（300+ 模型）+ 用 agent 轨迹生产训练数据（`batch_runner.py`、`trajectory_compressor.py`） | 模型无关但对自家 Portal 有 entitlement/路由特判（`conversation_loop.py:269`）；一切皆插件好接自家模型 |
| Raven | EverMind-AI（记忆技术公司） | 卖记忆 substrate——**EverOS**（外部服务 + PyPI 包 `everos==1.1.3`，HTTP API `/api/v1/memory`） | 上下文/记忆做成架构主角；EverOS 默认后端、插件化可换；fork nanobot 再叠 Curator/Spine |
| CodeWhale | 社区项目（前身 deepseek-tui，DataWhale 合作） | **无直接变现**，靠社区贡献 + AI 辅助开发（`.mailmap` 把 Copilot/Cursor/Qwen 提交映射到维护者） | "模型是零件不是产品"；兼容六家目录降迁移成本；面向中文 + 多 provider 社区 |
| goose | Block 发起，捐给 Linux 基金会 **AAIF** | **不直接变现**（治理中立，价值观 Open/Flexible/Choice，`GOVERNANCE.md:7`）；Block 内部工具开源外溢 | 引擎即服务器 + 一切皆 MCP（生态天花板最高）；ACP 协议中立 |
| nanobot | **港大 HKUDS 学术开源**（Xubin Ren），MIT | **无直接变现**；以可读核心与完全自持做研究/社区影响力（LightRAG 同门） | MessageBus + 显式状态机；直连 SDK 自研 ~40 provider；住在聊天软件里的超轻量个人 agent |

**这张表是理解全部技术差异的钥匙。** 举三个最锋利的因果：

1. **只有模型厂商会锁定模型。** Claude Code 锁死 Anthropic 不是技术保守，而是商业逻辑的必然——Anthropic 做这个 agent 的**目的就是让你多用 Claude**。锁定反而让它能把 Anthropic API 的每个旋钮榨干（见 4.3）。反过来，其余六家没有一家自己造主力模型（Nous 有 Hermes 系列但不强制），所以"模型无关"对它们是刚需——**你不可能一边说"我中立"一边绑死一家**。
2. **卖什么，就把什么做成架构主角。** EverMind 是记忆公司，所以 Raven 把上下文工程做成 6 段显式流水线、把压缩做成无损归档、默认挂 EverOS——记忆是它的商品，当然要做成主角。Nous 靠 Portal 订阅和训练数据活着，所以 hermes 内置轨迹压缩工具、对 Portal 有特判。HKUDS 卖的是"可读、可自持"，所以 nanobot 把核心压到 loop/runner、能力全推边缘。**产品的商业内核，会顽固地凸显在它最用力的那个技术模块上。**
3. **不变现的项目，反而敢把架构做到最激进。** goose 捐给了基金会、没有变现压力，所以敢赌"一切皆 MCP、引擎即服务器"这种生态天花板最高但短期性能吃亏的路线（它甚至得发明 Builtin/Platform 两种"作弊"形态挽回性能）。CodeWhale 是纯社区项目，所以敢用 65 万行 Rust 死磕安全和缓存。nanobot 作为学术开源，敢把"核心保持小"写成铁律并拒绝 LiteLLM。**没有 KPI 的项目，才有资格做长期主义的技术豪赌。**

### 4.2 客户端的主角是谁，决定了架构的重心

"agent 的界面是什么"这个产品定位，直接决定了架构往哪儿长。七家的"主角客户端"完全不同，架构重心就跟着偏。

```mermaid
flowchart TD
    Q["产品问自己：用户主要从哪里用我？"] --> A["终端/编辑器<br/>（写代码的人）"]
    Q --> B["消息软件<br/>（Telegram/微信/飞书）"]
    Q --> C["桌面 GUI + 第三方宿主<br/>（非程序员 + 生态）"]
    A --> A1["Claude Code：单体 TUI，五层同进程<br/>CodeWhale：双二进制消息管道<br/>→ 重心在终端体验与主循环精度"]
    B --> B1["hermes：Python大脑 + ~20平台适配器<br/>nanobot：MessageBus + 17平台 + WebUI<br/>Raven：Spine调度脊骨 + 12平台<br/>→ 重心在'会话键映射'与'无人值守安全'"]
    C --> C1["goose：引擎即服务器 ACP<br/>opencode：服务器即本体 HTTP<br/>→ 重心在'协议'与'多客户端一致性'"]
```

- **主角是终端的**（Claude Code、CodeWhale），把功夫全下在主循环精度、流式渲染、ESC 急刹这些"终端手感"上。Claude Code 甚至为此魔改了整个 Ink。
- **主角是消息软件的**（hermes、nanobot、Raven），架构重心是两个别人不太操心的东西：一是**会话键映射**——同一个大脑要同时接十几个平台的私聊/群聊/话题；二是**无人值守安全**——IM 场景没人守在电脑前按"允许"，所以 nanobot / Raven 干脆放弃逐次审批、押注沙箱（这就是第二章权限对决里"沙箱派"的产品根源）。它们的定位是"agent 是你通讯录里的一员"，不是"你打开的工具"。
- **主角是 GUI + 第三方的**（goose、opencode），必须先有协议。goose 的 `goose serve` 是 ACP over HTTP/WebSocket，桌面端、Zed、任何第三方都是客户端；opencode 更极端，**连同进程的 TUI 都走完整 HTTP 协议**（`cli/cmd/tui.ts:210` 的 Worker + 内存 fetch），理由是"本地单机和远程团队应该是同一份代码路径"。代价也实打实——goose 要给自家 GUI 配 `GOOSE_SERVER__SECRET_KEY` 防冒名接入（`gooseServe.ts:336`），协议要终身维护，跨语言类型还得靠 codegen 管线同步（goose 用 `schemars` 生成 JSON Schema → OpenAPI → TS，`Justfile:165`）。

**各家的想法**：这个选择"最好在第一天就想清楚"，因为**从单体事后改成服务器几乎等于重写**。opencode 那句"服务器即本体，协议即产品"之所以成立，是因为它从第一个 commit 就按协议写。Claude Code 的单体后来要补"被集成"能力（SDK、ACP bridge 都是后加的），但它单机体验依然是标杆——**架构先进性和产品完成度是两回事**。

### 4.3 "第一方红利"与"provider 无关税"：一笔能算清的技术账

第四章最技术的一节。Claude Code 锁定 Anthropic 到底换来了什么、其余六家为"模型自由"付了什么税？这不是口号，是能在源码里逐条指认的账。

**Claude Code 的第一方红利（这些优化在多 provider 架构里根本写不出来）：**

| 红利 | 机制 | 证据 |
|---|---|---|
| 计费/QoS 里应外合 | `x-anthropic-billing-header` 带 `cc_version`/`cc_entrypoint`/`cc_workload` | `constants/system.ts:60` |
| 1 小时 prompt cache | 订阅且非 overage + 远程门控开，session latch 防中途 bust | `claude.ts:358` |
| 请求可追踪 | `CLIENT_REQUEST_ID` 仅对第一方 URL 发，超时能对上服务端日志 | `claude.ts:1810` |
| 内部 effort override | `anthropic_internal.effort_override` 是 ant-only 字段 | `claude.ts:458` |
| ant-only 服务端工具 | advisor / connector_text / research 字段 | `claude.ts:2003` |
| 签名 thinking 逐字节回放 | 累积 signature_delta、不计输出长度、换 key 剥签名 | `messages.ts:3078` |
| 员工调试整枝 | `USER_TYPE=ant` 可 mock rate limit、dump system prompt | `withRetry.ts:201` |

这些加起来是一个"深度"的具象：**当你只服务一家 API，你可以和它里应外合到计费头、缓存作用域、内部字段这种程度**。ToolSearch 的 `defer_loading` 依赖 Anthropic 特有的 `tool_reference` 能力也是同理。

**其余六家的"provider 无关税"（同样能在源码里指认）：**

- **适配税**：opencode 的 `provider/transform.ts` 两千多行，全是吸收各家模型怪癖（谁支持哪个 effort 档、Copilot 的计费字段、Azure 的 URL 模式）。nanobot 则自己养 ~40 家注册表——这是一笔**永远写不完**的税——每出一个新 provider，就多一段条件分支。
- **最小公分母妥协**：当某家 API 有独特能力（比如 Anthropic 的签名 thinking），多 provider 架构要么放弃它、要么写一堆特判。goose 为了兼容 Anthropic 的"重复 signed block 打 400"规则，得把 thinking 挂到 tool-call 上（`agent.rs:2346`）——这是被迫的迁就。
- **拐杖成本**：goose 遇到不支持原生工具调用的小模型，得写 `toolshim` 用解释器给它发"拐杖"（`providers/toolshim.rs:1`），先攒全量再解析，还得关掉真流式。这是为"什么模型都能用"付的实现税。

**各家的想法**：这笔账的结论是——**锁定做深和无关做宽，是两种无法兼得的深度**。Claude Code 的缓存工程、ToolSearch、fallback 丝滑，都是"只服务一家"才磨得出来的；代价是命运绑定（供应商涨价、限流、换代，用户没退路）和对非 Anthropic 用户的硬排斥。做宽的六家换来用户自由和议价权（本地模型还能数据不出机），代价是那两笔永远交不完的税。有意思的是各家都在偷偷往对面靠：opencode 自建了 Zen 模型网关（想拿回一点"深度"），Claude Code 也加了 SDK/bridge（想补一点"宽度"）。**长期看，谁先在"provider 无关"的架构里做出"接近第一方的深度优化"，谁就能吃两边的红利——这是模型无关阵营下一个真正的技术高地。**

### 4.4 开源治理形态，会反向塑造代码

最后一个容易被忽略的产品逻辑：**项目归谁管、怎么协作，会渗进代码风格里。**

- **基金会治理 → 类型契约与文档纪律。** goose 捐给 AAIF 后，治理写在 `GOVERNANCE.md`（Contributors → Maintainers → Core Maintainers，僵局由创始人打破），代码上体现为强协议边界（ACP + codegen 类型同步）和"最 hackable"的价值观。中立治理逼着它把接口做规范，因为要给全世界的贡献者用。
- **纯社区 + AI 辅助 → 错题本与兼容优先。** CodeWhale 的 `.mailmap` 把 Claude/Copilot/Cursor/Qwen 的 bot 提交都映射到维护者名下，`CONTRIBUTORS.md` 强调 harvest PR（不能原样合也保留署名）。这种"AI 参与开发"的现实，配合它满是 `#issue` 注释的错题本文化——因为贡献者流动、AI 会重犯旧错，把坑写进注释是唯一的防线。它兼容六家技能目录、认所有人的 `AGENTS.md`/`CLAUDE.md`，也是"社区项目用兼容性换用户"的产品逻辑。
- **公司主导 pre-alpha → 诚实的残桩。** Raven 由 EverMind 主导、fork 自 nanobot、还在 pre-alpha，所以代码里全是诚实标注：`/yolo` 残桩老实标 `supported: false` 而不删（`_stubs.py:40`），`NOTICES.md` 甚至记录了"什么情况下放弃这个 hermes fork"的评估条件，`CONTEXT.md` 给每个术语标注"避免叫什么"。这种"诚实到牙齿"的工程文化，是"公司要对一个尚未成熟的开源项目负责"的产品姿态。
- **学术开源 → 可读核心与设计铁律。** nanobot 把 "Less structure, more intelligence" 和 "核心保持小" 写进 `.agent/design.md`，宁可 channels/providers 重复也不过早抽象——这是实验室项目面向学生贡献者时的自然选择：代码必须能读、能改、能完全自持。
- **闭源第一方 → 门控即产品边界。** Claude Code 的 `feature()` / GrowthBook 门控、`USER_TYPE=ant` 整枝、遥测从 Statsig 迁移到 GrowthBook 的双缓存（`growthbook.ts:792`），本质是"一个要服务海量用户、还要给员工留调试后门、还要远程调实验"的闭源产品的必然形态。它的很多"隐性产品边界"就藏在这些 `feature()` 调用点里。

**各家的想法**：治理形态和代码形态是同构的。**中立基金会催生协议化，纯社区催生兼容与错题本，公司主导催生诚实残桩或门控，学术开源催生可读铁律**。读一个开源 agent 的代码风格，几乎能反推出它背后是什么样的组织。

```mermaid
flowchart LR
    T["团队基因<br/>谁出钱"] --> B["商业逻辑<br/>靠什么活"]
    B --> M["模型策略<br/>锁定 vs 无关"]
    B --> A["架构重心<br/>终端/IM/服务器"]
    G["治理形态<br/>公司/社区/基金会"] --> S["代码风格<br/>门控/错题本/协议/残桩"]
    M & A & S --> R["最终技术形态<br/>= 产品逻辑的下游"]
    style R fill:#e8f5e9
```

---

## 五、总结评价：重要功能点的思路对决与利弊

前两章是"并列对比"（第二章比产品维度、第三章比技术实现，第四章讲清了产品逻辑与技术形态的因果），这一章换一个视角：挑出五个最重要的功能点，看七家在同一个问题上的**思路对决**。每一点按同一节奏展开：各家怎么想 → 解决方式对比 → 利与弊 → 我们的评价（适合谁）。

### 对决一：权限模型的路线之争——安全与打扰的权衡

**各家怎么想**：所有 agent 面对同一个恐怖故事——模型可能执行 `rm -rf`、可能把密钥发到网上、可能被恶意网页注入指令。问题在于：**闸门设在哪、由谁来把守**。Claude Code 不信任任何事先的判断，危险操作一律给人否决的机会；opencode 和 hermes 信任"用户事先立的法"；CodeWhale 干脆从结构上消灭可能性（没有工具可越权，谈何审批）；nanobot / Raven 承认常驻 IM 场景里根本没有"人"可按按钮，把信任交给隔离。

**解决方式对比**：弹窗询问（Claude Code 五关裁决 + 兜底弹窗）→ 规则前置（opencode 三态通配、hermes 三级白名单 + YOLO 导入即冻结）→ 结构安全（CodeWhale 能力标记推导审批、Plan 不注册写工具、constitution.json 只加锁不放权、provenance 降级；goose 的 Chat 禁工具与 readOnlyHint 也算这一派的轻量版）→ 沙箱隔离（**nanobot** 黑名单 + 工作区 + SSRF + bwrap；Raven 升级 boxlite microVM；hermes 六种后端按任务选隔离）→ **LLM 分类器**（grok-build：PermissionClassifier 实时判断风险，Auto 模式 3/20 累积上限兜底弹窗）→ **带外审批 / 收件箱挂起**（**OpenWorker**：`needs_user` 不在循环里干等，而是把请求变成**幂等的收件箱项**并把回合挂起——有人值守内联弹卡、无人值守进跨会话收件箱，任意界面首答生效、重启后 `resume` 不双跑；再配 **§25 标准规则**，连接器按精确 target 免问、shell 永远要问）。

**利与弊**：弹窗派掌控感最强，但**打扰成本随使用时长线性增长**，且依赖"面前有人"——Claude Code 在远程通道激活时干脆禁用提问工具（弹窗会挂死会话）。规则前置派把成本摊到配置上，熟练用户几乎零打扰，但新用户无从下手，且规则永远滞后于新威胁。结构安全派一劳永逸、可审计、安全上限最高，但**灵活性最低**——它回答"能不能"，不回答"这次该不该"。沙箱派对无人值守是唯一正确答案，但黑名单注定尽力而为，隔离边界配错就是裸奔。LLM 分类器派把审批本身智能化，但引入了新攻击面——分类器可能被对抗样本欺骗，且仍需上限兜底。

**我们的评价（适合谁）**：这不是单选题，而是光谱。**高频交互的编程场景**，Claude Code 的弹窗派体验最好——它的"始终允许"会把你的每次批准沉淀成规则，越用越接近规则派；**自动化/CI 场景**，规则前置 + 结构安全是正解（CodeWhale 的 execpolicy + constitution 组合就是为流水线设计的）；**常驻 IM 助手/无人值守**，沙箱派是唯一不自欺的选择（想读轻量 Python 出厂设置看 **nanobot**，想看 microVM 升级看 Raven）；**把 agent 跑在陌生代码库上**时，结构安全派（Plan 模式不注册写工具）给你的保护是提示词派永远给不了的；**grok-build 的 LLM 分类器派**是最具探索性的路线，值得作为"下一代权限系统"的研究样本。而 **OpenWorker 的"收件箱挂起派"是"无人值守 + 有人随时接管"这一新场景的答案**——它不追求把审批智能化，而是把审批做成一条**可持久、可从任意界面回答、重启不丢**的队列，让同一个跑法在"人在"和"人不在"之间无缝切换；配上"外部风险按精确 target 免问、shell 永远要问"的 §25 分级，它敢让一个 agent 无人值守地替你发 Slack、改日历，这是别家权限模型都没正面回答的问题。

```mermaid
quadrantChart
    title 权限路线象限：打扰成本 × 安全上限
    x-axis 低打扰 --> 高打扰
    y-axis 防呆（防误操作） --> 防恶意（防注入/越权）
    quadrant-1 重兵把守
    quadrant-2 严防死守但最烦
    quadrant-3 佛系
    quadrant-4 省心但防不住高手
    "结构安全派 CodeWhale": [0.35, 0.85]
    "沙箱派 nanobot/Raven": [0.15, 0.7]
    "弹窗派 Claude Code": [0.8, 0.75]
    "规则派 opencode/hermes": [0.5, 0.5]
    "goose SmartApprove": [0.45, 0.6]
    "收件箱挂起派 OpenWorker": [0.3, 0.68]
```

### 对决二：上下文工程的两种哲学——token 成本与信息保真

**各家怎么想**：上下文窗口是 agent 唯一的"工作台"，工具结果像快递一样不断堆上来，总要扔点什么。摘要丢弃派（六家含 nanobot）认为：**旧细节的价值会指数衰减，花 token 保真不值得**——浓缩成要点就够了，反正工作台腾出来了。无损派（Raven）认为：**被挤出去的事实恰恰是长任务的关键**——"目标、未决话题、已做决策"丢了，agent 就会重复劳动甚至前后矛盾，所以压缩应该是"归档 + 引用 + 蒸馏回注"，不是"扔掉"。

**解决方式对比**：摘要派内部又分三档精细度——最朴素的是 hermes/nanobot 的"辅助模型写纪要 + 微压缩"；中等的是 Claude Code 的"fork 分身 + 9 小节模板 + 413 被动抢救"与 goose 的"隐形标记 + 衔接台词"；最精细的是 opencode（尾部保留受 token 预算约束、可从回合中间切开、溢出型重放最后用户消息）和 CodeWhale（先零成本机械修剪、pin 工作集、失败宁可不压）。无损派只有 Raven 的 Curator：小模型只看 Manifest 索引出方案，落地由确定性代码执行并校验，出错还有完全不依赖 LLM 的 Fail-Safe——它连"小模型不可靠"这件事都防御了（而 Curator 正是对 nanobot 有损 Consolidator 路径的升级）。

**利与弊**：摘要派胜在**简单、便宜、久经考验**——各家的实现都已在大规模真实使用中打磨，缺点则是"摘要是有损的"：被压掉的文件路径、错误堆栈、中间结论，可能正是三小时后需要的东西；而且压缩本身打翻缓存（CodeWhale 因此对压缩格外吝啬）。无损派胜在**保真与可审计**（Archive 一个字不丢，Working State 可读可查），代价是**多花一个小模型的钱**、系统复杂度显著上升，且收益要在"足够长的任务"里才兑现——短会话里它的 Fast Path 零 LLM 调用，等于白背着这套复杂度。

**我们的评价（适合谁）**：**摘要派代表现实，无损派代表方向。** 今天的主战场（几十轮内的编码任务）里，精细版摘要（CodeWhale/opencode 那一档）已足够好、性价比无敌；而当 agent 走向"连跑几小时的无人值守任务"时，Raven 提出的问题会变成所有人的问题——各家其实都在向"无损"悄悄靠拢：Claude Code 的工具结果落盘本质上就是"归档 + 引用"的雏形。建议初学者先读懂摘要派（存量世界的标准答案；nanobot 的微压缩 + Dream 是轻量可读样本），再把 Curator 当作理解下一代上下文工程的最佳样本。

### 对决三：扩展生态的开放度——标准化与体验的取舍

**各家怎么想**：没有任何团队能写完世界需要的所有工具，所以问题变成：**第三方的能力以什么形态进来**。goose 的答案最纯粹：一切皆 MCP，连内置能力都走协议，整个生态只有一个插座标准。Claude Code 的答案最"产品经理"：MCP 管工具接入，但上层再做插件市场（git 仓库 + 稀疏克隆 + 官方市场），把"发现、安装、管理"的体验握在自己手里。hermes 的答案最"平台化"：一切皆插件——消息平台是插件、模型供应商是插件、看板是插件，核心只做循环。opencode 的答案最"程序员"：把 Hooks 开到底（连权限询问、请求参数、shell 环境都可挂），再兼容 Claude Code 的技能和记忆文件降低迁移成本。CodeWhale 把"兼容"做成策略本身（技能目录扫六家）。nanobot 用 entry_points + channel 插件 + ClawHub 保持核心小。Raven 则刻意收窄：pre-alpha 阶段只开放两类贡献点。

**利与弊**：一切皆 MCP（goose）架构最统一、生态天花板最高（全世界已有的 MCP 服务器即插即用），代价是**每个扩展一个进程**的开销与故障面——goose 自己也不得不发明 Builtin/Platform 两种"作弊"形态挽回性能。自有市场（Claude Code）体验最好、分发最强，但生态被绑定在单一厂商的治理之下。插件/Hooks 全开（hermes/opencode）灵活度最高，但插件 API 稳定性是长期负担——每开一类钩子，就是一份要维护十年的契约。技能文件（七家共识）是最聪明的折中：**不写代码、只占 token、渐进披露**，用最低成本吃到"用户自定义"的红利。

**我们的评价（适合谁）**：**标准化决定生态的下限，自有体系决定体验的上限。** 如果你是**普通用户**，Claude Code 的市场 + 技能是上手成本最低的；如果你是**企业/团队**，goose 的 MCP + recipe 组合最划算——recipe 让"最佳实践"可以像脚本一样被评审、分享、定时执行，这是七家里唯一把"工作流"做成可流通资产的设计；如果你是**开发者想做生态**，先写 MCP 服务器（一次投入、七家通用），再为特定宿主写插件（吃体验红利）。一句话：写 MCP 服务器是"存款"，写宿主插件是"理财"。

### 对决四：架构形态——终端程序 vs 本地基础设施

**各家怎么想**：这个问题的本质是"**你的 agent 是一个产品，还是一种基础设施**"。Claude Code 和 CodeWhale 回答"产品"：一个进程（或一对二进制）把体验做到极致，UI 与核心之间连协议都不需要。opencode 和 goose 回答"基础设施"：agent 是一台本地服务器，TUI/GUI/编辑器/CI 都只是客户端——opencode 甚至让同进程的 TUI 也走完整 HTTP 协议，理由是"本地单机和远程团队应该是同一份代码路径"。hermes、nanobot 和 Raven 的回答介于两者之间：核心是一台"大脑服务"，但客户端的主角不是编辑器，而是**消息平台**（Telegram/微信/飞书），agent 是你通讯录里的一员，不是你打开的工具——nanobot 用 MessageBus 解耦门市与后厂，Raven 再升级为 Spine。

**利与弊**：终端程序（单体）迭代最快、调试最直观、性能损耗最小，Claude Code 能把"ESC 一路通到网线的急刹车"做到这种精度，靠的就是全链路同进程；代价是"被集成"能力需要事后补课（SDK、ACP、bridge 都是后加的）。服务器/网关形态的红利是**一次建设、处处接入**：goose 的桌面端与 Zed 骑同一只鹅，nanobot / Raven 的十几个 IM 平台共用一条总线或一个 Spine。代价同样真实：协议要终身维护（Raven 光消息模型就 911 行）、进程管理多一层故障面（goose 要给自家 GUI 配 SECRET_KEY 防冒名接入）、单机体验反而可能输给单体。

**我们的评价（适合谁）**：这个选择几乎不可兼得，且**最好在第一天就想清楚**——opencode 的分析里那句"服务器即本体，协议即产品"之所以成立，是因为从第一个 commit 起就按协议写代码；事后从单体改造成服务器，等于重写。**做给个人开发者的极致工具**，学 Claude Code；**做给团队/生态的平台**，学 opencode 和 goose；**做常驻助理 / 消息网关**，学 hermes、**nanobot** 和 Raven 的网关思路（想读最轻量的 Python 核心优先 nanobot）。对初学者的一个观察：服务器化/网关化是大趋势，但单体的 Claude Code 依然是体验标杆——架构先进性和产品完成度，是两回事。

### 对决五：模型策略——锁定单一厂商做深 vs provider 无关做宽

**各家怎么想**：Claude Code 和 grok-build 同押"**模型的差异性值得深度利用**"：CC 的 ToolSearch 依赖 Anthropic 特有的 `tool_reference`、缓存作用域和计费头里应外合；grok-build 锁定 xAI Grok，多媒体工具（图片生成/编辑/视频）深绑 xAI 独有能力，Goal Mode 五件套正是围绕 Grok 的推理深度设计——这些优化在多 provider 架构里根本写不出来。七家开源项目则押注"**模型是零件，不是产品**"（CodeWhale 的 README 原话）：用户该有用脚投票的自由，agent 的价值在循环、工具与工程，不在绑定哪家大脑。

**解决方式对比**：做深的代表是 Claude Code（围绕 Anthropic API 每个特性做文章）和 grok-build（Goal Mode + 多媒体工具围绕 Grok 深度设计）；做宽的代表里，opencode 用 `provider/transform.ts` 两千多行吸收各家模型怪癖，hermes 用"按模型家族定制提示"化解差异（GPT 系给 patch 格式、Claude/Gemini 系给 replace 格式），goose 用 toolshim 给不支持原生工具调用的小模型发"拐杖"，CodeWhale 让 36 个 provider 跑同一套运行时和工具集，nanobot 用自研 ~40 家注册表直连官方 SDK（Raven 则外包给 LiteLLM），**OpenWorker 则直接建在吴恩达团队的 aisuite 上**——把"统一 provider + 每次调用按当前模型能力适配 PDF/视觉、会话中途换模型只改字段"整个外包给这层抽象，自己一行怪癖代码都不写。

**利与弊**：锁定做深的优化深度无可替代——prompt cache 工程、ToolSearch、模型 fallback 的丝滑切换，都是"只服务一家"才能打磨出来的；代价是**命运绑定**：供应商涨价、限流、模型换代，用户没有退路，而且对非本家模型用户是硬排斥。值得注意的是，"锁定做深"阵营现在是两家：Anthropic 方向的 CC 和 xAI 方向的 grok-build，各自把本家模型的独特能力用到了极致。做宽的用户自由和议价权最大（本地模型还能做到数据不出机），但要交两笔税：一是**适配税**（transform.ts / 自研注册表式的几千行怪癖代码，且永远写不完），二是**最小公分母妥协**——当某家 API 有独特能力时，多 provider 架构要么放弃它、要么写一堆条件分支，优化深度天然受限。

**我们的评价（适合谁）**：**如果你是 Claude 的重度用户，没什么可纠结的**——Claude Code 围绕你的模型把每个细节磨到了骨头里。**如果你是 Grok 重度用户且需要多媒体生成或数小时级长时自主任务**，grok-build 的 Goal Mode 是目前最成熟的显式状态机方案。**如果你对成本敏感、想自由组合（便宜模型打杂、强模型攻坚）、或必须私有化部署**，provider 无关是刚需：要工程严谨选 opencode，要本地模型选 hermes/CodeWhale/goose；想研究"多模型时代怎么写不憋屈的抽象层"，`transform.ts`、hermes 的模型家族定制提示、以及 nanobot 的自研注册表 vs Raven 的 LiteLLM 分野，都是最好的教材。长期看，"做宽"阵营里谁先解决"深度优化"与"provider 无关"的矛盾，谁就能吃到两边的红利。

---

## 六、给初学者的选型建议

读完九份分析和上面的对比，最后一个实际问题：**我该拿哪个项目怎么办？** 按五类需求分别回答（第五类是 OpenWorker 代表的"非编程"新场景）。

**想读懂 agent 原理，读谁的源码？** 推荐路线：**先 Claude Code 深入版**（主线最清晰，循环/权限/压缩三大件讲透了）→ **再 CodeWhale**（带 issue 编号的错题本，看生产级 agent 要处理多少脏问题）→ 按兴趣分流：看严谨读 opencode（边界处理密度无人能及），看现代设计读 Raven（Curator/Spine/双轨记忆），看协议读 goose（引擎即服务器 + 一切皆 MCP），**想读轻量 Python 核心 / Raven 上游出厂设置 → nanobot**（MessageBus + 显式状态机，核心可读）。**想看 Goal Mode 显式状态机与 Rust 重型工程 → grok-build**（GoalTracker/GoalPlanner/GoalWorker 三套源码是自主 agent 最好的教材）。hermes 的近 5500 行单函数循环是硬汉路线，放在最后。

**想日常写代码，用谁？** 有 Claude 订阅：直接用 **Claude Code**，体验标杆，没有平替。有 Grok 订阅 / 需要图片生成、视频生成、或数小时自主执行任务：**grok-build**（Goal Mode + 多媒体工具是独有能力）。想自由切换模型或控制成本：**opencode**（TUI 体验最精致、模型切换最顺滑）或 **CodeWhale**（Rust 单二进制、缓存遥测可见、自动路由省心）。想要图形界面和可分享的自动化工作流：**goose**（桌面端 + recipe + 定时任务）。想要一个住在 Telegram/微信里的常驻助手：**hermes**、**nanobot** 或 **Raven**（nanobot 更轻、更自持；Raven 记忆/主动性更重）。

**想二次开发（做自己的 agent 或插件），选谁？** 只想**扩展能力**：先写 MCP 服务器，八家通用、一次投入处处可用。想**深度定制宿主**：opencode（Hooks 门类最全、MIT 许可、Effect 架构适合长期演进）或 hermes（Python 上手快、一切皆插件）；想**先吃透轻量 Python 内核再 fork**，选 **nanobot**（设计铁律明确、MessageBus 好读）；想做**多 agent / 调度 / 记忆方向的研究**，Raven 的底座设计（Spine + 引擎插件化）是最清晰的起点，但要接受它 pre-alpha 的现实。想用 Rust 做重型工程：CodeWhale 和 goose 是两条现成的路；**想研究 Goal Mode 状态机与 ACP 协议**：grok-build（Apache-2.0，不接受 PR 但代码可读）。

**想要本地模型 / 数据不出机，选谁？** 三家都是一等公民：**hermes**（custom provider 接 vLLM/SGLang/llama.cpp，还有自家 Hermes 系列开放权重模型配套）、**CodeWhale**（vLLM/SGLang/Ollama 免 key，AGENTS.md 里直接给了内网部署命令）、**goose**（Ollama + 内置 llama.cpp/candle 运行时，`goose local-models` 一条龙下载模型）。nanobot 可通过 OpenAI 兼容端点接入本地服务，但不以本地模型运行时为主打。**OpenWorker 也把"本地优先 + Ollama 全本地"写进了产品定位**（数据只经你选的模型/集成出门），适合"要数字同事又要数据不出机"的人。Claude Code、opencode 和 grok-build 不以本地模型为主打，此场景不推荐。

**想要一个能无人值守、跨应用的"数字同事"（而不是编程工具），用谁？** 这是前八家都没正面回答的场景——答案是 **OpenWorker**。它不为写代码优化，而为"像同事一样把日常活儿干完"优化：接 Slack（@它就在桌面开会话干活、线程回你）、按 cron 定时跑（晨间简报/周报/盯频道）、40 个连接器（GitHub/Jira/Notion/Gmail/日历…）、动手前先问、跑完给成品文件、还能全本地。它最值得学的三样：**收件箱**（无人值守时把 ask 安全停下等你、重启不丢）、**持久恢复**（回合挂起后重启还能续）、**§25 标准规则**（敢让它无人值守替你发消息但绝不越权开 shell）。想读一个"通用任务 Agent 而非编程 Agent"的完整工程样本，它是目前开源里想得最全的一个。

```mermaid
flowchart TD
    Q1{"你的首要目标？"} --> A["读懂 agent 原理"]
    Q1 --> B["日常写代码"]
    Q1 --> C["二次开发"]
    Q1 --> D["本地 / 私有模型"]
    A --> A1["① Claude Code 深入版（主线最清晰）<br/>② CodeWhale（带 issue 编号的错题本）<br/>③ 按兴趣：opencode 严谨 / Raven 现代 / goose 协议 / nanobot 轻量 Python / grok-build Goal Mode"]
    B --> B1{"订阅类型？"}
    B1 -->|Claude 订阅| B2["Claude Code（体验标杆）"]
    B1 -->|Grok 订阅 / 多媒体 / 长时自主| B2b["grok-build（Goal Mode + 多媒体）"]
    B1 -->|没有或想多模型| B3["opencode / CodeWhale / goose（GUI）"]
    C --> C1{"做什么层面的开发？"}
    C1 --> C2["扩展能力：先写 MCP 服务器（七家通用）<br/>定制宿主：opencode（Hooks 全）/ hermes（Python 友好）<br/>轻量内核：nanobot；研究多 agent 与记忆：Raven"]
    D --> D1["hermes / CodeWhale / goose<br/>（vLLM / Ollama / llama.cpp 均为一等公民）"]
```

**如果只记住一件事**：九个项目各有一句话值得带走——

```mermaid
mindmap
  root((九家各记住一件事))
    Claude Code
      围绕单一模型，把循环/权限/缓存/生态每个细节磨到极致的教科书
    opencode
      服务器即本体，协议即产品；用 Effect 的严谨赌长期主义
    hermes-agent
      前缀缓存神圣；闭环学习不是口号而是代码路径
    Raven
      上下文不是消耗品，是被管理的资产
    CodeWhale
      安全是机制，不是承诺：模型根本没有越权的工具
    goose
      一切皆 MCP 扩展，引擎即服务器：agent 是本机基础设施
    nanobot
      核心保持小、能力在边缘；住在聊天软件里、可完全自持
    grok-build
      Goal Mode 五件套显式状态机让 agent 在数小时级长任务里自主坚持，Rust 单二进制 + ACP 协议是壁垒
    OpenWorker
      收件箱 + 持久恢复：能无人值守跑、要人时挂起等你、重启不丢——把审批做成一条可从任意界面回答的队列
```

而这九句话背后是同一句更大的话：**agent 的核心技术早已不是秘密——模型 + 工具 + 循环，人人都会写；拉开差距的，是围绕这颗心脏的那一圈系统：权限的闸门、上下文的资产管理、缓存的精打细算、记忆的时间尺度、生态的开放姿势。** 前八家在"编程"这条赛道上给出了八种答案，OpenWorker 则把同一套心跳搬到了"通用任务同事"的新赛道——证明这张工程地图不止属于编程 Agent。看懂了九家在这些问题上的不同答案，你就看懂了 2026 年 AI agent 工程的整张地图。

---

---

<h2 id="ch7">七、第十个样本：Open Design——当"宿主"坐上桌子</h2>

> 本章的事实依据是《[open-design-源码分析.md](./open-design-源码分析.md)》（18 章，基于 commit `c893b60`）。它之所以单开一章而不是并进前六章的表格，是因为**它和前九家不是同类可比对象**：前九家争"谁的 Agent 更好"，Open Design 问的是"**这些 Agent 已经很好了，怎么让它们一起干一件它们本来不擅长的事**"。

### 7.1 它究竟是什么：一句话与三个数字

Open Design（下称 OD）是 **Claude Design 的开源替身**。Anthropic 2026 年 4 月发布的 Claude Design 第一次让大模型直接交付设计成品，但它闭源、纯云、锁死在 Anthropic 的模型和技能上。OD 把同一套 agent-native 循环——**发现简报 → 锁定方向 → 流式产出 → 评审 → 交付**——变成一个**任何 CLI Agent 都能读懂的文件系统**。

| 数字 | 值 | 说明 |
|---|---|---|
| star / 时间 | **82.0k / 3 个月** | 2026-04-28 创建，本文写作时 82k star、9.5k fork |
| 仓库规模 | **11 745 文件 / 321.9 MB** | TS 2 341 · HTML 2 493 · MD 2 246 |
| 支持的 CLI | **多条运行时定义**（具体数量随版本变动，分析时 commit `c893b60` 的 `runtimes/defs/` 下含 8 个 .ts 定义文件） | `byok-opencode` 复用 OpenCode 可执行文件 |
| 自己实现的主循环 | **0** | 全部委托给被托管的 CLI |
| 提示词组装器 | **2 075 行** | 比运行时注册表（81 行）大 25 倍 |
| 审美 linter | **1 000 行 / 16 条规则** | 本系列独一份 |

### 7.2 一张表看清它和前九家的坐标差

| 维度 | 前九家（编程 / 任务 Agent） | **Open Design（宿主）** |
|---|---|---|
| **主循环** | 各自实现（8 000–20 000 行量级） | **不实现**，委托给 25 个 CLI |
| **工具系统** | 自己定义 20–74 个工具 | **零个**，用被托管 CLI 的原生工具 |
| **权限模型** | 从 CodeWhale 的 safe-by-construction 到 OpenWorker 的五档模式 + 收件箱 | **无执行层闸门**，全部委托 + 边界防御 |
| **上下文工程** | 压缩、裁剪、双轨记忆、Curator 无损 | **分层组装 + 按变化频率分带缓存**（20+ 层） |
| **质量保证** | 测试通过 / 编译通过 / 工具调用正确 | **审美 linter + 五陪审员评审剧场** |
| **扩展机制** | 插件 / MCP / 技能 | **四平面**：skills + design-templates + design-systems + craft |
| **交付物** | 代码变更 | **HTML / PDF / PPTX / MP4 真实文件** |
| **它与前九家的关系** | 互为竞品 | **把其中至少 5 家（claude / opencode / hermes / grok-build / deepseek）当成自己的执行引擎** |

> 🧠 **最有意思的一点**：本系列分析过的 **CodeWhale**（即 DeepSeek TUI，上游改名前）、**hermes-agent**、**opencode**、**grok-build** 全部出现在 OD 的 `runtimes/defs/` 目录里，各自是一个几百字节到几 KB 的对象字面量。**你读过的那些几万行的 Agent，在 OD 眼里就是一条 argv 构建规则加一个流格式标签。**

### 7.3 三个前九家都没有的设计

#### ① 适配器即数据：把「支持 N 个 CLI」的成本从 O(N) 压到 O(1)

前九家里凡是要接多个后端的（goose 的 52+ provider、nanobot 的 ~40 家自研注册表、Raven 的 LiteLLM），处理的都是**模型 provider**——协议高度同质（都是 chat-completions）。OD 要接的是**完整的 Agent CLI**：25 个不同的 argv 约定、7 种 stdout 格式、3 种会话续跑机制、各不相同的权限 flag。

它的解法是把适配器写成**纯数据对象**（`runtimes/types.ts:101-253`，~45 个字段）：

> An adapter is **not** a class that implements the agent loop. It is a **plain data object**… **There is no per-agent subclass and no `run()` / `cancel()` method to implement.**

45 个字段里**只有 `buildArgs` 一个函数，而且是纯的**（输入提示词和选项，输出 argv 数组）。探测、启动、调用、取消、解析全在共享引擎里。结果：

- 新增一条已知 wire format 的 CLI = **一个对象字面量 + registry 里一行**，最小的定义仅数百字节
- 九条 ACP 适配器共用同一个传输层
- 引擎里**零个 per-agent 分支**

**对照 goose 的"一切皆 MCP 扩展"**：goose 把所有能力统一成一种协议，代价是外部世界必须先适配 MCP；OD 反过来——**接受外部世界的异质性，用一层数据规格把差异吸收掉**。前者要求生态迁就自己，后者迁就生态。

#### ② 把「审美质量」变成程序化闸门

这是本系列**独一份**的能力。前九家的质量闸门都是**功能性**的：测试通过、编译通过、工具调用格式正确、P0 checklist 全过。OD 做的是"**这东西看起来像不像 AI 拉的**"：

**第一层 `lint-artifact.ts`（1 000 行 / 16 条规则）**——把主观判断落成可执行代码：

| 类别 | 具体到什么程度 |
|---|---|
| 默认 Tailwind indigo 当 accent | 精确到 **7 个 hex** |
| 两段式「信任渐变」 | 蓝色系 **13 个** × 青色系 **8 个** hex 的配对 |
| emoji 当功能图标 | **17 个** emoji × 4 种上下文 |
| ALL CAPS 字距不足 | 带 **CSS 变量递归求值（深度 4）+ 主题作用域解析 + 字号折算** |

最后一条最见功力：`letter-spacing: 1px` 在 12px 字上够（0.083em），在 48px 字上远远不够（0.021em），所以要把同规则里的 `font-size` 折算出来；`letter-spacing: var(--caps-tracking)` 要穿透 token 求值；亮色主题 0.08em、暗色主题被覆盖成 0.02em 也要分别判定。还有一条注释是正则工程的教科书案例——把字符类从 `[^}]*` 改成 `[^{}]*`（只匹配最内层规则），否则 `@media` 里的大写标题会整段漏检。

**第二层 Design Jury**——五位陪审员（Designer / Critic / Brand / Accessibility / Copy）加权合成，≥8.0/10 才发货，最多 3 轮。两个设计决策值得记：

- **Designer 权重为 0**：「their dimensions are aesthetic preferences rather than ship gates」——保留席位让定性意见进记录，但不让主观审美卡住发布；
- **五位陪审员 = 同一个 CLI 会话的五个回合**，不是五个进程——「prevents the "panelist disagrees with itself across processes" failure mode」，而且鉴权/环境/日志不用维护五份。

**而且两层都不硬阻断**：P0 命中不阻止落盘，评审不达标走 `ship_best`。这是「有闸门但不锁死用户」的成熟取舍。

#### ③ 提示词的缓存分区排序：把 hermes 的「前缀神圣」推进了一步

hermes-agent 的绝活是**前缀缓存神圣**——绝不在已缓存的前缀中间插入内容。OD 在此之上多做了三件事：

1. **按变化频率给每一段打标签**，分成四带：全局静态（设计师宪章）→ 会话稳定（模式/locale）→ 项目稳定（设计系统/技能/元数据）→ 回合可变（deck/media 信号触发块）；
2. **触发信号的稳定性决定块的位置**，不是内容的重要性。同一个「平台契约块」：由项目创建时固定的元数据触发 → 放项目稳定带；由对话文本触发（用户中途说「改成 iOS 版」）→ **必须推到回合可变后缀**，因为「an early insert would break the cached prefix for **every section after this line**」；
3. **缓存命中归因**：`describeStablePromptCache()` 在未命中时逐段 diff 出是哪一段漂移了，并且刻意**不在「无基线」的情况下报告全量变化**——那会淹没真正关心的信号。

### 7.4 它的代价：三处比前九家更重的取舍

#### 取舍一：主动放弃权限闸门（本系列唯一）

| 项目 | 执行层权限模型 |
|---|---|
| CodeWhale | safe by construction——模型根本没有越权的工具 |
| nanobot | bwrap 沙箱 |
| OpenWorker | 五档模式 + 收件箱挂起 + shell 防注入 |
| Claude Code | 权限闸门 + hooks |
| **Open Design** | **无**——给每个 CLI 喂最危险的非交互 flag |

OD 给子进程喂的是：`--permission-mode bypassPermissions`（Claude）· `--force`（Cursor）· `--permission-mode dangerous`（Devin）· `--yolo`（Qoder/Trae）· `--allow-all-tools`（Copilot）· `--auto`（DeepSeek）· `--dangerously-allow-all`（Amp）。

**理由是自洽的**：daemon 在**没有 TTY** 的环境里跑它们，交互式批准提示会直接把 run 挂死。**文档也坦率**：「The effective project cwd is **an execution root, not a uniform Open Design sandbox**… users must treat these runs as **trusted agent execution**.」

它的安全预算全花在**边界**上（loopback 默认 + SSRF 精确主机 allowlist + 桌面导入的短寿命单次 HMAC + 沙箱 iframe + `.od-skills` 真实拷贝而非软链），而不是**执行**上。**对个人本地使用合理；对团队共享部署需要非常认真的评估。**

#### 取舍二：两份提示词组装器必须逐字节同步

daemon 侧 `prompts/system.ts`（2 075 行）和 contracts 侧（1 064 行）是**两份独立实现的同一套语义**——前者服务本地 CLI 执行，后者服务 BYOK 直连。仓库里至少六处注释在提醒「keep both in sync」「keep this whole block **BYTE-IDENTICAL**」。这是已知且被显式管理的技术债，但它是真实的。

#### 取舍三：内容库的体量本身就是维护负担

151 个设计系统包 × 每包至少 3 个文件、164 个技能、115 个模板、277 个官方插件、183 个示例——2 246 个 Markdown、2 493 个 HTML、322 MB。质量守卫写了很多（`pnpm guard`、`pnpm lint:craft`、包质量守卫、派生文件一致性、token 契约、组件 fixture、来源证据、预览覆盖），但**内容腐烂的速度和内容增长的速度是同一个量级**。

### 7.5 更新后的定位象限

```mermaid
quadrantChart
    title 十大 Agent 定位象限（Open Design 独占「宿主」区）
    x-axis 锁定单一厂商 --> provider 无关
    y-axis 终端程序 --> 本地基础设施
    quadrant-1 开放平台型
    quadrant-2 深度绑定的基础设施
    quadrant-3 单一厂商的极致工具
    quadrant-4 开放的终端工具
    "Claude Code": [0.12, 0.18]
    "grok-build": [0.10, 0.25]
    "CodeWhale": [0.72, 0.22]
    "opencode": [0.85, 0.68]
    "hermes-agent": [0.80, 0.78]
    "Raven": [0.68, 0.82]
    "nanobot": [0.75, 0.72]
    "goose": [0.90, 0.88]
    "OpenWorker": [0.82, 0.58]
    "Open Design": [0.96, 0.94]
```

> Open Design 落在最右上角，而且它的「provider 无关」是**另一个量级**的无关：其余九家无关的是**模型**，OD 无关的是**整个 Agent**。

### 7.6 第六场思路对决：「造引擎」还是「造插座」

前面第五章讲了五场对决（权限路线 / 上下文哲学 / 生态开放度 / 架构形态 / 模型策略）。Open Design 加进来之后出现了第六场，而且它比前五场都更根本。

| | **造引擎派**（前九家） | **造插座派**（Open Design） |
|---|---|---|
| **主张** | Agent 循环是产品的核心，必须自己掌控 | 循环已是成熟商品，护城河在它之外 |
| **代表** | Claude Code · goose · hermes · CodeWhale · OpenWorker | Open Design |
| **优势** | 完全控制推理质量、权限、成本、体验一致性 | 边际成本极低；用户已有的最强 Agent 就是你的引擎；精力全花在领域知识上 |
| **劣势** | 你的循环大概率不如全职团队做一年的那个；每个 provider/协议变化都要跟 | **控制不了它怎么想**；权限模型没了；要处理 N 种 stdout；用户没装 CLI 就跑不起来 |
| **护城河在哪** | 循环本身 + 生态 | **内容库 + 提示词工程 + 质量闸门** |
| **适合谁** | 核心竞争力就是推理编排；面向不懂技术的普通用户 | 产品价值在**内容和体验**（设计/写作/报表）；用户已经有 CLI Agent |

**判断这两条路谁对，看一个数字就够了**：OD 的适配器层只有约 3 000 行，提示词组装器 2 000 行 + 审美 linter 1 000 行 + 151 个品牌包 + 11 份工艺规则 + 330 行行为脚本。**它 80% 的工程量花在了「什么是好设计」上，只有 20% 花在「怎么调 CLI」上。**如果你的领域也有这样一份"难以言传但可以编码"的专业知识，造插座就是对的；如果没有，你造出来的只是一个壳。

### 7.7 给初学者的补充建议

第六章按人群给了九家的选型建议。加上 OD 之后补三条：

- **你想做的是"内容/体验型"AI 产品（设计、写作、数据报表、PPT）** → 先读 Open Design。它会告诉你：**不要从写主循环开始**。
- **你已经在做一个编程 Agent，想知道怎么让它被别人集成** → 读 OD 的 `runtimes/defs/` 目录，看看**别人是怎么描述你的**。你的 CLI 如果不支持 stdin 投递提示词、不吐结构化流、没有稳定的会话续跑标识，在这套体系里就是「最难伺候的那一条」（参见 DeepSeek 适配器的三重 Windows 命令行守卫）。
- **你在评估要不要给团队部署一个设计 Agent** → 先读第 7.4 节的取舍一。OD 是本系列**唯一没有执行层权限闸门**的项目。

---

---

<h2 id="ch8">八、第十一个样本：MiMo Code——同一副骨架，小米焊了什么</h2>

> 事实依据《[MiMo-Code-源码分析.md](./MiMo-Code-源码分析.md)》（19 章，commit `076b790`）。**它是本文第二个分析对象 opencode 的深度 fork**，所以这一章的写法和第七章不同：第七章讲的是"另一个物种"，这一章讲的是**同一副骨架上的增量**——可比性最高，也最能看出"一个团队认为编程 Agent 缺什么"。

### 8.1 身世：读 fork 要读差分

| 证据 | 说明 |
|---|---|
| 核心包名 | `packages/opencode`（**至今未改**） |
| 根 `package.json` 的 `name` | `"opencode"` |
| `LICENSE` | **Copyright 2026 MiMo Code, Xiaomi** 与 **Copyright 2025 opencode** 两行并列 |
| 继承的部分 | Effect 服务层 · SolidJS TUI · SQLite+Drizzle · provider 抽象 · ACP 协议 · server 模式 |

本系列里有先例：**Raven 的 Python 运行时 fork 自 nanobot**，当时的读法就是"对照上游看后加改造"。MiMo × opencode 是同一个模式，**只是改造幅度大得多**——大到值得一份 19 章的单独分析。

规模：**5 222 文件 / 83.8 MB**，12.5k star / 1.27k fork / **838 open issue**，仓库 2026-06-10 创建（**一个半月**）。许可 **MIT + 一份独立的 `USE_RESTRICTIONS.md`**。

### 8.2 它的主题：不信任模型的自我报告

本系列每家都有一句能概括的主题。MiMo 的这句尤其清晰，因为它被做成了**四个层次、六道装置**：

```mermaid
flowchart TB
    subgraph S["步级（每一步都查）"]
        A1["空参数工具调用检测<br/>软→硬 2 级"]
        A2["重复动作签名<br/>键序归一化 + 排除叙述文本"]
        A3["文本复读 + n-gram<br/>剥开场白后比对"]
    end
    subgraph T["回合级"]
        B1["步数上限：禁工具 + 强制交代"]
    end
    subgraph C["会话级"]
        C1["Goal 独立裁判<br/>temperature=0 · 只读 transcript"]
        C2["Try-Best 检测器"]
    end
    subgraph D["产物级"]
        D1["checkpoint-validator + retry"]
    end
    S --> T --> C --> D
```

**和其余十家的纵深对比**：

| 项目 | 死循环 / 假性完成的防护层数 |
|---|---|
| 多数项目 | **1–2 层**（通常只有步数上限，或加一个重复检测） |
| Claude Code | 步数上限 + 权限闸门 |
| openworker | 中断不留孤儿 + 收件箱挂起 |
| Open Design | 审美 linter + 五陪审评审（**但那是质量闸门，不是防自欺**） |
| **MiMo Code** | **步级 3 道 + 回合级 1 道 + 会话级 2 道 + 产物级 1 道 = 7 道** |

### 8.3 三个前十家都没有的设计

#### ① Goal 独立裁判：把「验收」和「施工」用两次模型调用彻底分开

`/goal` 设一个停止条件后，主循环**拒绝停止**，直到一个**独立裁判模型**读完 transcript 判定条件已满足（或确实不可能）。

> The judge is a separate model call that **only reads the transcript** — it does not do the work, **so its verdict stays cold relative to the working agent's optimism**. <span>（`session/goal.ts:17-21`）</span>

裁判提示词里最关键的一句：

> the assistant claiming the goal is impossible is **evidence, not proof**; **independently confirm** the condition is genuinely unachievable rather than deferring to the assistant's self-assessment.

**没有这句，模型只要说一句"这做不到"就能让裁判放行，整个机制就废了。**

配套四道逃生口：**fail-open**（「a flaky judge can never trap the user」）· **`impossible`** · **`MAX_GOAL_REACT = 12`** · **只对 main agent 生效**。

> 🔍 **和 Open Design 的 Design Jury 正好相反**：OD 的五陪审员是**同一会话的五个回合**（共享上下文以保持一致性、省鉴权与日志）；MiMo 的裁判是**独立调用、故意不共享**——因为要的就是"冷"。**同一个问题，两个方向相反的正确答案。**

#### ② 把「按模型调形」从雏形推到极致（**继承 + 大幅扩张**）

> ⚠️ **归属必须先说清楚**：**分家族提示词路由和 GPT 工具门控的判定表达式都来自上游 OpenCode**——把它们当成小米首创是最常见的误读。逐项证据见《MiMo-Code-源码分析.md》第 20 章（基于两边源码的**真实文件级 diff**）。

| 维度 | 上游 OpenCode `62e4641` | **MiMo Code `076b790`** |
|---|---|---|
| 提示词家族 | **10 份**（含同结构 `if` 链路由） | **20 份**（含 3 个 .ts 代码文件） |
| 其中字节级未改 | — | **5 份**（gemini 15372=15372 · kimi 8695=8695 · codex 7390=7390 · trinity 差 1B · copilot-gpt-5 差 2B） |
| 三份主力提示词 | gpt 9.3 KB · default 8.5 KB · anthropic 8.2 KB | **25.4 KB（2.7x）· 20.8 KB（2.4x）· 14.3 KB（1.7x）** |
| 路由输入 | 只用 `model.api.id` | **双 ID 兜底** |
| GPT 门控判定式 | `registry.ts:292-296` 内联变量 `usePatch` | **一字不差**，但抽成 `tool/gpt.ts` 的命名函数 |
| GPT 门控作用域 | `apply_patch` ↔ `edit`/`write` **二选一** | **整套 ABI 替换**（再门控 `exec`/`view_image`，隐藏 read/grep/glob/multiedit/notebook_edit） |

**小米的实质贡献是"把这条路走到上游没走到的地方"**：加 5 个家族、重写命中率最高的 3 份、加双 ID 兜底（因为它的模型接入路径比上游多得多）、把门控从"换一把螺丝刀"扩成"换一整个工具箱"。

理由是**对齐训练分布**：Codex 系模型在 OpenAI 自家 harness 里就是用这套 ABI 训练的，给它 Claude 风格的工具反而更容易调错。

**这是对"通用 harness"假设的正面否定**：

| 主张 | 代表 |
|---|---|
| 写一套好提示词让所有模型都能用 | 其余十家 |
| **每个模型的训练分布不同，harness 就该为它调形** | **MiMo Code**（口号 "Models and Agents Co-Evolve" 就是这个意思） |

代价文档自己承认了：提示词路由和工具路由是**两套独立的字符串规则**，「尚未统一成模型能力协商层」。实测 `gpt-oss-120b` 就会出现**提示词走 GPT、工具面却不是 GPT ABI** 的不一致。

#### ③ 沙箱换引擎：acorn 手写解释器 → QuickJS WASM（**重写**）

> ⚠️ **"在受限沙箱里跑脚本编排工具"这个想法也来自上游**（`tool/code-mode.ts`，工具名 `execute`）。但**实现被整个换掉了**，而这次替换本身才是值得记的工程决策：

| 维度 | 上游 `@opencode-ai/codemode` | **MiMo** |
|---|---|---|
| **隔离实现** | **acorn + 3 465 行手写 AST 解释器** | **QuickJS-emscripten（真 WASM 引擎）** |
| **暴露什么** | **只有 MCP 工具** | **宿主工具**（late-bound registry） |
| 已知代价 | 要自己实现 JS 语义，覆盖面有洞 | 句柄不释放会**硬崩进程** |

**为什么值得换**：手写 JS 解释器是无底洞——每处没实现到的是行为差异，每处实现错的可能是逃逸口，而且 3 465 行要长期维护。换真引擎后，风险从**「语义正确性」**转成**「资源管理」**，后者有确定的检查清单，前者没有。

而暴露面从 MCP 工具扩到宿主工具，**权限风险抬高一级**——所以才倒逼出下面这一整套配套设计：

| 约束 | 做法 |
|---|---|
| **能组合** | 模型提交 async function body，`tools.<name>()` 调宿主工具 |
| **不放大权限** | **late-bound registry**——拿到和外层完全相同的、已过滤的工具表；**控制流工具（task/actor/skill/workflow/cron/session）被排除**，因为它们"改变对话或调度状态，不适合隐藏在一次脚本调用中" |
| **不误杀正常脚本** | **活跃计算计时**——等宿主工具返回的时间不计入 60 秒预算，wall clock 30 分钟单独兜底 |
| **不崩进程** | 每个 `QuickJSHandle`（**含未结算的 deferred**）必须在 context dispose 前释放，否则硬 abort |

而且边界说得很清楚：**「QuickJS 只隔离 `exec` 代码。`bash` 仍是真实 Shell，不是容器 sandbox。」**

### 8.4 值得单独记的几个工程细节

这份仓库的注释密度很高，几个细节的普适价值超出项目本身：

| 细节 | 洞察 |
|---|---|
| **空步检测刻意不抓"空终端"** | 「An empty terminal is a **natural turn end**, not a spin」——早期版本抓它，**误报到不可用**。检测器划清边界比多抓一类更重要 |
| **`stableStringify` 按排序键序列化** | 模型「routinely re-emit the same arguments with **keys in a different order**」，不归一化就**漏掉真实的循环** |
| **签名刻意排除文本与 reasoning** | 「the model **narrates each step in slightly different words while taking the exact same action**」——算进去就**掩盖**了要抓的重复动作 |
| **文本归一化剥掉 "Let me/I'll/I will"** | 复读时开场白会随机切换，不剥就看不出后面是同一句 |
| **环境块的日期锚到会话创建时间** | 「including ones that **cross midnight**」——用请求时间会让跨午夜的会话突然缓存失效 |
| **BM25 用相对地板而非绝对阈值** | 「BM25 magnitudes are **corpus-size-dependent**… any fixed absolute floor would **wrongly wipe real hits**」；**第一名永远保留** |
| **checkpoint 截断修代理对** | `slice()` 按 UTF-16 码元切会劈开代理对，**emoji 变乱码** |
| **去抖窗口对齐 checkpoint 边界** | 「The boundary only advances **when a checkpoint/rebuild actually discards context**」——不是拍脑袋的消息数 |
| **never-worse 守门** | 任何"优化"管线都该有"没变好就回退原样"的兜底，**最坏情况等于不优化，而不是等于搞砸** |
| **`Object.create(null)` 做注册表** | 键可能来自模型输出时，防原型链污染是必须的 |
| **FORCED_ASK** | 「the intent to perform an irreversible action must be **recorded in-band**, not inherited from a broad blanket rule」——通配符 allow 压不住删除 |

### 8.5 更新后的定位象限

```mermaid
quadrantChart
    title 十一大 Agent 定位象限
    x-axis 锁定单一厂商 --> provider 无关
    y-axis 终端程序 --> 本地基础设施
    quadrant-1 开放平台型
    quadrant-2 深度绑定的基础设施
    quadrant-3 单一厂商的极致工具
    quadrant-4 开放的终端工具
    "Claude Code": [0.12, 0.18]
    "grok-build": [0.10, 0.25]
    "CodeWhale": [0.72, 0.22]
    "MiMo Code": [0.78, 0.42]
    "opencode": [0.85, 0.68]
    "hermes-agent": [0.80, 0.78]
    "Raven": [0.68, 0.82]
    "nanobot": [0.75, 0.72]
    "goose": [0.90, 0.88]
    "OpenWorker": [0.82, 0.58]
    "Open Design": [0.96, 0.94]
```

> MiMo Code 落在 opencode 的**左下方**：同样 provider 无关，但**产品重心明显更靠"终端程序"**。
>
> **这个位移有硬证据**（基于 commit `076b790` vs 上游 `62e4641` 的文件级 diff，具体数字可能因版本检测方式不同而有小幅偏差）：MiMo 大幅削减了 `server/` 和 `acp/` 的代码量，同时显著扩充了 `cli/`（含 i18n）。**砍掉的服务器野心，加倍投在了终端体验上。**

### 8.6 第七场思路对决：「通用 harness」还是「一模型一形」

| | **通用 harness 派** | **按模型调形派**（MiMo Code） |
|---|---|---|
| **主张** | 一套提示词 + 一套工具，覆盖所有模型 | 每个模型家族一份提示词，强家族还换工具面 |
| **代表** | 其余十家 | **MiMo Code** |
| **优势** | 维护面小；新模型接入零成本；行为一致好排查 | **贴合训练分布，工具调用可靠性明显更高**；能针对模型弱点单独下药 |
| **劣势** | 弱模型被强模型的提示词拖累，反之亦然 | **20 份要各自维护**；路由是字符串 `if`，**提示词与工具两套规则可能不一致** |
| **适合谁** | 模型池小、或以一两个强模型为主 | **模型池大、且真的会有人用弱模型**（MiMo 支持 MiMo/GPT/Claude/Gemini/DeepSeek/Kimi/GLM/MiniMax/Trinity……） |

**判断依据**：如果你的用户只会用 1–2 个前沿模型，通用 harness 够了；**如果你要认真支持十来个能力参差的家族，"一套提示词打天下"就是在给最弱的那个陪葬。**MiMo 支持的模型家族数量决定了它必须走第二条路。

> 📌 **注意这场对决的双方其实是"上游 vs 下游"**：OpenCode 已经开了分家族的头（10 份），但它的重心在服务器与协议；MiMo 把同一条路推到 20 份、并扩到工具面。**同一个仓库血脉，两个团队在同一个问题上投入的力度差了一个量级**——这比拿它和无关项目对比更能说明"按模型调形"的边际收益在哪。

### 8.7 三处取舍

| 取舍 | 说明 |
|---|---|
| **`session/prompt.ts` 单文件 4 595 行 / 208 KB** | 主循环 + 提示词组装 + goal gate + 各种闸门 + 结构化输出全在一个文件。对比 Open Design 的 `system.ts`（2 075 行，**只是组装**）。注释密度很高，但**关注点太多** |
| **实验功能默认关闭** | Orchestrator、Token Efficient 都靠 flag 门控且默认关——**设计精良的能力大部分用户不会开**，而且 flag 分支让代码路径组合爆炸 |
| **fork 的双重成本** | 上游演进要持续合并（包名至今未改）；Effect 的阅读门槛是上游带来的；遗留文件（`default.old.txt`）留在树里 |
| **一次"反重构"** | 上游把 LLM 请求构造/原生运行时/工具装配拆在 `session/llm/` 四五个文件里（共 33 KB）+ `session/tools.ts`（23 KB）；**MiMo 把它们全删掉、收拢进 `prompt.ts`**。合的好处是控制流一眼看得完，坏处就是 4 595 行单文件。**在别人的骨架上塞新机制，集中改一处比先重构上游分层快得多——但这笔债会滚** |

### 8.8 一个有意思的生态关系

- **Open Design** 把 `mimo` 当引擎（它是 OD 26 条适配器之一，`streamFormat: json-event-stream`，MCP 注入走 `MIMOCODE_CONFIG_CONTENT`）
- **MiMo Code** 的 26 个内置技能里有 `claude-code`、`codex`、`grok-build`——**它又能把这些外部 CLI 当工具调**（且只在对应可执行文件已安装时才暴露）
- **MiMo 的记忆索引**可以配置纳入 `~/.claude/projects`——**读 Claude Code 的项目记忆**；workflow 和技能也扫 `.claude/` 目录

> 🧠 **这个生态里没有绝对的宿主和客体。**每家都在把别家当工具，同时被别家当工具。

---

## 九、第十二个样本 MiroFish：当 Agent 不再干活，而是演化

> 事实依据：《[MiroFish 源码分析](MiroFish-源码分析.md)》（`666ghj/MiroFish`，commit `60757b3`，2026-07-23），AGPL-3.0，盛大集团孵化。

**前面十一章都建立在一个共同前提上：Agent 是用来干活的。**这一章要拆掉这个前提。

### 9.1 它不属于前十一家的任何一类

| 维度 | 前十一家 | **MiroFish** |
|---|---|---|
| **核心问题** | 怎么让 AI 替我干活 | **怎么让一群 AI 替我把事情预演一遍** |
| **Agent 数量** | 1 个主 + 少量子 agent | **成百上千个，平等互动** |
| **Agent 的目的** | 完成任务 | **表现得像那个人** |
| **成功标准** | 任务做对了 | **演化出的宏观现象像真的** |
| **循环形态** | ReAct（想 → 做 → 观察） | **社会演化**（发帖 → 被推荐 → 被互动 → 影响他人） |
| **记忆** | Agent 自己读写 | **一整个世界共同书写的时序图谱** |
| **交付物** | 代码 / 文件 / 设计稿 | **一份报告 + 一个可以走进去对话的世界** |
| **护城河** | 工程复杂度 | **领域常识参数 + 产品点子** |
| **许可** | 多为 MIT / Apache | **AGPL-3.0**（十二家里唯一） |

**一组说明问题的数字**：8 个月、**69 573 star**、真实业务代码只有 **24 428 行 Python**。**每千行代码约 1 550 颗星**——Open Design 约 20，MiMo Code 约 3。

> 🧠 它的价值**不在代码量上**。这是全系列第一个「**点子的价值远大于工程价值**」的样本。

### 9.2 三件前十一家都没做过的事

#### ① 知识图谱 ↔ 模拟的双向闭环

前十一家的记忆系统（openworker 的 SQLite 事实、MiMo 的 FTS5、Raven 的 EverOS 双轨、hermes 的闭环学习）都是**单向**的：Agent 写记忆、Agent 读记忆。

MiroFish 的图谱**被一整个模拟世界共同书写**：

```mermaid
flowchart LR
    A["种子材料"] -->|本体+摄取| G[("Zep 时序图谱")]
    G -->|实体→人设| S["OASIS 模拟"]
    S -->|活动带帖子原文回写| G
    G -->|GraphRAG 检索| R["报告"]
    S -->|采访活着的 Agent| R
```

两条回边是关键。`zep_graph_memory_updater.py` 把模拟活动**带着帖子原文**批量写回图谱（`BATCH_SIZE=5`，按平台各自计数，`MAX_EPISODE_CHARS=9_500`）。没有这条回边，报告 Agent 只能读到「模拟前的世界」。

#### ② 采访还活着的 Agent

`zep_tools.py:1270` 的 `interview_agents` docstring 写着：

> 需要获取模拟 Agent 的**真实回答（非 LLM 模拟）**

这条能力由三样东西共同支撑：**跑完不关门**（`run_parallel_simulation.py` 明写「完成模拟后不立即关闭环境，进入等待命令模式」）+ **文件系统 IPC 采访通道**（`simulation_ipc.py`）+ **LLM 选人和拟题**（`:1549` / `:1632`）。

> 📌 别的多 Agent 项目跑完就把日志丢给 LLM 总结；MiroFish 让报告 Agent **回头去问当事人**。
>
> **这是把一次性的过程变成可持续查询的资产**——它把产品从「预测报告生成器」变成了「可探索的平行世界」。

#### ③ 把社会科学参数当成产品核心配置

前十一家的「配置」都是**工程参数**：超时、并发、重试、token 预算、权限档位。

MiroFish 的核心配置是**社会学参数**：

| 参数 | 值 | 对应现象 |
|---|---|---|
| 中国作息活跃度曲线 | 峰 ×1.5 / 夜 ×0.5 / 工作 ×0.7 / 晨 ×0.4 / **凌晨 ×0.05** | 舆情有没有「节奏感」 |
| 病毒传播阈值 | Twitter **10** vs Reddit **15** | 信息破圈的临界点 |
| 回声室效应强度 | Twitter 0.5 vs Reddit **0.6** | 观点极化、信息茧房 |
| 推荐权重三元组 | Twitter 新鲜度 0.4 / Reddit 热度 0.4 | 平台算法决定谁看到什么 |

**注意后三行是分平台两套取值。**这五个数字把「Twitter 快而浅、Reddit 慢而深且更抱团」这个人人都有直觉但很难量化的判断，变成了可以驱动模拟的参数——而且方向全对。

> 🔑 **抄它的 24 000 行代码很容易，抄不走「凌晨活跃度应该设 0.05、22 点到 23 点应该有一级中间台阶」这类经验。**

### 9.3 它把「外包」这件事做到了极致

| 最难的部分 | 谁做的 |
|---|---|
| 社会仿真引擎（平台环境 / 动作空间 / 推荐算法） | **OASIS**（camel-ai） |
| 时序知识图谱记忆 | **Zep Cloud** |
| 语言模型 | 任意 OpenAI 兼容 API |
| **剩下的粘合层 + 产品化** | **自己写，24 428 行** |

`requirements.txt` **全文只有 12 个包**。没有 LangChain（虽然 docstring 这么写）、没有向量数据库、没有消息队列（用文件系统 IPC）、没有 ORM。

对比 Open Design 的做法很有意思——**两者都是「不自己造引擎」，但方向相反**：

| | Open Design | MiroFish |
|---|---|---|
| 外包什么 | **主循环**（把 25 个编程 CLI 当引擎） | **仿真与记忆**（OASIS + Zep） |
| 自己写什么 | 提示词工程、质量执法、评审剧场 | 本体、人设、参数、编排、闭环 |
| 结果 | 一行主循环都没写 | **24 000 行撑起 6.9 万星** |

> 🧠 **共同的教训**：先验证点子，再考虑替换依赖。如果 MiroFish 团队先花半年自研仿真引擎和图数据库，很可能到今天还没发布。

### 9.4 第八场思路对决：「一个很能干的 Agent」还是「一千个很像人的 Agent」

| | 前十一家：**能力上限派** | MiroFish：**涌现行为派** |
|---|---|---|
| **优化目标** | 让单个 Agent 更强、更不容易跑偏 | 让**群体**演化出可信的宏观现象 |
| **典型手段** | 独立裁判、死循环闸门、上下文压缩、权限闸门 | 人设保真度、平台机制参数、时间节奏 |
| **失败的样子** | Agent 干错了活、卡死、烧钱 | 演化结果**不像真的**（曲线太平、极化太快） |
| **验证方式** | 跑任务看产出对不对 | **没有标准答案**——只能看是否符合领域直觉 |
| **代价** | 复杂度堆在一个循环里 | **无法证伪**；成本随 Agent 数 × 轮数爆炸 |

**MiroFish 这一派的软肋非常明确：无法验证。**编程 Agent 写的代码能跑测试，设计 Agent 的产出能过 linter，而「武汉大学舆情会怎么走」没有 ground truth。它的说服力只能来自**过程的可解释性**——你能看到谁在第几轮说了什么、信息怎么扩散、哪个节点是转折点，而不是一个黑箱结论。

> 这也解释了为什么「**跑完不关门 + 能采访**」不是锦上添花，而是**这条路线的必需品**：无法验证结果，就必须让过程可被追问。

### 9.5 更新后的定位象限（换一组轴）

前面几章的象限轴（provider 锁定度 / 终端 vs 基础设施）对 MiroFish 完全不适用。换两个轴才放得下它：

```mermaid
quadrantChart
    title 十二大 Agent：Agent 数量 × 成功标准
    x-axis "少数 Agent，各司其职" --> "大量 Agent，平等互动"
    y-axis "追求任务正确" --> "追求现象逼真"
    quadrant-1 "群体智能 / 仿真"
    quadrant-2 "创作与生成"
    quadrant-3 "编码助手 / 工具型"
    quadrant-4 "编排与工作流"
    "Claude Code": [0.16, 0.14]
    "MiMo Code": [0.22, 0.11]
    "opencode": [0.13, 0.09]
    "grok-build": [0.11, 0.13]
    "goose": [0.19, 0.17]
    "CodeWhale": [0.14, 0.08]
    "hermes-agent": [0.24, 0.19]
    "nanobot": [0.21, 0.16]
    "Raven": [0.26, 0.28]
    "OpenWorker": [0.30, 0.22]
    "Open Design": [0.38, 0.64]
    "MiroFish": [0.90, 0.92]
```

> **右上角只有 MiroFish 一个。**这不是说它更好，是说**它在回答另一个问题**。
>
> 十一个项目挤在左下角互相比谁的 Agent 更能干——这本身就是一个信号：**「很多个平庸 Agent 的涌现行为」可能是一片没人认真做过的空地。**

### 9.6 它的代价：比前十一家更松的几处

| 代价 | 说明 |
|---|---|
| **只能用 Zep Cloud，且强制** | `config.py:71-72` 明确拒绝 `ZEP_API_URL`。数据必须出境、服务挂了产品不可用、对政务金融客户几乎致命 |
| **成本完全没有护栏** | 数百 Agent × 数十轮 × 每轮 LLM 调用。唯一的保护是 README `.env` 示例里一行注释：`# High consumption, try simulations with fewer than 40 rounds first`。对比 MiMo Code 把计费档位编码进 `/context-limit` |
| **无持久化数据库** | `models/` 只有 dataclass + JSON 落盘。无并发控制、无事务、多实例会打架 |
| **单文件过大** | `Step4Report.vue` 5 162 行 · `api/simulation.py` 2 878 行 · `report_agent.py` 2 619 行（塞了 8 个类） |
| **一处静默的数据破坏** | `_to_pascal_case` / `_to_upper_snake_case` 的 `[^a-zA-Z0-9]` 只认 ASCII：`武汉大学` → `Unknown`。多个中文类型在 `entity_types` 字典里互相覆盖，**不抛异常不打日志**。唯一的防线是提示词里那句 `MUST be in English`——而它就拼在「请使用中文回答」后面 |

### 9.7 它有一处做得比前十一家都干净：全栈 i18n

这是一个和主题无关、但值得单独抄走的实现。

`locales/languages.json` 是一张**语言注册表**（7 种，每种带 `label` 和 **`llmInstruction`**）。前端 `i18n/index.js`（27 行）用 Vite glob 取**注册表 ∩ 实际翻译文件**的交集，所以切换器只显示真正有翻译的语言，**不会出现「选了没用」**。而后端 `utils/locale.py:8` 一路 `'..','..','..'` 跳出 backend，**读的是同一个目录**。

`llmInstruction` 被注入 **7 个提示词构造点**——**而每一个注入点后面都紧跟一条「但这些字段必须是英文」的例外**：

```
IMPORTANT: The 'stance' field value MUST be one of the English strings:
'supportive', 'opposing', 'neutral', 'observer'.
Only natural language text fields should use the specified language.
```

> 🔑 **多语言 LLM 应用最容易踩、也最难查的坑**：让模型「用法语回答」，它会把 `stance: "supportive"` 写成 `"favorable"`，下游 `if stance == 'supportive'` 直接失配——**而且只在切到非默认语言时才出现**。
>
> **纪律**：自然语言字段跟随用户语言，**枚举值、类型名、字段名永远英文**。MiroFish 在每一个注入点都写了这条例外，一个没漏。
>
> 一张 JSON 注册表同时驱动前端菜单 + 后端日志 + LLM 输出语言 + 缺失回退，**总共不到 80 行代码**。

### 9.8 给读到这里的人的一句话

前十一个项目告诉你**怎么把一个 Agent 做好**。MiroFish 提出的问题是：

> **如果不用一个很能干的 Agent，而是用一千个很像人的 Agent，能做什么？**

答案是：**你可以在事情发生之前，先把它跑一遍。**

这个答案对不对，8 个月 6.9 万星给了一个初步的市场判断。但它真正的启发在于——**当所有人都在优化单个 Agent 的能力上限时，赛道旁边可能还有一整片没人站的空地。**

---

> **写作说明**：第一、二、五、六章的事实依据本仓库九份单项目分析文档（Claude Code 深入版、`项目分析/`下八份，含新增的 grok-build-源码分析与 openworker-源码分析），行号引用为转引自这些文档的标注。**第三、四章的技术细节是在此基础上对各源码仓库做的二次深挖核对**（覆盖语言/运行时、流式管线、持久化、并发取消、错误处理、商业模式六个轴），新增行号可回到对应 commit 核对：Claude Code（`claude-code/src/`）、opencode（`62e4641`）、hermes-agent（`d9165d7`）、Raven（`85c7a5b`）、CodeWhale（`88a158e`）、goose（`d17d65f`）、nanobot（`6a9157f`）、grok-build（xai-org/grok-build）、**OpenWorker（`andrewyng/openworker`，`db93d75`，包名 `coworker`）**。凡二次核对中"未找到/不确定"的地方，本文一律不写成确定结论或如实标注，未做任何脑补。**特别说明：OpenWorker 是"通用任务同事"而非编程 Agent，本文将它作为跨界样本并入，编程语境下的部分维度（如工具"数量级"、prompt cache"工程化"）对它的可比性弱于其余八家，已在对应处标注其不同侧重。** **第七章的事实依据是《open-design-源码分析.md》（`nexu-io/open-design`，commit `c893b60`，2026-07-28）；第八章的事实依据是《MiMo-Code-源码分析.md》（`XiaomiMiMo/MiMo-Code`，commit `076b790`，2026-07-27）；第九章的事实依据是《MiroFish-源码分析.md》（`666ghj/MiroFish`，commit `60757b3`，2026-07-23）。三份文档均已逐文件核对并标注 `文件:行号`。**特别说明：MiroFish 不是编程 Agent，也不是"干活"型 Agent，前六章的多数维度对它不可比，因此第九章另起了一组对比轴，并未强行并入第一章速览总表。**项目迭代很快，行号在新版本会漂移，但模块边界与设计原则稳定。
