Version: 0.9.24.dev.260416

后端:
1. Memory 预取缓存改为会话级隔离 + 管理面自动失效 + 空检索清理
- 预取缓存 key 从 smartflow:memory_prefetch:{userID} 改为 smartflow:memory_prefetch:u:{userID}:c:{chatID},隔离不同会话的记忆上下文,避免会话间互相覆盖
- 新增 DeleteMemoryPrefetchCacheByUser 方法,使用 SCAN+UNLINK 按模式批量删除指定用户所有会话的预取缓存
- ItemRepo 四个变更方法(SoftDeleteByID / RestoreByIDAt / UpdateManagedFieldsByIDAt / UpdateStatusByIDAt)通过 Model 携带 UserID,使 GORM cache deleter 可精准定位用户
- GormCachePlugin 将 MemoryItem 从忽略列表移至主动处理,新增 invalidMemoryPrefetchCache 异步失效方法
- 后台检索返回空结果时主动清除该用户所有预取缓存,避免过期记忆残留
2. 修复 RAG 召回未过滤 deleted 状态记忆的严重 bug
- MemoryCorpus.BuildRetrieveFilter 新增 status="active" 硬过滤,Milvus 向量检索直接排除已删除/已归档记忆
- 此前删除记忆后即使 MySQL 标记为 deleted,Milvus 中向量仍可被语义召回并注入 prompt
前端:无
仓库:无
This commit is contained in:
Losita
2026-04-16 23:33:38 +08:00
parent d554b52784
commit dd6638f8db
17 changed files with 414 additions and 102 deletions

View File

@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"log"
"strings"
"time"
newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
"github.com/LoveLosita/smartflow/backend/newAgent/tools/schedule"
@@ -105,6 +107,9 @@ func (n *AgentNodes) Plan(ctx context.Context, st *newagentmodel.AgentGraphState
return nil, errors.New("plan node: state is nil")
}
// 等待后台记忆检索完成,注入最新记忆后再启动 Plan。
ensureFreshMemory(st)
if err := RunPlanNode(
ctx,
PlanNodeInput{
@@ -206,6 +211,9 @@ func (n *AgentNodes) Execute(ctx context.Context, st *newagentmodel.AgentGraphSt
st.EnsureConversationContext().SetToolSchemas(toolSchemas)
}
// 等待后台记忆检索完成,注入最新记忆后再启动 Execute。
ensureFreshMemory(st)
if err := RunExecuteNode(
ctx,
ExecuteNodeInput{
@@ -292,6 +300,34 @@ func (n *AgentNodes) Deliver(ctx context.Context, st *newagentmodel.AgentGraphSt
return st, nil
}
// --- 记忆预取消费辅助 ---
// ensureFreshMemory 等待后台记忆检索完成,将最新结果注入 ConversationContext。
//
// 设计说明:
// 1. 只在首次调用时等待 channel最多 500ms后续调用直接跳过
// 2. 覆盖 ConversationContext 中已有的缓存记忆UpsertPinnedBlock 按 key 覆盖);
// 3. timeout 后保留缓存记忆不替换,保证 Execute ReAct 循环不会因超时丢失记忆。
func ensureFreshMemory(st *newagentmodel.AgentGraphState) {
if st == nil || st.Deps.MemoryConsumed || st.Deps.MemoryFuture == nil {
return
}
st.Deps.MemoryConsumed = true // 标记已消费,后续调用直接跳过
select {
case content := <-st.Deps.MemoryFuture:
if strings.TrimSpace(content) != "" {
st.EnsureConversationContext().UpsertPinnedBlock(newagentmodel.ContextBlock{
Key: newagentmodel.MemoryContextBlockKey,
Title: newagentmodel.MemoryContextBlockTitle,
Content: content,
})
}
case <-time.After(newagentmodel.MemoryFreshTimeout):
// timeout保留 ConversationContext 中已有的缓存记忆,不做额外操作
}
}
// --- 持久化辅助 ---
// saveAgentState 在节点执行成功后,将当前运行态快照保存到 Redis。