我先看一下代码,逐条核对这些结论,而不是凭印象评价。

我把 GPT 提到的每一处都对照代码核了一遍(没有跑探针,只读源码),结论先说:问题清单基本靠谱,9 条里 7 条是真 bug,1 条是文档/契约缺口,1 条是刻意的设计取舍被夸大了;但它的修复计划明显过度工程化,尤其批次 4 和批次 5。

逐条核对

#GPT 的说法我的核对我的评级
handoff 恢复丢失P1成立。 save_running 在 handoff 工具结果后立刻落盘(loop.py:1020),此时 RunHead 只有 agent_name=a,没有 pending 字段(checkpoint.py:159)。而且 _apply_handoffactivate() 之前就把 pending_handoff 清空了(loop.py:1129),目标 agent 插件 setup 失败时同样丢。现有测试 test_resume_drains_pending_handoff_call 只覆盖"call 无 result"的情况,恰好没覆盖"有 result 但未切换"。P1,同意
delete_on_success 删早了P1成立,而且比 GPT 说的更值得优先。 complete() 直接 delete(checkpoint.py:90),Session 写入在后。有意思的是 completed-replay 路径(loop.py:288-295)已经是"先 session 后 delete"的正确顺序,注释还写着 "mirroring the normal completion order" —— 但正常路径并不是这个顺序。web supervisor 两处都用了 delete_on_success=True,所以这是 web 实际跑的路径。P1,最先修
父 usage 重复累计P2成立。 _finalize_run 加一次(loop.py:1210),checkpoints.complete() 抛错时 run_completed 仍为 False,异常路径再加一次(loop.py:488)。窗口很窄,修法一行。P2,顺手修
初始化失败无 RunFailedP2成立。 _bootstrap/_resolve_resumetry 之外;RunHandle._iterexcept Exception 注释说 "The loop already emitted RunFailed",对这条路径不成立。result() 语义是对的,只是流契约不完整。P3,小修
摘要 usage 不计入P2成立。 lovia/context/ 里没有任何 usage 字样。默认 summarizer 用的是 run 自己的模型,一次 burst 多次 fold 的开销确实完全隐形,budget 也管不到。P2,同意
摘要分块没扣旧摘要P2成立但被高估。 cap = usable // 2stages.py:411),确实没扣 running 和模板。但探针场景是 4K 窗口 + 15K 字符旧摘要——真正的根因是 max_summary_chars=16_000 没有相对窗口设上限,4K 模型下摘要自己就把窗口占满了。128K+ 窗口下这个问题不会出现。另外 GPT 说注释声称"分块保证不 overflow",实际注释明确写了 oversized chunk 的存在,这里它有点断章取义。P3,小修(扣 count_text(running) + 模板开销,并给 max_summary_chars 加相对窗口的上限)
最后一轮取消仍 completedP2是契约缺口,不算 bug。 文档说 "next safe point",最后一轮文本没有 safe point 是事实。但 GPT 建议的"记录 usage 后、标 completed 前检查"会产生一个尴尬状态:interrupted 快照里带着完整的最终回答但没有 output,resume 时会再跑一轮模型。要么在 transcript.extend 之前检查(丢弃这一轮),要么就在文档里写明"落在最后一轮的 cancel 让这一轮跑完"。我倾向后者——用户已经付费的答案没必要扔。P4,改文档
同名 agent 恢复选错P2成立但已知。 reachable_agents 的 docstring 明确写了 "first reached wins"。加一个 found[name] is not agent → UserError 是几行的事,可以做。P3
指纹漏检同长度改写P2(条件性)这是刻意的取舍,不是 bug。 docstring 写明是 "cheap structural digest",还专门解释为什么排除 tool result 长度。同长度替换 user 消息在 append-only 的 Session 里基本不会发生。要改也很简单——对 Input/Assistant/Call 的内容做 hash(sha1 几 MB 也就毫秒级,GPT "别每轮重 hash 大文本"的顾虑是多余的),而且不需要它说的"指纹版本号":现在 mismatch 就只重置 summary、保留 clear/offload,天然兼容。P4,可做可不做
TokenCounter 缓存断崖性能成立但影响小。 FIFO 淘汰 + 顺序扫描确实会 thrash。但默认没有 provider 实现 estimate_tokens,走的是 chars/4,thrash 后也就 17ms/轮,对比模型调用可忽略。8192 条 entry 的 transcript 本身也罕见。修法几行(按 entries 长度自动扩容,或 memo 淘汰时跳过本轮已访问的)。P4
checkpoint 双重切片性能成立,是指针复制,微秒级。不值得单独 PR。顺手
文档漂移DX成立。 architecture.md:196 写 0.75/0.50,实际 0.85/0.60;"按 session_id 缓存"也已过时。context was …% full 用的是压缩后的 tokens,确实误导。应修

对修复计划的意见

批次 1、2、3 方向对,但每一批的验收清单都膨胀了。 例如批次 1 列的"不可序列化 output 兼容路径",complete() 现在已经处理了。批次 2 说"不能凭 Transferred to … 自然语言重建"——其实工具名 + handoff 图就足够推导,不过我同意直接在 RunHead 加一个可选的 pending_handoff: str | None 更干净。恢复顺序现有代码已经对:先 drain 原 agent 的 pending 工具,再 _apply_handoff,只需要在 drain 前把 state.pending_handoff 从 head 还原。

批次 4 是过度设计。 "提取请求构造逻辑、摘要投影分块、span、不静默截断、每次 fold 前检查取消预算"——这是把摘要改造成半个 Runner。实际需要的只有三件事:

  1. cap 扣掉 count_text(running) 和模板开销;max_summary_chars 加相对窗口的上限。

  2. CompactionRequest 加一个可选的 usage sink(或直接一个 Usage 字段),LLMSummarizer._generate 消费 UsageDelta 累加进去,runtime 在 compact() 返回后并入 run_ctx.usage——不碰 last_input_tokens

  3. 可选:fold 之间检查 cancel token,同时让 except Exception 放过 RunCancelled

批次 5 的"指纹版本化"没必要,理由上面说了。

批次 6 有一条和仓库习惯冲突:"不在每个修复 PR 中机械递增版本"——这个仓库每个 PR 都 bump 版本,CI 靠版本变化触发发布,这是既定流程,不该改。

我建议的顺序

  1. delete_on_success 顺序(web 在用,修法:complete() 总是写 completed 快照 → session → 再 delete)+ 父 usage 双计 + RunFailed 补发。三个都是几行的改动,可以一个 PR。

  2. handoff pending 持久化(一个字段 + resume 还原 + _apply_handoff 成功后再清空)+ 同名 agent 检查。

  3. 摘要 usage + cap 修正,按上面缩小后的范围。

  4. 文档漂移 + % full 提示语义。

  5. 缓存/切片/指纹:随手为之,不单独立项。

整体上 GPT 第二轮的自我修正是有价值的(把几个 P2 的边界说清楚了),它对"做得好的部分"的判断我也同意——view/transcript 分离、preflight 串行 + 执行并行、pair 保护这些不该动。要不要我按上面第 1 批开始做?