auto-sync: 2026-05-29 20:49:44
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
| v2.7.2 | 2026-05-26 | 防重复调用 & 防无限续杯 + counter v2.1 |
|
||||
| v3.0 | 2026-05-28 | **本版**:PRD 3.0 对齐 + 源码回溯 + 完整现状记录 |
|
||||
| v3.0.1 | 2026-05-28 | 补充6个讨论话题(§6.5/§10.4~10.6)+ 新增§21 T阶段规划(T1~T6) |
|
||||
| v3.0.2 | 2026-05-29 | §12.3 Mail API 防御 + Prompt 约束设计;§6.5 补充 spawn 改造方向 |
|
||||
| v3.0.2 | 2026-05-29 | §12.3 Mail API 防御 + Prompt 约束设计(纳入司马懿评审意见);§6.5 补充 spawn 改造方向 |
|
||||
|
||||
### 1.2 v3.0 定位
|
||||
|
||||
@@ -969,6 +969,7 @@ route(task_info, action_type):
|
||||
### 12.3 Mail API 防御 + Prompt 约束 📋
|
||||
|
||||
> 来源:#02 Main Session + Delegation 设计过程中发现的 Mail 自环问题
|
||||
> 司马懿评审:mail-1780058519676 回复(M1/M2/S1-S3/A9),已纳入
|
||||
|
||||
#### 问题
|
||||
|
||||
@@ -976,22 +977,46 @@ Agent 回复邮件时把 `to` 写成自己,导致自环(mail-1780056763652
|
||||
1. **API 层无防御**:`send_mail` 不校验 `to` 的合法性,不自动推断回复收件人
|
||||
2. **Prompt 层无约束**:回复模板中 `to` 字段要求 Agent 手动填写,Agent 容易写错
|
||||
|
||||
#### 设计约束
|
||||
|
||||
1. **Mail 是 Agent 间 1 对 1 通信**。`from` 和 `to` 都必须是有效 Agent,`from="user"` 不允许(用户收不到邮件)
|
||||
2. **严格 1 对 1**。禁止第三方插入会话。多 Agent 协作走 Task(广播/mention)
|
||||
3. **有效 Agent 列表**统一维护一份,API 校验和 Prompt 模板共用同一数据源
|
||||
|
||||
#### API 层防御(`mail_routes.py` `send_mail`)
|
||||
|
||||
| # | 场景 | 校验逻辑 | 错误处理 |
|
||||
|---|------|---------|----------|
|
||||
| A1 | `from` 缺失/为空 | 必填校验 | 400:"`from` 必填" |
|
||||
| A2 | `to` 缺失/为空(非回复) | 必填校验 | 400:"`to` 必填" |
|
||||
| A3 | `from == to`(自环) | 防自环 | 400:"不能给自己发邮件" |
|
||||
| A4 | `to` 对应的 Agent 不存在 | 从注册 Agent 列表校验 | 400:"`{to}` 不是有效的 Agent" |
|
||||
| A5 | `in_reply_to` 指向不存在的邮件 | 原邮件存在性校验 | 400:"回复的邮件 `{id}` 不存在" |
|
||||
| A6 | `in_reply_to` 存在,`to` 与原邮件 `from` 不一致 | **自动纠正** | 用原邮件 `assigned_by` 替换 `to`,响应中提示 |
|
||||
| A7 | `in_reply_to` 存在,`to` 未传 | **自动填充** | 从原邮件取 `assigned_by` 作为 `to` |
|
||||
| A8 | `in_reply_to` 存在,`from` 不是原邮件 `to` | 允许(第三方参与会话) | 正常处理 |
|
||||
**校验执行顺序**(按此顺序逐条校验,任一失败立即返回 400):
|
||||
|
||||
**A4 校验方式**:从 ticker 的 `self.agents` 或 Agent 注册表获取有效 Agent ID 列表。硬编码 6 个 Agent(zhangfei-dev / guanyu-dev / zhaoyun-data / jiangwei-infra / pangtong-fujunshi / simayi-challenger)作为初始方案,后续改为从配置读取。
|
||||
| 顺序 | # | 场景 | 校验逻辑 | 错误处理 |
|
||||
|------|---|------|---------|----------|
|
||||
| 1 | A1 | `from` 缺失/为空 | 必填校验 | 400:"`from` 必填" |
|
||||
| 2 | A9 | `from` 不是有效 Agent | 从注册 Agent 列表校验 | 400:"`from` 不是有效的 Agent" |
|
||||
| 3 | A5 | `in_reply_to` 指向不存在的邮件 | 原邮件存在性校验 | 400:"回复的邮件不存在" |
|
||||
| 4 | A6/A7 | `in_reply_to` 存在时自动纠正 `to` | 自动从原邮件取 `assigned_by`(原发件者)作为 `to` | 静默纠正,响应中加 `auto_corrected` 提示 |
|
||||
| 5 | A2 | `to` 缺失/为空(非回复) | 必填校验 | 400:"`to` 必填" |
|
||||
| 6 | A3 | `from == to`(自环) | 防自环 | 400:"不能给自己发邮件" |
|
||||
| 7 | A4 | `to` 不是有效 Agent | 从注册 Agent 列表校验 | 400:"`to` 不是有效的 Agent" |
|
||||
| 8 | A8 | `in_reply_to` 存在,`from` 不是原邮件的 `assignee` 或 `assigned_by` | 严格 1 对 1:只有原邮件的双方能回复 | 400:"只有邮件的发送者或接收者可以回复" |
|
||||
| 9 | A10 | `text` 和 `description` 都为空 | 正文非空校验 | 400:"邮件正文不能为空" |
|
||||
|
||||
**自动纠正逻辑**:A6 + A7 合并处理——有 `in_reply_to` 时,`to` 不传或传错都自动从原邮件取 `assigned_by`(原始发件者)。
|
||||
**关键设计说明**:
|
||||
|
||||
- **A6/A7 自动纠正**:有 `in_reply_to` 时,无论 Agent 传什么 `to`,都自动从原邮件取 `assigned_by`(黑板字段,即原发件者)作为回复的 `to`。回复方向固定为 reply → original sender,不支持自定义收件人
|
||||
- **A8 严格 1 对 1**:只有原邮件的 `assigned_by`(发件者)和 `assignee`(收件者)可以回复。其他人想参与需开新邮件,不能用 `in_reply_to` 插入别人的对话
|
||||
- **A9 `from` 校验**:`from` 必须是有效 Agent。现有代码 `body.get("from", "user")` 的默认值 `"user"` 需移除,改为必填
|
||||
- **A4/A9 有效 Agent 列表**:同一数据源(从 ticker `self.agents` 或配置读取)。Prompt 模板也用此列表的 `{valid_agents}` 变量替换
|
||||
|
||||
**A6 自动纠正响应格式**:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"mail_id": "mail-xxx",
|
||||
"auto_corrected": {"field": "to", "original": "simayi-challenger", "corrected": "pangtong-fujunshi"}
|
||||
}
|
||||
```
|
||||
|
||||
让调用者知道 `to` 被纠正了,Agent 下次可以避免同样的错误。
|
||||
|
||||
#### Prompt 层约束(`spawner.py` 模板)
|
||||
|
||||
@@ -999,7 +1024,7 @@ Agent 回复邮件时把 `to` 写成自己,导致自环(mail-1780056763652
|
||||
1. 回复模板中 curl 命令的 JSON 双花括号转义,Agent 容易写错
|
||||
2. 没有告诉 Agent "to 会自动推断",Agent 手动填写容易出错
|
||||
3. 没有给 Agent "发新邮件" 的模板,Agent 自己猜格式
|
||||
4. 没有列出有效 Agent ID,Agent 可能给不存在的 agent 发邮件
|
||||
4. Agent ID 列表硬编码在模板字符串里,加 Agent 要改代码
|
||||
|
||||
**改动**:
|
||||
|
||||
@@ -1024,7 +1049,7 @@ curl -s -X POST http://localhost:8083/api/mail \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{{"from": "{agent_id}", "to": "对方agent-id", "title": "标题", "text": "正文", "type": "inform"}}'
|
||||
|
||||
⚠️ to 必须是有效的 agent id(zhangfei-dev / guanyu-dev / zhaoyun-data / jiangwei-infra / pangtong-fujunshi / simayi-challenger)
|
||||
⚠️ to 必须是有效的 agent id: {valid_agents}
|
||||
⚠️ 纯通知用 type=inform,需要对方回复不填 type(默认 request)
|
||||
⚠️ 不能给自己发邮件
|
||||
⚠️ 不要执行任何状态转换命令(标 working/done/review/failed 等),系统会自动处理。
|
||||
@@ -1036,27 +1061,28 @@ MAIL_INFORM_TEMPLATE = """你收到一封飞鸽传书(纯通知)。
|
||||
主题: {title}
|
||||
内容: {text}
|
||||
|
||||
已阅即可。如需回复,同上用 in_reply_to 回复发件者。
|
||||
已阅即可。如需回复,用 in_reply_to 回复发件者(不需要填 to)。
|
||||
⚠️ 不要执行任何状态转换命令。
|
||||
"""
|
||||
```
|
||||
|
||||
**关键改动**:
|
||||
1. 回复模板去掉 `to` 字段,告诉 Agent "系统自动回复给发件者"
|
||||
2. 新增"给其他人发新邮件"模板,列出所有有效 Agent ID
|
||||
3. 三条 ⚠️ 覆盖三种错误场景(自环 / 幻觉 agent / 状态误操作)
|
||||
4. inform 模板也加上回复指引
|
||||
2. 新增"给其他人发新邮件"模板
|
||||
3. Agent ID 列表用 `{valid_agents}` 运行时替换(和 A4/A9 同一数据源),不硬编码
|
||||
4. 三条 ⚠️ 覆盖三种错误场景(自环 / 幻觉 agent / 状态误操作)
|
||||
5. inform 模板也加上回复指引
|
||||
|
||||
#### 涉及文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/api/mail_routes.py` | `send_mail` 加 A1-A8 校验 + 自动推断 |
|
||||
| `src/daemon/spawner.py` | `MAIL_REQUEST_TEMPLATE` + `MAIL_INFORM_TEMPLATE` 更新 |
|
||||
| `src/api/mail_routes.py` | `send_mail` 加 A1-A10 校验 + 自动推断 + 有效 Agent 列表 |
|
||||
| `src/daemon/spawner.py` | 模板更新 + `{valid_agents}` 运行时替换 |
|
||||
|
||||
#### 状态
|
||||
|
||||
📋 设计完成,待评审 → 实施 → 验收
|
||||
📋 设计已纳入司马懿评审意见,待确认 → 实施 → 验收
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user