feat(memory): 临时记忆到期物理删除(有界清扫 + 同步清向量 + 删前审计)
design/23 §19 的第 ② / ③ 步:读侧时效闸(M1)只负责「看不见」,本切片负责 「真删掉」。三条护栏照 §19.3:删除是本包内唯一 DELETE 出口、有界(500 行/批 × 20 批)、删前留审计。 - internal/runtime/memory/expire.go:扫描集合写死为「expires_at ≠ '' ∧ expires_at ≤ now ∧ status='active' ∧ 来源非受限」。空 expires_at(长期/日常 记忆,含 M2 登记的「INSERT 与补写之间中断」窗口)结构性进不了集合;受限来源 复用 nonOwnerSource(与整理/审查同一处判据)。键集游标 (expires_at, rowid) 保证被跳过的行只读一次、扫描位置一定前移。审计串 = id + persona_id + scope + expires_at + 按 rune 截断(48)的摘要 —— 本仓唯一一处允许携带内容片段的审计。 - internal/runtime/memory/expire_loop.go:清扫循环挂在 TidyService 的生命周期上 (AttachExpirySweep 由组合根调用),随心跳 ctx 跑、随 Stop 停;一轮只写一行 汇总日志,逐条的那一行进审计。 - internal/runtime/memory/vector/delete.go:新增可选删除能力 vector.Deleter + 两个后端实现,删行时同步清向量(M1 reviewer 的 finding:过期向量占 top-K 预算)。 - cmd/cyrene/wire_memory_tidy.go:挂上清扫;顺带把已过时的「这十键」改成「这十一 键」(M2 加了第十一键 tempTtlSec)。
This commit is contained in:
1 parent
80096c8bd1
commit
a017ab6cf8
7 files changed
+1262
-6
No files matched your search
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// 本文件把记忆整理(DevDocs/design/23-记忆整理与去重 §4)接进组合根。
|
||||
//
|
||||
// 四件事只在这里定:
|
||||
// 六件事只在这里定:
|
||||
// - **形态**:定期后台任务(安静时段降频)的心跳(memory.TidyService),不是
|
||||
// 会话内派 subagent。它在 main.go 与 backthink 并列启动、在 close 里先于关库停止;
|
||||
// - **依赖面**:只给它记忆存储与 embedder —— 服务侧拿不到任何会话/历史读数,
|
||||
@@ -26,7 +26,12 @@ import (
|
||||
// 变成「无限制」,也不能让整理频率变成忙轮询);
|
||||
// - **启动播种**(2026-10-07 追加,主人「后端**自动创建**独立的面板配置文件**要做**」):
|
||||
// 那个文件不存在时由后端自己写一份「当前生效值」进去(seedMemoryTidyPanel),
|
||||
// 使「面板优先」从第一次启动起就成立,而不是靠 `.env` 一直兜着。
|
||||
// 使「面板优先」从第一次启动起就成立,而不是靠 `.env` 一直兜着;
|
||||
// - **到期清扫的挂点**(2026-10-13 追加,切片 M3,design/23 §19):临时记忆到期
|
||||
// 后要**真删掉**(读侧时效闸只负责「看不见」)。清扫是 memory 包内的后台件
|
||||
// (expire.go / expire_loop.go),这里只把它挂到整理服务的生命周期上
|
||||
// (AttachExpirySweep)—— 它随心跳 ctx 跑、随 Stop 停,因此不新开关闭链、
|
||||
// 不新开端口、不加 env 键。
|
||||
//
|
||||
// 刻意**不做**的一件事(design §4.1 F):
|
||||
// - 不把 sess-memorytidy 加进 flowPub 的图源白名单(默认关:整理是系统任务,
|
||||
@@ -49,6 +54,11 @@ func (a *app) wireMemoryTidy() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("memory tidy: %w", err)
|
||||
}
|
||||
// 到期清扫(design/23 §19 的第 ② / ③ 步)挂到整理服务的生命周期上:它随心跳
|
||||
// 的 ctx 跑、随 a.memoryTidy.Stop 停(`design/23 §19.3-3` 的「可停」),因此
|
||||
// 这里**不新起一条关闭链**、不新开监听端口、不加任何 env 键。清扫自身的能力面
|
||||
// 只在 memory 包内(expire.go / expire_loop.go),组合根这一句只负责挂上去。
|
||||
svc.AttachExpirySweep()
|
||||
a.memoryTidy = svc
|
||||
return nil
|
||||
}
|
||||
@@ -58,11 +68,13 @@ func (a *app) wireMemoryTidy() error {
|
||||
// 而不是每次都靠 `.env` 兜。失败只记日志、**绝不阻断启动**:运行期仍按 `.env` +
|
||||
// 代码默认跑,行为与播种前逐字相同(与 materializeModels 同一口径)。
|
||||
//
|
||||
// 闸与 T1 的四键(ingestGate / t1Judge / t1PerWindow / t1WindowSec)在 `.env` 里
|
||||
// **没有出处**(`.env` 已冻结,它们只由这个面板文件与代码默认决定,见
|
||||
// wireMemoryReview 的三个 helper),而播种只发生在**面板文件不存在**时 —— 那正是
|
||||
// 闸与 T1 的四键(ingestGate / t1Judge / t1PerWindow / t1WindowSec)**外加**临时
|
||||
// 记忆 TTL 一键(tempTtlSec,切片 M2 加进来的第五个 review 键)在 `.env` 里
|
||||
// **都没有出处**(`.env` 已冻结,它们只由这个面板文件与代码默认决定,见
|
||||
// wireMemoryReview 的四个 helper:memoryIngestGateOff / memoryT1JudgeOff /
|
||||
// memoryT1RateLimit / memoryTempTTL),而播种只发生在**面板文件不存在**时 —— 那正是
|
||||
// 「零值 + normalizeReviewConfig」这一条路径,因此这里给零值 ReviewConfig 就是
|
||||
// **当时**的生效值,不是猜的。审查开关本身(MEMORY_REVIEW_ENABLED)不在这十键里,
|
||||
// **当时**的生效值,不是猜的。审查开关本身(MEMORY_REVIEW_ENABLED)不在这十一键里,
|
||||
// 因此不必为它去读一次 env。
|
||||
func (a *app) seedMemoryTidyPanel(tidy memory.TidyConfig) {
|
||||
path := runtimeconfig.DefaultMemoryTidyPath(resolveDataDir(a.cfg))
|
||||
|
||||
Reference in new issue
Block a user