Compare commits

...

1 Commits

Author SHA1 Message Date
cfdaily 85e2004e69 fix(17): redesign on_failure to use Gitea management instead of Mail API
- Rewrite §5.2: on_failure 三分路设计(业务失败→PR comment @assignee / 系统失败→Gitea Issue @pangtong / 基础设施失败→toolchain task @jiangwei)
- Update §7.4: _send_mail 不参与 toolchain 失败处理
- Update §11.1: 向后兼容表新增 on_failure 路径变更说明
- Add D17-8: 设计决策记录 on_failure 走 Gitea 不走 Mail
- Update §10: 影响范围新增 toolchain_handler.py on_failure 改动行

Closes: on_failure redesign per 'toolchain 事件全生命周期在 toolchain 流程内闭环'
2026-06-13 22:52:30 +08:00
+150 -8
View File
@@ -353,13 +353,136 @@ def verify_completion(self, task_id: str, db_path: Path) -> VerifyResult:
保留 fallback 层次是为了平滑过渡:改造初期 Agent 可能还不习惯提交 action_reportfallback 避免"改造后所有 task 都 failed"的问题。
### 5.2 on_failure 处理
### 5.2 on_failure 分路处理
verify 失败时的处理逻辑(现有逻辑保留):
**设计原则**:toolchain 事件全生命周期在 toolchain 流程内闭环,不走 Mail API。失败本身按错误类型分类,路由到不同的 Gitea 管理动作。
1. 标 task 为 `failed`
2. 通过 Mail API 通知庞统(`_notify_via_mail_api`
3. 通知内容包含:事件类型、事件详情、失败原因、Gitea 链接、行动指引
#### 错误分类三分路
| 错误类型 | 例子 | 处理方式 | 管道 |
|---------|------|---------|------|
| **业务失败** | verify 不过(no action_report)、Agent 忽略步骤 | 在关联 PR/Issue 上创建 comment @原始 assignee | Gitea webhook → §25 @mention → toolchain task |
| **系统失败** | spawner crash、timeout、max_retries | 创建 Gitea Issue 指派 pangtong-fujunshibody 包含错误详情 + task ID + 日志摘要 | Gitea webhook → issue_assigned → toolchain task |
| **基础设施失败** | Gitea API 不可用、网络不通 | `_send_toolchain_task` 直接创建 toolchain task 指派 jiangwei-infra | toolchain 内部直接创建 |
三条路全在 toolchain 流程内,Mail 完全不参与。
#### 业务失败处理(PR/Issue comment @assignee
verify 失败时,在原始事件关联的 Gitea PR 或 Issue 上创建 comment@原始 assignee
```python
def on_failure(self, task_id, agent_id, db_path, verify):
self._mark_task_status(db_path, task_id, "failed")
# 读取事件上下文
meta = self._read_task_meta(db_path, task_id)
failure_count = meta.get("failure_count", 0) + 1
# 分类路由
if verify.reason in SYSTEM_ERROR_REASONS:
# 系统失败 → Gitea Issue 给庞统
self._create_gitea_issue_for_system_error(task_id, meta, verify)
else:
# 业务失败 → PR/Issue comment @assignee
self._create_gitea_comment_for_business_failure(task_id, meta, verify, failure_count)
```
**PR/Issue comment 内容**
```
@{assignee} 任务处理失败,需要你的支持
📋 原始事件:{event_type}
❌ 失败原因:{verify.reason}
📊 失败次数:第 {failure_count} 次
请检查并处理。完成后提交 action report。
```
**为什么 @mention 而非新建 task**
- @mention 通过 §25 webhook 自然触发新 toolchain task,不多建一条路
- Gitea comment 自带完整 PR/Issue 上下文,Agent 收到时能理解全貌
- 人也能在 Gitea 上看到失败记录,天然审计
**失败上限**:同一事件 failure_count ≥ 3 时,升级为系统失败(创建 Gitea Issue 给庞统),避免无限循环。
#### 系统失败处理(Gitea Issue 给庞统)
spawner 错误(crash/timeout/max_retries)或业务失败连续 3 次,创建 Gitea Issue
```python
def _create_gitea_issue_for_system_error(self, task_id, meta, verify):
"""创建 Gitea Issue 指派庞统排查"""
repo = meta.get("context", {}).get("repo", "sanguo/sanguo_moziplus_v2")
title = f"[toolchain] 系统错误排查: {meta.get('event_type', 'unknown')} ({task_id})"
body = (
f"## 系统错误\n\n"
f"**Task ID**: {task_id}\n"
f"**事件类型**: {meta.get('event_type', 'unknown')}\n"
f"**失败原因**: {verify.reason}\n"
f"**证据**: {verify.evidence}\n"
f"**原始 assignee**: {meta.get('context', {}).get('assignee', 'unknown')}\n\n"
f"## 排查方向\n"
f"- 检查 spawner 日志\n"
f"- 确认 Agent 是否正常运行\n"
f"- 检查是否有系统性问题(API down / 配置错误 / 资源不足)\n"
)
# Gitea API 创建 Issue,指派 pangtong-fujunshi
_create_gitea_issue(repo, title, body, assignee="pangtong-fujunshi")
```
**为什么用 Gitea Issue**
- 系统问题需要排查和 trackingIssue 有状态管理(open/closed
- Issue body 包含完整错误详情,便于事后 troubleshooting
- 指派庞统后通过 issue_assigned webhook 自然触发 toolchain task
#### 基础设施失败处理(toolchain task 给姜维)
Gitea API 不可用或网络不通时,无法创建 PR comment 或 Issue。此时降级为直接创建 toolchain task
```python
def _create_infrastructure_task(self, task_id, meta, verify, error_detail):
"""基础设施失败 → toolchain task 指派姜维"""
_send_toolchain_task(
to_agent="jiangwei-infra",
title=f"[基础设施] Gitea 不可用导致 on_failure 降级: {task_id}",
description=f"尝试处理 task {task_id} 失败时 Gitea API 不可用。\n错误: {error_detail}",
event_type="infrastructure_failure",
action_type="infrastructure_failure",
steps=[
"检查 Gitea 服务状态(http://192.168.2.154:3000",
"检查网络连通性",
"Gitea 恢复后检查原始 task 状态并补处理",
"提交 action report",
],
context_data={
"original_task_id": task_id,
"original_event_type": meta.get("event_type", ""),
"gitea_error": error_detail,
},
)
```
**为什么不走 Gitea**:Gitea 本身就是问题源,调 Gitea API 创建 Issue 会再次失败。直接在 _toolchain DB 内创建 task 是最后的降级手段。
#### 三分路总结
```
on_failure
├─ 系统错误(crash/timeout/max_retries/连续3次业务失败)?
│ └─ Gitea API 可用?
│ ├─ YES → 创建 Issue @pangtong-fujunshi → webhook → toolchain task
│ └─ NO → _send_toolchain_task @jiangwei-infra(基础设施降级)
└─ 业务错误(no action_report?
└─ Gitea API 可用?
├─ YES → PR/Issue comment @assignee → §25 webhook → toolchain task
└─ NO → _send_toolchain_task @jiangwei-infra(基础设施降级)
```
所有路径都在 toolchain 流程内闭环,Mail 不参与。
### 5.3 action_report comment 格式
@@ -670,11 +793,13 @@ async def _handle_pr_opened(payload: Dict[str, Any]) -> None:
| `_send_deploy_failure_mail` | deploy_failure | `_send_toolchain_task(...)` |
| `_send_mention_mails` | mention | `_send_toolchain_task(...)` |
### 7.4 _send_mail 保留不变
### 7.4 _send_mail 不参与 toolchain 失败处理
`_send_mail` 函数完全保留不变,只服务两个场景:
`_send_mail` 函数只服务两个场景:
1. PR 合并通知(inform 纯通知)
2. ToolchainHandler on_failure 的 Mail 通知(通过 Mail API 发给庞统
2. Agent 间点对点通信(inform / request
**toolchain on_failure 不调用 `_send_mail`**。失败处理三分路(PR comment / Gitea Issue / toolchain task)全在 toolchain 流程内闭环。
---
@@ -786,6 +911,7 @@ ticker 需要扫描 `_toolchain` 虚拟项目。当前 ticker 通过 `TaskTypeRe
| `src/daemon/prompt_composer.py` | 修改 | PromptContext 新增 `action_type``action_steps` 字段 |
| `src/blackboard/db.py` | 修改 | comments 表 CHECK 约束处理(去掉 CHECK 或加 action_report |
| `src/daemon/mail_notify.py` | 修改 | `_REASON_MAP` 新增 `no_action_report` reason |
| `src/daemon/toolchain_handler.py`on_failure | 修改 | on_failure 三分路重写(去掉 Mail API 调用,改为 Gitea API + _send_toolchain_task | +~40 行 |
### 改动量估算
@@ -811,6 +937,7 @@ ticker 需要扫描 `_toolchain` 虚拟项目。当前 ticker 通过 `TaskTypeRe
| Agent 间手动发 request Mail | ✅ 无影响 |
| MailHandler 的 verify / on_failure | ✅ 无影响 |
| `_send_mail` 函数 | ✅ 保留不变 |
| ToolchainHandler on_failure 改为 Gitea 管理(PR comment / Issue / toolchain task),不经过 Mail API | ✅ 无影响 |
### 11.2 _mail DB 中已有的 toolchain task
@@ -905,6 +1032,21 @@ ticker 需要扫描 `_toolchain` 虚拟项目。当前 ticker 通过 `TaskTypeRe
- action_report + verify 是更可靠的完成路径
- 减少 Agent 需要执行的 API 操作(从"标 done + 提交产出"简化为"提交 action_report"
### D17-8: on_failure 走 Gitea 管理,不走 Mail
**决策**:toolchain 失败处理三分路——业务失败在 PR/Issue comment @assignee,系统失败创建 Gitea Issue @pangtong-fujunshi,基础设施失败创建 toolchain task @jiangwei-infra。三条路全在 toolchain 流程内,Mail 不参与。
**讨论的替代方案**
- A(Mail API 通知庞统):跨系统,回溯需要在 _mail 和 _toolchain 两个 DB 之间跳
- Bon_failure 直接创建新 _toolchain task):强约束语义不对,失败应该是协作求助而非又一个强制任务
- C(只记日志 + 前端告警):system comment 没人主动看,等于没人管
**理由**
- 错误分类后路由到不同处理方式,比统一通知更精准
- Gitea Issue/PR comment 是天然的管理和审计工具,人也能看到
- §25 @mention webhook 已有端到端集成,comment 创建后自然触发新 toolchain task,管道复用
- Mail 只服务 Agent 间点对点通信和 PR 合并通知,职责清晰
---
## §13. 实施计划