From 607523ef5c05531a18d3d5c5719b4c4777e6d216 Mon Sep 17 00:00:00 2001 From: iomgaa Date: Wed, 14 Oct 2026 22:46:56 -0400 Subject: [PATCH] docs: fold round-2 review fixes into completion verdict design --- ...10-14-issue30-31-completion-verdict-design.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/research-wiki/designs/2026-10-14-issue30-31-completion-verdict-design.md b/research-wiki/designs/2026-10-14-issue30-31-completion-verdict-design.md index f862ec7..591f6a0 100644 --- a/research-wiki/designs/2026-10-14-issue30-31-completion-verdict-design.md +++ b/research-wiki/designs/2026-10-14-issue30-31-completion-verdict-design.md @@ -7,7 +7,7 @@ date: 2026-10-14 # #30/#31 统一完成判定设计 -> 状态: 方向已获人类批准(2026-10-14,四项取舍与五个拍板点按推荐);人类授权"Claude 与独立 subagent 确认无误后直接执行",不再走人类设计审阅。第一轮独立审查 3 阻塞 + 9 应修已逐条核实并修订(见 §11)。 +> 状态: 方向已获人类批准(2026-10-14,四项取舍与五个拍板点按推荐);人类授权"Claude 与独立 subagent 确认无误后直接执行",不再走人类设计审阅。两轮独立审查已闭合(R1 3 阻塞 + 9 应修;R2 通过,2 应修已并入),见 §11。 > 基线: `22609c9`(1.3.9);目标版本 **1.3.10**;分支 `fix/issue30-31-completion-verdict`。 > 需求: [#30](https://gitea.iomgaa.online/iomgaa/PolyGateway/issues/30)、[#31](https://gitea.iomgaa.online/iomgaa/PolyGateway/issues/31)。 > 修订对象: [1.3.9 保真设计](2026-10-01-issue29-openai-fidelity-design.md) §4.1/§4.2/§5/§6、ARCHITECTURE §6.2/§7/§14。 @@ -76,7 +76,7 @@ date: 2026-10-14 | truncated | `length` | | filtered | `content_filter`、`sensitive` | | upstream_error | `network_error`、`error` | -| unknown | 其余非空字符串: 按 natural 交付、**不可缓存**、按源+值告警一次 | +| unknown | 其余非空字符串: 按 natural 交付、**不可缓存**、按源+值告警一次(文案写明"该值不缓存",便于下游定位命中率下降);`end_turn`/`stop_sequence` 等无证据不预先归类 | | absent | None | ### 4.2 第一步: 流完整性(仅流式) @@ -88,7 +88,7 @@ date: 2026-10-14 | 干净 EOF、无 DONE、finish 已见、finish 帧**同帧或之后**收到的 usage 被 `_resolve_usage` 判为 measured | `eof_after_finish`,usage 记 measured(不降级) | | 干净 EOF、其余情形 | 截断: openai → Transient `missing_done`(仍不打捞);text → 原 `_check_done` 语义(零产物 early_eof、`retry` 抛、`salvage` 记 `salvaged`) | -聚合器每尝试记录 `finish_seen` 与 `usage_after_finish`: 收到非 null usage 时,若 `finish_seen` 已真或同帧带 finish,则以该 usage 置后者;usage-only 帧(`choices: []`)在 finish 之后到达是常规形态,正确置位。状态随重试/对冲每尝试新建。 +聚合器每尝试记录 `finish_seen` 与 `usage_after_finish`: 收到非 null usage 时,若 `finish_seen` 已真或同帧带 finish,则置后者为真;是否 measured 以**最后一个**非 null usage(与计费所用 `sink["usage"]` 同一对象)在流结束时判定;usage-only 帧(`choices: []`)在 finish 之后到达是常规形态,正确置位。状态随重试/对冲每尝试新建。 诚实边界: 中转站在上游出错后主动正常结束响应,与真完整在传输层不可区分;防线是"finish 必须存在、不属 upstream_error、且其后有 measured usage",文档如实写明。 @@ -118,13 +118,13 @@ ResultInvalid 理由: `retry.py:409-414` 对其"不重试、不换源、熔断 ### 4.4 文本模式 reasoning 别名 -投影 `thinking`: 本次尝试出现过 `reasoning_content` 则只用它,否则用 `reasoning`。保真 `completion.message` 保留上游原键不改名。两模式活性、产物摘要、`thinking_observation` 均基于该投影。 +投影 `thinking`: 本次尝试出现过**非空** `reasoning_content` 则只用它,否则用 `reasoning`。保真 `completion.message` 保留上游原键不改名。两模式活性、产物摘要、`thinking_observation` 均基于该投影。 ## 5. 字段合并与活性 ### 5.1 null 与已知字段 -null 规则**优先于**已知字段类型校验: 任意层任意键值为 null 视为缺省;键从未出现时保留 `key: null`(含 `tool_calls`、`refusal`、`role`、`function.arguments`)。保真成功仍须最终 role=assistant(全程只有 `role: null` 即缺 role,照旧失败)。其余已知字段(content/reasoning_content/role/tool_calls 及工具 id/type/function.name/arguments)非 null 时语义逐字沿用 1.3.9。 +null 规则**优先于**已知字段类型校验: chunk、choice、delta、工具条目、function 五层任意键值为 null 视为缺省,数组中的 null 元素(如 `tool_calls: [null]`)跳过;键从未出现时保留 `key: null`(含 `tool_calls`、`refusal`、`role`、`function.arguments`)。保真成功仍须最终 role=assistant(全程只有 `role: null` 即缺 role,照旧失败)。其余已知字段(content/reasoning_content/role/tool_calls 及工具 id/type/function.name/arguments)非 null 时语义逐字沿用 1.3.9。 ### 5.2 未知字段(delta、`tool_calls[]` 条目、`function` 三层统一) @@ -142,7 +142,7 @@ null 规则**优先于**已知字段类型校验: 任意层任意键值为 null ### 5.3 活性 -已知字段活性逐字沿用 1.3.9(非空 content/reasoning_content、工具 name/arguments 非空增量、首次建立工具身份)。未知字段仅当合并后可见字符串内容增长、且键不属标签集合时算进展(`reasoning` 由此计入);同帧只记一次。文本模式同口径。README 中"增量 chunk = content 或 reasoning_content"的 stall/TTFT 口径表述同步更新为本节口径。 +已知字段活性逐字沿用 1.3.9(非空 content/reasoning_content、工具 name/arguments 非空增量、首次建立工具身份)。未知字段仅当本帧在非标签键上并入非空字符串片段(含追加型数组中的字符串元素)时算进展(`reasoning` 由此计入);同帧只记一次。残余风险如实登记: 上游每帧重复下发的非标签字符串(如 `status`)会被拼接并持续刷新看门狗。文本模式同口径。README 中"增量 chunk = content 或 reasoning_content"的 stall/TTFT 口径表述同步更新为本节口径。 ### 5.4 告警节流 @@ -165,7 +165,7 @@ null 规则**优先于**已知字段类型校验: 任意层任意键值为 null | 读 | `_is_cacheable` 在命中覆盖(`cache.py:224` `fields.update`)**之前**、对存储原值判定;不可缓存 → miss + warning | | 读: 非法 `stream_end` | 不在 Literal 域内 → miss + warning(关乎内容完整性,故与 `_coerce_observation` 的宽容方向相反) | | 读: 顶层无 finish_reason | 回退 `completion.finish_reason`(1.3.9 保真 length 条目因此可识别);文本旧条目两字段缺省 → None → 仍可命中 | -| 命中返回 | `stream_end=None`;`finish_reason` 保留存储值 | +| 命中返回 | `stream_end=None`;`finish_reason` 取判定所用值(顶层缺省时为回退值),非字符串视 miss | | 序列化 | `_RESPONSE_FIELDS` 由 `dataclasses.fields` 自动派生,无需改动 | 旧 salvage/length 文本污染无法辨认: CHANGELOG 继续要求受影响集合迁移 namespace/salt。 @@ -243,3 +243,5 @@ null 规则**优先于**已知字段类型校验: 任意层任意键值为 null | R1 | S5 无产物 + length 与 #30 原文冲突 | 采纳(人类授权代决): 保真交付、文本 ResultInvalid、stop/absent 仍 Transient | | R1 | S6-S9 缓存顺序、遥测落点、原因码、null 优先 | 采纳: §6/§7/§4.3/§5.1 | | R1 | 建议 1-7 | 采纳 1/2/3/5/6/7;建议 4 不标不可缓存(上游确定性,非毒化) | +| R2 | 结论通过;N1 measured 以最后 usage 判定、N2 非空 reasoning_content | 采纳: §4.2/§4.4 | +| R2 | 建议 1-4: 命中回退值、未知 finish 告警文案、数组字符串活性与残余风险、null 五层与数组 null 元素 | 全部采纳: §6/§4.1/§5.3/§5.1 |