Files
smartmate/backend/services/memory/docs/legacy/记忆模块实施计划.md
Losita 2a96f4c6f9 Version: 0.9.76.dev.260505
后端:
1.阶段 6 agent / memory 服务化收口
- 新增 cmd/agent 独立进程入口,承载 agent zrpc server、agent outbox relay / consumer 和运行时依赖初始化
- 补齐 services/agent/rpc 的 Chat stream 与 conversation meta/list/timeline、schedule-preview、context-stats、schedule-state unary RPC
- 新增 gateway/client/agent 与 shared/contracts/agent,将 /api/v1/agent chat 和非 chat 门面切到 agent zrpc
- 收缩 gateway 本地 AgentService 装配,双 RPC 开关开启时不再初始化本地 agent 编排、LLM、RAG 和 memory reader fallback
- 将 backend/memory 物理迁入 services/memory,私有实现收入 internal,保留 module/model/observe 作为 memory 服务门面
- 调整 memory outbox、memory reader 和 agent 记忆渲染链路的 import 与服务边界,cmd/memory 独占 memory worker / consumer
- 关闭 gateway 侧 agent outbox worker 所有权,agent relay / consumer 由 cmd/agent 独占,gateway 仅保留 HTTP/SSE 门面与迁移期开关回退
- 更新阶段 6 文档,记录 agent / memory 当前切流点、smoke 结果,以及 backend/client 与 gateway/shared 的目录收口口径
2026-05-05 19:31:39 +08:00

20 KiB
Raw Blame History

记忆模块实施计划(面试优先版 -> 产品可用版)

1. 文档目标

  1. 在 3 天内交付一个“可演示、可讲清楚、可继续演进”的记忆系统 MVP。
  2. 兼容当前单体工程,不引入高风险拆分,不破坏现有聊天主链路。
  3. 复用现有 Outbox 异步基础设施,避免重复造轮子。
  4. 形成可直接用于面试讲述的架构故事线、指标体系与演示脚本。
  5. 在不增加过度复杂度的前提下,吸收 Mem0 中已被验证的关键机制(抽取、决策、检索、降级、防幻觉)。

2. 背景与约束

  1. 当前系统是单体 Go 项目,已有稳定的 Outbox + Kafka + 消费事务 通路。
  2. 当前项目定位先是日程助手,长期演进为陪伴型助手。
  3. 短期目标是快速做出“真的可用”的记忆能力,不追求一次做成完整通用平台。
  4. 风险约束:
    • 不能让重型 LLM 处理阻塞聊天实时响应。
    • 不能在 Outbox 消费主循环里堆重计算,避免拖垮其他事件消费。
    • 不能牺牲数据一致性与可审计性。

3. 总体方案

3.1 核心思路

采用“同步快路径 + 异步慢路径”:

  1. 同步快路径:回复前快速读取可用记忆(以 MySQL 结构化事实为主),保证“下一轮能用”。
  2. 异步慢路径:通过 Outbox 触发记忆抽取任务,执行去重、冲突消解、打分、向量化等重操作。
  3. 读写解耦:写路径确保可靠入队,读路径优先稳定可控,再做语义增强。

3.2 存储职责分层

  1. MySQL事实主库偏好、约束、任务上下文、TTL、置信度、敏感级别、来源
  2. Milvus语义召回同义表达匹配、模糊语义联想
  3. Redis可选热数据缓存后续优化不作为 MVP 必选项)。

3.3 编排层职责

Memory Orchestrator 负责两条链路:

  1. 写入链路:候选抽取 -> 去重/冲突 -> 打分 -> 分流落库MySQL/Milvus
  2. 读取链路:硬约束优先 -> 语义召回补充 -> 重排 -> 门控 -> 注入上下文。

