Files
smartmate/backend/memory/第三步治理与观测落地计划.md
Losita a1b2ffedb8 Version: 0.9.22.dev.260416
后端:
1. 品牌文案与聊天定位统一切到 SmartMate,并放宽非排程问答能力
   - 系统人设、路由、排程、查询、交付提示统一从 SmartFlow 改为 SmartMate
   - 明确普通问答/生活建议/开放讨论可正常回答,deep_answer 不再输出“让我想想”等占位话术
   - thinkingMode=auto 时,deep_answer 默认开启 thinking,execute 继续跟随路由决策,其余路由默认关闭
2. Memory 读取链路升级为“结构化强约束 + 语义候选”hybrid 模式,并补齐注入渲染 / Execute 消费
   - 新增 read.mode、四类记忆预算、inject.renderMode 等配置及默认值
   - 落地 HybridRetrieve,统一 MySQL/RAG 读侧作用域、三级去重(ID/hash/text)、统一重排与按类型预算裁剪
   - 新增 FindPinnedByUser、content_hash DTO/兜底补算、legacy/RAG 共用读侧查询口径与 fallback 逻辑
   - 记忆注入支持 flat/typed_v2 两种渲染,execute msg3 正式消费 memory_context,主链路注入 MemoryReader 时同步透传 memory 配置
3. Memory 第二步/第三步 handoff 与治理文档补齐
   - HANDOFF_Memory向Mem0靠拢三步冲刺计划.md 从 newAgent 迁到 memory 目录,并补充“我的记忆”增删改查与最小留痕口径
   - 新增 backend/memory/记忆模块第二步计划.md、backend/memory/第三步治理与观测落地计划.md,分别拆解 hybrid 读取注入闭环与治理/观测/清理路线
   - 同步更新 backend/memory/Log.txt 调试日志
前端:
1. 助手输入区新增“智能编排”任务类选择器,并把 task_class_ids 作为请求 extra 透传
   - 新建 frontend/src/components/assistant/TaskClassPlanningPicker.vue,支持拉取任务类列表、临时勾选、已选标签回显与清空
   - 更新 frontend/src/components/dashboard/AssistantPanel.vue、frontend/src/types/dashboard.ts:Chat extra 正式建模 task_class_ids / retry 字段;当本轮带编排任务类时强制新起会话,避免把现有会话历史误混入新编排
2. 会话上下文窗口统计接入前端展示
   - 更新 frontend/src/api/agent.ts、新建 frontend/src/components/assistant/ContextWindowMeter.vue、更新 frontend/src/components/dashboard/AssistantPanel.vue、frontend/src/types/dashboard.ts:接入 /agent/context-stats,兼容 object/string/null 三种返回;在输入工具栏展示 msg0~msg3 占比与预算使用率
3. 助手面板交互细节优化
   - 更新 frontend/src/components/dashboard/AssistantPanel.vue:thinking 开关改为 auto/true/false 三态选择;切会话与重试后同步刷新 context stats;历史列表首屏不足时自动继续分页直到形成滚动区
仓库:无
2026-04-16 18:29:17 +08:00

13 KiB
Raw Blame History

Memory 第三步治理与观测落地计划

1. 这份文档解决什么问题

这份文档只回答第三步要做什么,不再重复前两步已经完成的抽取、决策、召回细节。

第三步的目标很简单:

  1. 把 memory 从“能跑”升级成“敢灰度、敢排障、敢清理、敢回滚”。
  2. 把“日志打在哪里、我怎么看、会不会给接口”说清楚。
  3. 把改动范围收敛在治理层,不再继续扩算法和能力边界。

一句人话总结:

前两步解决的是“有没有能力”,第三步解决的是“出了问题怎么查、怎么收、怎么退”。


2. 先说结论

第三步我会分成两块做:

  1. 观测与切流
  2. 用户管理与清理

为什么这么拆:

  1. 现在第二步最小闭环已经通了,最怕的不是“能力不够多”,而是“出了问题不知道卡在哪一层”。
  2. 如果没有统一日志、指标和开关,后面再继续加功能,只会让 memory 变成一个越来越难维护的黑箱。
  3. 历史重复脏数据不先治理,后面读链路和注入链路的数据噪音会越来越重。

