# 召回可用性、纠错捕获与可观测性(2026-09-30) 本轮修复针对日常 DSH 的检修结论:已安装版本仍能注入,但日常学习库没有一次召回,且 MSE 注入行被聊天列表隐藏,用户看不到状态;同时提到产品名的正常任务会被内部任务过滤跳过。报告见 `dist/inspection-20260930-live-injection/INSPECTION.md` 与 `dist/review-20260930-multidimension/REVIEW.md`。 ## 1. 内部任务判定改为可信来源 判定不再读取用户正文里的产品名。跳过条件只剩可信结构信号: - 会话 `header.origin === 'subagent'` 或 `delegationDepth > 0`; - 非首步(step ≠ 1)或本轮文本没有可信 `source.kind === 'user'` 消息(原有的 `directText` 过滤); - 文本命中显式内部复盘信封:`[[mse-internal-review]]` 或 `Review the conversation above and update the skill library|consider saving to memory`; - 旧 `missherEvolutionCore` 控制器存在时,整桥暂停(既有机制,未改)。 因此“开发 MSE 插件”“MSE 注入为什么没生效”这类任务照常学习与召回;真正内部复盘既不学习也不注入,并在状态里显示 `internal_task` / `skipped: internal_review`。Hermes bridge 使用同一套 `INTERNAL_REVIEW` 判定,不再用 `\bmse\b` 关键词。 ## 2. 纠错捕获 `extractCorrection` 支持:显式学习请求(记住/请记住/记一下/纠正一下/注意)、未来要求(以后/下次/今后/从现在起/from now on/next time)、否定要求(不要再/别再/不要用/不能再用)、改口(改成/改为/换成/纠正为)、`应该/必须 + 动作`。含反引号的字段引用(`amount`)按原文保存。 分句保留标点(`split(/(?<=[。!?!?;;\n])/)`),因此句末问号仍参与判断。除此之外仍被拒绝(保守来源校验): - **疑问意图**:句末 `?/?`,或含 为什么/怎么/如何/是否/难道/会不会,或句末 吗/呢 —— 例如「金额必须用人民币结算?」不会落库; - **仅限本次**:仅这次/就这次/此次/暂时/临时/这次先…,除非同句还有持续性标志(以后/下次/每次都/统一/一律/永远)—— 例如「仅这次把报表金额改成人民币」不是长期规则,「仅这次先这样,以后统一用人民币结算」是; - **他人转述**:由说话人引入的引号内容(`他说"…"`)、逗号/冒号变体(`他说,以后…`、`客户说:以后…`)、`据说/转述/转达/原话`;单纯赞同(「他说得对,但我们以后统一用人民币结算」)不受影响。**引号本身不再触发整条拒绝**:`以后导出 JSON 时空值必须写成 "unknown"` 与 `From now on don't convert …` 正常保存;被讨论的引用(`从「以后」这个词开始分析…`)不触发标记; - **混合语句**:`仅这次把报表金额改成美元,以后报表金额统一用人民币结算` 只保存后半句(长期分句),临时分句既不保存也不整体拒绝; - **示例与代码材料**:围栏代码、`例如/示例/假设/比如/for example`、引用块或缩进块、`user:`/`assistant:` 交替文本; - **普通任务句**:以「帮我/请帮/看一下/写一个/重构」开口且要求标志不在句首。 正常提及 MSE 的正文不受影响;反引号字段引用是唯一被保留的引号形式。 ## 3. 召回判定(本地、无模型调用) `src/recall.mjs` 把任务与经验都转换为三类证据: - **语义键**:术语归一后的等价组(金额/数额/amount → `amount`)与不在组内的原词。等价组只收真正可互换的表达:JSON/YAML/XML/TOML/CSV/Excel 各自独立成组,同类格式之间只共享「导出/格式化」这类弱键; - **强/弱键**:对象类主题词为强键;动作与填充词(导出/排序/处理/输出/use)为弱键,弱键单独命中永不召回; - **CJK 二元组**:`字段`、`导出` 一类子串证据,只作佐证。 判定门槛(`GATES`):至少 1 个共同主题词且含强键;至少 2 个语义键命中或 2 个二元组佐证;加权证据 ≥ 4;用户任务主题覆盖 ≥ 0.45(未命中的通用动作词不稀释分母);经验主题覆盖 ≥ 0.2。实际值由 `tests/recall.test.mjs` 的 12 条正例、15 条无关负例标定。 **格式适用条件**:格式组(json/jsonl/yaml/xml/toml/csv/tsv/excel)除作为主题键外,还构成适用条件 —— 经验绑定某格式而任务只提到其它格式时直接判 `format_mismatch`,不再看总分。转换或多格式任务因为任务侧包含多个格式,只要与经验格式有交集就照常放行;没有绑定格式的通用规则不受影响。CSV 与 TSV 自 0.9.0-alpha.5 起分离,不再互为别名。 经验不再使用入库时的 `terms` 数组做匹配,而是按 `instruction` 现场归一(有界缓存),因此 0.8/0.9 期间写入的旧经验也会用新规则参与召回,数据无需迁移。存储中的 `terms` 字段保留原格式与校验。 ## 4. 召回原因与状态 `prepare()` 每次返回唯一 `reason`,并附 `diagnostics`: | 原因 | 含义 | | --- | --- | | `recalled` | 本轮提供经验 | | `correction_learned` | 本轮学会一条纠错,本轮不重复提供 | | `not_learned` | 该作用域没有任何可用经验 | | `scope_mismatch` | 库里只有其他项目作用域的经验 | | `match_insufficient` | 有可召回经验但主题不匹配(含最近候选与拒绝门槛) | | `method_unvalidated` | 只剩未验证的方法候选 | | `already_offered` | 同一会话已提供过该版本 | | `budget_exhausted` | 有匹配经验但没有任何一条完整经验能装入本轮或会话额度(含 128 字节下限与剩余额度不足两种情形) | | `internal_task` | 可信内部任务/子代理/非首步跳过 | | `store_failure` | 适配器读取或写入失败(含错误码) | | `turn_closed` / `filtered_origin` | 本轮已结算 / 非用户来源 | DSH 侧新增插件内状态面(不进入模型上下文、不占召回预算): - `/mse`:本轮召回条数、字节数、原因、会话预算、库内纠错/已验证方法/未验证候选/其他作用域/其他环境/失效计数; - `/mse why`:最近一轮来源(经验 ID、版本、类型、字节、正文片段)与最近 8 轮; - `/mse now <任务>`:只读预演,不注入、不写库,给出原因与最近候选。 `bridge.recallStatus(sessionId)` 与 `engine.diagnose({ ... })` 提供同样的只读数据;CLI 增加 `diagnose` 操作。命令通过公开 `commands` 服务注册,记录为可见的 `command/run`+`command/done` 行,因此聊天列表能看到真实状态,但显示本身不是注入证据——注入证据仍只来自消息、最终请求和检查结果。 ## 5. 环境身份 记录、复盘票据、评测与召回使用同一个 `environmentId`。**缺省值是 `default`,DSH、Hermes 与通用 SDK 完全一致**:适配器只有在操作者显式配置 `environmentId`(DSH 配置项 / `HERMES_MSE_ENVIRONMENT`)时才传值,因此旧版本写入的 validated 方法升级后仍然召回;显式换到另一个环境则明确不召回,不扩大验证适用范围。复盘票据保存环境,`reflectionResult` 使用票据环境落库;`findLesson` 在调用方声明环境不一致时返回 `environment_mismatch`。环境先于验证状态判定:别的环境的方法计入“其他环境”,不会被当成“本地未验证候选”。 模型路由与工具链指纹的逐项绑定仍是缺口,本轮不伪造。 ## 6. 内部任务与状态一致性 - 内部判定优先用可信结构:子代理/内部 origin、非首步、非 `user` 来源消息、`metadata.internalReview` 或 `source.form === 'internal-review'`。`[[mse-internal-review]]` 与英文复盘句只是**启发式兜底**,在状态里单独标注 `internal_review_text`,它属于正文匹配,不是可信来源。 - `closeSession` 只影响该会话:排队复盘不启动、在途复盘被取消、迟到结果不落库;其他会话照常。 - 回合结算在状态里显示与核心一致的结果(verified/failed/cancelled/unknown);学习库写入失败显示 `pending` 与错误码,不显示成功。 - `/mse now` 会读取控制器开关:暂停时明确显示「控制器已暂停,本轮不会注入」,不报告可注入。 - `/mse` 的项目作用域来自调用方提供的可信 `session.header.cwd`,不依赖先跑过一轮的内存缓存(新建、重启恢复、缓存淘汰后同样正确)。 ## 7. 未改动 - 未验证方法依旧不进入召回;日常 3 条候选不会因为本版变为可召回。 - 每轮 768 字节 / 2 条、会话累计 1536 字节、同会话同版本只提供一次、跨重启账目均由既有测试覆盖,未放宽。 - 相反偏好并存、到期方法重学、结果写入失败重试仍登记为后续范围(结果写入失败现在会显示为 `pending`,但没有自动重试队列)。 ## 8. 迁移边界 - **schema 2 的小版本升级**(0.9.0-alpha.N → alpha.M):字段与校验未变,直接可读,无需迁移。 - **schema 1 → 2**(0.8.x 及更早写入的库):必须显式 `migrate({ fromSchema: 1 })`,迁移前会写 `before-schema2-rN.json` 原字节快照,会话预算文件不改写;未迁移时 prepare 返回 `migration_required`。 - 这两件事不能混为一谈:上一版回执中“旧库无需迁移”的表述只在 schema 2 范围内成立。