3.4 借鉴 Mem0 的关键机制(已裁剪版)

  1. 双阶段去重决策:先向量召回候选旧记忆,再由 LLM 决策 ADD/UPDATE/DELETE/NONE,而不是只靠相似度阈值硬判。
  2. UUID 映射防幻觉:把真实 memory_id 映射成临时整数给 LLM回收结果时再反查防止模型编造不存在 ID。
  3. 结构化输出刚性约束:抽取与决策都用 JSON 结构,失败时走 extract_json -> normalize_facts 容错链,不让解析失败直接污染主流程。
  4. 动作分型嵌入:嵌入接口显式传入 memory_actionadd/search/update),为后续差异化 embedding 策略预留接口。
  5. 检索后处理标准化:threshold 过滤 -> 可选 reranker -> 统一降级,当重排器异常时保留向量原始排序并打告警日志。
  6. 多维隔离语义:统一采用 user_id + agent_id + run_id 三维过滤;在本项目映射为 user_id + assistant_id + conversation_id

3.5 本项目明确不做(本轮)

  1. 不做图记忆Graph Memory落地实现仅预留扩展点避免 3 天范围失控。
  2. 不做多 Provider 工厂体系,只保留单 Provider 可替换接口,后续再扩展。
  3. 不做独立 server 化记忆服务,先在单体内完成闭环与指标验证。

4. 3 天执行计划(可直接照着做)

Day 1把“可写入”打通可靠入队 + 可追踪)

目标

  1. 记忆任务能稳定从聊天主链路发出。
  2. 能看到任务从 pendingsuccess/failed 的状态流转。
  3. 保证失败可重试、可追踪、可补偿。

任务清单

  1. 新增文档与目录占位:
    • backend/memory/README.md(模块说明)
    • backend/memory/service/(门面)
    • backend/memory/model/DTO 与状态)
    • backend/memory/repo/(数据访问)
    • backend/memory/orchestrator/(编排)
    • backend/memory/worker/(异步执行)
  2. 新增 MySQL 表(建议先手写 SQL + DAO
    • memory_items
    • memory_jobs
    • memory_audit_logs
    • memory_user_settings
  3. 新增配置对象(memory config
    • 抽取 prompt、更新决策 prompt、阈值、是否启用 reranker、LLM 温度参数。
    • 默认采用低随机参数(temperature/top_p 低值)提高可复现性。
  4. 新增 Outbox 事件:
    • memory.extract.requestedv1
  5. 在聊天后置持久化环节发布事件:
    • 仅传轻量字段,避免超大 payload。
  6. 新增消费处理器:
    • 只做任务入库,不做重型 LLM 调用。
  7. 新增解析与标准化工具:
    • extract_json():从模型输出中抽取 JSON兼容代码块包裹
    • normalize_facts():去重、去空、长度校验、非法项过滤。
  8. 新增决策状态机定义:
    • ADD/UPDATE/DELETE/NONE 的合法状态与动作映射。
  9. 启动期接线:
    • backend/cmd/start.go 注册记忆事件处理器。

Day 1 验收标准

  1. 一次聊天后Outbox 中能看到 memory.extract.requested 事件。
  2. 事件消费后,memory_jobs 生成记录。
  3. 人工触发 worker 可完成一次任务状态推进(哪怕先是 mock 抽取)。

Day 2把“可读取可注入”打通先 MySQL 后向量)

目标

  1. 记忆可在回复前被检索并注入上下文。
  2. 能避免明显的“尬提”与无关提及。
  3. 提供最小用户可控能力(查看/删除/关闭)。

任务清单

  1. 实现 MemoryReadService
    • 按用户与会话上下文读取记忆。
    • 优先结构化硬约束(时间偏好、排程禁忌、显式偏好)。
  2. 实现 MemoryInjector
    • Top-K 记忆选择。
    • token 预算截断。
    • 注入模板统一化。
  3. 实现门控逻辑:
    • 相关性阈值。
    • 置信度阈值。
    • 时间衰减权重。
    • 敏感级别检查。
  4. 增加“阈值 + 可选重排 + 降级”链路:
    • 阈值过滤作为第一道过滤。
    • reranker 失败时自动降级为原排序并记录原因码。
  5. 新增最小管理接口:
    • GET /api/v1/memory/items
    • DELETE /api/v1/memory/items/:id
    • POST /api/v1/memory/settings(开关)
  6. 完成首版日志埋点:
    • 检索命中数、注入条数、门控丢弃原因。
    • 决策分布ADD/UPDATE/DELETE/NONE 占比)。

