Apply independent review fixes to the O2 design and plan
Why: independent review returned PASS-with-fixes with eight non-blocking clarifications. Evidence: review session fdb9367a (fresh, docs-only input), base2b7fdc3head5a7a0da. Impact: docs and task card only. Refs: docs/superpowers/specs/2026-10-19-fast-slow-o2-route-contract-eof-design.md Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -108,7 +108,7 @@
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- Review 记录:独立 Review(Cursor Task 子代理 code-reviewer,fresh_session=true,session=fdb9367a-08bb-4e87-ae9d-ac8608290886;只接收需求、base 2b7fdc3/head 5a7a0da 与两份文档,未转交实施会话);结论 PASS-with-fixes,0 阻塞、8 非阻塞(截断用例、C4/C6 定义、重定向与基址规范化、schema 语义整体迁移、拒绝事件逐字段一致、系统层扁平键与 5 个调用点、哨兵扫描、前提①风险引用),已在后续提交全部闭合。
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
- **允许路径**:`runtime/llm/**`、`runtime/argschema/**`(新)、`runtime/tools/registry.go`、`runtime/tools/registry_test.go`。
|
||||
- **动作**:
|
||||
1. 新包 `runtime/argschema`:`Validate(schema, args json.RawMessage) error`,语义与现 `validateArgs` 逐项相同;`registry.validateArgs` 改为委托,现有 registry 测试不改断言。
|
||||
1. 新包 `runtime/argschema`:`Validate(schema, args json.RawMessage) error`,整体迁移现 `validateArgs` 与 `matchesType`,语义逐项相同;`registry.validateArgs` 改为委托,现有 registry 测试不改断言。
|
||||
2. 解析器:内部选项 `deferMissingDone`;新增计数 `finishChunks`、标志 `toolAfterFinish`。只有在“除缺 `[DONE]` 外全部现有检查通过”时才返回候选终态,否则行为同今。公开 `ParseSSE` 签名与零值行为不变。
|
||||
3. 传输层:`Client.RouteContract`;在 `bodyObserver` 快照后按方案 §4.2 的 C1–C9 判定;放行发 `message_end`(`CompletionBasis=route_contract_eof`,`StreamComplete=false`),拒绝发与今天相同文案的终态失败并附 `ContractCheck`。
|
||||
4. `Event`/`TerminalObservation` 新增 `CompletionBasis`、`ContractCheck`(JSON 字段名 `completionBasis`、`contractCheck`)。
|
||||
@@ -35,12 +35,16 @@
|
||||
| `close_delimited`、`chunked`、`unknown` | 拒绝(C2) |
|
||||
| 缺 Usage | 拒绝(C5) |
|
||||
| Usage 在 `finish_reason` 块之前 / 之后 | 均放行(顺序不作要求,评估稿 §3) |
|
||||
| 两个非空 `finish_reason` 块;`finish_reason=stop` 却有工具 | 拒绝(C4) |
|
||||
| `finish_reason` 之后追加工具片段 | 拒绝(C6) |
|
||||
| 两个非空 `finish_reason` 块;`finish_reason=stop` 或 `length` 却有工具;一块含多个 choice | 拒绝(C4) |
|
||||
| 在帧边界截断、无 `finish_reason`,但 HTTP/2 正常结束 | 拒绝(C4) |
|
||||
| HTTP/2 `RST_STREAM` 或连接中途断开导致读错 | 拒绝(C3) |
|
||||
| 与 `finish_reason` 同一块内的工具片段 / 后续块再出现工具片段 | 放行 / 拒绝(C6) |
|
||||
| 合同开但任一条件不满足 vs 合同关 | 两个终态事件除 `contractCheck` 外 deep-equal |
|
||||
| 哨兵扫描 | `contractCheck`、`completionBasis` 不含正文、工具名、参数或错误原文 |
|
||||
| 半帧 / 无效帧 / 供应商错误帧 | 拒绝(C3/C9) |
|
||||
| ID 或名称缺失、参数非 JSON 对象 | 拒绝(C7) |
|
||||
| 工具名不在请求 `tools`;缺 required;属性类型不符 | 拒绝(C8) |
|
||||
| 模型名不符、基址不符、摘要不符、`APIBase` 覆盖到他址、重定向到他主机 | 拒绝(C1) |
|
||||
| 模型名不符、基址不符(含 http、userinfo、query)、摘要不符、`APIBase` 覆盖到他址、重定向(他主机、同主机改路径、降级) | 拒绝(C1) |
|
||||
| 读取中取消 | 中止事件,无放行 |
|
||||
|
||||
每个用例同时断言 Usage 保留与“放行次数/工具调用数”。
|
||||
@@ -50,7 +54,7 @@
|
||||
## 3. I2 Backend(单元 `o2-i2-backend-route-contract-config`)
|
||||
|
||||
- **允许路径**:`internal/runtime/**`、`internal/settings/**`、`internal/runmgr/**`(仅透传字段)、Backend `deps/agent-native` gitlink(host 改钉,不由 worker 改)。
|
||||
- **动作**:系统层设置键 `models.chat.routeContractEOF`(缺省关闭;用户/项目层与画像覆盖一律忽略);`openLLMClient` 增参 `ResolvedModel` 与配置,三元组与摘要全部精确一致才挂合同;轻量档、视觉改模、子代理独立判断;运行记录与快慢通道收据透传 `completionBasis`。
|
||||
- **动作**:`settings.Schema()` 以 `SettingScopeSystem` 声明三个扁平键 `models.routeContractEOF.model/.baseUrl/.identitySha256`(任一为空即关闭;用户/项目层由 `ManagedPaths` 剥除);`openLLMClient` 增参 `ResolvedModel` 与配置,三元组与摘要全部精确一致才挂合同(按三元组、不看 `Kind`),5 个调用点 `llm.go`、`sub_loop.go`、`squad_runner.go`、`verify.go`、`workflow_compiled.go` 全部覆盖;轻量档、视觉改模、子代理独立判断;运行记录与快慢通道收据透传 `completionBasis`。
|
||||
- **测试**:缺省关闭;用户层写入被忽略;模型/基址/摘要任一不符不挂合同;视觉改模后不继承;挂合同后假流放行一次工具并记录 `route_contract_eof`;删除配置后恢复拒绝。
|
||||
- **验证**:`gofmt`;`go vet ./internal/runtime ./internal/settings ./internal/runmgr`;`go test ./internal/runtime ./internal/settings ./internal/runmgr -count=1`。
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ type RouteContractEOF struct {
|
||||
|
||||
1. `Protocol` 为 openai-chat;
|
||||
2. 本次请求实际使用的 `model` 与 `Model` 精确相等;
|
||||
3. 本次请求实际使用的基址(含 `StreamOptions.APIBase` 覆盖)规范化后与 `BaseURL` 精确相等;
|
||||
3. 本次请求实际使用的基址(含 `StreamOptions.APIBase` 覆盖)规范化后与 `BaseURL` 精确相等。规范化规则:只允许 `https`;scheme 与 host 转小写;去掉末尾 `/`;含 userinfo、query 或 fragment 一律视为不匹配(规则只会失败关闭,不放宽);
|
||||
4. 由 1–3 重算的摘要等于 `IdentitySHA256`(防止宿主误配一半字段);
|
||||
5. 请求没有被重定向到其他主机(最终响应 URL 主机与基址主机一致)。
|
||||
5. 请求没有被重定向:`resp.Request.URL`(net/http 的最终请求)的 scheme、host、port、path 与原始请求逐项相等;同主机改路径或 https 降级都视为不匹配。
|
||||
|
||||
r0.6 探针所记的受保护路由文件 SHA-256(`2a92569d…`)是**证据身份**,用于核对“被观察的就是这条路由”;生产绑定用上面的三元组摘要,二者在计划的付费探针前做一次映射核对。
|
||||
|
||||
@@ -78,9 +78,9 @@ r0.6 探针所记的受保护路由文件 SHA-256(`2a92569d…`)是**证据
|
||||
| C1 | 路由合同生效(§4.1 的 1–5) | `Client` + 请求 + 响应 |
|
||||
| C2 | 传输按协议结束:`FramingClass=http2_stream` 且 `EOFSeen`,或 `known_length` 且 `LengthRelation=match` | `BodyBoundaryObservation` |
|
||||
| C3 | `ReadOutcome=clean_eof`;无取消、读错、无效帧、供应商错误 | `TerminalObservation` |
|
||||
| C4 | 恰好一个带非空 `finish_reason` 的块,且其值为 `tool_calls` | 解析器新增计数 |
|
||||
| C4 | 恰好一个带非空 `finish_reason` 的块,且其值为 `tool_calls`;合同生效时每块 `len(choices) <= 1`(解析器只读 `choices[0]`,多 choice 不得被静默丢弃) | 解析器新增计数 |
|
||||
| C5 | 请求开启 `include_usage`(客户端恒开)且收到 Usage | `UsagePresent` |
|
||||
| C6 | `finish_reason` 块之后**没有**任何工具片段 | 解析器新增标志 |
|
||||
| C6 | `finish_reason` 块之后的**后续块**中没有任何工具片段(与 `finish_reason` 同一块内的片段允许) | 解析器新增标志 |
|
||||
| C7 | 每个工具调用 ID、名称齐全,参数为 JSON 对象 | 现有检查 |
|
||||
| C8 | 每个调用的名称在本次请求声明的 `tools` 中,参数通过该工具 schema 子集校验(§4.3) | 请求 `tools` |
|
||||
| C9 | 最后一帧 JSON 完整,`TailEventKind=json_frame` | `TerminalObservation` |
|
||||
@@ -89,19 +89,20 @@ r0.6 探针所记的受保护路由文件 SHA-256(`2a92569d…`)是**证据
|
||||
|
||||
### 4.3 参数 schema 子集
|
||||
|
||||
C8 采用与 `tools.registry.validateArgs` **相同**的子集:顶层 `type: object`、`required` 必填项、已声明属性的基本类型(string/number/integer/boolean/array/object)。实现上把该校验提炼到新的无依赖小包 `runtime/argschema` 中的单一函数,`runtime/llm` 与 `runtime/tools` 都调用它(两包当前互不导入,放进任一方都会引入新的包依赖方向),避免两套语义漂移。超出子集的约束(枚举、格式、嵌套)不在本轮,registry 执行前校验仍保留作为第二道门。
|
||||
C8 采用与 `tools.registry.validateArgs` **相同**的语义:整体迁移 `validateArgs` 与 `matchesType`,语义不变——顶层 `type: object`、`required` 必填项(空白字符串视为缺失)、已声明属性的基本类型、声明了 `properties` 时拒绝未知参数。实现上把该校验提炼到新的无依赖小包 `runtime/argschema` 中的单一函数,`runtime/llm` 与 `runtime/tools` 都调用它(两包当前互不导入,放进任一方都会引入新的包依赖方向),避免两套语义漂移。超出子集的约束(枚举、格式、嵌套)不在本轮,registry 执行前校验仍保留作为第二道门。
|
||||
|
||||
### 4.4 输出与可观测性
|
||||
|
||||
- 放行时:`message_end` 正常携带工具调用;`StreamComplete` **保持 false**(字面含义仍是“未见 `[DONE]`”);新增 `Event.CompletionBasis`:`done_marker`(现有路径)或 `route_contract_eof`。
|
||||
- `TerminalObservation` 新增 `CompletionBasis` 与 `ContractCheck`(失败时记录第一个不满足的条件编号,例如 `C2`),不保存正文、参数或密钥。
|
||||
- 拒绝时:错误文案保持 `tool call stream ended before [DONE]`,附 `ContractCheck`;Usage 照旧保留以便计费与停止。
|
||||
- 拒绝时:除 `TerminalObservation.ContractCheck` 外,终态事件与“合同关闭”时**逐字段一致**(Content、Thinking、`StopError`、Usage、FinishReason、错误文案 `tool call stream ended before [DONE]`)。实现上解析器的候选终态同时携带预先构造的失败事件,`Client.run` 判定失败即原样发出。
|
||||
- `CompletionBasis`、`ContractCheck` 只取枚举值,不含正文、工具名、参数或供应商错误原文。
|
||||
- 下游(Backend 运行记录、快慢通道收据)只把 `CompletionBasis` 作为新增字段透传,不把 `route_contract_eof` 计作 `StreamComplete=true`。
|
||||
|
||||
### 4.5 宿主配置(Backend)
|
||||
|
||||
- 新设置键 `models.chat.routeContractEOF`(JSON:`model`、`baseUrl`、`identitySha256`),**默认缺省 = 关闭**;只允许系统层写入,不接受用户/项目层与画像覆盖。
|
||||
- `openLLMClient` 在 `ResolvedModel` 与该配置三元组全部精确一致时才挂合同;轻量档、视觉改模、子代理按各自解析结果独立判断,不继承。
|
||||
- 三个扁平设置键 `models.routeContractEOF.model`、`.baseUrl`、`.identitySha256`,在 `settings.Schema()` 中以 `Scope: SettingScopeSystem` 声明(`ManagedPaths` 会剥除并拒绝用户/项目层写入),画像覆盖不参与;任一为空 = 关闭(默认)。
|
||||
- `openLLMClient` 在实际解析出的 `(model, base)` 与配置三元组全部精确一致时才挂合同,按三元组判定、不看 `Kind`;轻量档、视觉改模、子代理按各自解析结果独立判断,不继承。全部调用点(`llm.go`、`sub_loop.go`、`squad_runner.go`、`verify.go`、`workflow_compiled.go`)统一经过同一判定。
|
||||
- 回滚:删除该设置键即恢复现行拒绝,无需发版;身份漂移(换模型、换基址)自动失效。
|
||||
|
||||
## 5. 风险与取舍
|
||||
@@ -109,6 +110,7 @@ C8 采用与 `tools.registry.validateArgs` **相同**的子集:顶层 `type: o
|
||||
| 风险 | 处置 |
|
||||
|---|---|
|
||||
| 截断被误判为完成 | C4 要求 `finish_reason` 已到达;C2 要求传输按协议结束;C6 拒绝 `finish_reason` 之后再出现片段 |
|
||||
| 前提 ① 只部分满足(文档写明 `finish_reason` 非空即结束,但无 Usage 顺序与版本承诺) | 以精确身份绑定、默认关闭和 P1 付费探针兜底;P1 不通过即保持关闭 |
|
||||
| 供应商行为漂移、无版本承诺 | 合同只绑定精确身份;默认关闭;`ContractCheck` 使漂移在收据中可见;必要时直接删配置回滚 |
|
||||
| 参数语义错误进入执行 | C8 在放行前校验;registry 执行前再校验一次 |
|
||||
| `known_length` 是否纳入 | 方案纳入(仅 `match`);r0.6 观察到的是 `http2_stream`。**请人审确认**是否只保留 `http2_stream` |
|
||||
|
||||
Reference in New Issue
Block a user