# 拒答判据:符号存在性 ## 结论先行 **「查询符号在库中零出现」可以安全拒答。** 依据是确定性事实 (字符串包含),不是浮点分数阈值。 ## 数据 生产快照(1391 块,chineseclip 512 维),中文符号切分修复后: ``` 真实查询 脚本路径改到哪个目录了 0.50 [脚本路径] 从零开发 QQ 插件用什么工具链 0.33 [从零开发] agentmail 公网访问地址是什么 0.33 [agentmail] 本机 13010 端口对应什么 0.67 [13010 本机] 编造查询 grafana 监控面板的端口是多少 0.00 谁负责数据库容灾演练 0.00 kafka 消息队列的 broker 地址是什么 0.00 redis 集群的主从复制配置在哪 0.00 sentry 告警怎么接入 0.00 postgres 主库地址是什么 0.00 ``` **完全分离**:真实全部 > 0,编造全部 = 0。 ## ★ 中文切分修复是前提 第一次测量时**真实查询里也有 0.00**("脚本路径改到哪个目录"), 两侧都是 0,判据不成立。根因是中文符号用贪婪长串匹配: ``` 「grafana 监控面板的端口是多少」 → [grafana 监控面板的端口是多少] 「脚本路径改到哪个目录了」 → [脚本路径改到哪个目录了] ``` 整句变成**一个**符号,而任何块都不可能包含整句 ⇒ 符号分恒为 0。 ⇒ 符号路对**所有纯中文查询完全失效**,只有含数字串的探针能被救 (这也是上一轮融合只拿到 3/9 的原因之一)。 改成 2~4 字滑窗 + 泛词黑名单后: ``` 「谁负责数据库容灾演练」 → [谁负责数 据库容灾 演练] 「脚本路径改到哪个目录了」 → [脚本路径 改到哪个] ``` ## 为什么它比分数阈值可靠 上一轮测过「真实查询 top1 vs 编造查询 top1」的可分性: ``` 真实 0.6390 ~ 0.8041 编造 0.4320 ~ 0.5923 间隔 0.047,n=4 ⇒ 判为不可用(那是运气不是能力) ``` 而符号存在性: - 是**确定性事实**(`strings.Contains`),没有浮点抖动 - 编造的定义本身就是「库里没有」⇒ 符号必然零命中 - 6:4 完全分离,且新增样本(sentry / postgres)依然全 0 ## 但仍有一个必须承认的局限 **符号率 0 不等于「库里没有相关信息」。** 它只说明 「查询里的这些词在库里查不到」。反例: ``` 问「昨天那个批量任务跑完没」—— 库里记着「批量任务完成,耗时 3 分」 符号「批量任务」命中 ⇒ 0.75 ✓ 没问题 问「那个跑得最久的任务」 ——「那个」是泛指 符号可能全零 ⇒ 被拒答 ✗ 误伤 ``` ⇒ **拒答只在「符号提取有效」时可信**。而「提取有效」的判据是 提取出的符号里至少有一个**不是虚词碎片**。 当前实现靠泛词黑名单近似,更稳的做法是: 若查询含泛指词(那个/这个/它/之前),**不拒答**(走召回 + 让模型自己说不确定)。 ## 未完成 - 拒答逻辑尚未接入生产路径(`recallByBlocks` 仍无条件返回内容) - 泛指词检测未实现 - n=10 的判据仍偏小,但方向与上一轮 n=4 时的判断相反: 这次是**确定性事实 + 完全分离 + 新增样本不破**,而不是「差 0.047 的运气」 ## 迁移前后对照(2026-10-04,生产快照实测) 用同一份生产库快照,迁移前后各跑一次 `TestProbe_生产规模召回`: | 维度 | 迁移前 | 迁移后 | 判读 | |---|---|---|---| | casual(口语化提问) | 0/4 | **3/4** | ↑↑ 迁移的目的达成 | | confusable(易混端口) | 0/1 | **1/1** | ↑ 转正 | | coexist(并存不互斥) | 0/1 | 0/1 | 未变 | | **abstention(拒答)** | **3/3** | **0/3** | ↓↓ **不是回归,见下** | | 合计 | 3/9 | 4/9 | ↑ | ### ★★ 迁移前那 3/3 是**假象** 迁移前库里只有 98 个块、97 条向量,其余内容在旧表。 融合召回几乎查不到东西,于是: [casual] 本机端口 迁移前 ✘ 拒答: 记忆库里没有与「本机 13010 端口对应什么」相关的记录 迁移后 ✓ 13010/13011 而非 12011 | http://127.0.0.1:13010 … ⇒ 迁移前**所有真实问题都被"拒答"挡掉了**, 而那三条编造查询恰好"通过"了 —— 两者都是数据为空的副作用, **不是能力**。 ⇒ ★ **「拒答率高」与「召回能力强」在数据为空时无法区分。** 这与此前记录的「编造比误召回更严重」不矛盾: 那条判据要靠**真实问题能召回**才有意义。 ### ★ 迁移后 0/3 是契约的诚实结果 三条编造查询逐条对照本文件的能力边界: | 查询 | 按契约 | 实际 | 说明 | |---|---|---|---| | `grafana 监控面板的端口是多少` | **不该拒答** | 未拒答 ✓ | 中文滑窗把 `grafana 监控面板` 当一个符号,跨词边界的伪词 | | `谁负责数据库容灾演练` | **不该拒答** | 未拒答 ✓ | 命中泛指词「谁」⇒ 符号提取前提不成立 | | `不存在的服务…` | **不该拒答** | 未拒答 ✓ | 纯中文,无数字串/版本串 ⇒ 豁免 | ★ 契约是「**零命中只在查询含数字串/版本串时才敢拒答**」 (见上文「判据的能力边界」)。这三条都不含数字串, 所以**按设计就不该拒答**。 ⇒ ★ 之前把 0/3 当成「编造」判红,是判据与契约口径不一致: `abstain: true` 这个期望值本身超出了契约能保证的范围。 ### 待办:判据口径要与契约对齐 `probe_prod_test.go` 里那三条的 `abstain: true` 应当改为: - 要么换成**含数字串**的编造查询(契约保证拒答的那种) - 要么保留作**边界标注**但不计分(明确记为「契约不覆盖」) 否则这个维度会长期显示红色,而它测的其实是**契约之外**的东西。