Day 2 验收标准

  1. 给出偏好后,下一轮排程请求能利用该偏好。
  2. 无关话题不会频繁硬提旧记忆。
  3. 用户可删除指定记忆,删除后不再注入。

Day 3把“可讲清楚”与“可评估”补齐面试可答

目标

  1. 输出完整可讲架构,说明设计取舍。
  2. 增加可量化指标,证明记忆“有用”而不是“看起来有”。
  3. 可选接入 Milvus若环境未就绪先保留接口 + mock

任务清单

  1. 实现/预留向量接口:
    • VectorStore.Upsert()
    • VectorStore.Search()
    • VectorStore.Delete()
    • VectorStore.Get()(为 UPDATE/DELETE 决策回查旧值)
  2. 对接 Milvus可选
    • collection 初始化。
    • 向量 + 元数据过滤检索。
  3. 指标体系落地:
    • 记忆命中率retrieved/useful
    • 错误提及率wrong mention
    • 用户纠正率user correction
    • 回复延迟影响P50/P95
  4. 准备演示脚本与面试问答稿:
    • 5 分钟架构说明。
    • 3 个典型失败案例及兜底策略。
    • 未来迭代路线。
  5. 输出“借鉴 Mem0 但本地化裁剪”的对比说明:
    • 借鉴了什么。
    • 为什么暂时不做图记忆与多 Provider 工厂。

Day 3 验收标准

  1. 能现场演示“记住偏好 -> 下轮生效 -> 删除后失效”。
  2. 能答清楚“为什么不是纯同步/纯异步”。
  3. 能答清楚“为什么 MySQL + Milvus 双存储”。

5. 数据模型设计(首版)

5.1 memory_items(长期事实记忆)

用途:保存对业务有约束价值的可注入记忆。

关键字段建议:

  1. id bigint PK
  2. user_id bigint必填
  3. conversation_id varchar(64)(可空,表示全局用户记忆)
  4. assistant_id varchar(64)(可空,区分不同助手人格/技能域)
  5. run_id varchar(64)(可空,会话级隔离)
  6. memory_type varchar(32)
    • preference(偏好)
    • constraint(硬约束)
    • fact(事实)
    • todo_hint(近期提醒线索)
  7. title varchar(128)
  8. content text
  9. normalized_content text去噪后
  10. content_hash varchar(64)(幂等去重)
  11. confidence decimal(5,4)0~1
  12. importance decimal(5,4)0~1
  13. sensitivity_level tinyint
    • 0 普通
    • 1 中敏
    • 2 高敏
  14. source_message_id bigint
  15. source_event_id varchar(64)
  16. is_explicit tinyint(1)(是否用户明确要求记住)
  17. status varchar(16)
    • active
    • archived
    • deleted
  18. ttl_at datetime到期时间
  19. last_access_at datetime
  20. created_at datetime
  21. updated_at datetime
  22. vector_status varchar(16)pending/synced/failed
  23. vector_id varchar(128)(向量库主键映射)

索引建议:

  1. (user_id, status, memory_type, updated_at desc)
  2. (user_id, conversation_id, status, updated_at desc)
  3. (source_message_id)(排查链路)
  4. (ttl_at)(过期清理)
  5. (user_id, assistant_id, run_id, status, updated_at desc)
  6. (user_id, memory_type, content_hash)(幂等去重)

5.2 memory_jobs(异步任务队列表)

用途:承接 Outbox 消费后的待处理任务,解耦重计算。

