# v2.7.1 Mail 信封/载荷分离优化

**版本**: v1.0  
**日期**: 2026-05-24  
**作者**: 庞统  
**状态**: 实施中（评审通过，用户确认）

---

## 1. 背景

v2.7.0 Mail 飞鸽传书功能已稳定上线。实测"庞统发 request 给赵云"一封来回邮件：

- 庞统侧 6 次 LLM 调用（input 1,702 + output 430）
- 赵云侧 4 次 LLM 调用（input 12,528 + output 277）
- 合计 10 次 LLM 调用，~14,937 tokens（不含 cache）

**核心问题**：Agent 同时管信封（状态流转）和载荷（业务内容）。状态转换命令（claimed→working→done）占用 LLM tokens，且不可靠。

## 2. 业界调研

| 系统 | 信封（系统管） | 载荷（Agent/LLM 管） |
|------|--------------|---------------------|
| **Hermes Kanban** | Dispatcher 负责 claim → spawn → reclaim → 状态流转；Agent 通过 `kanban_complete()` 声明完成，状态写入由系统工具执行 | Agent 调 `kanban_show()` 读任务、调 `kanban_heartbeat()` 保活、调 `kanban_complete(summary)` 声明完成 |
| **MCP Agent Mail** | 系统管投递、线程聚合、消息路由 | Agent 调 `fetch_inbox()` + `acknowledge_message()` 读内容和 ack |
| **ClawTeam Inbox** | JSON 文件即消息，系统管 claim → ack 原子操作 | Agent 读文件内容、执行业务、写产出文件 |
| **EIP 信封模式** | Header（from/to/type/routing）由消息基础设施处理 | Body（业务内容）由接收方处理 |

**共识**：状态流转是信封，属于系统。业务内容是载荷，属于 Agent。

## 3. 设计原则

1. **系统管信封**：claimed → working → done/failed 全部由系统完成
2. **Agent 管载荷**：只看业务内容，只做业务决策（回复/不回复/触发行动）
3. **统一超时机制**：Mail 任务走标准 task 超时路径（`default_task_timeout_minutes`），不开特殊路径
4. **产出验证**：request 类型邮件验证 Agent 是否真的回复了（幻觉门控）

## 4. 新生命周期

### 4.1 正常流

```
系统（Dispatcher）                          Agent（Main Session）
─────────────                          ─────────────
1. 邮件创建（Mail API）
2. Dispatcher 路由到目标 Agent
3. [新增] 系统标 working               →  5. 收到精简 prompt
4. spawn Agent                         →  6. LLM 理解内容
                                       →  7. [inform] 只读，无需操作
                                       →     [request] 回复发件者（curl）
                                       →  8. Agent turn 结束
9. on_complete 触发（进程退出）
10. [新增] 系统标 done/failed：
    - inform → 直接标 done
    - request → 幻觉门控验证后标 done/failed
```

### 4.2 异常流

| 异常 | 处理方 | 方式 | 现有机制？ |
|------|--------|------|-----------|
| spawn 失败 | 系统 | 标 failed（reason: spawn_failed），不标 working | ❌ 需新增 |
| Agent 执行超时/崩溃 | 系统 | Ticker `_check_timeouts`：working 超 `default_task_timeout_minutes` 标 failed | ✅ 已有 |
| Agent 忘记回复（request） | 系统 | 幻觉门控：on_complete 时查 DB 有无 in_reply_to 的回复邮件 | ❌ 需新增 |
| Agent 回复 type 写错 | prompt | 模板提醒 | ✅ 已有 |
| on_complete 标 done API 失败 | 系统 | 重试 3 次 + 写日志 | ❌ 需新增 |

### 4.3 on_complete 的触发时机

on_complete 在 openclaw 子进程退出时触发（spawner `_handle_exit`）。子进程的生命周期是：

