auto-sync: 2026-05-27 00:06:34

This commit is contained in:
cfdaily
2026-05-27 00:06:34 +08:00
parent 0c6608aa09
commit 3fac407560
3 changed files with 241 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
---
name: trial-and-error-patterns
description: >-
从任务失败和重试中提炼的可复用模式:Counter/锁生命周期、进程管理、续杯机制、广播路径、JSON解析验证、中断恢复。
在涉及并发控制、任务重启、系统资源管理的复杂任务时触发。
---
# Trial and Error Patterns
> 来源:从三国团队对话历史中的 226 次试错和 200 次成功中蒸馏(批次1-2)
## 适用场景
- 并发控制任务(counter/锁)
- 任务重启/续杯(retry)机制
- 进程管理(启动/退出/资源释放)
- 多路径调度(广播 vs 单播)
- 外部命令输出解析
- Agent 中断恢复与自检
## 模式清单
### 模式 1:Counter/锁的生命周期必须贯穿完整调用链(severity: high
**场景**Agent 并发控制需要 counter/锁机制。如果 acquire-release 不成对,会导致永久占满或双重释放。
**错误做法**
retry 路径提前调 `on_complete("retry_release")` 释放 counter,但续杯 spawn 时 counter 已释放,导致同一 Agent 被并发 spawn 多次。另一条路径中 `_do_retry` 的 on_complete 设为 Nonecounter 仍被占用但无法释放,续杯永远无法 acquire。 [批次2卡片6]
**正确做法**
1. Counter 贯穿 retry 链:acquire 在 spawn 前,release 在最终退出(done/failed)后
2. Retry 不释放 counter,而是复用同一个 counter slot
3. 所有 `release` 调用都要带 session_id
4. release 后短期 cooldown,防止立即 re-acquire 导致抖动
5. 广播 spawn 路径也要 catch AgentBusyError
**关键细节**
- 司马懿建议的四层控制:cooldown → global limit → per-agent → per-session
- counter 提前释放曾导致 zhipu API 429rate limit 被打爆),Gateway 假死
---
### 模式 2:进程退出 ≠ 资源释放(severity: high
**场景**:异步系统中,子进程退出和回调执行不是原子操作。假设进程退出时资源已释放,会出现竞态条件。
**错误做法**
spawner 在 retry 时:进程退出 → on_complete 未调用 → counter 仍占用 → can_acquire 返回 False → AgentBusyError → 续杯永远不会成功。 [批次2卡片7]
**正确做法**
1. 资源释放应该有明确的触发点,不能依赖"进程退出自然释放"
2. 在 retry 入口手动释放 counter,并将 on_complete 设为 None 防止 double release
3. 审查每条退出路径(正常退出、异常退出、超时退出、retry 退出)是否都有对应的资源释放
4. on_complete=None 后需要替代兜底机制(如 ticker 超时),并记录为已知 trade-off
**关键细节**
- 这类竞态条件在单元测试中难以复现,只有 E2E 真实环境才能暴露
---
### 模式 3:续杯(retry)机制是最大假死来源(severity: high
**场景**:任务执行超时后需要 retry"续杯")。retry 机制本身有 Bug 时,会导致无限重试或永久卡住。
**错误做法**
多个 Bug 叠加导致续杯死循环:
1. retry_count 在续杯中永远不变(retry_count=1/3),续杯永不停
2. Mail 续杯用 RETRY_PROMPT(含 review 标记指令),Agent 标了 review,但 auto_complete 只认 done
3. 续杯创建新 session 而非复用 main session,每次重试丢失之前工作 [批次2卡片8]
**正确做法**
1. 续杯模板必须专用(如 MAIL_RETRY_PROMPT),明确禁止状态转换命令
2. retry_count 必须在每次续杯时递增,max_retries 硬性兜底
3. 续杯应复用 main session 保持上下文连续性,不创建新 session
4. use_main_session 在所有路径(主 dispatch、legacy broadcast、retry)必须统一
**关键细节**
- 续杯模板的"不要执行状态转换"指令是关键——Agent 容易自作主张标 done
- 续杯死循环会阻塞 tick,导致整个调度停滞
---
### 模式 4:广播路径与单播路径行为必须一致(severity: medium
**场景**:任务调度有两条路径:指定 Agent 的单播和广播认领。如果两条路径的行为不一致,某些任务类型会系统性地出问题。
**错误做法**
广播路径 `ticker._broadcast_claim``spawn_full_agent` 时没传 `task_id``db_path`,导致续杯时无法查询任务状态,retry_count 永远不变。 [批次2卡片9]
**正确做法**
1. 所有 spawn 路径(单播、广播、retry)必须传递完整的上下文参数
2. 新增功能时逐条检查是否覆盖了所有 spawn 路径,不能只改单播忘了广播
3. 统一 TickResult 返回值 + 方法注册,避免路径分歧
**关键细节**
- 状态机枚举值不一致是同类问题——SQL 查询用 `'executing'` 但实际枚举是 `'working'`,重启恢复时 working 节点被遗漏 [批次2卡片2]
- 安装目录同步(auto-sync)可能静默修改配置,需要与设计文档对照
---
### 模式 5stdout JSON 解析路径必须实测验证(severity: medium
**场景**:外部命令的输出结构可能随版本变化。如果解析路径是猜的而非实测的,会导致静默失败。
**错误做法**
spawner 代码取 `data.response.meta.transport`,但实际 `openclaw agent --json` 的输出是 `{ response: { meta: { transport, ... } } }`,路径不对。解析不到数据时走了 fallback,掩盖了问题。这个 Bug 从第一天就存在。 [批次2卡片14]
**正确做法**
1. 解析外部命令输出前,先用实际命令验证输出格式
2. 评审时要求看到实测输出样例,不接受"应该是这样"
3. 解析失败时显式报错或日志警告,不要静默 fallback
**关键细节**
- 静默失败比报错更危险——fallback 掩盖了问题,导致所有依赖 stdout 判定的功能(如幻觉门控)静默失效
- 外部 CLI 的 `--json` 输出格式属于外部接口,需要像对待 API 一样验证
---
### 模式 6:中断恢复后必须自检(severity: medium
**场景**:Agent 在执行过程中出现中断(进程挂起、网络断开、超时等),恢复后继续工作,但未检查中断期间发生了什么。
**错误做法**
Agent 在设计评审完成后开始编码时"死掉"了 40 分钟,恢复后直接继续编码,没有自查中断原因、没有检查中断期间是否有新的指令或状态变化。 [批次1卡片10]
**正确做法**
1. 发现自己有中断时,主动检查中断原因和时长
2. 检查中断期间是否错过了重要事件(新邮件、状态变更、用户指令)
3. 向用户汇报中断情况和恢复状态
4. 如果中断导致上下文丢失,回退到最近已确认的状态重新开始
**关键细节**
- Agent 不一定能感知自己的中断,但可以通过时间戳、日志断点来推断
- 中断可能影响依赖链上的其他 agent——任务遗漏或重复执行
---
## 检查清单(快速参考)
并发控制:
- [ ] Counter 的 acquire 和 release 是否成对?
- [ ] 所有 release 调用是否带 session_id
- [ ] 续杯路径是否复用 counter slot 而非释放再获取?
Retry/续杯:
- [ ] retry_count 是否每次递增?max_retries 是否兜底?
- [ ] 续杯模板是否明确禁止状态转换?
- [ ] 是否复用 main session 而非创建新 session
- [ ] 进程退出后资源是否有明确的释放触发点?
路径一致性:
- [ ] 广播路径和单播路径是否传递相同的完整参数?
- [ ] 新增功能是否覆盖了所有 spawn 路径?
外部接口:
- [ ] stdout JSON 解析路径是否用实际命令验证过?
- [ ] 解析失败时是否显式报错而非静默 fallback?
中断恢复:
- [ ] 中断恢复后是否检查了中断期间的事件?
- [ ] 是否向用户汇报了中断情况?