mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 01:48:11 +00:00
refactor(memory): 移除 media_refs/引用计数,媒体成为一等记忆块
媒体此前是"文本块 + digest 引用 + owner 账本 + 独立 GC":ContextEvent.Media
记 digest,media_refs 表用 owner_kind/owner_id 保活,ref_count 决定 GC 能否清。
这与文本记忆块的管理方式不一致,也是本次一并纠正的核心偏差。
改为与文本块完全一致的生命周期:
1. 一等记忆块直接由所在层持有
- ContextEvent.Blocks / Doc.Blocks / GraphDB memory_blocks
- 块带 modality/digest/MIME/size/vector/fingerprint,文本、图片、视频同构
- Context→Document→Graph 迁移的是块本身(ID 不变),迁移后清空源容器,
同一块不同时存在于两层
2. 删除平行生命周期账本
- media.Store 去掉 media_refs 表、OwnerKind 常量、RefCount 字段、
AddRef/DropRef/DropOwner/Refs、ref_count 列与索引
- 删除 mediaGCLoop、GC(keep,minAge)、容量上限与 media.gc_* / media.max_mb 配置
- 媒体内容在块被永久删除时一并删除(media.Store.Delete + forgetPayloads),
与"删除文本块即删除内容"同一语义
3. L3 原生结构
- memory_blocks / memory_block_edges(contains/depicts/derived_from)
- 边端点必须是真实图节点,不再用 owner 字符串伪装关系
- BlocksForNode 支持 sentence --contains--> block 反查
4. SDK 与检索同步
- 插件附件/标记直接变成块,不再 AddRef
- 跨模态检索改用 QueryMediaScored(CAS 内不再有孤儿缓存需要过滤)
测试全部改写为块语义:删除 refcount/media_refs/GC 断言,新增块迁移、
单层不变量、Delete 语义与并发删除回归。
注:cmd/homed/main.go 同时携带工作区中既有的 CLIP→Qwen 模型目录接线改动。
This commit is contained in:
@ -52,14 +52,10 @@ type Agent struct {
|
||||
// 文本记忆(原始对话日志)
|
||||
textMem *text.Memory
|
||||
|
||||
// 媒体存储(内容寻址):对话里出现的图片/音频按 sha256 落盘去重,
|
||||
// L0/L2/L3 只记 digest。为 nil 时全部媒体接线静默跳过——
|
||||
// 它是记忆增强而非对话必需品,缺了不该让对话失败。
|
||||
// 媒体存储(内容寻址):对话里出现的图片/音频按 sha256 落盘去重。
|
||||
// 它是记忆块的内容存储,不单独做生命周期管理:块的创建/迁移/删除
|
||||
// 由记忆系统本身决定。为 nil 时全部媒体接线静默跳过。
|
||||
mediaStore *media.Store
|
||||
// mediaGCInterval 为 0 时不跑 GC 循环(容量上限就仅在手动调 GC 时生效)。
|
||||
mediaGCInterval time.Duration
|
||||
// mediaGCMinAge 保护新入库媒体:刚 Put 还没来得及 AddRef 的项引用计数也是 0。
|
||||
mediaGCMinAge time.Duration
|
||||
// mediaDescribe 控制是否跑后台描述循环(要消耗视觉模型配额)。
|
||||
mediaDescribe bool
|
||||
|
||||
@ -96,8 +92,8 @@ type Agent struct {
|
||||
selfInputCh chan selfInputMsg
|
||||
|
||||
// 子任务异步执行
|
||||
childMu sync.Mutex
|
||||
childNextID int64
|
||||
childMu sync.Mutex
|
||||
childNextID int64
|
||||
// childTasks 记录子任务状态:运行中 / 结果 / 是否已交付。
|
||||
//
|
||||
// 为什么保留结果而不是“读到即删”:完成通知会写进持久上下文
|
||||
@ -186,8 +182,6 @@ type AgentConfig struct {
|
||||
SocialStore *social.SocialStore
|
||||
TextMemory *text.Memory
|
||||
MediaStore *media.Store
|
||||
MediaGCInterval time.Duration
|
||||
MediaGCMinAge time.Duration
|
||||
MediaDescribe bool
|
||||
MultimodalSpace vector.MultimodalEmbedder
|
||||
FusionCfg CrossModalFusionConfig // 跨模态融合权重;零值用默认
|
||||
@ -248,13 +242,6 @@ func New(cfg AgentConfig) *Agent {
|
||||
if cfg.IO != nil {
|
||||
rc.SetChannelDefLookup(cfg.IO.GetInputChannelDef)
|
||||
}
|
||||
// 必须把媒体存储也注给 RelevanceContext:L0→L2 归档(Prune)靠
|
||||
// rc.transferMediaRefs 把引用从 context owner 转给 document owner。
|
||||
// 漏了这一行的后果是静默的:rc.mediaStore 为 nil 时转移直接 return,
|
||||
// 而携带引用的 ContextEvent 已被归档删除 → 引用永久悬空在
|
||||
// context owner 上、计数永不归零 → 对应 blob 永远不会被 GC 回收。
|
||||
rc.SetMediaStore(cfg.MediaStore)
|
||||
|
||||
// 注入稠密多模态向量空间(可选):配置后文档检索、L0 相关性裁剪、
|
||||
// 跨模态检索全部共享同一向量空间,取代稀疏 fastText 语义路。
|
||||
// 未配置时退化到 TF-IDF/fastText 稀疏检索,保持既有行为。
|
||||
@ -284,8 +271,6 @@ func New(cfg AgentConfig) *Agent {
|
||||
social: cfg.SocialStore,
|
||||
textMem: cfg.TextMemory,
|
||||
mediaStore: cfg.MediaStore,
|
||||
mediaGCInterval: cfg.MediaGCInterval,
|
||||
mediaGCMinAge: cfg.MediaGCMinAge,
|
||||
mediaDescribe: cfg.MediaDescribe,
|
||||
personality: cfg.Personality,
|
||||
pluginReg: cfg.PluginReg,
|
||||
@ -322,7 +307,6 @@ func (a *Agent) Start() {
|
||||
go a.archiveLoop()
|
||||
go a.mergeLoop()
|
||||
go a.reviewLoop()
|
||||
go a.mediaGCLoop()
|
||||
go a.mediaDescribeLoop()
|
||||
a.reembedStaleMedia()
|
||||
log.Printf("[agent] %s started, waiting for IO interrupts", a.id)
|
||||
|
||||
Reference in New Issue
Block a user