第三步不追求“更聪明”,追求“更稳、更可控”。


3. 你最关心的三个问题

3.1 日志会打在哪里

第三步不会把所有信息都塞进一个地方,而是分三层:

A. 运行日志

运行日志打到后端服务本身的标准日志,也就是当前 backend 进程控制台 / 容器 stdout。

这层主要看实时链路,适合排查:

  1. 这次写入为什么是 ADD / UPDATE / DELETE / NONE
  2. 这次召回为什么没命中
  3. 这次注入为什么降级到 flatlegacy
  4. 这次 worker 为什么走了 fallback

这层的形态参考当前 RAG 轻量 Observer 的做法,不单独造一套散装日志方案。

参考文件:

  1. backend/cmd/start.go
  2. backend/infra/rag/core/observer.go

B. 变更留痕

变更留痕继续落库,不只打终端。

当前已经有:

  1. memory_audit_logs
  2. backend/model/memory.go
  3. backend/memory/repo/audit_repo.go

这层主要看“已经发生过的变更事实”,适合研发排查和后端自查:

  1. 哪条记忆被删了
  2. 删之前和删之后内容是什么
  3. 这次 dedup 清理保留了哪条,归档了哪条
  4. 某次 update / delete / restore 是谁触发的,原因是什么

C. 汇总指标

第一版不先上完整 Prometheus / Grafana 平台,而是先把关键指标打稳,再视需要接统一观测平台。

这层主要看趋势和健康度,适合回答:

  1. 最近写入成功率怎么样
  2. hybrid 召回到底有没有提升
  3. 去重到底丢了多少垃圾数据
  4. 是否频繁回滚到 legacy

3.2 我会怎么看

开发和联调阶段,推荐分两种看法:

看实时问题

直接看后端运行日志。

适合看:

  1. 单次请求链路
  2. 单次 worker 执行过程
  3. fallback / 降级 / 回滚是否发生

看历史问题

直接查数据库留痕表和主表。

适合看:

  1. 某条 memory 历史上被怎么改过
  2. 某次清理动作具体处理了哪些记录
  3. 当前 active / archived / deleted 分布

建议排查时优先查这几张表:

  1. memory_jobs
  2. memory_items
  3. memory_audit_logs

第一版就够用了,不强依赖前端页面才能排查。


3.3 会不会提供接口

会,但原则上只补“面向当前用户管理自己记忆”的接口,不补“原始运行日志接口”,也不把 memory 先做成全项目唯一完整的审计后台。

原因很直接:

  1. 原始日志噪音很大,不适合直接给前端看。
  2. 原始日志字段会迭代,直接对外暴露会把内部实现绑死。
  3. 原始日志可能带内部 trace、错误细节不适合直接外露。

所以第三步对外提供的是“用户管理自己记忆”的接口,不是“把 stdout 原样吐给前端”,也不是“先给 memory 单独造一套管理后台接口”。

第三步建议优先补这几类用户接口:

第一组:当前用户查看自己的记忆

  1. GET /api/v1/memory/items
    • 分页查看“我自己的记忆”
  2. GET /api/v1/memory/items/:id
    • 查看“我自己的某条记忆”详情

第二组:当前用户主动维护自己的记忆

  1. POST /api/v1/memory/items
    • 手动新增一条记忆
  2. PATCH /api/v1/memory/items/:id
    • 修改自己的一条记忆
  3. DELETE /api/v1/memory/items/:id
    • 删除自己的一条记忆

第三组:当前用户恢复误删内容

  1. POST /api/v1/memory/items/:id/restore
    • 若底层采用软删或归档,可补恢复动作

这些接口都默认只允许操作“当前登录用户自己的记忆”,不支持跨用户查询和跨用户修改。

原则:

  1. 原始日志看后端 stdout
  2. 内部变更留痕优先给后端查表和排障使用,不急着做成前端正式能力
  3. 对外先开放用户真正会用到的“我的记忆”增删改查

4. 第三步到底要做什么

