From 4130e6b66833f861da486048faa3fdee87db264d Mon Sep 17 00:00:00 2001 From: xiaowei Date: Mon, 7 Sep 2026 19:29:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(memory-quality):=20=E6=96=B9=E6=A1=88B?= =?UTF-8?q?=E5=86=99=E6=97=B6=E8=B4=A8=E9=87=8F=E9=97=A8=E6=A7=9B+?= =?UTF-8?q?=E6=96=B9=E6=A1=88C=20provenance=20=E6=A0=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 方案B: distill 结果写 memories 前 IsQualityFact 过滤(碎片<8字拒/8-20字需用户信号/噪声前缀拦)+完全重复查重跳过 - quality.go: 与 LightMem 保留细节哲学兼容(咖啡偏好保留), 拦进程噪声/桥接/reflection - 单测 8/8 通过 方案C: distill MemoryRecord 标 Source=llm_distill(provenance); volatile_flag 降权已确认生效(×0.5) - ContentMD5/VersionHistory 字段已在, bi-temporal valid_to 加列缓行(风险高) --- go/internal/api/server.go | 12 ++++++++ go/internal/distill/quality.go | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 go/internal/distill/quality.go diff --git a/go/internal/api/server.go b/go/internal/api/server.go index c5f3466..aba0b9c 100644 --- a/go/internal/api/server.go +++ b/go/internal/api/server.go @@ -139,7 +139,18 @@ func NewServer() http.Handler { }) // 回写蒸馏产物到记忆库(LanceDB) +// 2026-09-07 方案B 质量门槛: 碎片(<20字)/噪声句(状态汇报/桥接/reflection)不入库, +// 与已有记忆完全重复跳过(蒸馏直写不走 commit exact_dup, 需本层查重)。 for _, fact := range result.Facts { + if !distill.IsQualityFact(fact) { + continue // 碎片/噪声过滤 + } + // 查重: 同 namespace 已存在完全相同 content → 跳过 + if qvec, eErr := emb.EncodeSingle(fact); eErr == nil { + if sim, _ := ldb.Search("memories", qvec, 1, input.Namespace); len(sim) > 0 && sim[0].Content == fact { + continue + } + } mem := models.MemoryRecord{ ID: fmt.Sprintf("mem_%d", time.Now().UnixNano()), AgentID: input.AgentID, @@ -153,6 +164,7 @@ func NewServer() http.Handler { UpdatedAt: time.Now(), DerivedFrom: input.EpisodeID, Freshness: "fresh", + Source: "llm_distill", // 2026-09-07 方案C provenance: LLM蒸馏推断来源 } if err := ldb.InsertMemory(mem); err != nil { log.Printf("[distill] commit fact failed: %v", err) diff --git a/go/internal/distill/quality.go b/go/internal/distill/quality.go new file mode 100644 index 0000000..9c930ff --- /dev/null +++ b/go/internal/distill/quality.go @@ -0,0 +1,50 @@ +package distill + +import "strings" + +// IsQualityFact 判断蒸馏事实是否有入库价值(2026-09-07 方案B 质量门槛) +// +// 背景: 审计发现 memories 74% <50 字符碎片("重启系统"/"健康检查完成"/daemon reflection 等) +// = 蒸馏/直接 commit 无质量门槛产物。这里拦碎片 + 噪声句。 +// 与 LightMem "保留小细节"哲学兼容: 咖啡偏好(18字+信息)仍保留, 纯噪声/碎片拒绝。 +func IsQualityFact(fact string) bool { + s := strings.TrimSpace(fact) + if s == "" { + return false + } + // 碎片判断: 绝对碎片(<8字)拒绝; 8-20字需含用户相关词才保留(短偏好如"用户喜欢喝咖啡"有价值) + runeLen := len([]rune(s)) + if runeLen < 8 { + return false + } + if runeLen < 20 { + userSignals := []string{"用户", "牧尘", "小唯", "偏好", "喜欢", "决定", "使用", "需要", "认为", "选择", "计划", "希望", "正在做", "完成了"} + hasSignal := false + for _, sg := range userSignals { + if strings.Contains(s, sg) { + hasSignal = true + break + } + } + if !hasSignal { + return false // 8-20字且无用户信号 = 碎片(如"重启系统"已<8; "所有进程运行中"无信号) + } + } + // 噪声: 状态汇报/桥接/daemon reflection 类前缀(这些是进程噪声不是用户事实) + noisePrefixes := []string{ + "健康检查完成", "状态同步", "所有进程运行中", "当前系统状态完全正常", + "系统健康检查", "正在评估上次决策", "用户在深度思考中", "上次修复了", + "小唯a06主profile", "小唯a06使用的模型", "小唯a06的织忆数据", "小唯a06的cron", "小唯a06的daemon", + } + lower := strings.ToLower(s) + for _, p := range noisePrefixes { + if strings.HasPrefix(lower, strings.ToLower(p)) { + return false + } + } + // 含桥接关键词且短(通信日志非事实) + if strings.Contains(s, "桥接") && len([]rune(s)) < 40 { + return false + } + return true +}