```
moziplus spawn openclaw 子进程
  → 子进程调 Gateway API 投递消息到 Agent session
  → Agent main session 收到 prompt
  → LLM 生成回复
  → turn 结束
  → openclaw 子进程退出
  → spawner 检测退出 → on_complete 触发
```

**on_complete 触发 = Agent 已处理完这封邮件（turn 结束）**。

如果 Agent spawn 了子任务继续干，on_complete 仍会触发（主 turn 结束 ≠ 子任务完成）。这是合理的——邮件投递 = 信息传递到位，不等后续衍生工作。

### 4.4 超时机制

Mail 任务走标准 task 超时路径，不做特殊处理：

```
Ticker 每 30s 扫一次 working 状态任务
  → 有 task.deadline？→ timeout = deadline - started_at
  → 无 deadline？→ timeout = default_task_timeout_minutes（30 分钟）
  → elapsed > timeout → 标 failed
```

## 5. Prompt 模板

### 5.1 inform 模板（~80 tokens，当前 ~200 tokens）

```
你收到一封飞鸽传书（纯通知）。

发件者: {from_agent}
主题: {title}
内容: {text}

已阅即可，无需操作。
```

### 5.2 request 模板（~150 tokens，当前 ~400 tokens）

```
你收到一封飞鸽传书，需要处理并回复。

发件者: {from_agent}
主题: {title}
内容: {text}

请处理后回复发件者：
curl -s -X POST http://localhost:8083/api/mail \
  -H 'Content-Type: application/json' \
  -d '{"from": "{agent_id}", "to": "{from_agent}", "title": "回复: {title}", "text": "你的回复内容", "type": "inform", "in_reply_to": "{task_id}"}'

⚠️ 将"你的回复内容"替换为实际回复。type 必须用 inform 防止循环。
```

**变化**：去掉所有状态转换命令（working/done 的 curl），Agent 只看业务内容。

## 6. 幻觉门控

**来源**：Hermes v0.13 实践——Agent 声称完成时验证产出是否真实存在。

**Mail 场景**：request 类型邮件 on_complete 时，系统查 `_mail` DB 有无 `in_reply_to = task_id` 的回复邮件记录：

```
on_complete 触发
  → _classify_outcome 判断 outcome
  → outcome 正常（release_counter）
    → inform → 直接标 done
    → request → 查 DB 有无回复邮件
      → 有 → 标 done
      → 无 → 标 failed（reason: no_reply_found）
```

inform 类型不需要门控——不需要回复，on_complete 触发即完成。

## 7. 改动范围

| 文件 | 改动 | 行数 | 说明 |
|------|------|------|------|
| `src/daemon/spawner.py` | 精简两个模板常量 | 改 ~40 行 | 去掉 working/done curl 命令 |
| `src/daemon/dispatcher.py` | spawn 前标 working + on_complete 增强 + 幻觉门控 | 新增 ~50 行 | `_mail` 专用的 on_complete 回调 |
| `src/daemon/ticker.py` | 不改 | 0 | 走标准超时机制 |

总计 ~90 行改动，涉及 2 个文件。

## 8. 预期效果

| 场景 | 当前 v2.7.0 | 优化后 v2.7.1 | 说明 |
|------|------------|-------------|------|
| inform 邮件（Agent 侧） | 4 次 LLM, ~13k tokens | **1 次 LLM** | Agent 读内容后无操作，系统标 done |
| request 邮件（Agent 侧） | 4 次 LLM, ~13k tokens | **2 次 LLM** | Agent 读+回复，系统标 done |
| inform prompt | ~200 tokens | **~80 tokens** | 省掉状态转换命令 |
| request prompt | ~400 tokens | **~150 tokens** | 省掉状态转换命令 |
| 状态可靠性 | 依赖 LLM 遵守 | **系统保证** | 最大收益 |

## 9. 不做的事

- ❌ isolated session 投递（必须是主 Agent 来决定是否 spawn）
- ❌ Mail 专用超时配置（走标准 task 超时路径）
- ❌ inform 类型跳过 LLM（Agent 需要读内容，信息不能丢）
