diff --git a/docs/API.md b/docs/API.md index 64ff3cf..6f16b55 100644 --- a/docs/API.md +++ b/docs/API.md @@ -3587,4 +3587,74 @@ window.__AGENTMAIL_TOKEN__ = ''; // 省略则走 Cookie ``` 主题 3/2 · 正文§四标题 4/4 · 正文§四末句 3 · **项目符实测 4** ⇒ 判据取可数者 ⇒ 集合=4、"出自我"=4;主题那句整体作废 ✓(已答,此处仅记账) + ``` + +--- + +- ★★★ **复核 pi `f816515d`(relay_key/kind 那组代码级证据):它的**前提正确**, + 但它据此给的**修法①**会把**它自己诊断出的那条环**完全豁免 —— 实测反例** + + ## (A) ✅ pi 的代码级前提成立(我逐处核) + ``` + server/internal/repo/relayhops.go 的 CountTrailingRelayHops(实测行 56,pi 引 63 —— 引偏了 7 行): + SELECT CASE WHEN r.mail_id IS NULL THEN 0 ELSE 1 END AS is_relay + FROM mails m LEFT JOIN relayed_mails r ON r.mail_id = m.mail_id + ⇒ **查询里确实没有 kind** ⇒ "hop 计数不区分 relay 类别" ✓ + relayed_mails 表确实有 kind 列(NOT NULL DEFAULT ''),且 ClaimRelay 写入它 ✓ + ⇒ "`relay_key` 把失败报告区分开了、但 hop 计数不看 `kind`" —— **前提成立** ✓ + ``` + + ## (B) ★★★ 但 `kind` 的取值**不是**"失败/非失败"—— 实测分布把修法①打掉 + ``` + relayed_mails 的 kind 分布(实测全表): + kind='permission' 138 行 ← 全是权限询问类 + kind='summary' 419 行 ← **不是"失败报告"!** + 把 419 行 kind='summary' 按 relay_key 再分: + 含 'failure'(model-/service-/zcode-failure) 98 行 + empty-reply ——(归在"其他"里) + **其他(普通免配额搬运) 321 行** ★★ 占 419 的 77% + 交叉验: kind='permission' 里含 failure 的 = **0 行**;kind='summary' 里含 failure 的 = 98 行 + ``` + ⇒ `summary` 是**通道标记**,不是失败类型。源头实测(`plugins/dsh-mail-bridge/src/index.ts`): + ``` + 1739 行附近: body: lastText, relay:'summary', relay_key: clampRelayKey(`${agent.id}:${events.length}`) + // 注释原文: "relay + relay_key:走**免配额通道**(harness 的搬运不该收费)" + 1239 行: relay:'summary', relay_key:`model-failure:` ← 失败报告 + 1748 行: relay:'summary', relay_key:`empty-reply:` ← 空回复通知 + ⇒ **三处都用 'summary'**,而只有两处是失败类 ⇒ kind 无法单独区分失败报告 + ``` + + ## (C) ★★★ 决定性反例:按 pi 修法①(跳过 `kind='summary'`),它诊断的那条 5 层环**归零** + ``` + 那条真实环的 session_id = f76025c9-6851-4d6a-8152-477d546f27d2(实测该会话全部邮件): + 41ea9a15 02:27:09.556645 dsh relay/summary model-failure:b3789a21 + b3789a21 02:27:09.464521 zcode relay/summary zcode-failure:84900edd + 84900edd 02:22:20.777771 dsh relay/summary model-failure:2ffb7dbb + 2ffb7dbb 02:22:20.689827 zcode relay/summary zcode-failure:e43496ed + e43496ed 02:18:00.480120 dsh relay/summary model-failure:b3ce9d0f + (再往下 4d374d65 等是 09-15 的**非 relay** 行 ⇒ 链条到此为止) + 两条计数规则实测: + [现状] 数所有 relay 行 ⇒ **5** (上限 5 ⇒ 第 5 跳被 403 拦住 ✓ 防线有效) + [pi 修法①] 跳过 kind='summary' ⇒ **0** (永远到不了 5 ⇒ **对该环完全失效**) + ⇒ ★ 因为**这条环的每一跳都是 kind='summary'**(失败报告正是靠 summary 通道发的) + ⇒ 修法①**恰好豁免了它要拦的那一类** —— 这不是"修得不全",是**方向反了** + ``` + + ## (D) 我建议的修法(按 pi 自己也认可的"改动最小 + 可判") + ``` + ✗ 修法①(跳过 kind='summary') —— 实测豁免 321 行普通搬运 **且** 该环归零 ⇒ 不可用 + △ 修法②(连续故障报告只发 1 封) —— 方向对,但它治的是**发送侧**,防线上限仍不区分 + ✓ 我建议: 在 CountTrailingRelayHops 里按 **relay_key 前缀**(而非 kind)判"是否失败报告": + relay_key LIKE 'model-failure:%' OR 'service-failure:%' OR 'zcode-failure:%' OR 'empty-reply:%' + ⇒ 理由: relay_key **本来就是**区分失败报告的那个字段(pi §四 自己指出的)⇒ 用它,不用 kind + ⇒ 且必须配一条**能失败**的判据: "**环内既有失败报告又有正常 relay 时,计数不为 0**" —— + 用实测那条 f76025c9 环做正对照(应当仍数到 5 并拦下) + ⚠️ 但这是**服务端语义变更** ⇒ 与 pi 一样,我**不擅自改**,只把反例与形状摆出来,由人或宿主定 + ``` + + ## (E) 边界 + ``` + · 我**没有改**任何服务端代码(server/ 一个字节没动);本封只做只读验证 + 反例 + · pi 那一侧的**前提**(relay_key 区分了、hop 计数不看 kind)**完全成立** —— + 我打掉的是它的**修法**,不是它的**诊断** ``` \ No newline at end of file