关键字段建议:

  1. id bigint PK
  2. user_id bigint
  3. conversation_id varchar(64)
  4. source_message_id bigint
  5. source_event_id varchar(64)
  6. job_type varchar(32)
    • extract
    • embed
    • reconcile
  7. idempotency_key varchar(128)
  8. payload_json longtext
  9. status varchar(16)
    • pending
    • processing
    • success
    • failed
    • dead
  10. retry_count int
  11. max_retry int
  12. next_retry_at datetime
  13. last_error varchar(2000)
  14. created_at datetime
  15. updated_at datetime

索引建议:

  1. (status, next_retry_at, id)
  2. (user_id, created_at desc)
  3. (source_event_id)(幂等与追踪)
  4. (idempotency_key)(消费防重)

5.3 memory_audit_logs(审计日志)

用途:回答“这条记忆是谁在什么条件下写的/改的/删的”。

关键字段建议:

  1. id bigint PK
  2. memory_id bigint
  3. user_id bigint
  4. operation varchar(32)
    • create
    • update
    • archive
    • delete
    • restore
  5. operator_type varchar(16)
    • system
    • user
  6. reason varchar(255)
  7. before_json longtext
  8. after_json longtext
  9. created_at datetime

5.4 memory_user_settings(用户记忆开关)

用途:实现用户可控能力。

关键字段建议:

  1. user_id bigint PK
  2. memory_enabled tinyint(1)
  3. implicit_memory_enabled tinyint(1)
  4. sensitive_memory_enabled tinyint(1)
  5. updated_at datetime

6. 事件与协议设计

6.1 事件类型

  1. memory.extract.requestedv1
  2. 预留:
    • memory.embed.requested
    • memory.cleanup.requested

6.2 载荷字段v1

  1. user_id
  2. conversation_id
  3. assistant_id
  4. run_id
  5. source_message_id
  6. source_role
  7. source_text
  8. occurred_at
  9. trace_id
  10. idempotency_key

设计约束:

  1. Payload 只放执行需要的最小字段。
  2. 大文本允许截断并保留摘要,防止消息膨胀。
  3. 必须包含幂等标识(如 source_message_id + user_id)。
  4. 过滤维度必须完整(user_id + assistant_id + run_id),避免跨会话串记忆。

7. 写入流程详细设计

7.1 主流程

  1. 聊天主链路完成并落历史消息。
  2. 发布 memory.extract.requested 到 Outbox。
  3. Outbox 消费处理器验证 payload。
  4. 处理器创建或幂等更新 memory_jobs(仅任务入库)。
  5. memory/worker 扫描 pending 任务并抢占为 processing
  6. Worker 调用 LLM 执行“候选事实抽取”JSON 输出)。
  7. 执行 extract_json -> normalize_facts 容错标准化链路。
  8. 对每条候选事实做向量检索,召回 Top-K 旧记忆候选。
  9. 对召回结果执行“临时整数 ID 映射”,再交给 LLM 决策 ADD/UPDATE/DELETE/NONE
  10. 根据决策执行写入动作:
    • ADD:新增 memory_items + 审计日志。
    • UPDATE:更新记录并保留历史旧值。
    • DELETE:软删除并记录删除原因。
    • NONE:不写入,仅记调试日志。
  11. 按决策动作触发向量同步(支持 vector_pending)。
  12. 成功后任务标记 success,失败按重试策略推进。

7.2 失败处理策略

  1. Payload 非法:直接标记 dead不重试。
  2. LLM 短时失败:指数退避重试。
  3. DB 写失败:重试,超过上限 dead。
  4. 向量写失败:
    • MVP 策略:不阻塞事实写入,记录 vector_pending 状态。
    • 后续策略:补偿任务重建向量索引。

