Files
sanguo_moziplus_v2/docs/design/archive-2.0/v2.7.1-mail-envelope-separation.md
T
2026-05-28 08:45:47 +08:00

173 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 信封模式** | Headerfrom/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 AgentMain 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 失败 | 系统 | 标 failedreason: 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_minutes30 分钟)
→ 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
→ 无 → 标 failedreason: 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 需要读内容,信息不能丢)