mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 09:58:06 +00:00
问题:图记忆里堆着「结果(192) / 什么(59) / 哪个(99) / 待命(176) / 报告(174) / context_archived(43)」这类节点,mention_count 冲到几百、度数只有 1~2—— 占着热实体位、挤满召回预算,却不带任何结构。 根因:这层过滤原本存在,后来被换掉没补回。1cb3e87(NLP 三元组提取系统) 把 doc→graph 从「CutExact 滑窗词链」换成依存句法提取器时,CutExact (去停用词 324 条 + validEntityName + 去重,注释至今还写着「用于 doc→graph 蒸馏」)失去了唯一生产调用点,只剩 cut_test.go 在调它。此后落库闸门只剩 validEntityName——它挡的是「不像名字的字符串」(2–50 字符、含字母), 完全不挡「像名字的常用词」。 改动: - 新增 internal/memory/noise.go:IsNoiseEntity 收敛判定(停用词 / context_archived / 模板摘要回声),FilterNoiseTriples 给自动填充路用, PurgeNoise + PurgeOrphans + cmd/memgc 供存量清理(默认 dry-run)。 判定刻意保守:只拦三类客观噪音,开放类词(报告/对话/处理)不拦—— 它们挡不挡是领域决策,见函数注释。 - docToTriples 接上闸门(用户点名的「文档常用词」路)。 对话蒸馏路 extractKeyTriples **不接**:CutExact 当年也只挂 doc→graph, 且 pipeline_test 明确断言「我 --读书--> 杭州」必须抽出(代词主语是该路 既定行为),是否拦属行为决策,已在代码注释里写明并留给使用者定夺。 - cut.go 补全封闭类常用词:咱俩/咱们(我们/你们/他们 早有)、任何/此/本/ 其中/以及/那么/这样/那样/一样/还有/还要/只是/老是/全部/所有/有些/一些/ 别的/其他/其余/各自/本身/方位词/部分/方面。 「不能/不会」试过又撤回:它们是 embedder tokenize 的实词路径, 加进停用词会让 static_embedder 的领域聚类用例翻转(今天天气句与股票句 的相似度大小关系反了),属真回归,不留。 - graph.go:CleanupOrphanedSentences 拆出 Locked 版供 PurgeNoise 复用。 验证:go build/vet 干净,go test -count=1 ./... 全绿。 新增用例:IsNoiseEntity 判定表、FilterNoiseTriples 顺序与边界、 PurgeNoise(统计/幂等/dry-run 不写库/不误删仍被媒体块边引用的句子)、 PurgeOrphans(识别/不误判有边实体/幂等)、docToTriples 噪音闸门与 模板锚点不被误杀。 存量清理(生产库 /home/newqqagent/memory/graph.db,先用 sqlite3 .backup 备份 到 graph.db.bak-20260915-073210): - 噪音实体 29 个 + 其关系 59 条 - 零关系孤立实体 25 个 - 结果:实体 778→724,关系 639→580;sentences 3 条与 block_edges 3 条原样保留 (媒体块引用不被误删),PRAGMA integrity_check=ok、无悬空关系/块边。 - 运行中的 homed 无需重启:下一个 archive 心跳会 Indexer.Sync 重建实体名向量索引。
89 lines
2.8 KiB
Go
89 lines
2.8 KiB
Go
// memgc 清理图记忆里已存在的「噪音实体」「孤立实体」及其关系。
|
||
//
|
||
// 为什么需要这个命令:噪音闸门(internal/memory.IsNoiseEntity)只能拦住
|
||
// **新写入**的噪音。旧库里那批(常用词 / 归档内部标记 / 模板摘要回声)是
|
||
// 闸门上线前攒下的存量,没人清就一直在——热实体被它们占着,召回预算被
|
||
// 同构垃圾边挤满。清理是一次性动作,但需要可重复执行、可先看不做。
|
||
//
|
||
// 两件事分开开关:-orphans 处理的是「零关系的空节点」(清理噪音后另一端
|
||
// 留下的壳),它们的名字本身可能没问题,但已经不在图里了。
|
||
//
|
||
// 用法(默认 dry-run,只列不删):
|
||
//
|
||
// memgc -db /home/newqqagent/memory/graph.db
|
||
// memgc -db /home/newqqagent/memory/graph.db -orphans -apply
|
||
//
|
||
// 清理生产库前请先备份:sqlite3 graph.db ".backup 'graph.db.bak-<ts>'"
|
||
// 不要用 cp —— WAL 模式下会复制出主库与 -wal 不一致的快照。
|
||
package main
|
||
|
||
import (
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
|
||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory"
|
||
)
|
||
|
||
func main() {
|
||
path := flag.String("db", "", "graph.db 路径(必填)")
|
||
apply := flag.Bool("apply", false, "真正删除;不加则只 dry-run 打印")
|
||
orphans := flag.Bool("orphans", false, "同时处理「零关系孤立实体」(先被清理的噪音在另一端留下的空节点)")
|
||
flag.Parse()
|
||
|
||
if *path == "" {
|
||
flag.Usage()
|
||
log.Fatal("memgc: 必须指定 -db")
|
||
}
|
||
|
||
g, err := memory.NewGraphDB(*path)
|
||
if err != nil {
|
||
log.Fatalf("memgc: open %s: %v", *path, err)
|
||
}
|
||
defer g.Close()
|
||
|
||
junk, err := g.NoiseEntities()
|
||
if err != nil {
|
||
log.Fatalf("memgc: scan: %v", err)
|
||
}
|
||
|
||
fmt.Printf("噪音实体 %d 个:\n", len(junk))
|
||
for _, e := range junk {
|
||
fmt.Printf(" %-64s type=%-8s mentions=%d\n", e.Name, e.Type, e.MentionCount)
|
||
}
|
||
|
||
de, dr, err := g.PurgeNoise(!*apply)
|
||
if err != nil {
|
||
log.Fatalf("memgc: purge: %v", err)
|
||
}
|
||
if *apply {
|
||
fmt.Printf("[APPLIED] 噪音:已删除 实体=%d 关系=%d\n", de, dr)
|
||
} else {
|
||
fmt.Printf("[DRY-RUN] 噪音:将删除 实体=%d 关系=%d(未写库,加 -apply 才落地)\n", de, dr)
|
||
}
|
||
|
||
if *orphans {
|
||
list, err := g.OrphanEntities()
|
||
if err != nil {
|
||
log.Fatalf("memgc: orphans: %v", err)
|
||
}
|
||
fmt.Printf("孤立实体(零关系)%d 个:\n", len(list))
|
||
for _, e := range list {
|
||
fmt.Printf(" %-64s type=%-8s mentions=%d\n", e.Name, e.Type, e.MentionCount)
|
||
}
|
||
n, err := g.PurgeOrphans(!*apply)
|
||
if err != nil {
|
||
log.Fatalf("memgc: purge orphans: %v", err)
|
||
}
|
||
if *apply {
|
||
fmt.Printf("[APPLIED] 孤立实体:已删除 %d 个\n", n)
|
||
} else {
|
||
fmt.Printf("[DRY-RUN] 孤立实体:将删除 %d 个\n", n)
|
||
}
|
||
}
|
||
|
||
if *apply {
|
||
fmt.Println("提示:运行中的进程会在下一个 archive 心跳(Indexer.Sync)重建实体名向量索引,无需重启。")
|
||
}
|
||
}
|