Files
sanguo_moziplus_v2/skills/trial-and-error-patterns.md
2026-05-27 00:06:34 +08:00

162 lines
7.6 KiB
Markdown
Raw Permalink 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.
---
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?
中断恢复:
- [ ] 中断恢复后是否检查了中断期间的事件?
- [ ] 是否向用户汇报了中断情况?