4.1 观测与切流

这是第三步的第一优先级。

要做的事

  1. 给写入决策链路补统一结构化日志
  2. 给读侧召回链路补统一结构化日志
  3. 给注入渲染链路补统一结构化日志
  4. 给上述三条链路补关键计数指标
  5. 把现有配置字段整理成清晰的切流顺序和回滚手册

为什么先做这个

因为第三步如果先做 dedup 清理,但没有日志和切流能力,一旦清错了,排查成本会很高。


4.2 用户管理与清理

这是第三步的第二优先级。

要做的事

  1. 给“我的记忆”补完整增删改查语义
  2. 给历史重复数据补离线 dedup 工具
  3. 给关键变更动作补最小留痕
  4. 把 dedup 保持在后端内部治理流程,不急着做成前端接口

为什么不一上来绑主 worker

因为第一版 dedup 的目标是“可留痕、可回滚”,不是“全自动”,也不是先给 memory 单独造一个很重的治理后台。

离线或手动触发更安全,出问题也更容易止血。


5. 具体改动计划

5.1 第一轮:先把观测底座补起来

目标

先让系统“可看见”。

预计改动

新增:

  1. backend/memory/observe/log_fields.go

修改:

  1. backend/memory/worker/decision_flow.go
  2. backend/memory/worker/apply_actions.go
  3. backend/memory/service/read_service.go
  4. backend/memory/service/retrieve_merge.go
  5. backend/service/agentsvc/agent_memory.go
  6. backend/service/agentsvc/agent_memory_render.go

这一轮会补什么日志

写入决策日志

至少记录这些字段:

  1. trace_id
  2. user_id
  3. conversation_id
  4. job_id
  5. fact_type
  6. candidate_count
  7. final_action
  8. fallback_mode
  9. success

读侧召回日志

至少记录这些字段:

  1. trace_id
  2. user_id
  3. read_mode
  4. query_len
  5. legacy_hit_count
  6. semantic_hit_count
  7. dedup_drop_count
  8. final_count
  9. degraded

注入渲染日志

至少记录这些字段:

  1. trace_id
  2. user_id
  3. inject_mode
  4. input_count
  5. rendered_count
  6. token_budget
  7. fallback

5.2 第二轮:补指标,不急着开 overview 接口

目标

先让系统“可量化”。

第一版建议补的指标

优先补这 8 个:

  1. memory_job_success_rate
  2. memory_job_retry_rate
  3. memory_decision_distribution
  4. memory_decision_fallback_rate
  5. memory_retrieve_hit_count
  6. memory_retrieve_dedup_drop_count
  7. memory_inject_item_count
  8. memory_rag_fallback_rate

暂不强求第一版就补:

  1. memory_wrong_mention_rate
  2. memory_user_correction_rate

因为这两个更依赖后续“用户纠错入口”。

这一轮先不做什么

这一轮先不单独新增 GET /api/v1/memory/overview

原因不是这个接口没价值,而是现在别的模块还没有统一的观测面板和汇总接口规范。memory 这一轮先把指标打稳,后续如果全项目一起做观测面板,再统一收口更对称。

也就是说,这一轮优先把“数据先有”做出来,不急着把“看板接口先长出来”。


5.3 第三轮:补用户管理动作

目标

先让用户“能管理自己的记忆”。

预计改动

修改:

  1. backend/memory/service/manage_service.go
  2. backend/memory/repo/item_repo.go
  3. backend/memory/utils/audit.go
  4. backend/memory/module.go

新增:

  1. backend/api/memory.go
  2. 路由注册文件中的 memory 接线

要补的动作

  1. list
  2. detail
  3. create
  4. update
  5. delete
  6. 若底层保留软删语义,再补 restore

接口建议

新增:

  1. GET /api/v1/memory/items
  2. GET /api/v1/memory/items/:id
  3. POST /api/v1/memory/items
  4. PATCH /api/v1/memory/items/:id
  5. DELETE /api/v1/memory/items/:id
  6. POST /api/v1/memory/items/:id/restore
    • 仅在底层采用软删或归档方案时开放

