# 核心概念

CMDC 的设计就是 7 个互不重叠的抽象搭积木。读完本章你能在脑里画出整个调用图。

---

## 一、Agent — gen_statem 状态机

每个会话对应一个 OTP 进程，跑 `:gen_statem` 状态机，4 个状态：

```
idle ─prompt──▶ running ─stream chunk──▶ streaming ─tool calls──▶ executing_tools
  ▲                                                                    │
  └────────── 工具批次完成 / abort / agent_end ───────────────────────┘
```

外部代码不直接持 Agent struct，**只通过 pid 或 session_id 字符串交互**：

```elixir
{:ok, session} = CMDC.create_agent(model: "anthropic:claude-sonnet-4-5")

CMDC.prompt(session, "...")       # 异步 cast，立即返回
CMDC.steer(session, ref, "...")   # 中段干预
CMDC.abort(session)               # 中止
CMDC.status(session)              # 状态快照
CMDC.stop(session)                # 终止 Supervisor 树
```

完整 API 矩阵见 [`CMDC`](CMDC.html)，状态机详细行为见
[Agent 状态机与事件](agent-loop.html)。

---

## 二、Session — Supervisor Tree

`create_agent/1` 实际启动了一棵 Supervisor 树：

```
SessionServer (Supervisor :rest_for_one)
├── Agent (gen_statem)
└── SubAgent.Supervisor (DynamicSupervisor)
    ├── 子 Agent #1 (gen_statem)
    └── 子 Agent #2 (gen_statem)
        └── ...（孙 Agent 同样隔离）
```

子 Agent crash 不影响父 Agent；父 Agent crash 由 SessionServer 决定是否
重启全树。Stop 一个 session = 停整棵树。

---

## 三、EventBus — 唯一的对外合约

Agent 内部所有"事情发生了"都通过 `CMDC.Emitter` 广播到
[`CMDC.EventBus`](CMDC.EventBus.html)，订阅者收到统一格式：

```
{:cmdc_event, session_id, event}
```

事件类型分七大类（`session` / `stream` / `tool` / `approval` / `subagent` /
`plugin_event` / `error`），完整清单见 [`CMDC.Event`](CMDC.Event.html)。

**消费方式**：

| 场景 | API |
|---|---|
| 当前进程订阅 | `CMDC.subscribe/2` |
| 重连补帧 | `CMDC.subscribe(session, since: last_index)`（需 `event_buffer_size > 0`）|
| Agent 崩溃监控 | `CMDC.monitor/1` → `{:cmdc_down, ref, sid, reason}` |

---

## 四、Plugin — 切面拦截器

Plugin 实现 [`CMDC.Plugin`](CMDC.Plugin.html) behaviour，在 13 个 hook 中
返回 8 种 action 之一：

| 关键 hook | 用途 | 常用 action |
|---|---|---|
| `:session_start / :session_end` | 资源初始化/清理 | `:emit` |
| `{:before_prompt, text}` | 用户 prompt 进 Agent 前 | `:intervene` 改写、`:abort` 拦截 |
| `{:before_request, messages}` | LLM 请求前 | `:switch_model` 路由模型 |
| `{:after_response, msg}` | LLM 回复后 | `:abort` / `:intervene` |
| `{:before_tool, name, args}` | 工具执行前 | `:block_tool` 拦截、`:replace_tool_args` 改参 |
| `{:on_tool_error, ...}` | 工具失败 | `:continue` 重试、`:abort` 放弃 |
| `{:after_tool, name, call_id, result}` | 单工具后 | `:replace_tool_result` 改 result |
| `{:before_compact, messages}` | 压缩前 | 持久化关键事实 |

完整矩阵 + 5 个范例见 [写一个 Plugin](plugins.html)。

---

## 五、Tool — Agent 可调用的能力

Tool 是 LLM 视角的"函数"，实现 [`CMDC.Tool`](CMDC.Tool.html) behaviour：

```elixir
@callback name() :: String.t()
@callback description() :: String.t()
@callback parameters() :: map()                          # JSON Schema
@callback execute(args :: map(), ctx :: CMDC.Context.t()) ::
            {:ok, String.t()} | {:error, String.t()} | {:effect, term()}
```

CMDC 内置 11 个：`ReadFile / WriteFile / EditFile / Shell / Grep / ListDir /
Glob / Task（子代理）/ WriteTodos / AskUser / CompactConversation`。

文件类工具默认走 [`CMDC.Sandbox`](CMDC.Sandbox.html) 代理，所有路径在
`working_dir` 内做边界校验。详见 [写一个 Tool](tools.html)。

---

## 六、Backend / Sandbox — 文件与执行的抽象

[`CMDC.Backend`](CMDC.Backend.html) 是文件 / 状态 / 远程存储的统一访问层
（10 callback：ls / read / write / edit / grep / glob / upload / download +
扩展的 execute / id），3 个内置实现：

