docs(design): 落成 0005,修订存储接缝的原子写与步记录三处
第二轮 Codex 对抗审查报的五条全部核实成立,0003/0004 已冻结,修订新写一份。 五条都不改任何决策的结论,改的是结论到形状之间那一段的落实。 决策一:步记录加一个布尔的完成信号列。0004 决策二承诺靠它区分两条完成通路, 而决策四的字段表里没有这个字段。它自己就够区分,不必查注册表—— 事后分析手上常常只有轨迹文件。 决策二:存储接缝六个方法重切一刀,把「按预分配 ID 写一条结果」拆成 「写模型调用结果」与「写动作结果与步记录」。原子性从散文变成签名里不可表达其他形态。 决策三:观察那一列的口径改成「回填进历史的那段文本」,不加字段。 dissect 的 render_messages 把 step.observation 原样套模板发出, raw_output 也是解析器截断后的版本——两侧同构,都是一段文本加一个数字。 决策四:作废 0004 词表里「一个可能取不到的完成信号」,那是上一轮 fatal bug 的原话。 决策五:存储接缝加前缀持久性。少了它,一次明明成功的模型调用会被恢复 记成「状态未知」,而它成功的证据就在同一份日志里。 结论已回写 architecture.md 第九节与决策索引;migrations/dissect.md 需求七 补上「分开记的是文本加数字,不是两段文本」,堵掉这次审查里出现过的误读。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -402,13 +402,31 @@ GovDoc 的编排层读停止原因决定这个阶段算不算可挽救,写任
|
||||
留下了记录;工具注册表上的完成标记是 agent 自己宣布的(调了某个被标记的工具即视为达成)。
|
||||
两者都由库消费,不各开接缝。
|
||||
|
||||
**存储**(挂定义)。六个方法,全部带运行标识,端口不持有「当前运行」的隐式状态——一个有
|
||||
隐式当前运行的端口在并发下会把 A 的意图写进 B 的日志。两个形态:一种逐行写本地 jsonl
|
||||
文件,一种写关系数据库。
|
||||
**两者共用一个停止原因,靠步记录上的完成信号那一列区分。** 停在「目标达成」而那一列为
|
||||
「已完成」的走的是环境那条,为「未完成」的只可能是完成标记被触发。这个区分不能靠「工具名
|
||||
落在注册表的完成标记里」来做——注册表不在轨迹文件里,而事后分析手上常常只有那个文件。
|
||||
|
||||
写入粒度是契约的一部分:两条意图记录各自单独落地;**动作结果与步记录必须作为一次原子写入
|
||||
落地**,要么都可见、要么都不可见。后者不原子的话,崩在两者之间会让那一步的历史文本永远丢失,
|
||||
而恢复判定会把它读成「执行完了,跳过」。
|
||||
**观察那一项是回填进历史的那段文本,不是环境返回的原文。** 执行器丢掉了多少由「被截断的
|
||||
字符数」单独记。存原文的话,恢复时得拿原文重跑一遍截断逻辑才能得到历史,而截断逻辑会随
|
||||
版本变——那正是记录逐步结果要消掉的那类重算。
|
||||
|
||||
**存储**(挂定义)。六个方法:写运行开始、写一条意图、写模型调用结果、写动作结果与步记录、
|
||||
读回整份日志、写运行结束。全部带运行标识,端口不持有「当前运行」的隐式状态——一个有隐式
|
||||
当前运行的端口在并发下会把 A 的意图写进 B 的日志。两个形态:一种逐行写本地 jsonl 文件,
|
||||
一种写关系数据库。
|
||||
|
||||
写入粒度是契约的一部分:两条意图记录各自单独落地;**动作结果与步记录一次原子落地**,要么
|
||||
都可见、要么都不可见。不原子的话,崩在两者之间会让那一步的历史文本永远丢失,而恢复判定会
|
||||
把它读成「执行完了,跳过」。**这条不靠散文约束,靠方法的切法**——两者由同一个方法收下,
|
||||
「只写了一半」在签名上无从表达。那个方法的动作结果一项可以为空(模型调用失败的步、解析
|
||||
失败的步照样有步记录),但必填、无默认值。
|
||||
|
||||
**存储实现必须保证前缀持久性:第 k 次写入被确认已经持久时,第 1 到 k-1 次写入也已经持久。**
|
||||
少了它,模型调用结果还在缓冲区而后面那条动作意图(屏障)已经落盘,恢复会把一次明明成功了
|
||||
的模型调用记成「状态未知」——而它成功的证据就在同一份日志里,那条动作意图正是从它的返回值
|
||||
解释出来的。两个已知形态天然满足:同一个文件的追加写,`fsync` 一次刷掉之前全部;同一个连接
|
||||
上顺序提交的事务,先提交的先持久。代价是排除了「不同记录类型写进彼此无序的多个后端」这种
|
||||
形态,现在没有消费者要它。
|
||||
|
||||
**事件出口**(挂定义)。投递失败由库捕获、记日志、把失败计数加一,然后继续跑。失败不再
|
||||
转成一条事件从同一个出口发出去——那会自我喂食,一个持续失败的出口会让失败处理路径变成
|
||||
@@ -508,7 +526,8 @@ dissect 的每一次运行都是论文数据点,这个性质对结构提了几
|
||||
第八、九、十一节要补上英文名。
|
||||
|
||||
**停止判定的顺序、停止原因的取值、两个预算计数的语义、步记录的字段清单不在本文件里。**
|
||||
这四样已经定了,在 `../design/0004-stopping-and-step-record.md`;落地之后权威转移到代码——按
|
||||
这四样已经定了,在 `../design/0004-stopping-and-step-record.md`,字段表经
|
||||
`../design/0005-storage-atomicity-and-record-fields.md` 修订过;落地之后权威转移到代码——按
|
||||
`../../CLAUDE.md` §0,公共类型的字段与枚举取值的权威是 `src/polyloop/`,不另写参考文档复述。
|
||||
那份 design doc 记的是第一版为什么定成这样,不是查字段的地方。
|
||||
|
||||
@@ -532,6 +551,7 @@ dissect 的每一次运行都是论文数据点,这个性质对结构提了几
|
||||
| 崩溃恢复承诺什么、为什么不承诺原子性、重放策略为什么由工具声明 | `../design/0002-step-level-resume.md` |
|
||||
| 公共 API 分几层、五个接缝为什么是这五个、依赖规则为什么这么定 | `../design/0003-public-api-shape.md` |
|
||||
| 停止原因为什么是这十个、判定为什么按这个顺序、步记录为什么是这些字段 | `../design/0004-stopping-and-step-record.md` |
|
||||
| 存储的方法为什么这么切、为什么要前缀持久性、步记录那三处为什么改 | `../design/0005-storage-atomicity-and-record-fields.md` |
|
||||
|
||||
边界的当前裁决清单(哪些在界内、哪些在界外)在 `scope.md`,那份是常青的,会随新消费者
|
||||
接入而更新。每个下游要迁什么、迁完算不算数在 `../migrations/` 下对应那份。
|
||||
|
||||
Reference in New Issue
Block a user