Commit Graph

2 Commits

Author SHA1 Message Date
iomgaa 76495d9e39 fix(session,_recovery,types): 修 Codex 对抗审查报的五条
逐条核实全部成立。最重的是第一条:停止判定的结果只存在于结束记录里,而步记录与结束记录是
两次写。崩在两者之间那次判定就丢了,恢复照常回到预算准入——一次「恰好用满预算完成」被改写
成「预算耗尽」(两条轨迹长度一模一样),一次已经达成目标的运行接着往下跑,一次该以连续解析
失败收尾的运行再花一次模型调用。修法是续跑时把那次判定重演一遍:判定要的东西全在日志里
(动作结果在步记录那条原子写里、工具名在步记录上、模型回复在模型调用结果里)。最终回答那
一档更麻烦,答案文本只存在于结束记录里,靠重新解释那条已存下来的回复找回来——那时副作用还
没发生,重新解释是安全的。五种收尾各一条测试。

其余四条:
- 模型调用失败步在续跑时补写,prompt_chars 填了 0。重新装配出来的和被打断时是同一份,
  照它算。填 0 是把轨迹里那一列改写成假值。
- 取消正好落在写运行开始记录那一下时不留取消标记,留下一份只有开始记录的日志:run 因标识
  已存在而拒绝、resume 当成可以从第 0 步续跑。用 shield 让那条记录一定落地,取消结束记录
  才有地方挂(反过来先写结束记录会拼出结构上说不通的日志)。
- ModelCallResult 能同时带 reply 和 failure,而恢复只看 reply is None,于是把一次失败的
  调用当成成功、接着解释那段回复执行动作。加构造期不变量。
- call_id 空串一路能进持久化记录。docstring 里那句「绝不能是空串」原本没有任何东西守着。

顺带把主循环里重复的一次上下文装配去掉,并把只在续跑第一次迭代成立的那个分支挪出循环。
2026-08-10 03:12:42 -04:00
iomgaa 0132887cb9 feat(session): 落成主循环、两个装配对象与两条入口
A 到 H 八档由这里唯一执行,每档的判定住在 _stopping。三处容易写错的地方都有测试钉着:
恰好用满预算完成记成目标达成而不是预算耗尽(结算在下一次迭代开头);规模超限不写步也不写
意图(唯一一种真的一步都没走的终止);未执行与环境故障两档的观察取库合成的那段,执行器
给的不进历史。

写入序列断言成一条流水:模型意图 → 模型结果 → 动作意图 → 步记录,模型调用失败也落一条
结果记录(不落的话恢复会把一次已知的失败判成状态未知走重放)。结果 ID 从运行标识与序号
推出来而不是随机数——重放同一步拿到同一个 ID,轨迹里也少一列每次都不同的值。

写这块时修掉的两个自己的坑:
- 重放时会重复写意图,而同一步两条同种意图被 _recovery 判成「日志被并发写过」,一次成功的
  重放反倒把日志弄坏。加了两个一次性开关跳过已经落过盘的那条。
- _recovery 数连续解析失败时把模型调用失败那一步也算进去了。它压根没走到解释器,判据改成
  「解释器给了一段回喂文本」——解析失败必定带着那段说明,模型调用失败没有。

取消:CancelledError 原样重抛、run 不返回结果,但结束标记要在宽限期内尽力写下去,写不完
只记日志不再抛(再抛会把取消这件事本身盖掉)。并发隔离:全部可变状态住在每次运行一个的
_Driver 里,定义与请求都是 frozen 的,有一条并发跑两次的测试。

Budget 加了构造期校验(四项都必须为正):零或负数会产出一次「零步、预算耗尽」的运行,
那和一次真的跑满上限的运行在停止原因上完全一样,混进统计里分不出来。

事件出口收下了但没有调用点——Event 还没有字段,发一条内容为空的事件既没用又会变成一份
要兼容的形状。
2026-08-10 02:59:34 -04:00