From 7116446e937b494e70847b16c73366e3f77f5c48 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sun, 24 May 2026 19:40:52 +0800 Subject: [PATCH] auto-sync: 2026-05-24 19:40:52 --- .../design/v2.7.1-mail-envelope-separation.md | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 docs/design/v2.7.1-mail-envelope-separation.md diff --git a/docs/design/v2.7.1-mail-envelope-separation.md b/docs/design/v2.7.1-mail-envelope-separation.md new file mode 100644 index 0000000..9d2dae6 --- /dev/null +++ b/docs/design/v2.7.1-mail-envelope-separation.md @@ -0,0 +1,172 @@ +# 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 需要读内容,信息不能丢)