| Backend | 用途 | 持久性 |
|---|---|---|
| `Backend.State` | 内存 ETS，单测 / 短会话 | 进程级 |
| `Backend.Filesystem` | 本地文件，CLI / 开发 | 跨进程 |
| `Backend.Composite` | prefix 路由组合，生产推荐 | — |

[`CMDC.Sandbox`](CMDC.Sandbox.html) 是 Backend 的子集 + 加 `execute`
（shell 命令）。`Sandbox.Local` 是默认实现。

**典型生产配置**（一个会话挂多套存储）：

```elixir
backend = CMDC.Backend.Composite.new(
  default: CMDC.Backend.Filesystem.new(root_dir: "/tmp/work", virtual_mode: true),
  routes: %{
    "/memories/" => MyApp.PgBackend.new(),                # 长期记忆
    "/conversation_history/" => CMDC.Backend.State.new()  # 短期 ETS
  }
)

{:ok, session} = CMDC.create_agent(model: "...", backend: backend)
```

---

## 七、Skill / Memory / Checkpoint — 三类持久化

CMDC 把"持久化"按时间尺度拆成三层，命名独立、互不重叠：

| 模块 | 时间尺度 | 用途 | 数据形态 |
|---|---|---|---|
| [`CMDC.Skill`](CMDC.Skill.html) | 永久 | 加载 SKILL.md 注入 system prompt | `.md` 文件 + frontmatter |
| [`CMDC.Memory`](CMDC.Memory.html) behaviour | 跨会话 | 语义记忆存储（store / search / similarity） | 由 backend 决定（ETS / Postgres + pgvector） |
| [`CMDC.Checkpoint`](CMDC.Checkpoint.html) | 单会话快照 | BEAM 重启 / 跨设备恢复 | `Snapshot` struct 序列化 |

**易混淆**：

- `Backend` 是「文件接口」（read_file 之类）
- `Memory` 是「语义记忆接口」（向量检索之类）
- `Checkpoint` 是「会话快照」（save / load / list）

详见 [`CMDC.Backend`](CMDC.Backend.html) / [`CMDC.Memory`](CMDC.Memory.html) /
[`CMDC.Checkpoint`](CMDC.Checkpoint.html) 三个模块文档。

### v0.5 新增 facade API

- **[`CMDC.checkpoint!/2`](CMDC.html#checkpoint!/2)** — 从运行中 session 抓快照
  （内部走 `Agent.get_state` + `Checkpoint.save`，序列化策略自动剥离运行期字段）
- **[`CMDC.resume_session!/2`](CMDC.html#resume_session!/2)** — 从 snapshot 重建 session,
  续 prompt 时 LLM 看到完整历史，无需自己拼装
- **[`CMDC.Checkpoint.Snapshot.redact/2`](CMDC.Checkpoint.Snapshot.html#redact/2)** —
  backend 写前预处理 hook（接 Cloak / KMS / 任意脱敏函数）
- **[`CMDC.Plugin.Builtin.AutoCheckpoint`](CMDC.Plugin.Builtin.AutoCheckpoint.html)** —
  内置 Plugin，按 turn / on_tools / on_events OR 触发自动存档 + max_checkpoints / ttl_seconds GC,
  走 `CMDC.AsyncTaskSupervisor` 异步执行不阻塞 gen_statem

### v0.5 新增 Hibernate 配置

[`CMDC.Options.hibernate_after_ms`](CMDC.Options.html) —
Agent 进程空闲 N 毫秒后走 OTP 原生 `:hibernate_after` 自动 hibernate，
单进程 heap 8KB → 1.5KB（节省 ~80% 内存）。长会话多租户场景必备。

---

## 依赖关系一览

```
                    ┌─────────────────┐
                    │   CMDC (facade) │
                    └────────┬────────┘
                             │
        ┌────────────┬───────┴────────┬────────────┐
        ▼            ▼                ▼            ▼
   SessionServer  EventBus       Checkpoint    Telemetry
        │
   ┌────┴────┐
   ▼         ▼
  Agent  SubAgent.Supervisor
   │
   ├── State / Stream / ToolRunner / Compactor   ← @moduledoc false 私有
   ├── Plugin Pipeline
   │     └── 16 内置 Plugin
   ├── Tool（11 内置）
   │     └── Sandbox / Backend
   ├── Provider（req_llm 封装）
   ├── Skill / Memory / Blueprint
   └── MCP（Bridge / Client / Supervisor）
```

层级规则：**只允许向下依赖，同层不互相依赖**。

---

## 下一步

- 想看 Agent 状态机怎么转：[Agent 状态机与事件](agent-loop.html)
- 想写第一个切面：[写一个 Plugin](plugins.html)
- 想加个外部能力：[写一个 Tool](tools.html)
- 想看常见组合：[常见配方](cookbook.html)