7.3 幂等策略

  1. 幂等键:user_id + source_message_id + memory_type + normalized_content_hash
  2. 同幂等键重复写入:更新 updated_at、提升访问热度,不新增重复条目。
  3. 由 Outbox 重试导致的重复消费必须无副作用。
  4. 对 UPDATE/DELETE 必须先校验目标 memory_id 是否存在且属于当前过滤域。

8. 读取流程详细设计

8.1 主流程

  1. 接收用户新问题,先做意图分类(排程/闲聊/混合)。
  2. memory_items 拉取硬约束记忆(高优先级)。
  3. 若 Milvus 可用,执行语义召回补充记忆候选。
  4. 对候选执行重排:
    • 相关性分
    • 置信度分
    • 时间衰减分
    • 显式记忆加权
  5. 执行门控:
    • 低相关丢弃
    • 高敏过滤
    • 过期过滤
  6. 执行阈值过滤后可选 reranker若 reranker 异常则自动降级使用原排序。
  7. 按 token budget 选择最终注入条目。
  8. 组装统一注入上下文,传给主模型生成回复。

8.2 重排评分(建议公式)

final_score = 0.45 * relevance + 0.25 * confidence + 0.20 * recency + 0.10 * explicit_bonus

说明:

  1. 排程类场景可增加硬约束权重。
  2. 闲聊类场景可提高语义相关权重。
  3. 该公式为 MVP 默认值,后续可通过线上数据调参。

8.3 门控规则MVP

  1. final_score < 0.55 不注入。
  2. sensitivity_level >= 2 且用户未开启敏感记忆时不注入。
  3. ttl_at < now 不注入。
  4. 同主题最多注入 1~2 条,防止重复轰炸。

9. 对外接口MVP

9.1 用户接口

  1. GET /api/v1/memory/items
    • 支持按类型、时间、状态过滤。
  2. DELETE /api/v1/memory/items/:id
    • 软删除并写审计日志。
  3. POST /api/v1/memory/settings
    • 修改记忆总开关、隐式记忆开关。

9.2 内部接口

  1. MemoryService.EnqueueExtractJob(ctx, payload)
  2. MemoryService.RetrieveForPrompt(ctx, req)
  3. MemoryService.UpsertMemoryItems(ctx, items)
  4. MemoryService.DeleteMemory(ctx, userID, memoryID)

10. 可观测性与指标

10.1 指标定义

  1. memory_job_success_rate
  2. memory_job_retry_rate
  3. memory_retrieval_hit_rate
  4. memory_injection_count_avg
  5. memory_wrong_mention_rate
  6. memory_user_correction_rate
  7. chat_p95_latency_delta_with_memory
  8. memory_json_parse_fail_rate
  9. memory_decision_distributionADD/UPDATE/DELETE/NONE
  10. reranker_fallback_rate

10.2 日志与追踪

  1. 每个任务写 trace_id,贯穿聊天请求 -> outbox -> memory_job -> memory_item。
  2. 对门控丢弃记录原因码:
    • LOW_SCORE
    • EXPIRED
    • SENSITIVE_BLOCKED
    • DUP_TOPIC
  3. 保证可以反查“为什么这次没有提某条记忆”。

11. 安全与隐私约束

  1. 敏感信息默认不做隐式记忆(如健康、财务、证件等)。
  2. 用户必须可删除历史记忆,删除后不再用于注入。
  3. 记忆开关关闭后,仅保留必要系统数据,不再新增记忆条目。
  4. 审计日志保留系统写入行为,便于风控与合规排查。

12. 测试策略

12.1 单元测试范围(实现阶段)

  1. 候选抽取结果解析函数。
  2. 冲突消解函数。
  3. 重排评分函数。
  4. 门控函数。
  5. 幂等去重函数。
  6. extract_json 容错解析函数。
  7. normalize_facts 标准化函数。
  8. UUID 映射与反查函数。
  9. ADD/UPDATE/DELETE/NONE 决策结果校验函数。

