Files
Agent-System-Dev/docs/superpowers/specs/2026-10-19-fast-slow-o2-route-contract-eof-design.md
T
weichenandCursor 22cb4c1192
ci / svc-realcombo (pull_request) Successful in 2m39s
ci / test (pull_request) Successful in 8m10s
ci / test-pg (pull_request) Successful in 9m17s
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), base 2b7fdc3 head 5a7a0da.
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>
2026-10-19 17:21:08 +00:00

10 KiB
Raw Blame History

快慢通道 O2:限路由等价终态(route_contract_eof)方案

  • 状态:阶段 4 方案稿,待人审。 本文不授权产品代码改动、付费请求、A01 复验或部署;阶段 4/5 人审通过后才按配套计划派 worker 实施零付费部分,付费探针另行提资源方案。
  • 日期:2026-10-19;作者:host(只读调查与文档撰写);来源等级:SELF。
  • 决策来源:用户 2026-10-19 在完成信号口径评估中选定 O2(另起任务)。
  • 配套计划:阶段 5 实施与测试计划。
  • 上游约束:r0.6 传输完整性设计的四个前提(评估稿第 4 节逐项对照);本方案是该设计要求的“另一轮方案评审”,不改变 r0.6 已批准的观察字段。

1. 目标与非目标

目标:对一条经核验的 MiniMax 路由,在流缺少字面 [DONE]、但能证明“按协议结束且工具调用完整”时,放行该轮工具调用;其余一切情形保持现行拒绝。

非目标:

  • 不改变有 [DONE] 的现有放行路径与 StreamComplete 语义。
  • 不对其他 OpenAI 兼容服务、其他 MiniMax 模型或其他主机泛化;不提供“全局放宽”开关。
  • 不在本轮跑真实 A01 或任何付费请求;不改 Anthropic 协议路径。
  • 不引入完整 JSON Schema 引擎(见 §4.3 的子集约定)。

2. 现状(代码定位,Agent-Native origin/main 9c24e22 / Dev 钉 28f3ea7 同文件)

位置 现行行为
runtime/llm/sse.go emitParsed 第 260 行起 有工具调用且 !sawDone → sendTerminalFailure("tool call stream ended before [DONE]"),工具全部丢弃,Usage 保留
runtime/llm/sse.go 第 300 行 正常结束发 message_end,StreamComplete = sawDone
runtime/llm/client.go run 第 129–139 行 用 bodyObserver 包住响应体;ParseSSE 的终态事件发出后才补 TerminalObservation.BodyBoundary
runtime/llm/body_observation.go 只按 net/http 可见信息分类:http2_stream / known_length / chunked / close_delimited / unknown;LengthRelation 仅在已知长度、未解压、未饱和且见 EOF 时为 match/mismatch
runtime/tools/registry.go validateArgs 工具执行前按 schema 子集(type、required、属性类型)校验参数;失败返回错误结果、不执行
Backend internal/runtime/model.go ResolvedModel 路由身份 = Kind + Model + Base(设置层 → 画像覆盖);生产无独立“路由文件”
Backend internal/runtime/llm.go openLLMClient 构造 agentllm.Client;MiniMax-M* 置 ReasoningSplit

关键约束:判定所需的传输边界只在 client.go 可见,ParseSSE 看不到;因此终态判定不能只放在解析器里。

3. 方案总览

ParseSSE(解析器)                       Client.run(传输层)
──────────────────                       ────────────────────
既有检查全部照旧                          bodyObserver 快照
仅当“唯一缺陷 = 缺 [DONE]”且               ↓
调用方声明了路由合同时 →                  RouteContractEOF.Evaluate(candidate, boundary, tools)
  返回 候选终态(不外发)  ────────────▶    全部条件成立 → message_end(CompletionBasis=route_contract_eof)
                                           任一不成立 → 与今天相同的终态失败(Usage 保留)
  • 合同未配置(零值)时,解析器与客户端的行为逐字节等同现状。
  • 解析器只做“延后判定”,不做放行;放行只在传输层、且只对绑定身份。

