# DeepSeek-Reasonix 源码分析：一条「成本焦虑」如何长成「架构宪法」

> **分析对象**：[esengine/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)（产品名 **Reasonix**）  
> **基线 commit**：`627b051`（`Merge pull request #7368 … remote-workbench-reconnect-hardening`，默认分支 **`main-v2`**）  
> **许可**：MIT（`LICENSE`）  
> **代码规模（基线）**：**1686** 个 `.go` 文件；`internal/` 非测试 **216,079** 行；全仓含测试 **553,005** 行  
> **产品一句话**：面向终端的 **DeepSeek 原生** coding agent——**配置 + 插件驱动的极薄 harness**，单一静态 Go 二进制（`CGO_ENABLED=0`），**围绕 DeepSeek 自动前缀缓存调优**，让长会话 token 成本可控。  
> **读者对象**：已读过本系列 Claude Code / Open Design / Hermes / Codex / OpenAI4S 至少一份分析的产品经理与工程师。  
> **重写说明**：本版按「**动机 → 约束 → 被否方案 → 选择 → 代价**」五段式重排。先回答“开发者为什么做、脑子里那根主轴是什么”，再进代码；每处关键设计都尽量说清“不这么做会怎样”。  
> **本地基线路径**：`参考项目/DeepSeek-Reasonix @ 627b051`  

---

## 目录