设计要求

  1. 所有接口默认只作用于“当前登录用户自己的记忆”
  2. 后端仍保留最小变更留痕,但不把它包装成用户侧“审计接口”
  3. 接口返回给前端的是“人能看懂的记忆内容和操作结果”,不是底层日志

5.4 第四轮:做离线 dedup 治理

目标

先让系统“可清理”。

预计新增

  1. backend/memory/cleanup/dedup_runner.go
  2. backend/memory/cleanup/dedup_policy.go

第一版治理规则

按以下维度扫描重复组:

  1. user_id
  2. memory_type
  3. content_hash
  4. status = active

每组处理规则:

  1. 选一条主记录保留
  2. 优先保留最近更新的
  3. 若最近更新时间接近,则优先保留置信度更高的
  4. 其余记录改为 archived
  5. 每次治理动作都写最小变更留痕

接口建议

这一轮不对外新增 dedup 接口。

dedup 先保留为后端内部治理能力,必要时通过离线任务、后台命令或内部 job 触发,避免 memory 先演化成一个比其他模块更重的专用治理后台。

明确限制

第一版不做:

  1. 直接危险 SQL 清表
  2. 自动定时常驻清理
  3. 无留痕的批量删除

6. 日志、留痕、接口分别给谁看

这个地方一定要分清,不然第三步会越做越乱。

运行日志

给研发和排障看。

特点:

  1. 实时
  2. 噪音大
  3. 字段多
  4. 不直接给前端

变更留痕

先给研发和后端排障使用。

特点:

  1. 是持久化结果
  2. 适合看历史
  3. 这一轮不急着做成正式用户接口

用户接口

给用户和前端页面看。

特点:

  1. 只暴露“我的记忆”内容和操作结果
  2. 不暴露内部 raw log
  3. 不承载平台级观测职责

7. 切流顺序

第三步不允许一刀切。

建议严格按下面顺序灰度:

  1. 阶段 A决策层 shadow
    • 真正写库仍然走 legacy
    • 新决策层只记日志,不生效
  2. 阶段 B决策层仅对显式记忆生效
  3. 阶段 C决策层对全部写入生效
  4. 阶段 D读侧切到 hybrid
  5. 阶段 E注入切到 typed_v2
  6. 阶段 F历史清理跑完再考虑关闭 legacy 默认路径

这里的配置基础已经存在,关键是把切流顺序写清、用清、能回退。

参考文件:

  1. backend/memory/model/config.go
  2. backend/memory/service/config_loader.go

8. 回滚方案

第三步的回滚不应影响前两步代码保留,只回切开关。

最小回滚动作

  1. 写侧回到 legacy
  2. 读侧回到 legacy
  3. 注入回到 flat
  4. 停掉 dedup 清理任务

回滚原则

  1. 先停治理动作,再回切主路径
  2. 不做破坏性 schema 回滚
  3. 不依赖人工热修逻辑判断

9. 第三步明确不做什么

为了防止范围失控,这一轮明确不做:

  1. 不做图记忆
  2. 不做多 Provider 工厂化
  3. 不拆独立 memory 服务
  4. 不在这一轮给 memory 先单独做完整审计后台
  5. 不把 WebSearch 和 Memory 强行合并成一轮上线
  6. 不再扩新的召回算法分支

10. 完成标准

满足以下条件,算第三步完成:

  1. 能从日志看清某条记忆为什么被判成 ADD / UPDATE / DELETE / NONE
  2. 能从指标看清召回命中、去重、降级、回滚情况
  3. 用户能通过接口管理自己的记忆
  4. 能对历史重复数据做可留痕清理
  5. 出异常时能通过开关在分钟级切回 legacy
  6. 文档和代码现状一致,不再靠口头传递

11. 如果只看一页,请看这个执行顺序

第三步不要散着做,建议按这个顺序推进:

  1. 先补统一日志字段和结构化日志
  2. 再补指标,把观测数据打稳
  3. 再补“我的记忆”增删改查能力
  4. 最后做离线 dedup 和内部清理能力

一句人话总结:

先让系统“看得见”,再让系统“能管理”,最后再让系统“敢清理”。