Files
sanguo_moziplus_v2/docs/research/distill-cards-batch2-simayi-mixed.md
T
2026-05-26 23:39:57 +08:00

539 lines
21 KiB
Markdown
Raw 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.
# 经验卡片集 — 批次2 司马懿纠正 + 庞统经验混合
> 数据源:151 个片段(庞统经验 21 条 + 司马懿纠正 122 条 + Sanguo Mail 纠正 8 条)
> 归纳时间:2026-05-26
> 覆盖时间段:2026-05-06 ~ 2026-05-26
---
## 汇总表
| # | 分类 | 严重度 | 频率 | 来源 | 核心教训 |
|---|------|--------|------|------|----------|
| 1 | 代码评审 | high | 5+ | simayi-correction | 评审必须验证最终代码,不能只审方案 |
| 2 | 代码评审 | high | 3+ | simayi-correction | 状态机/枚举值不一致是高频 Bug 根源 |
| 3 | 系统设计 | high | 3 | simayi-correction, experience | 评审者自我纠正:承认判断错误比坚持错误更有价值 |
| 4 | 系统设计 | high | 4+ | simayi-correction | 设计文档-代码一致性审查不能省 |
| 5 | 系统设计 | medium | 2+ | experience | "一键三连"闭环标准:文档→代码→最终确认 |
| 6 | 系统设计 | high | 3 | simayi-correction | Counter/锁的生命周期必须贯穿完整调用链 |
| 7 | 系统设计 | high | 2 | simayi-correction | 进程退出 ≠ 资源释放:on_complete 回调时序是关键 |
| 8 | 编排调度 | high | 5+ | simayi-correction | 续杯(retry)机制是最大的假死来源 |
| 9 | 编排调度 | medium | 3 | simayi-correction | 广播路径与单播路径必须行为一致 |
| 10 | 编排调度 | medium | 3+ | simayi-correction | E2E 测试要用真实环境,不能只测单元 |
| 11 | Agent 行为 | medium | 4+ | simayi-correction | Agent 认领任务前必须检查角色匹配 |
| 12 | Agent 行为 | high | 2 | simayi-correction | inform 类型邮件不应触发完整 Agent 执行 |
| 13 | 沟通协作 | medium | 3 | pangtong-correction, simayi-correction | 回答用户问题要直接,不要绕弯子 |
| 14 | 系统设计 | medium | 2 | simayi-correction | stdout JSON 解析路径必须实测验证 |
| 15 | 经验总结 | low | 2 | experience | 跨项目调研的实践对照表要做落地映射 |
---
## 卡片 1:评审闭环必须包含最终代码确认
```yaml
---
category: "代码评审"
tags: [review, 闭环, 最终代码]
severity: "high"
frequency: 5
sources: ["simayi-correction", "experience"]
---
```
### 场景
多步骤开发中,方案评审通过后,实现阶段可能有微调。如果评审者只确认了方案而没看最终代码,Bug 可能被遗漏。
### 错误做法
庞统在 M2 阶段让司马懿 review 了 Checkpoint 和 Hooks 的**方案**,但实现后只说"已采纳建议",没发最终代码让司马懿确认。用户发现后指出:
> "一键三连最终肯定是以司马懿 review 完你的最终成果物收尾的,不是他 review 完你的方案就算完事" [pangtong/experience]
### 正确做法
**一键三连 = 改文档 → 改代码 → 司马懿确认最终代码片段**。每个方案落地后,必须把最终代码(不是方案描述)发给评审者确认。司马懿在评审中多次明确引用代码行号做交叉验证,这种精度只有看最终代码才能做到。
### 关键细节
- "已采纳"不等于"已验证"——采纳后的代码可能引入新问题
- 司马懿多次在代码评审中发现方案评审没暴露的 Bug(如 `_do_retry` 续杯失效)
- 续杯评审中,司马懿看到最终代码才发现 on_complete=None 导致幻觉门控丢失
### 反面教训
未确认最终代码导致 Checkpoint 实现与设计不一致,返工修复。
---
## 卡片 2:状态机枚举值不一致是高频 Bug 根源
```yaml
---
category: "代码评审"
tags: [状态机, 枚举, 一致性]
severity: "high"
frequency: 3
sources: ["simayi-correction"]
---
```
### 场景
系统有多处引用任务/节点状态值(数据库查询、状态转换、恢复逻辑),如果枚举定义与实际使用不一致,会导致静默失败。
### 错误做法
M1 遗留 Bug P0-2`get_all_executing_tasks` SQL 查询用 `'executing'`,但实际枚举值是 `'working'`,导致重启恢复时 working 节点被遗漏。
> "启动恢复查询用节点状态包含 'executing',但实际节点状态枚举中无此值(应为 'working'" [pangtong/simayi-review]
E2E 测试发现 `failed→pending` 转换不清 `assignee`,与另一路径 `ticker._transition_status` 的行为不一致。
### 正确做法
1. 状态枚举集中定义,所有引用点从枚举取值,硬编码字符串零容忍
2. 状态转换函数只保留一个入口(如 `_transition_status`),禁止分散在多个模块
3. 评审时重点检查:新代码中的状态值是否与枚举定义一致
### 关键细节
- 司马懿在 counter v2.1 评审中发现 `max_per_agent=2` vs 设计文档的 `1`,也是同类问题
- auto-sync 可能静默修改配置文件,需要与设计文档对照
### 反面教训
状态不一致导致任务永久卡住(P0-1)和重启恢复失败(P0-2)。
---
## 卡片 3:评审者自我纠正是高质量评审的标志
```yaml
---
category: "代码评审"
tags: [自我纠正, 评审质量, 诚实]
severity: "high"
frequency: 3
sources: ["simayi-correction", "mail"]
---
```
### 场景
评审者在审查代码时可能做出错误判断。高质量的处理不是掩盖,而是主动承认并修正。
### 错误做法
无。此处记录的是正确做法的范例。
### 正确做法
庞统在评审 spawner-monitor 时发现两处自己之前的判断有误,主动发邮件给司马懿修正:
> "修正 1: 问题4 recent_compact 不是死代码,是设计实现遗漏" — 原判断"建议删除",修正为"设计文档 §B2/B3 明确要求" [pangtong/mail]
司马懿回复:
> "你说得对,我判断错了。设计文档 §B2/B3 明确要求区分 compact 进行中和进程卡死" [simayi/mail]
### 关键细节
- 评审者翻回设计文档交叉验证,是发现误判的有效手段
- 庞统和司马懿都在同一次评审中做了自我纠正——这说明交叉验证机制在工作
- 修正邮件明确标注"修正"而非静默修改,保留了审计轨迹
### 反面教训
如果不主动修正,错误判断会被当作已确认结论传播,后续实现可能基于错误假设。
---
## 卡片 4:设计文档-代码一致性审查不可省
```yaml
---
category: "系统设计"
tags: [设计文档, 代码一致性, 三层审查]
severity: "high"
frequency: 4
sources: ["simayi-correction"]
---
```
### 场景
迭代开发中设计文档可能更新不及时,或者代码实现偏离设计。需要 PRD→设计→代码的三层一致性审查。
### 错误做法
spawner-monitor 实现中,设计文档 §B2(compact 进行中继续等)在代码中完全遗漏。司马懿初评时也未发现,直到庞统重新读设计文档才暴露:
> "代码只实现了 B1/B3/B4B2 遗漏了。recent_compact 是 B2 实现的数据基础" [pangtong/mail]
### 正确做法
1. 每次代码评审前,先对设计文档逐条检查实现覆盖度
2. 设计文档用明确编号(§B1/B2/B3)便于逐条追踪
3. 庞统安排了 "背靠背一致性审查":PRD→设计→代码全覆盖审计
4. 司马懿的核心职责就是做这种三层对照
### 关键细节
- 庞统专门发了 #311 邮件请求"PRD→设计→代码三层一致性审查"
- 司马懿在 counter 生命周期评审中发现设计说 crash 3 次标 failed,但代码只 release counter 无计数
### 反面教训
设计-代码不一致导致假死检测失效,系统在 compact 后无法区分"还在跑"和"卡死了"。
---
## 卡片 5:方案确认的完整闭环标准
```yaml
---
category: "系统设计"
tags: [闭环, 一键三连, 确认标准]
severity: "medium"
frequency: 2
sources: ["experience"]
---
```
### 场景
多方案并行开发时,哪些方案算"完成"容易模糊。
### 错误做法
庞统在 M2 阶段同时推进多个方案,只跟踪了"方案评审"状态,没跟踪"最终代码确认"状态。用户追问才发现 Checkpoint 和方案 C 的代码未闭环。
### 正确做法
**一键三连收尾标准**(用户明确定义):
1. 改文档(设计/方案)
2. 改代码(实现)
3. **司马懿 review 并确认最终代码片段**
每步完成都要有明确证据(邮件编号、代码片段引用)。
### 关键细节
- 状态跟踪表要包含三列:方案评审 / 最终代码确认 / 状态
- 司马懿的确认必须是具体的("代码片段与实际改动一致"),不是笼统的"通过"
### 反面教训
未闭环的方案被标记为"已完成",实际遗漏了代码审查环节。
---
## 卡片 6:Counter/锁的生命周期必须贯穿完整调用链
```yaml
---
category: "系统设计"
tags: [counter, 锁, 生命周期, 资源管理]
severity: "high"
frequency: 3
sources: ["simayi-correction"]
---
```
### 场景
Agent 并发控制需要 counter/锁机制。如果 counter 的 acquire-release 不成对,会导致永久占满或双重释放。
### 错误做法
spawner `_handle_exit` 中 retry 路径提前调 `on_complete("retry_release")` 释放 counter,但续杯 spawn 时 counter 已释放,导致同一 Agent 被并发 spawn 多次:
> "retry 路径提前调 on_complete("retry_release") 释放 counter,导致续杯 spawn 时 counter 已空" [simayi/mail]
司马懿在评审中发现 `_do_retry` 中 counter 仍被占用但 on_complete=None,导致续杯永远无法 acquire。
### 正确做法
1. **Counter 贯穿 retry 链**acquire 在 spawn 前,release 在最终退出(done/failed)后
2. Retry 不释放 counter,而是复用同一个 counter slot
3. 司马懿建议的 cooldown 机制:release 后短期冷却,防止立即 re-acquire 导致抖动
4. 三层控制:cooldown → global limit → per-agent → per-session
### 关键细节
- 所有 `release` 调用都要带 session_idcounter v2.1 的教训)
- on_complete 回调的时序:进程退出 ≠ 回调完成
- 广播 spawn 路径也要 catch AgentBusyError
### 反面教训
counter 提前释放导致 zhipu API 429rate limit 被打爆),Gateway 假死。修复后 counter 泄漏导致续杯死循环。
---
## 卡片 7:进程退出 ≠ 资源释放
```yaml
---
category: "系统设计"
tags: [进程管理, 回调时序, on_complete]
severity: "high"
frequency: 2
sources: ["simayi-correction"]
---
```
### 场景
异步系统中,子进程退出和回调执行不是原子操作。如果假设进程退出时资源已释放,会出现竞态条件。
### 错误做法
spawner 在 retry 时:进程退出 → on_complete 未调用 → counter 仍占用 → spawn_full_agent 的 can_acquire 返回 False → AgentBusyError → 续杯永远不会成功。
### 正确做法
1.`_do_retry` 中手动释放 counter,并将 on_complete 设为 None 防止 double release
2. 资源释放应该有明确的触发点,不能依赖"进程退出自然释放"
3. 司马懿在评审中强调:需要确认每条退出路径都有对应的资源释放
### 关键细节
- on_complete=None 后续杯 spawn 完成无幻觉门控,靠 ticker 超时兜底——影响可接受但需要记录
- 安装目录同步是必须的检查项(司马懿每次评审都验证)
### 反面教训
未处理回调时序导致续杯死循环,任务永远无法完成。
---
## 卡片 8:续杯(retry)机制是最大假死来源
```yaml
---
category: "编排调度"
tags: [续杯, retry, 假死, 死循环]
severity: "high"
frequency: 5
sources: ["simayi-correction"]
---
```
### 场景
任务执行超时后需要 retry"续杯")。如果 retry 机制本身有 Bug,会导致无限重试或永久卡住。
### 错误做法
多个 Bug 叠加导致续杯死循环:
1. retry_count 在续杯中永远不变(retry_count=1/3),续杯永不停
2. Mail 续杯用 RETRY_PROMPT(含 review 标记指令),Agent 标了 review,但 _mail_auto_complete 只认 done
3. 续杯创建新 session 而非复用 main session,每次重试都是全新上下文,丢失之前工作
### 正确做法
1. 续杯模板必须专用(MAIL_RETRY_PROMPT),明确禁止状态转换命令
2. retry_count 必须在每次续杯时递增,max_retries 兜底
3. 续杯应复用 session 保持上下文连续性
4. 司马懿在 Bug-7 评审中确认:retry 应复用 main session,每次重试不创建新 session
### 关键细节
- 续杯模板的"不要执行状态转换"指令是关键——Agent 容易自作主张标 done
- 司马懿在 Bug-8 评审中发现 use_main_session 在三处(主 dispatch、legacy broadcast、retry)必须统一
### 反面教训
续杯死循环阻塞 tick,导致整个调度停滞。PM2 restart 后旧 session 仍在续杯,新 session 又被创建,资源爆炸。
---
## 卡片 9:广播路径与单播路径行为必须一致
```yaml
---
category: "编排调度"
tags: [广播, 单播, 路径一致性]
severity: "medium"
frequency: 3
sources: ["simayi-correction"]
---
```
### 场景
任务调度有两条路径:指定 Agent 的单播和广播认领。如果两条路径的行为不一致,某些任务类型会系统性地出问题。
### 错误做法
广播路径 `ticker._broadcast_claim``spawn_full_agent` 时没传 `task_id``db_path`,导致续杯时无法查询任务状态,retry_count 永远不变:
> "广播路径不传 task_id/db_path → 续杯时 retry_count=1/3 永远不变" [simayi/mail]
### 正确做法
1. 所有 spawn 路径(单播、广播、retry)必须传递完整的上下文参数
2. 评审时重点检查:新功能是否覆盖了所有 spawn 路径
3. 司马懿多次提醒:广播 spawn 路径也要 catch AgentBusyError
### 关键细节
- Mail 变广播导致所有 Agent 假死的事件就是路径不一致的典型案例
- 司马懿在 v2.7.2 Pipeline 重构评审中建议统一 TickResult 返回值 + 方法注册
### 反面教训
广播路径缺失参数导致续杯死循环,影响所有通过广播认领的任务。
---
## 卡片 10:E2E 测试必须用真实环境
```yaml
---
category: "编排调度"
tags: [E2E测试, 真实环境, 集成测试]
severity: "medium"
frequency: 3
sources: ["simayi-correction"]
---
```
### 场景
单元测试只能验证单个模块逻辑。编排系统的 Bug 往往出在模块交互(spawn → 执行 → 续杯 → 状态转换),只有真实环境 E2E 能暴露。
### 错误做法
早期只跑了单元测试,部署后发现大量中间状态卡住的任务。用户发现:
> "dashboard的任务中,大量的停在了中间阶段" [user/simayi-correction]
### 正确做法
1. E2E 测试走真实 Ticker + 真实 Agent spawn,不手动推动状态
2. 测试用例覆盖完整生命周期:创建→调度→执行→续杯→完成/失败
3. 司马懿独立跑 E2E 测试并做根因分析,与庞统的修复做交叉验证
4. 每次修复后全量重跑 E2E(不是只跑失败的用例)
### 关键细节
- 司马懿的 E2E 报告格式:通过/失败计数 + 每个失败用例的根因分析
- 测试从 6/10 → 8/10 → 9/10 → 10/10 的渐进修复过程证明 E2E 驱动开发的有效性
### 反面教训
未做 E2E 导致假死问题在生产环境中才暴露,排查成本远高于测试阶段。
---
## 卡片 11:Agent 认领任务前必须检查角色匹配
```yaml
---
category: "Agent 行为"
tags: [认领, 角色匹配, 任务分配]
severity: "medium"
frequency: 4
sources: ["simayi-correction"]
---
```
### 场景
黑板广播任务时,所有 Agent 都能看到。如果审查者(司马懿)认领了编码任务,会导致角色错配。
### 错误做法
simayi-challenger(审查者)认领了 coding 类型任务:
> "问题1Agent 角色错配(P1)— simayi-challenger(审查者)认领了 coding 任务" [pangtong/mail #337]
数据中有大量司马懿被广播认领 E2E coding 任务的记录(frag_0076, frag_0305, frag_0320 等),说明这是系统性问题。
### 正确做法
1. 任务模板中明确角色匹配规则:只认领符合专长的任务
2. 评审/审查类角色不应认领编码任务
3. 任务描述中包含"只认领符合你专长的任务"的提示,但 Agent 仍然会忽略
### 关键细节
- 这个问题出现了 20+ 次(大量 E2E 广播任务被司马懿认领)
- 根因是黑板没有角色过滤机制,完全靠 Agent 自律
### 反面教训
角色错配导致编码任务被审查者执行,产出质量不符合预期。
---
## 卡片 12:inform 类型邮件不应触发完整 Agent 执行
```yaml
---
category: "Agent 行为"
tags: [邮件, inform, 资源浪费]
severity: "high"
frequency: 2
sources: ["simayi-correction", "mail"]
---
```
### 场景
Agent 之间的邮件有两种:需要回复的(request)和纯通知的(inform)。如果 inform 也触发完整 Agent 执行,会浪费大量 token 和 counter 资源。
### 错误做法
庞统的 daemon 用 `openclaw agent --agent simayi-challenger --timeout 300` 处理 inform 邮件,Agent 被完整 spawn 执行,310s 超时后重新投递:
> "daemon 用 openclaw agent --agent simayi-challenger --timeout 300 处理 inform 类型邮件,310s 超时后重新投递" [simayi-correction]
### 正确做法
1. Mail 类型简化为两种:inform(通知)和 request(需要回复)
2. inform 类型由 ticker 直接标 done,或用轻量 prompt"读完标 done")让 Agent 感知但不做完整执行
3. 默认类型是 request,inform 是显式指定的特殊场景
### 关键细节
- 庞统和用户讨论后确认:inform 仍让 Agent 看到内容(可能包含重要信息),但 prompt 告诉它不需要回复
- 这种设计比 ticker 直接标 done 更合理
### 反面教训
inform 触发完整执行 + 超时重投递,形成死循环,消耗大量 API 调用和 token。
---
## 卡片 13:回答用户问题要直接,不要绕弯子
```yaml
---
category: "沟通协作"
tags: [沟通, 直接回答, 效率]
severity: "medium"
frequency: 3
sources: ["pangtong-correction", "experience"]
---
```
### 场景
用户提出具体问题时,Agent 有时回避核心问题,用相关信息绕弯子。
### 错误做法
庞统在讨论 compact 卡死问题时,反复讨论"compact 发生的概率"和"v1 vs v2 的区别",而用户已经明确说了"集中讨论 compact 发生后如何处理"
> "你说得对,我一直在说废话。子进程就是 `openclaw agent`,等子进程就是等 `openclaw agent` 执行完" [pangtong/experience]
> "别绕了,我都说过了,请你集中在 compact 发生之后如何处理" [user]
### 正确做法
1. 先直接回答用户的问题(哪怕答案是"不能/不知道"
2. 不确定时说"不能",然后说明可以做什么
3. 庞统最终的模式值得学习:"处理方式就三个问题:超时后怎么处理、重试用什么 session、重试几次后放弃。这就是全部。没有更复杂的了。"
### 关键细节
- "不能区分超时原因"本身就是一个有用的答案
- 不需要区分原因也能给出处理方案——区分原因只对监控有意义
### 反面教训
绕弯子浪费多轮对话,用户反复追问同一问题。
---
## 卡片 14stdout JSON 解析路径必须实测验证
```yaml
---
category: "系统设计"
tags: [JSON解析, stdout, 实测]
severity: "medium"
frequency: 2
sources: ["simayi-correction"]
---
```
### 场景
`openclaw agent --json` 的输出结构可能随版本变化。如果解析路径是猜的而非实测的,会导致静默失败。
### 错误做法
spawner 代码取 `data.response.meta.transport`,但实际 `openclaw agent --json` 的顶层结构是 `{ response: { meta: { transport, ... } } }`
> "P0_parse_stdout_json 解析路径错误(根因)— openclaw agent --json 输出格式是 { response: { meta: { transport, ... } } } 但代码取的路径不对" [pangtong/mail]
### 正确做法
1. 解析外部命令输出前,先用实际命令验证输出格式
2. 评审时要求看到实测输出样例
3. 司马懿在 Spawner v3.0 评审中指出 JSON 解析路径需验证:`data.result.meta.executionTrace` 是新路径,需确认
### 关键细节
- 这个 Bug 从第一天就存在("从第一天就存在的根因 bug")
- 静默失败比报错更危险——解析不到数据时走了 fallback,掩盖了问题
### 反面教训
解析路径错误导致 stdout 信息丢失,所有依赖 stdout 判定的功能(如幻觉门控)失效。
---
## 卡片 15:跨项目调研的实践对照要做落地映射
```yaml
---
category: "经验总结"
tags: [调研, 对照表, 落地映射]
severity: "low"
frequency: 2
sources: ["experience"]
---
```
### 场景
Agent 调研多个开源项目的最佳实践后,需要将发现映射到自己的项目架构中。
### 错误做法
庞统调研了 OpenAI Agent SDK、LangGraph、Claude Code 三个项目,产出了详细的对照表,但部分条目只停留在"可借鉴"层面,没有明确"怎么做"。
### 正确做法
1. 调研产出必须包含"moziplus 可借鉴点"列,明确优先级(M2 直接借鉴 / 够用 / 暂不需要)
2. 标注哪些是**可直接复用的模式**(如 Lifecycle Hooks → on_node_start/end),哪些需要适配
3. 司马懿在评审 Pipeline 重构时引用了业界调研,确认"Temporal 模式最接近我们"
### 关键细节
- 调研本身就是 Agent 经验声明的一种形式
- 庞统的对照表格式(优秀实践 / 来源 / 核心模式 / 可借鉴点)是好的模板
### 反面教训
没有落地映射的调研容易变成"资料搬运",无法转化为架构改进。