**Part I · 起点：开发者为什么做这个东西**  
1. [痛点与洞察：成本焦虑撞上现成的省钱机制](#ch1)  
2. [产品位与动机证据：README / REASONIX / SPEC 里的原话](#ch2)  
3. [仓库地图与技术栈：数字形状与依赖方向](#ch3)  

**Part II · 思维导图：一条约束如何推出十项设计**  
4. [开发者思维导图：从「前缀必须稳定」到十项设计](#ch4)  
5. [Cache-first 宪法：稳定前缀 vs turn tail](#ch5)  
6. [双模型为何必须分 session](#ch6)  

**Part III · 实现：从约束到代码**  
7. [主循环：Agent.Run → runToolLoop](#ch7)  
8. [Compose：把易变内容赶进 turn tail](#ch8)  
9. [压缩三阶梯：soft → snip → summary（明码标价的缓存重置）](#ch9)  
10. [工具 ABI：builtin + MCP + use_capability 固定代理](#ch10)  
11. [权限、沙箱、工作区围栏：应用层规则 × OS 围栏](#ch11)  
12. [Checkpoint / Delivery / Goal：交付证据与完成协议](#ch12)  
13. [control.Controller：一张脸接所有前端](#ch13)  
14. [记忆、指令、扩展、桌面/ACP](#ch14)  

**Part IV · 品味与边界**  
15. [十项决策五段式复盘](#ch15)  
16. [横向对比（编程 Agent 桌）](#ch16)  
17. [诚实边界](#ch17)  
18. [源码导览索引与本地复现](#ch18)  

---

# Part I · 起点：开发者为什么做这个东西

<h2 id="ch1">第 1 章 痛点与洞察：成本焦虑撞上现成的省钱机制</h2>

### 1.1 先回答「为什么」

读 Reasonix 的源码之前，先读它的 README。中文 README 开篇没有讲“我们的 Agent 多聪明”，而是先把产品位钉在成本上（`README.zh-CN.md:42-43`）：

> 面向终端的 DeepSeek 原生 AI coding agent。  
> 由配置与插件驱动的极薄 harness——单一静态 Go 二进制，围绕 DeepSeek 的前缀缓存调优，长会话也能把 token 成本压低。

这不是营销话术，而是整套架构的出发点：

1. **痛点**：长会话里 token 成本随轮次线性增长；DeepSeek 的定价让“一直聊下去”变贵。
2. **现成机制**：DeepSeek 自动做前缀缓存——只要请求前缀字节稳定，命中的部分就能打折。
3. **洞察**：省钱的钥匙不是“少调用模型”，而是“**让前缀一直命中**”。于是 cache 从「优化项」升格为「架构约束」。

```mermaid
flowchart LR
    P["痛点：长会话 token 成本高"] --> I["洞察：DeepSeek 自动前缀缓存<br/>前缀稳定 = 命中 = 省钱"]
    I --> C["约束：系统前缀必须字节稳定"]
    C --> D["推论：易变内容外移 turn tail<br/>工具 schema 稳定 · 压缩明码标价 · miss 可诊断"]
```

### 1.2 三个可验证的工程主张

| 主张 | 在代码里长什么样 |
|---|---|
| **DeepSeek 原生** | Provider 层保留 `reasoning_content` 往返；缺推理内容时静默重试（`agent.go:1388-1394` 的 `observeMissingToolCallReasoning`） |
| **Cache-first** | 系统前缀（base + tools + memory）跨 turn **字节稳定**；易变内容走 `control.Compose` 的 turn tail（`REASONIX.md:14-16`，`control/input.go:181-183`） |
| **薄 harness + 单二进制** | `docs/SPEC.md` 契约：核心只认接口；`CGO_ENABLED=0`；能力靠 config / plugin / `init()` 自注册 |

### 1.3 这条约束被写成了「宪法」，而不是注释

仓库自己的 standing instructions（`REASONIX.md:14-16`）：

```text
- Cache-first: the system-prompt prefix (base prompt + tools + memory) must stay
  byte-stable across turns so DeepSeek's automatic prefix cache stays warm. Never
  mutate it mid-session — ride the turn tail instead (see `control.Compose`).
```

甚至 PR 模板强制要求 `Cache-impact` / `Cache-guard` 元数据（`REASONIX.md:64-71`）。  
本系列里只有 Hermes 的「前缀缓存神圣」能对上劲；Reasonix 的差别是把它焊进了 **Go 运行时诊断 + CI 脚本**，让“不许打破缓存”成为可执行的协作纪律。

---

<h2 id="ch2">第 2 章 产品位与动机证据：README / REASONIX / SPEC 里的原话</h2>

### 2.1 动机证据表

| 证据 | 出处 | 它证明了什么 |
|---|---|---|
| 「围绕 DeepSeek 的前缀缓存调优，长会话也能把 token 成本压低」 | `README.zh-CN.md:42-43` | 成本是产品出发点 |
| 「must stay byte-stable… Never mutate it mid-session」 | `REASONIX.md:14-16` | 约束用宪法口吻写 |
| 「switching models inside one shared conversation would break the prefix and tank cache hits, so we don't」 | `docs/SPEC.md:238-240` | 连双模型都被约束反推成独立 session |
| Compaction 是「cache-reset point」，cache hit rate 是「key observability signal」 | `docs/SPEC.md:328-329` | 压缩是明码标价的缓存重置 |
| PR 强制 `Cache-impact` / `Cache-guard` | `REASONIX.md:64-71` | 约束焊进协作流程 |
| 「Memory added mid-session rides the turn (never the cached system prefix)」 | `internal/control/input.go:181-183` | 每个实现点都在解释为什么 |
| Economy 档面向「成本敏感任务」 | `docs/COLLABORATION_MODES.zh-CN.md:86,136` | 产品档位按成本切 |

### 2.2 产品经理视角：它在卖什么体验

| 卖点 | 工程落点 |
|---|---|
| 长会话更省 token | 稳定前缀 → DeepSeek 自动 prefix cache；`PrefixShape` / `CompareShape` 解释 cache miss（`agent/cache_shape.go`） |
| 装完就能用 | 单静态二进制；npm 包 `reasonix`；`reasonix.example.toml` |
| 多前端同一套行为 | `control.Controller` 背后挂 TUI / HTTP-SSE / Wails desktop / ACP（`REASONIX.md:11-13`） |
| 可验证交付 | Delivery Profile：`complete_step` 证据签收 + readiness 门禁（`docs/GOAL_ENFORCEMENT.zh-CN.md`） |
| 协作模式可切换 | Economy / Balanced / Delivery；可选 `planner_model` 双模型（`docs/COLLABORATION_MODES.zh-CN.md`） |

> 🧠 **一句话**：别家在优化「工具调用多聪明」；Reasonix 在优化「**同一次会话里，前缀到底能不能一直命中缓存**」——并把这件事当成不能违约的产品契约。

---

<h2 id="ch3">第 3 章 仓库地图与技术栈：数字形状与依赖方向</h2>

### 3.1 数字化的项目形状（基线 627b051）

| 指标 | 量级 |
|---|---|
| Go 源文件 | **1686** 个 `.go` |
| `internal/` 非测试 | **216,079** 行 |
| 全仓含测试 | **553,005** 行 |
| 巨文件心脏 | `control/controller.go` 6,560 · `cli/chat_tui.go` 5,154 · `agent/agent.go` 4,362 |
| 内置工具契约 | `docs/TOOL_CONTRACT.md`（compile-time builtins + 全量 boot 表面，由测试守护） |

`docs/SPEC.md` 给出的布局契约：

```text
cmd/reasonix/main.go          # 入口；blank-import providers + builtin tools
internal/
  cli/                        # 子命令、装配、退出码
  control/                    # 传输无关 Controller（所有前端的背后）
  agent/                      # Session + harness 循环 + compact + cache shape
  provider/                   # Provider 接口 + openai/anthropic/responses
  tool/ + tool/builtin/       # Tool 接口 + init() 自注册
  permission/                 # allow / ask / deny + bash 分解
  sandbox/                    # OS 沙箱封装
  memory/ · instruction/      # 背景事实 vs standing instructions
  plugin/ · mcp* / capability # MCP / 能力代理
  checkpoint/ · evidence/     # 变更回滚与交付证据
  acp/ · bot/ · desktop*      # 编辑器协议、机器人、桌面
```

依赖方向（`docs/SPEC.md:54-56`）：`cli → {agent, plugin, config} → {tool, provider}`；父包不 import 自注册子包。

### 3.2 技术栈

| 层 | 技术 | 位置 |
|---|---|---|
| 语言 / 分发 | Go，`CGO_ENABLED=0` 单静态二进制 | `Makefile`、`cmd/reasonix` |
| 配置 | TOML（`BurntSushi/toml`） | `internal/config`、`reasonix.example.toml` |
| TUI | Bubble Tea 系全屏聊天 | `internal/cli/chat_tui.go` |
| 桌面 | Wails（Go 后端 + 前端） | `desktop/` |
| 协议 | ACP、HTTP/SSE `serve`、MCP stdio JSON-RPC | `internal/acp`、`internal/control`、`internal/plugin` |
| Provider | OpenAI 兼容 / Anthropic / Responses | `internal/provider/*` blank-import |

入口极薄（`cmd/reasonix/main.go`）：

```go
// Blank imports wire compile-time built-ins into their registries.
_ "reasonix/internal/provider/anthropic"
_ "reasonix/internal/provider/openai"
_ "reasonix/internal/provider/responses"
_ "reasonix/internal/tool/builtin"
```

### 3.3 与 Claude Code / Hermes / Codex 的关系

| 维度 | Claude Code | Hermes Agent | OpenAI Codex | **Reasonix** |
|---|---|---|---|---|
| 语言形态 | TypeScript（闭源） | Python 大脑 + TS 脸 | Rust workspace | **Go 单二进制** |
| 模型策略 | 绑 Anthropic | 模型无关 + 学习闭环 | 绑 OpenAI 栈 | **DeepSeek 优先，OpenAI 兼容可插** |
| 上下文哲学 | 强提示词工程 | 前缀缓存神圣 + 记忆写盘 | 两层 turn + 压缩 | **Cache-first 写进宪法 + 运行时 shape 诊断** |
| 前端 | 终端为主 | CLI/TUI/桌面/20+ IM | CLI + app-server | **Controller 统一：TUI / serve / Wails / ACP** |
| 权限 | 弹窗 + 规则 | 环境隔离多后端 | AskForApproval × OS 沙箱 | **Policy 规则 + bash 分解 + sandbox + workspace confine** |
| 完成判定 | 自然结束 + 工具 | 技能/记忆闭环 | 三方停机协议 | **自然结束 + maxSteps/todo stall；Delivery 证据签收** |

> 若只记一句：**Reasonix = Hermes 的 cache 哲学 × Codex 的工程硬度 × Claude Code 的终端 coding 品类，焊在 DeepSeek 的自动前缀缓存上。**

---

# Part II · 思维导图：一条约束如何推出十项设计

<h2 id="ch4">第 4 章 开发者思维导图：从「前缀必须稳定」到十项设计</h2>

> 这一章是整篇的“地图”：先看开发者脑子里那根主轴，再进代码。下面十项都能从「前缀必须字节稳定」推出；每一项也都有明确的代价。

| # | 推论 | 选择 | 如果不这么做会怎样 |
|---|---|---|---|
| 1 | 系统前缀必须稳定 | 易变上下文进 turn tail，不进 system（`control.Compose`） | 每轮插话/记忆更新都会打冷整段前缀 |
| 2 | 工具 schema 也是前缀 | schema 排序后再哈希（`normalizeToolSchemas`） | 工具注册顺序抖动造成“假 miss” |
| 3 | 插话不可避免 | Steer 明确付一次 cache 税（`run_loop.go:276-283`） | 假装零成本插话，缓存统计失真 |
| 4 | 上下文终会增长 | 压缩分 soft/snip/summary，压缩 = cache-reset point | 无节制 summary 频繁重置缓存 |
| 5 | MCP 库存会变 | `use_capability` 固定代理隔离动态 schema | 装一个 MCP 工具就冷一次缓存 |
| 6 | bash 语义必须精确 | 运行时只读降级 `BashCommandIsReadOnly` | schema 稳定但语义模糊，误伤并行/证据 |
| 7 | DeepSeek 协议有怪癖 | Missing reasoning 静默重试一次 | thinking 模式下工具轮直接失败 |
| 8 | 跑满预算不该算崩溃 | maxSteps → Pause 而非 Fatal | 长任务在预算边界丢会话资产 |
| 9 | 多前端要一致 | 行为进 `control.Controller` 不进脸 | 每个前端各修各的，行为漂移 |
| 10 | 约束要可执行 | SPEC / TOOL_CONTRACT / 测试同源 + PR 元数据 | 宪法只是 wiki 摆设 |

---

<h2 id="ch5">第 5 章 Cache-first 宪法：稳定前缀 vs turn tail</h2>

### 5.1 两条轨道

| 轨道 | 内容 | 规则 |
|---|---|---|
| **Cache-stable prefix** | base system + tool schemas + standing memory/instructions | 会话内**禁止**字节级漂移 |
| **Turn tail（Compose）** | 用户正文、goal 块、plan marker、语言偏好、`<memory-update>`、background-jobs、hook context、retrieval recall | 每轮可变；**绝不能**回写进 prefix |

`Controller.Compose`（`control/input.go:137-212`）把这件事写进注释（`input.go:181-183`）：

```text
Memory added mid-session rides the turn (never the cached system prefix),
so it takes effect now without invalidating the prompt cache. It folds into
the system prefix on the next session, where it costs nothing per turn.
```

装配顺序（概念上）：

```mermaid
flowchart TB
    IN["用户 text"] --> G{"有 running goal?"}
    G -->|是| GB["activeGoalBlock + autoResearch"]
    G -->|否| P
    GB --> P{"planMode?"}
    P -->|是| PM["PlanModeMarker"]
    P -->|否| L
    PM --> L["response/reasoning language 包装"]
    L --> M["drainPending → memory-update"]
    M --> J["background-jobs note"]
    J --> H["hook context"]
    H --> R["memory.recall Block（真实 user turn）"]
    R --> OUT["交给 Agent.Run"]
```

这与 Open Design「按缓存频率分带装配 system prompt」、Hermes「记忆写盘立即生效、下个会话才进提示词」是同一哲学族。Reasonix 的独特处是：把 miss **诊断成结构化事件**，而不是只靠文档告诫。

### 5.2 `PrefixShape`：为什么 miss？

```13:48:参考项目/DeepSeek-Reasonix/internal/agent/cache_shape.go
// PrefixShape hashes the portions of the request prefix that influence
// provider-side prompt-cache reuse. Comparing snapshots across turns
// lets us explain *why* a cache miss happened.
type PrefixShape struct {
	SystemHash        string
	ToolsHash         string
	PrefixHash        string
	LogRewriteVersion int
	ToolSchemaTokens  int
}
```

`CaptureShape` 会对 tool schema **排序后再哈希**（`normalizeToolSchemas`），避免“顺序抖动”假 miss。`CompareShape` 给出 `system` / `tools` / `log_rewrite` 原因列表，并带上 usage 里的 cache hit/miss tokens。

发往 provider 前还会剥掉会打冷缓存的 UI 元数据：`LocalOnly` 显示记录不进 `ModelMessages`（`provider.go` 的 `LocalOnly` 标记），墙钟与取消半截流都不会污染前缀。

### 5.3 与 MCP 动态工具的张力

动态 `mcp__*` 工具一旦进主 Registry，就会改 tools 哈希 → 冷缓存。Reasonix 的解法（`docs/TOOL_CONTRACT.md:45-96`）：

- Delivery / Planner / 多数 sub-agent：暴露**固定名**代理工具 `use_capability`（list / inspect / call / decline）；
- **按需连 MCP 不改变**该代理的 provider-visible schema；
- Balanced 的 Executor **刻意保留**直接 `mcp__*`（接受可能的前缀变化）——这是有意识的产品取舍，不是疏漏。

> 🧠 **承重墙**：Cache-first 不是「尽量少改 prompt」，而是「**哪些变化允许付 cache miss 的税**」被显式分类：steer 付税、memory-update 不碰 prefix、MCP 库存变化用代理隔离。

---

<h2 id="ch6">第 6 章 双模型为何必须分 session</h2>

### 6.1 动机

想要“先规划后执行”的成本/质量平衡，但模型切换会破坏前缀。

### 6.2 约束与选择

`docs/SPEC.md:238-240` 把话挑明：

> switching models inside one shared conversation would break the prefix and tank cache hits, so we don't.

配置了 `planner_model` 时，**Planner 与 Executor 各有独立 session**（`agent/coordinator.go`）：两套前缀各自 cache-stable，会话永不混写。这与 UI 上的 Plan Mode（turn-tail marker）不是同一层。

### 6.3 代价

- 双份前缀、双份工具面，首轮成本更高；
- 规划期发现的能力必须在 handoff 后仍可直接 call（`use_capability` 的固定代理正好承担这件事）；
- 路由更复杂：`executor_only` | `plan_and_execute` | `plan_for_approval` | `plan_only`（`planner_route.go`）。

---

# Part III · 实现：从约束到代码

<h2 id="ch7">第 7 章 主循环：Agent.Run → runToolLoop</h2>

### 7.1 生命周期入口

`Agent.Run` 自己说得很清楚（注释始于 `agent.go:1310`，函数体 `1319-1378`）：

1. 解析本轮 `maxSteps`（可被 context 覆盖）；
2. 开 workspace lease / steer 队列 / background evidence 提交 defer；
3. `interceptAgentStart`（extension 可 abort）；
4. `beginRunTurn` 初始化状态；
5. **`return a.runToolLoop(ctx, state)`**。

没有神秘的「框架基类」——就是显式状态机，策略拆在 `beginRunTurn` / `handleFinalResponse` / `handleToolRound`。

### 7.2 `runToolLoop`：一轮里发生什么

核心循环在 `run_loop.go:274-355`：

```mermaid
sequenceDiagram
    participant U as User / Controller
    participant A as Agent.runToolLoop
    participant P as Provider.Stream
    participant T as executeOne(s)

    U->>A: Run(input)
    loop step = 0..maxSteps
        A->>A: consumeSteer?（写入 session，可接受一次 cache miss）
        A->>A: CaptureShape(system+tools)
        A->>P: streamWithMissingReasoningRecovery
        P-->>A: text + reasoning + tool_calls + usage
        A->>A: CompareShape → cache diagnostics
        alt 无 tool_calls
            A->>A: handleFinalResponse（含 maybeCompact）
        else 有 tool_calls
            A->>T: handleToolRound → 并行只读 / 串行写入
            T-->>A: tool results 写入 session
        end
    end
    A-->>U: maxStepsPause 或 error 或 nil
```

几个味道很重的细节：

| 细节 | 含义 | 位置 |
|---|---|---|
| **Steer** | 中途插话进队列；消费时写入 user 消息并带引导前缀；注释承认「一次 cache miss 不可避免」 | `run_loop.go:276-283` |
| **Missing reasoning recovery** | DeepSeek thinking 模式下 tool call 缺 `reasoning_content` → 同请求静默重放至多一次 | `run_loop.go:357-364`，`agent.go:1388-1394` |
| **Stream recovery** | 中断流最多 `maxStreamRecoveries=3`，且 **不消耗 maxSteps**（`step--`） | `run_loop.go:300-309` |
| **Cache diagnostics** | 每轮 `CompareShape` 对比 system/tools/log_rewrite | `run_loop.go:285-294` |
| **暂停而非崩溃** | `maxStepsPause` / `todoStallPause`：工作已在 session，用户再发一条即可续 | `agent.go:1469-1494` |
| **Delivery readiness 失败** | 终答前宿主校验 todo/criteria/verify/signoff → `FinalReadinessError`，开下一轮 | `handleFinalResponse` |
| **空终答 / thinking-only** | 最多 `maxEmptyFinalBlocks` 次回催 | `run_loop.go`，`agent.go:42` |
| **Recovery grace** | recovery episode 耗尽后 summarize-only 一圈，再 `RecoveryPauseError` | recovery 路径 |

### 7.3 装配入口：`boot.Build` → 同一 Runner

| 模式 | 触发 | Turn API |
|---|---|---|
| TUI | 裸 `reasonix` / `chat` | 异步 `Controller.SendWithRaw` |
| Headless | `reasonix run` / `-p` | 同步跑一轮；Ask 对 writer **fail closed**，需 `--auto`/`-y` |
| ACP | `reasonix acp` | 阻塞 `Controller.RunTurn`（`session/prompt`） |
| serve / desktop / bot | 各自入口 | SSE / Wails / IM → 同一 Controller |

`boot.Build`（`internal/boot`）一次性冻结 system prefix（output style、环境探针快照、memory、skill 索引），再把可选 `planner_model` 包成 `Coordinator`。

### 7.4 单工具执行管线

`executeOne`（`execute_one.go:65-101`）是固定五段：

1. **`parseToolCall`** — Resolve、歧义 MCP 名、重复成功/失败 loop guard、stale-anchor 编辑拦截；bash 可按参数降级为只读（`permission.BashCommandIsReadOnly`）；
2. **`interceptToolBefore`** — extension 可改写调用；
3. **`resolveToolPolicy`** — 权限门禁；
4. **`prepareToolExecution`** — 预览、mutation 记账、parent write 锁；
5. **`finishToolExecution`** — 真正执行 + 回执。

只读工具可并行，写入串行——与 Claude Code / OpenAI4S 的「只读波次」同族，但落地在 Go 的 mutation observer / evidence ledger 上。

---

<h2 id="ch8">第 8 章 Compose：把易变内容赶进 turn tail</h2>

### 8.1 动机

系统前缀要字节稳定，但 goal、plan、语言偏好、记忆更新、检索结果每轮都可能变。

### 8.2 约束

凡是不稳定、不必要时不出现的内容，都不能进 cache-stable prefix。

### 8.3 被否方案

- “把所有动态内容都拼进 system prompt”：省事但每轮冷缓存，直接违背宪法；
- “动态内容完全不进上下文”：goal/plan/记忆更新无法即时生效。

### 8.4 选择

`control.Compose`（`control/input.go:137-212`）在真实 user turn 前按固定顺序装配：activeGoalBlock → PlanModeMarker → 语言包装 → `<memory-update>` → background-jobs → hook context → `memory.recall` Block。

### 8.5 代价

- 每轮多一次装配与剥离（`StripComposePrefixes`）；
- 若前端/扩展绕过 `Compose` 直接发消息，缓存宪法会被破坏——所以所有前端都走同一 Controller。

---

<h2 id="ch9">第 9 章 压缩三阶梯：soft → snip → summary（明码标价的缓存重置）</h2>

`compact.go:20-37` 把策略钉死：

| 阶段 | 默认阈值（占 context window） | 做什么 |
|---|---|---|
| Soft | 0.5 | 报告上下文在涨，**仍保 cache-stable prefix** |
| Tool-result snip | 0.6 | 廉价改写陈旧 tool result |
| Summary compact | 0.8（force 0.9） | 摘要折叠；保留固定 token 的 recent tail（默认 16384） |

设计要点：

- **触发用比例，保留用绝对 token 预算**——大窗口不会过度压缩，小窗口也不会卡在阈值附近反复 compact；
- Summary 用结构化标题（Standing facts / Goal / Decisions / Files / Commands / Errors / Pending），包在 `<compaction-summary>` 里；
- Ablation 开关可关掉 cache 友好策略做对照实验（`internal/ablation`）；
- Compact 会 bump `LogRewriteVersion` → `CompareShape` 会标 `log_rewrite`——**诚实承认这是 cache 重置点**。

与 Raven「无损归档」、Claude Code「摘要丢弃」相比：Reasonix 明确站在 **「先廉价 snip，再付一次 summary 的 cache 税」** 这一边。

---

<h2 id="ch10">第 10 章 工具 ABI：builtin + MCP + use_capability 固定代理</h2>

### 10.1 Compile-time builtins（契约表）

`docs/TOOL_CONTRACT.md` 由测试守护（`TestBuiltinToolContractDocumentation`），与运行时 registry 同源。核心面：

| 类别 | 工具 |
|---|---|
| 读 | `read_file` `grep` `glob` `ls` `code_index` `web_fetch` |
| 写 | `write_file` `edit_file` `multi_edit` `move_file` `delete_range` `delete_symbol` `notebook_edit` |
| Shell | `bash` + `bash_output` / `wait` / `kill_shell` |
| 任务/交付 | `todo_write` `complete_step` `update_goal` |

实现侧全部 `init() { tool.RegisterBuiltin(...) }`（如 `builtin/readfile.go:27`）。

### 10.2 全量 boot 表面 vs Economy

默认 full-token boot 还挂 session / memory / skill / subagent / LSP / install / slash 等：`ask`、`task`/`fleet`/`parallel_tasks`、`memory`/`remember`/`forget`、`run_skill`、LSP 四件套、`explore`/`research`/`review`/`security_review` 等（`docs/TOOL_CONTRACT.md`）。

Delivery 另加：

- **`use_capability`**：稳定 MCP 代理；
- **`review_report`**：中高风险变更的结构化 review；
- Host 侧 evidence：verification / diff / files / manual，无证据的 `complete_step` **直接拒绝**。

**Economy** 刻意只暴露瘦面（约 `ask`/`bash`/`read_file`/`write_file`/`edit_file`/后台三件套 + **`connect_tool_source`**），其余按需挂载——用「一次看不全工具」换 token 与前缀稳定。

### 10.3 Tool 接口味道

工具带 `ReadOnly`、预览（`Previewer`）、图像、PlanMode 分类等能力位——权限与并行调度读的是这些位，而不是靠模型「自觉」。Checkpoint 只跟踪带 `Previewer` 的编辑工具（见第 12 章）。

---

<h2 id="ch11">第 11 章 权限、沙箱、工作区围栏：应用层规则 × OS 围栏</h2>

### 11.1 Permission Policy + Ask / Auto / Yolo

`permission.Policy`（`permission.go`）：

- 规则形态对齐 Claude Code：`Tool` / `Tool(glob)` / legacy `Tool=literal`（`ParseRule`）；
- `Decide(toolName, readOnly, args)`；bash 走**分段分解**（`DecomposeBashCommand`）；
- `BashCommandIsReadOnly`：把「schema 上可写的 bash」在具体 argv 上降成只读（影响并行、mutation、evidence）；
- `BashSubjectRequiresExplicitApproval`：间接执行、危险 git 等强制显式批准。

产品审批模式（`docs/TOOL_APPROVAL_MODES.md`）与 collaboration mode（normal/plan/goal）**正交**：

| 模式 | 行为要点 |
|---|---|
| **Ask** | 写入默认询问；headless `reasonix run` 对 writer **fail closed** |
| **Auto** | 普通 writer 自动放行；`deny`/`ask` 规则、Plan 确认、嵌套/间接 Bash、MCP destructive、敏感 `remember`/`forget` 仍要问 |
| **Yolo** | 唯一可绕过嵌套 Bash 人工门槛；**仍不绕过** `deny` 与 sandbox |

另有 `internal/guardian`：独立安全评审子会话（带 denial circuit breaker）——**不能**代替记忆写入的用户确认。Hooks（`internal/hook`）提供 `PreToolUse`/`PostToolUse`/`PostToolUseFailure`，并做 Claude 工具名兼容映射。

### 11.2 Sandbox + confine

`builtin/bash.go` 把命令包进 `sandbox.Command`：

| 平台 | Bash OS sandbox |
|---|---|
| macOS | `sandbox-exec`（Seatbelt） |
| Linux | `bwrap` |
| **Windows** | **产品固定 off**，命令 unconfined；文件类 builtins 仍有 workspace confine |

`enforce` 但无后端时 **fail closed**（不裸跑）。escape 走 `sandbox.EscapeApprover`。MCP stdio 默认不继承 Bash sandbox（`internal/plugin/plugin.go` 的产品默认是 host 进程，注释写明原因）。

`SECURITY.md` 把边界写清楚：workspace 围栏、权限、沙箱、密钥、HTTP serve 的 localhost/CORS、桌面/bot 隔离、updater 校验——以及什么不算漏洞。

### 11.3 Checkpoint / Rewind

对齐 Claude Code Esc-Esc / `/rewind` 叙事（`docs/CHECKPOINTS.md`）：

- **机制**：文件快照（**非 git**）；sidecar `<session-id>.ckpt/`；
- **跟踪范围**：带 `Previewer` 的编辑工具（`write_file` / `edit_file` / `multi_edit`）；
- **不跟踪**：`bash` 副作用；`move_file` 尚未实现 Previewer；
- **API**：`Controller.Checkpoints` / `Rewind` / `PrepareRewind`+`CommitRewind`（含 coverage confirmation）；
- **明确不做**：git-backed rollback（文档标 out of scope）。

---

<h2 id="ch12">第 12 章 Checkpoint / Delivery / Goal：交付证据与完成协议</h2>

### 12.1 正交三轴

`docs/GOAL_ENFORCEMENT.zh-CN.md` 开篇：

> Goal 是唯一的跨 turn 调度器，Delivery 是纯质量门禁，工具权限与沙箱不受 Goal 开关影响。

| 轴 | 职责 |
|---|---|
| **Goal**（`/goal`） | 跨 turn 推进、预算、pause/resume |
| **Delivery** | readiness：todo / criteria / verification / review / signoff / capability… |
| **Permission / Sandbox** | 始终独立 |

模型通过 `update_goal(continue|complete|blocked)` 申报意图；宿主用**结构化 `ReadinessResult`**（`agent.go:1497+`）决定是否真完成。宣称 `complete` 但缺证据 → 开启下一轮，而不是信模型嘴硬。

### 12.2 `complete_step`

内置工具描述写得很凶（`TOOL_CONTRACT.md`）：completion **必须**带 evidence；host 代为推进 todo。这与 grok-build Goal Mode、MiMo「不信任自我报告」同谱——Reasonix 用 **host-observed receipts** 落地。

### 12.3 协作 Profile

（`docs/COLLABORATION_MODES.zh-CN.md`）

| Profile | 要点 |
|---|---|
| Economy | 单模型，较瘦工具面，面向成本敏感任务 |
| Balanced | 完整工具面；可选独立 `planner_model` |
| Delivery | Balanced + `use_capability` + 验收合约 + review 门禁 |

---

<h2 id="ch13">第 13 章 control.Controller：一张脸接所有前端</h2>

### 13.1 设计铁律

`REASONIX.md:11-13`：

> One transport-agnostic `control.Controller` sits behind every frontend (chat TUI, HTTP/SSE serve, Wails desktop). Add behavior to the controller, not a frontend, so all three inherit it.

实际还有 ACP / bot 等入口。统一装配：`boot.Build` → `Controller` → `agent.Runner`（或 `Coordinator`）。

### 13.2 一次用户消息的路径

```mermaid
flowchart LR
    TUI["chat_tui"] --> SUB["Submit / Send"]
    HTTP["serve HTTP/SSE"] --> SUB
    DESK["Wails desktop"] --> SUB
    ACP["ACP"] --> SUB
    SUB --> ADM["admitGuardedTurn<br/>防重入 / park"]
    ADM --> COMP["Compose(turn tail)"]
    COMP --> RUN["Coordinator? → Agent.Run"]
    RUN --> EVT["event.Sink<br/>reasoning/text/tool/notice"]
    EVT --> TUI
    EVT --> HTTP
    EVT --> DESK
```

关键 API 族（`controller.go`）：`Send` / `Submit` / `SubmitHTTP` / `SubmitDisplay` / `RunTurn` / Goal 循环变体；`runGuarded` 做 turn admission，避免双开跑飞。

### 13.3 为什么 Controller 会变成 6k 行巨文件？

因为它吞下了：会话恢复、checkpoint、memory slash、MCP 生命周期、capability、steer fallback、yolo/plan、attachments、goal usage……**所有前端共享的产品行为**。代价是文件巨大；收益是「修一处，TUI/桌面/HTTP 一起对」。本系列里 OpenAI4S 的 `gateway.py`、Codex 的 `turn.rs` 同属「巨心脏」形态。

---

<h2 id="ch14">第 14 章 记忆、指令、扩展、桌面/ACP</h2>

### 14.1 Context Engine v2：指令 ≠ 记忆

`docs/SESSION_MEMORY_RETRIEVAL.md` 的中心法则：

- **Standing instructions**（`REASONIX.md` / `AGENTS.md` / `CLAUDE.md` + `.local` + 祖先目录 + 全局）：必须出现在相关 turn；进 **cache-stable prefix**；
- **Background memory**：可过时的事实；BM25 auto-recall（默认 `defaultAutoRecallLimit=4`、`defaultAutoRecallChars=2400`）进 **turn tail**；索引在下个会话才并入 prefix；
- `#note` / `/remember` 写指令文件；`remember` 工具写背景事实——**不要混用**；
- 安全写入：部分 scope create-only；其余需确认——**Auto/Yolo/Guardian 不能绕过**；
- 指令解析：更深目录覆盖更广；同目录 `.local` 胜出；`@path` 导入限 5 层且禁止逃逸。

### 14.2 三层扩展：MCP · Plugin Package · Extension Protocol v1

| 层 | 是什么 | 信任模型 |
|---|---|---|
| **MCP**（`internal/plugin`） | JSON-RPC；stdio / Streamable HTTP；工具名 `mcp__<server>__<tool>`；兼容项目 `.mcp.json` | 精确规则 + `mcp_connect__*`；OAuth 等仍在 deferred |
| **Plugin packages**（`internal/pluginpkg`） | `reasonix-plugin.json`；兼容 `.claude-plugin` / `.codex-plugin`；贡献 skills/hooks/mcp/commands/agents/themes | 安装启用后按贡献面生效 |
| **Extension Protocol v1**（`reasonix.extension.v1`） | NDJSON JSON-RPC sidecar；**17 个冻结 hook point**（`tool.before`、`permission.decision`、`system_prompt.build`、`compaction.*`…）；schema 生成 + CI drift-check | **Full trust**：安装即最高权限，可覆写 host deny；UI 标 `FULL TRUST` |

MCP 与 Extension 是不同层：前者接工具/资源，后者接宿主生命周期拦截。**Full-trust Extension** 是独立攻击面，也是「Open Design 式公开合约」在 Reasonix 里的最硬落地（`docs/EXTENSION_PROTOCOL.md`）。

### 14.3 Subagent profiles

Skill frontmatter `runAs: subagent`（`docs/SUBAGENT_PROFILES.md`）：内置 `explore` / `research` / `review` / `security_review`；CLI `reasonix subagent …`；调用走 `/<profile>` 或 `task`/`fleet` + `write_paths` 并行写隔离。默认并发 `max_subagent_concurrency=6`、`max_parallel_writers=3`。Claude 插件 `agents/*.md` 可映射为 `/<plugin>:agent:<name>`。

### 14.4 桌面与 ACP

- **Desktop**（`desktop/`）：嵌套 Wails 模块（保持父模块 `CGO_ENABLED=0`）；React+TS webview ↔ 同一 `Controller`（无 HTTP hop）；
- **ACP**（`internal/acp`，`docs/ACP.md`）：`reasonix acp [--profile …]`；NDJSON JSON-RPC；`loadSession` + `embeddedContext`；能力广告 **无 image/audio**；MCP `http: true`、`sse: false`；vendor 扩展含 `_reasonix.io/session/steer`；
- VS Code 扩展 `SivanLiu.reasonix-agent` 拉起本机 `reasonix acp`；
- Remote-SSH / workbench：基线 PR #7368 reconnect hardening——产品已从本地 TUI 长到远程工作台。

---

# Part IV · 品味与边界

<h2 id="ch15">第 15 章 十项决策五段式复盘</h2>

| # | 选择 | 动机 | 约束 | 被否方案 | 代价 |
|---|---|---|---|---|---|
| 1 | 易变上下文进 turn tail | 前缀稳定 = 省钱 | DeepSeek 自动前缀缓存按字节命中 | 全部塞 system | 每轮装配/剥离成本；绕过 Compose 会破坏宪法 |
| 2 | Tool schema 排序后哈希 | 避免假 miss | 工具注册顺序不可控 | 按注册序哈希 | 排序逻辑本身要测试守护 |
| 3 | Steer 付一次 cache 税 | 插话必须立即可见 | 模型必须看到新指令 | 不插话 / 假装零成本 | 每插一次话就 miss 一轮 |
| 4 | 压缩分三阶梯 | 延迟 summary 重置 | 上下文终会增长 | 一次到位 summary | 需要额外 snip 逻辑与阈值调参 |
| 5 | `use_capability` 固定代理 | 动态 MCP 不能动 schema | MCP 库存随时变 | 直接把 mcp__* 注册进 Registry | 多一层解析与权限名（`mcp_connect__*`） |
| 6 | bash 运行时只读降级 | schema 稳定、语义精确 | bash 是否只读取决于 argv | 只按 schema 判 | `BashCommandIsReadOnly` 要维护危险命令清单 |
| 7 | Missing reasoning 静默重试 | DeepSeek thinking 偶发缺 reasoning | provider 要求回放 thinking | 直接失败 | 增加一次同请求重放；需防抖 cooldown |
| 8 | maxSteps → Pause | 会话资产优先 | 预算有限但工作已落盘 | 报错重来 | 暂停状态机与恢复路径变多 |
| 9 | 行为进 Controller | 多前端一致 | 前端数量增长 | 各前端自实现 | Controller 变成 6k 行巨文件 |
| 10 | 契约可执行（SPEC/TOOL_CONTRACT/PR） | 宪法不能只靠自觉 | 仓库会一直长大 | wiki 文档 | CI/测试成本；PR 元数据要求 |

---

<h2 id="ch16">第 16 章 横向对比（编程 Agent 桌）</h2>

| 维度 | Claude Code | Open Design | OpenAI4S | Hermes | Codex | **Reasonix** |
|---|---|---|---|---|---|---|
| 品类 | 终端编程 | 设计宿主 | 科研双平面 | 个人学习 agent | 官方 Rust CLI | **DeepSeek 终端编程** |
| 主循环 | 自有 | **无（接别人）** | Engine+Cell | Python loop | `run_turn` | **`runToolLoop`** |
| 上下文 | 强提示工程 | 20 层分带缓存 | 科学态在内核 | 前缀神圣 | 两层 turn | **Cache-first + shape** |
| 完成信号 | 自然结束 | 产物文件 | finalize / submit_output | 技能沉淀 | 三方停机 | **自然结束 + Delivery readiness** |
| 扩展 | MCP/Skills | CLI 适配器 | Skills 食谱 | 74 工具插件 | MCP+crate | **MCP + Plugin包 + Full-trust Extension v1** |
| 分发 | 闭源产品 | daemon+web | Python daemon | uv/Python | Rust 二进制 | **Go 静态二进制** |
| 权限产品面 | 弹窗+规则 | 宿主委托 | Notebook 审批 | 环境多后端 | Ask×沙箱 | **Ask/Auto/Yolo × sandbox（正交）** |

---

<h2 id="ch17">第 17 章 诚实边界</h2>

- **巨文件成本**：`controller.go` / `chat_tui.go` / `agent.go` 均数千行，新人 onboarding 陡；
- **DeepSeek 优先税**：reasoning 恢复、cache 假设对其他网关不一定成立（有兼容路径，但产品心智仍偏 DeepSeek）；
- **Executor 直接 MCP**：Balanced Executor 仍可能因 MCP 库存变化冷缓存——文档已承认；
- **Windows Bash OS sandbox**：产品固定 unconfined；文件工具仍有 confinement；
- **Checkpoint 覆盖面**：不跟踪 bash 副作用；无 git-backed；`move_file` preview 未完成；
- **Extension full trust**：安装即最高权限——能力强，威胁模型需用户理解；
- **MCP long tail**：OAuth、`list_changed`、部分 scopes 等仍 deferred（`docs/SPEC.md:877`）；
- **ACP 能力广告**：无 image/audio；agent 侧 MCP SSE 收窄；
- **旧分支**：`v1` 仅关键修复；分析/二次开发应对齐 `main-v2`。

---

<h2 id="ch18">第 18 章 源码导览索引与本地复现</h2>

| 顺序 | 路径 | 看什么 |
|---|---|---|
| 1 | `README.zh-CN.md` + `docs/SPEC.md` | 产品位与契约 |
| 2 | `REASONIX.md` | 团队自己的 cache 宪法 |
| 3 | `cmd/reasonix/main.go` + `internal/boot` | 入口与 `boot.Build` |
| 4 | `internal/control/input.go` → `Compose` | turn tail |
| 5 | `internal/agent/run_loop.go` | 主循环 |
| 6 | `internal/agent/cache_shape.go` | miss 诊断 |
| 7 | `internal/agent/compact.go` + `prune.go` | 压缩三阶梯 |
| 8 | `internal/agent/execute_one.go` | 单工具管线 |
| 9 | `internal/agent/coordinator.go` | 双模型双 session |
| 10 | `docs/TOOL_CONTRACT.md` + `internal/tool/builtin/` | 工具面 |
| 11 | `docs/TOOL_APPROVAL_MODES.md` + `permission/` + `sandbox/` | Ask/Auto/Yolo × 围栏 |
| 12 | `docs/CHECKPOINTS.md` | rewind 快照 |
| 13 | `docs/GOAL_ENFORCEMENT.zh-CN.md` | 完成协议 |
| 14 | `docs/SESSION_MEMORY_RETRIEVAL.md` | 指令 vs 记忆 |
| 15 | `docs/EXTENSION_PROTOCOL.md` + `PLUGIN_PACKAGES.md` | 扩展三层 |
| 16 | `docs/ACP.md` / `desktop/README.md` | 嵌入与桌面 |
| 17 | `internal/control/controller.go`（选读） | 多前端编排 |

本地复现：

```bash
cd "参考项目/DeepSeek-Reasonix"
git fetch origin main-v2
git checkout main-v2
git reset --hard 627b051
# 可选：跑契约测试（需本机 Go）
# go test ./internal/tool -run TestBuiltinToolContractDocumentation
```

---

## 附录 A · 与本系列「Agent 工程模式目录」的对照

| 模式目录章节 | Reasonix 落点 |
|---|---|
| 一 Prompt Cache 工程 | **教科书级**：稳定前置 / 易变外移 / 工具稳定 / 压缩慎重 / 遥测可见（五板斧齐） |
| 二 停止判定 | 自然停止 + maxSteps/todo stall Pause + Delivery readiness |
| 三 权限闸门 | Ask/Auto/Yolo + 规则前置 + bash 分解 + OS sandbox（Win Bash 缺口） |
| 四 上下文压缩 | snip 优先，summary 付税；soft 明确保 prefix |
| 五 记忆体系 | 指令文件（prefix）≠ 背景事实（BM25/tail）；确认门不可被 Yolo 绕过 |
| 六 工具系统 | 固定代理隔离动态 MCP；Economy `connect_tool_source` |
| 七 沙箱 | Seatbelt/bwrap；应用围栏 → 权限 → OS sandbox |
| 八 子 Agent | Skill profile + task/fleet；write_paths 并行写隔离 |
| 十二 持久化 | 文件 checkpoint rewind（非 git）；不覆盖 bash 副作用 |
| 十三 扩展生态 | MCP + Plugin 包 + **Full-trust Extension Protocol v1** |
| 十五 模型策略 | DeepSeek 优先；`planner_model` 双 session 保各自 cache |

---

## 附录 B · 一句话收束

**Reasonix 不是「又一个 Go 写的 Claude Code 仿制品」。**  
它把 DeepSeek 的自动前缀缓存，从费用边角料提升为**架构中轴**：Compose 分流、Shape 诊断、Capability 代理、PR 元数据、Delivery 证据——全部绕着「别把缓存打冷」转。若你在本系列里已经理解 Hermes 的 cache 神圣与 Open Design 的分带装配，读 Reasonix 就是看同一种哲学在 **Go 单二进制 coding agent** 里被执行到什么硬度。

---

## 附录 C · 开发者动机还原：从「成本焦虑」到「架构宪法」

> 第 1、2、4 章已经把动机链展开；这里保留一张浓缩对照表，方便回看。

| 动机 | 证据 | 长成的架构 |
|---|---|---|
| 长会话 token 成本高 | `README.zh-CN.md:42-43` | 产品定位「围绕前缀缓存调优」 |
| 前缀必须字节稳定 | `REASONIX.md:14-16` | 宪法 + PR 元数据 + CI |
| 易变内容必须外移 | `control/input.go:181-183` | `Compose` turn tail |
| 双模型不能共用会话 | `docs/SPEC.md:238-240` | Planner / Executor 独立 session |
| 压缩必须诚实标价 | `docs/SPEC.md:328-329` | soft → snip → summary + `LogRewriteVersion` |
| miss 必须可解释 | `cache_shape.go` | `PrefixShape` / `CompareShape` 诊断 |
| 成本是产品轴 | `COLLABORATION_MODES.zh-CN.md:86,136` | Economy / Balanced / Delivery 三档 |

---

*分析基线：`参考项目/DeepSeek-Reasonix @ 627b051`（`main-v2`）。*  
*系列位置：编程 Agent 样本；与 Hermes（cache）、Codex（沙箱正交）、Claude Code（品类）最近邻。*
