feat(memory): 整理不再只产建议 —— 轮末自动落地高置信 + 停产 link 候选
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人原话:「那么多 link 是干什么的?该删就删,怎么这个记忆整理机制这么保守呢」。

① **link 停产**(依据 design/23 §14 把它列为「未细化」⇒ 既无落点也无数据模型;
   `applyable()` 也只认 merge/archive/reclassify ⇒ 应用 link 恒 `skipped`,是纯噪音):
   - 规则层 `containNone` 判定改为 `keep`(不再产出 501 条不可应用的待办);
   - `TidyLink` 常量保留,只为**读历史行**与「不可应用」判定;
   - 存量出路:新端点 `POST /api/v1/admin/memories/candidates/reject?action=&limit=`
     (**幂等、只改候选 status、不碰记忆**),主会话用它清掉存量 501 条。
② **轮末自动落地**(默认开,仅两类,判据是纯函数):
   - `merge`:**字面重复** ∨「事实集合等价 ∧ 相似度 ≥ 0.95」;
   - `archive`:理由 = `one_off_event` **且不像习惯**(`design/23 §5.2-1` 红线:
     习惯类会因正文带钟点被误判成一次性事件,归档它是错的)。
   - **不自动**:`reclassify`、低置信 merge、跨分区/跨角色、任何改正文、受限来源
     (群聊/陌生人)—— 新增执行面守卫 `tidySkipSourceRestricted`。
③ **六条护栏**(缺一不可):软动作(复用既有 apply 路径)/ 每轮上限 50(硬顶 500,
   超限如实 `Truncated` + 真总数)/ 审计 `memory.tidy.apply`(`auto=true`,不带正文)/
   **可 revert**(复用既有回滚)/ 幂等(复用候选状态条件更新)/ **一键关回只产建议**
   (`$DATA_DIR/config/memory-tidy.json` 的 `autoApply:false`,**零新 env 键**)。

验收(主会话复跑 + 亲自变异):memory 与 gateway 两包 build/vet/test 全绿;我用
-overlay 短路「习惯不归档」守卫 ⇒ TestTidyAutoNeverArchivesHabitMisjudgedAsOneOff
变红(逐字报「作息习惯被自动归档了 …design §5.2-1 红线」)。

⚠️ 未部署(要 build+restart 才生效);⚠️ 契约欠登记(新端点 B25 + §7.2),主会话补;
⚠️ 真机端到端未跑(不能重启后端)。
This commit is contained in:
AskaEth committed 2026-10-07 11:07:00 +08:00
1 parent 353293e807
commit 6d92536276
27 files changed
+2063 -197

No files matched your search

+15 -5
View File
@@ -10,7 +10,8 @@ import "strings"
// 2. 时效污染(一次性事件、有期限状态)—— §5.2-1;
// 3. 数值不一致 —— §5.2-2;
// 4. 低置信 —— §5.2-6;
// 5. 包含判定:互不包含 → 只建链接(§1.3);
// 5. 包含判定:互不包含 → 判 keep、**不产候选**(2026-10-07 口径:link 无执行
// 语义,见本文件 containNone 分支的说明);
// 6. 包含成立但否定不一致 → 冲突,走覆盖而非重复(§5.2-3)。
//
// 命中任一条否决规则即踢出候选(design §5.1 T0-4)。
@@ -43,9 +44,18 @@ func judgePair(a, b MemoryItem, score float64) tidyVerdict {
c := judgeContainment(a.Content, b.Content)
if c.Kind == containNone {
// 相似但互不包含(design §1.3 的 [1]/[3]「红牛 vs 冰美式」):
// 只建链接/分组,绝不合并。
return tidyVerdict{Action: TidyLink, Containment: c, Reason: "similar_but_not_contained"}
// 相似但互不包含(design §1.3 的 [1]/[3]「红牛 vs 冰美式」):判 **keep**,
// 一条候选都不产。
//
// 2026-10-07 主人口径(「那么多 link 是干什么的?该删就删」)——**为什么
// 不实现 link**:design/23 §14 把「`link` 关系如何存储与呈现(是否建
// `memory_links` 表;是否只做面板展示而不进检索)」明确列为**待细化** ⇒
// 它既没有落点(§10 的 memory_items 新列里没有链接列、§11 的端点表里没有
// link 端点),也没有数据模型;而执行面只认三种可应用动作
// (tidy_apply.go 的 applyable())。⇒ 产一条 link 候选的**唯一后果**是
// 一条永远应用不了、只能 skip 的待办(活库曾积压 501 条,把面板淹没)。
// 「以设计为准」的结论是:**不产它**,直到 §14 那条被细化出存储形态。
return tidyVerdict{Action: TidyKeep, Containment: c, Reason: tidyReasonSimilarNotContained}
}
if !sameNegations(a.Content, b.Content) {
// 事实集合看似包含,但一方带否定而另一方没有(「X」vs「戒了X」):
@@ -57,7 +67,7 @@ func judgePair(a, b MemoryItem, score float64) tidyVerdict {
Action: TidyMerge,
Target: &target,
Containment: c,
Reason: "fact_subset",
Reason: tidyReasonFactSubset,
}
}