4. 设计细节

4.1 路由合同与身份绑定(Agent-Native runtime/llm)

新增导出类型(命名可在计划阶段微调,语义固定):

// RouteContractEOF 只对一条经核验路由生效;零值/nil 表示关闭。
type RouteContractEOF struct {
    Model          string // 精确模型名,例如 MiniMax-M3
    BaseURL        string // 规范化后的精确基址,例如 https://api.minimaxi.com/v1
    IdentitySHA256 string // sha256("openai-chat\n" + Model + "\n" + BaseURL)
}

Client 增加可选字段 RouteContract *RouteContractEOF。生效前提(任一不满足即视为关闭,回到现行拒绝):

  1. Protocol 为 openai-chat;
  2. 本次请求实际使用的 model 与 Model 精确相等;
  3. 本次请求实际使用的基址(含 StreamOptions.APIBase 覆盖)规范化后与 BaseURL 精确相等。规范化规则:只允许 https;scheme 与 host 转小写;去掉末尾 /;含 userinfo、query 或 fragment 一律视为不匹配(规则只会失败关闭,不放宽);
  4. 由 1–3 重算的摘要等于 IdentitySHA256(防止宿主误配一半字段);
  5. 请求没有被重定向:resp.Request.URL(net/http 的最终请求)的 scheme、host、port、path 与原始请求逐项相等;同主机改路径或 https 降级都视为不匹配。

r0.6 探针所记的受保护路由文件 SHA-256(2a92569d…)是证据身份,用于核对“被观察的就是这条路由”;生产绑定用上面的三元组摘要,二者在计划的付费探针前做一次映射核对。

4.2 放行条件(全部成立才放行)

# 条件 数据来源
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;合同生效时每块 len(choices) <= 1(解析器只读 choices[0],多 choice 不得被静默丢弃) 解析器新增计数
C5 请求开启 include_usage(客户端恒开)且收到 Usage UsagePresent
C6 finish_reason 块之后的后续块中没有任何工具片段(与 finish_reason 同一块内的片段允许) 解析器新增标志
C7 每个工具调用 ID、名称齐全,参数为 JSON 对象 现有检查
C8 每个调用的名称在本次请求声明的 tools 中,参数通过该工具 schema 子集校验(§4.3) 请求 tools
C9 最后一帧 JSON 完整,TailEventKind=json_frame TerminalObservation

chunked、close_delimited、unknown、known_length 但 mismatch/unknown 一律不满足 C2。

4.3 参数 schema 子集

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),不保存正文、参数或密钥。
  • 拒绝时:除 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.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. 风险与取舍

风险 处置
截断被误判为完成 C4 要求 finish_reason 已到达;C2 要求传输按协议结束;C6 拒绝 finish_reason 之后再出现片段
前提 ① 只部分满足(文档写明 finish_reason 非空即结束,但无 Usage 顺序与版本承诺) 以精确身份绑定、默认关闭和 P1 付费探针兜底;P1 不通过即保持关闭
供应商行为漂移、无版本承诺 合同只绑定精确身份;默认关闭;ContractCheck 使漂移在收据中可见;必要时直接删配置回滚
参数语义错误进入执行 C8 在放行前校验;registry 执行前再校验一次
known_length 是否纳入 方案纳入(仅 match);r0.6 观察到的是 http2_stream。请人审确认是否只保留 http2_stream
两套 schema 校验漂移 §4.3 提炼为单一函数

6. 需人审确认的取舍

  1. C2 是否同时接受 known_length+match(建议接受),还是只接受 http2_stream。
  2. 宿主配置键仅系统层可写、默认关闭(建议如此)。
  3. 付费验证(1 次短工具探针 + 1 次 A01)在零付费部分合入后另提资源方案,不随本方案授权。