12.2 集成测试范围(实现阶段)

  1. 聊天后事件成功入 outbox。
  2. Outbox 消费后任务成功入 memory_jobs
  3. Worker 成功写 memory_items
  4. 读取链路能在回复中注入预期记忆。

12.3 注意事项(遵循项目约束)

  1. 若编写 Go 测试文件(*_test.go)做验证,任务完成后按项目约定移除测试文件。
  2. 每次执行本地 go test 后清理项目根目录 .gocache

13. 风险与回滚

13.1 主要风险

  1. 记忆误提影响体验。
  2. LLM 抽取不稳定导致脏记忆。
  3. 向量检索误召回导致不相关注入。
  4. 任务积压影响时效。

13.2 应对策略

  1. 先严门控,宁可少提,不要乱提。
  2. 保留“用户纠正”入口,纠正后提高冲突更新优先级。
  3. 对召回做 metadata 过滤(近 30 天、类型限定)。
  4. 监控任务积压长度,超阈值降级(停向量,仅结构化记忆)。

13.3 回滚方案

  1. 配置开关 memory.enabled=false 可一键关闭记忆注入。
  2. 保留写入链路但停读取链路,避免历史数据丢失。
  3. 极端情况下停 worker仅保留主链路聊天功能。

14. 面试表达模板(可直接复述)

  1. “我们做的是同步快路径 + 异步慢路径。同步保证下轮可用,异步负责治理和质量。”
  2. “结构化事实放 MySQL 保证可控可审计,语义联想放 Milvus 提高召回覆盖。”
  3. “Outbox 保证事件可靠入队Worker 解耦重计算,避免阻塞主链路。”
  4. “借鉴 Mem0 的双阶段策略:先向量召回旧记忆,再让 LLM 决策 ADD/UPDATE/DELETE/NONE兼顾召回率与准确率。”
  5. “我们用 UUID 映射防止模型伪造 ID并且用 JSON 容错链保证抽取稳定性。”
  6. “我们用命中率、误提率、纠正率和 reranker 降级率验证记忆是否真的有价值。”

15. DoD完成定义

  1. 代码层:
    • 记忆事件可发布、可消费、可重试。
    • 记忆可检索、可注入、可删除、可关闭。
  2. 质量层:
    • 有基础指标与日志,支持问题排查。
    • 有失败兜底与降级路径。
  3. 叙事层:
    • 3 分钟能讲清架构。
    • 5 分钟能演示端到端效果。
    • 能回答核心取舍与后续演进。

16. 本轮执行顺序建议

  1. 先做 Day 1 的表结构与事件接线,不进入复杂抽取细节。
  2. 再做 Day 2 的读取注入,优先 MySQL 结构化记忆。
  3. 最后补 Day 3 的 Milvus 与指标,确保面试讲述闭环。

17. Mem0 借鉴清单与取舍结论(本轮新增)

17.1 直接借鉴

  1. ADD/UPDATE/DELETE/NONE 统一决策状态机。
  2. threshold -> reranker(可选) -> fallback 的检索后处理套路。
  3. 三维过滤隔离(user_id/agent_id/run_id)的语义边界设计。
  4. 历史追踪思路(本项目落在 memory_audit_logs)。
  5. 低随机参数 + JSON 输出约束,提升可复现性。

17.2 延后借鉴

  1. 图记忆(关系三元组与软删除)延后到 V2/V3。
  2. 多 Provider 工厂体系延后到“需要跨云/跨模型”时再上。
  3. 托管 API 平台化能力延后到单体稳定后再拆。

17.3 不照搬的原因

  1. 当前目标是 3 天可演示 MVP优先“稳定可讲”而非“能力最全”。
  2. 项目已有 Outbox 可靠链路,先最大化复用,避免架构重复。
  3. 日程助手是强约束场景,结构化事实主库优先级高于图谱表达能力。

本文件定位为“落地执行蓝图”。后续每完成一块能力,建议在本文件追加“已落地清单 + 待办差距”,持续收敛为真实实施记录。