From bd26756da9b98039f24a046085cac130e300e24d Mon Sep 17 00:00:00 2001 From: xcs Date: Sat, 29 Aug 2026 07:42:27 +0800 Subject: [PATCH] =?UTF-8?q?docs(adr):=20ADR-014=20=C2=A73.2/=C2=A74/=C2=A7?= =?UTF-8?q?5/=C2=A711=20=E5=AF=B9=E9=BD=90=E8=90=BD=E5=9C=B0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=20=E2=80=94=E2=80=94=20=E4=B8=89=E5=8A=A8=E8=AF=8D?= =?UTF-8?q?=E6=94=B9=E4=BA=94=E5=8A=A8=E8=AF=8D,=E6=8B=8D=E5=86=85?= =?UTF-8?q?=E6=AC=A1=E5=BA=8F=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 评审指出 ADR-014 被标「已实现」后,正文 §3.2/§4/§5 仍是实施前的三动词提案, 与真实 schema(auto-recipe.ts)和 turn-runner.ts 的拍内次序对不上,会误导 下一个直接翻开这份 ADR 的读者(W05 重写实现者)。逐条对代码核过后分两档改: §3.2 动词表与 §5 拍内次序按代码改对(含之前遗漏的「有卡未答就静默重跑配方」 那道前置门);§4 的两个例子不逐行改,改成开头一段按语指出三处已知出入 (workOrder→as、view 挪到 PlanStep、self/joinRows 未实现)。顺带把 §11 待定的前两条标成已定并指回销账位置。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet --- docs/adr/ADR-014-deterministic-steps.md | 63 +++++++++++++++++++------ 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/docs/adr/ADR-014-deterministic-steps.md b/docs/adr/ADR-014-deterministic-steps.md index 1784776..0812545 100644 --- a/docs/adr/ADR-014-deterministic-steps.md +++ b/docs/adr/ADR-014-deterministic-steps.md @@ -68,18 +68,21 @@ - ADR-004 的立场是「系统不执行代码即知包为何物」;配方是**数据**,装进来仍然只是数据, 可静态审、可回放、可在治理台上摆出「这一步系统会做什么」。执行代码则相反。 - 执行第三方包要配沙箱、超时、内存墙,而 `scanArtifact` 只是文本扫描,拦不住混淆。 -- 配方要表达的形状**很窄**(§3.2 三个动词),不值得为它开一个能跑任意代码的面。 +- 配方要表达的形状**很窄**(§3.2 五个动词),不值得为它开一个能跑任意代码的面。 **代价**:表达力有上限。上限之外的**不要硬塞** —— 退回模型,或者把那一段切成独立的 agent 步(§3.4)。 -### 3.2 三个动词,复用已有的两套求值 +### 3.2 五个动词,复用已有的两套求值 -配方只有三个动词: +**改口**(2026-08-29,落地时定):设计之初只列了三个,实现时补了 `analyze` 与 `submit` +(§11 待定①②随之销账,见该节按语)。配方有五个动词: | 动词 | 做什么 | |---|---| | `project` | 从「某个来源的每一行」投影出台账行,`draftWrite` 落草稿层 | -| `card` | 发一张人类工具的卡(执行工单 / 数据工单 / 数据录入卡),**至多一张** | +| `analyze` | 一次结构化 AI 调用:系统组装入参、声明出参 JSON Schema,出参落拍内作用域供后续 `project` 消费。不给工具、不给记忆、不多轮 | +| `card` | 发一张人类工具的卡(执行工单 / 数据工单 / 决策卡),**至多一张** | +| `submit` | 提交放行,发验收卡挂起(走与模型 `submit` 同一条 `raiseAcceptance`) | | `say` | 一句给人看的交代(模板串,不是模型生成) | 条件与聚合**不发明第二套求值**: @@ -150,6 +153,16 @@ ## 4. 声明形状 +> **这一节是 2026-08-28 的原始提案,落地时有出入。精确语法以 +> `packages/contracts/src/auto-recipe.ts` 与《活动开发规范》§4.6 为准。** +> 已知的三处差异(下面 §4.1/§4.2 的例子按原始提案写,未逐行改过,读的时候按这三条心里默认翻译): +> `workOrder` → `as`(且没有 `entry`,只有 `execution` / `decide`);**卡上不写 `view`** —— +> 画面归 `PlanStep.view`(已有机制,一处声明,§3 没提是因为原提案漏写了这条,不是后来删的); +> `{ self: true }` 与 `{ joinRows: … }` **未实现** ——`each: 'in:candidateInternalPns'` 这种 +> **扁平数组**(元素不是对象、是原始值)的本项,实现里是 `rowsAt` 把每个元素包成 `{ value: 原值 }` +> 再取 `{ col: 'value' }`,不是 `self`;`joinRows`(多行一列拼成串)没有对应写法,真要拼多行, +> 目前得退回模型或切成独立步骤。 + ```ts // PlanStep 新增(可选,老活动一个字不改) auto?: AutoRecipe; @@ -243,25 +256,42 @@ auto: { ## 5. 引擎行为(拍内次序) +**改口**(2026-08-29,与 `turn-runner.ts` 实际次序对齐 —— 落地时多了 `analyze`/`submit` +两个动词,分支也比原提案细): + ``` tick 开始 + ├─ ⓪ 这一单还有任何卡没交回(不论哪一步发的)→ 这一拍不问模型; + │ 若当下这一步声明了 auto → 静默跑一遍它的配方(quiet:只跑 project / say, + │ 「保存即铺格」——人在件上按了保存,系统按最新草稿把该铺的行铺出来)→ + │ 记痕「等人交回」→ status awaiting,这一拍到此为止,不进入下面任何一步 ├─ ① 拍首自动收步(现有:doneWhen 为真的项,人工激活项需 worldTouched) ├─ ② 算 current(当下在办的那一步) - ├─ ③ current.auto 存在 → 跑配方 + ├─ ③ current.auto 存在 → 跑配方(走到这里已经过了 ⓪ 那道门,`quiet` 恒为 false; + │ `do` 数组按声明顺序逐笔执行,五个动词可任意穿插) │ ├─ 求 let - │ ├─ 按序执行每一笔(when 假则跳过) + │ ├─ 按序执行每一笔(when 假则跳过;project 写/撤过东西后,下一笔重新取一次读数) │ │ · project → 逐行 draftWrite(同 PK 覆盖;值没变静默跳过) - │ │ · card → 先看 worldTouched,发过就跳过;发了标 worldTouched、烧 humanCalls + │ │ · analyze → 一次结构化 AI 调用,出参落拍内作用域 `result:`,供后续 project 消费 + │ │ · card → 先看 worldTouched,发过就当这一笔已完成、跳过(不当被拒); + │ │ 发了标 worldTouched、烧 humanCalls,这一笔之后停(交出控制权) + │ │ · submit → 记下 rationale,这一笔之后停(交出控制权) │ │ · say → 进对话区 │ ├─ 每一笔留痕(by=系统,detail.auto) - │ └─ 抛错 → 留痕 + 交人(不重试、不退回模型) - ├─ ④ 发了卡 → status awaiting,收拍(零 token) - ├─ ⑤ 没发卡 → 重求 doneWhen;真了收步,回 ② (封顶 8 轮,与现有自动收步同款) - ├─ ⑥ 这一步有 guide → 照常问模型(半 auto) - └─ ⑦ 无 guide、doneWhen 不成立、也没有卡在等 → **交人**(配方没做到位,是包的 bug) + │ └─ 抛错 → 留痕 + 交人(不重试、不退回模型),这一拍到此为止 + ├─ ④ 配方发了卡 → 标 worldTouched、按声明推画面,status awaiting,收拍(零 token) + ├─ ⑤ 配方 submit → 发验收卡挂起(走与模型 `submit` 同一条 `raiseAcceptance`),status awaiting,收拍 + ├─ ⑥ 没发卡也没 submit,但配方写过东西(project 落了行/撤了行)→ 重求 doneWhen; + │ 真了收步,下一拍重新导航(封顶 8 轮,与现有自动收步同款) + ├─ ⑦ 卡答过了就收步(近道):doneWhen 未声明 · 卡已发过(worldTouched)· 配方会发卡 + │ · 这一步**没有** `guide`(全 auto)→ 引擎直接把这一项收掉,不问模型 + │ (**半 auto 的步 —— 声明了非空 `guide` —— 不走这条**,R9,见 §3.3) + ├─ ⑧ 这一步有 guide → 照常问模型(半 auto) + └─ ⑨ 无 guide、doneWhen 不成立、也没有卡在等 → **交人**(配方没做到位,是包的 bug) ``` -**预算**:配方不烧 token;发卡烧 `humanCalls`;配方段在同一 tick 内,不额外计 step。 +**预算**:配方不烧 token(`analyze` 是例外 —— 它是一次真实的模型调用,烧 token、计入预算, +但不计入 `stalledTurns` 熔断计数,见 debt.md);发卡烧 `humanCalls`;配方段在同一 tick 内,不额外计 step。 --- @@ -350,8 +380,11 @@ tick 开始 ## 11. 待定(要用户定的) -1. **决策卡要不要给配方**:S4 第一张卡的 ask 内容是计数投影,确定;但决策卡是岔路口,给了会不会滑坡? -2. **工具串接要不要做第四个动词**:S3 的 `compare → recommend` 之间模型只是转手 counts,却占掉整整一拍。 +1. ~~**决策卡要不要给配方**~~:S4 第一张卡的 ask 内容是计数投影,确定;但决策卡是岔路口,给了会不会滑坡? + **已定:给了**——`card.as: 'decide'`(`variant:'review'` 时放宽到单选项),见 §8「两处改口」。 +2. ~~**工具串接要不要做第四个动词**~~:S3 的 `compare → recommend` 之间模型只是转手 counts,却占掉整整一拍。 + **已定:没照原样给**——没有做「按序调用多个工具」那个第四动词,换成了更窄的 `analyze` + (一次结构化 AI 调用,不给工具不给记忆不多轮),见 §3.2 与 `docs/debt.md`「平台缺『确定性步骤』」条。 3. **`humanWrites` 该不该细到列**:现在是表级。auto 铺骨架 + 人填内容之后,列级更准。 4. **S2 与比对工具的 join 是否重复**:`compare-parameters` 1.2.0 起自己从台账取参数对, 而 S2 让模型抄的 `difference_item` 对照表是**同一份 join**。一份事实做了两遍,一遍还是手抄的 ——