Version: 0.9.11.dev.260409
后端: 1. conv 并行迁移与切流接线(旧目录下沉到 newAgent/conv) - 新建 newAgent/conv/schedule_provider.go、schedule_state.go、schedule_preview.go、schedule_persist.go,保持原有排程转换/预览/持久化能力; - 删除旧目录 conv/schedule_provider.go、schedule_state.go、schedule_preview.go、schedule_persist.go; - 更新 cmd/start.go 与 service/agentsvc/agent_newagent.go,ScheduleProvider/SchedulePersistor 与 preview 转换统一切到 newAgent/conv; - 删除旧 conv/schedule_state_test.go(迁移期测试文件清理)。 2. execute 循环上下文收口增强(历史归档 + 当前轮清晰化) - 更新 node/chat.go:仅在 completed 收口时写 execute_loop_closed marker,供后续 prompt 分层归档; - 更新 prompt/execute_context.go:msg1/msg2 升级为 V3,按收口标记拆分“历史归档 loop / 当前活跃 loop”,并增加 msg1 长度预算裁剪; - 更新 node/execute.go:新增 execute 置顶上下文同步(execution_context/current_step),在轮次开始与 next_plan 后即时刷新; - 更新 prompt/execute.go + execute_context.go:补齐“当前计划步骤 + done_when”强约束,禁止未达成判定时提前 next_plan。 3. 图路由与执行策略微调 - 更新 graph/common_graph.go:Plan/Confirm 分支允许直接进入 Deliver 收口; - 更新 node/plan.go:always_execute 链路下补发计划摘要并写入历史,保证自动执行与手动确认文案一致; - 更新 model/common_state.go:DefaultMaxRounds 从 30 提升到 60。 4. 复合工具规划器重构(去重实现,复用 logic 公共能力) - 更新 tools/compound_tools.go:min_context_switch / spread_even 改为调用 backend/logic 规划器(PlanMinContextSwitchMoves / PlanEvenSpreadMoves); - 新增 state_id↔logic_id 映射层,统一入参与回填,避免工具层与规划层 ID 语义耦合; - 删除 compound_tools 内部重复的规划/归一化/分组/打分实现,减少第三份复制逻辑。 5. 同步调试与文档 - 更新 newAgent/Log.txt 调试日志; - 新增 memory/记忆模块实施计划.md(面试优先版到产品可用版的落地路线)。 前端:无 仓库:无
This commit is contained in:
477
backend/memory/记忆模块实施计划.md
Normal file
477
backend/memory/记忆模块实施计划.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# 记忆模块实施计划(面试优先版 -> 产品可用版)
|
||||
|
||||
## 1. 文档目标
|
||||
|
||||
1. 在 3 天内交付一个“可演示、可讲清楚、可继续演进”的记忆系统 MVP。
|
||||
2. 兼容当前单体工程,不引入高风险拆分,不破坏现有聊天主链路。
|
||||
3. 复用现有 Outbox 异步基础设施,避免重复造轮子。
|
||||
4. 形成可直接用于面试讲述的架构故事线、指标体系与演示脚本。
|
||||
|
||||
## 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. 读取链路:硬约束优先 -> 语义召回补充 -> 重排 -> 门控 -> 注入上下文。
|
||||
|
||||
## 4. 3 天执行计划(可直接照着做)
|
||||
|
||||
## Day 1:把“可写入”打通(可靠入队 + 可追踪)
|
||||
|
||||
### 目标
|
||||
|
||||
1. 记忆任务能稳定从聊天主链路发出。
|
||||
2. 能看到任务从 `pending` 到 `success/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. 新增 Outbox 事件:
|
||||
- `memory.extract.requested`(v1)
|
||||
4. 在聊天后置持久化环节发布事件:
|
||||
- 仅传轻量字段,避免超大 payload。
|
||||
5. 新增消费处理器:
|
||||
- 只做任务入库,不做重型 LLM 调用。
|
||||
6. 启动期接线:
|
||||
- 在 `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. 新增最小管理接口:
|
||||
- `GET /api/v1/memory/items`
|
||||
- `DELETE /api/v1/memory/items/:id`
|
||||
- `POST /api/v1/memory/settings`(开关)
|
||||
5. 完成首版日志埋点:
|
||||
- 检索命中数、注入条数、门控丢弃原因。
|
||||
|
||||
### Day 2 验收标准
|
||||
|
||||
1. 给出偏好后,下一轮排程请求能利用该偏好。
|
||||
2. 无关话题不会频繁硬提旧记忆。
|
||||
3. 用户可删除指定记忆,删除后不再注入。
|
||||
|
||||
## Day 3:把“可讲清楚”与“可评估”补齐(面试可答)
|
||||
|
||||
### 目标
|
||||
|
||||
1. 输出完整可讲架构,说明设计取舍。
|
||||
2. 增加可量化指标,证明记忆“有用”而不是“看起来有”。
|
||||
3. 可选接入 Milvus(若环境未就绪,先保留接口 + mock)。
|
||||
|
||||
### 任务清单
|
||||
|
||||
1. 实现/预留向量接口:
|
||||
- `VectorStore.Upsert()`
|
||||
- `VectorStore.Search()`
|
||||
- `VectorStore.Delete()`
|
||||
2. 对接 Milvus(可选):
|
||||
- collection 初始化。
|
||||
- 向量 + 元数据过滤检索。
|
||||
3. 指标体系落地:
|
||||
- 记忆命中率(retrieved/useful)
|
||||
- 错误提及率(wrong mention)
|
||||
- 用户纠正率(user correction)
|
||||
- 回复延迟影响(P50/P95)
|
||||
4. 准备演示脚本与面试问答稿:
|
||||
- 5 分钟架构说明。
|
||||
- 3 个典型失败案例及兜底策略。
|
||||
- 未来迭代路线。
|
||||
|
||||
### 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. `memory_type` varchar(32)
|
||||
- `preference`(偏好)
|
||||
- `constraint`(硬约束)
|
||||
- `fact`(事实)
|
||||
- `todo_hint`(近期提醒线索)
|
||||
5. `title` varchar(128)
|
||||
6. `content` text
|
||||
7. `normalized_content` text(去噪后)
|
||||
8. `confidence` decimal(5,4)(0~1)
|
||||
9. `importance` decimal(5,4)(0~1)
|
||||
10. `sensitivity_level` tinyint
|
||||
- 0 普通
|
||||
- 1 中敏
|
||||
- 2 高敏
|
||||
11. `source_message_id` bigint
|
||||
12. `source_event_id` varchar(64)
|
||||
13. `is_explicit` tinyint(1)(是否用户明确要求记住)
|
||||
14. `status` varchar(16)
|
||||
- `active`
|
||||
- `archived`
|
||||
- `deleted`
|
||||
15. `ttl_at` datetime(到期时间)
|
||||
16. `last_access_at` datetime
|
||||
17. `created_at` datetime
|
||||
18. `updated_at` datetime
|
||||
|
||||
索引建议:
|
||||
|
||||
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.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. `payload_json` longtext
|
||||
8. `status` varchar(16)
|
||||
- `pending`
|
||||
- `processing`
|
||||
- `success`
|
||||
- `failed`
|
||||
- `dead`
|
||||
9. `retry_count` int
|
||||
10. `max_retry` int
|
||||
11. `next_retry_at` datetime
|
||||
12. `last_error` varchar(2000)
|
||||
13. `created_at` datetime
|
||||
14. `updated_at` datetime
|
||||
|
||||
索引建议:
|
||||
|
||||
1. `(status, next_retry_at, id)`
|
||||
2. `(user_id, created_at desc)`
|
||||
3. `(source_event_id)`(幂等与追踪)
|
||||
|
||||
## 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.requested`(v1)
|
||||
2. 预留:
|
||||
- `memory.embed.requested`
|
||||
- `memory.cleanup.requested`
|
||||
|
||||
## 6.2 载荷字段(v1)
|
||||
|
||||
1. `user_id`
|
||||
2. `conversation_id`
|
||||
3. `source_message_id`
|
||||
4. `source_role`
|
||||
5. `source_text`
|
||||
6. `occurred_at`
|
||||
7. `trace_id`
|
||||
|
||||
设计约束:
|
||||
|
||||
1. Payload 只放执行需要的最小字段。
|
||||
2. 大文本允许截断并保留摘要,防止消息膨胀。
|
||||
3. 必须包含幂等标识(如 `source_message_id + user_id`)。
|
||||
|
||||
## 7. 写入流程详细设计
|
||||
|
||||
## 7.1 主流程
|
||||
|
||||
1. 聊天主链路完成并落历史消息。
|
||||
2. 发布 `memory.extract.requested` 到 Outbox。
|
||||
3. Outbox 消费处理器验证 payload。
|
||||
4. 处理器创建或幂等更新 `memory_jobs`(仅任务入库)。
|
||||
5. `memory/worker` 扫描 `pending` 任务并抢占为 `processing`。
|
||||
6. Worker 调用 LLM 执行“候选记忆抽取”。
|
||||
7. 执行标准化(时间归一化、实体归一化、噪声去除)。
|
||||
8. 执行冲突消解(同类偏好最新优先、互斥约束降权)。
|
||||
9. 计算分值(置信度、重要度、时效度)。
|
||||
10. 写入 `memory_items` 与审计日志。
|
||||
11. 触发向量化(同步或异步二选一)。
|
||||
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 重试导致的重复消费必须无副作用。
|
||||
|
||||
## 8. 读取流程详细设计
|
||||
|
||||
## 8.1 主流程
|
||||
|
||||
1. 接收用户新问题,先做意图分类(排程/闲聊/混合)。
|
||||
2. 从 `memory_items` 拉取硬约束记忆(高优先级)。
|
||||
3. 若 Milvus 可用,执行语义召回补充记忆候选。
|
||||
4. 对候选执行重排:
|
||||
- 相关性分
|
||||
- 置信度分
|
||||
- 时间衰减分
|
||||
- 显式记忆加权
|
||||
5. 执行门控:
|
||||
- 低相关丢弃
|
||||
- 高敏过滤
|
||||
- 过期过滤
|
||||
6. 按 token budget 选择最终注入条目。
|
||||
7. 组装统一注入上下文,传给主模型生成回复。
|
||||
|
||||
## 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`
|
||||
|
||||
## 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. 幂等去重函数。
|
||||
|
||||
## 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. “我们用命中率、误提率、纠正率三项核心指标验证记忆是否真的有价值。”
|
||||
|
||||
## 15. DoD(完成定义)
|
||||
|
||||
1. 代码层:
|
||||
- 记忆事件可发布、可消费、可重试。
|
||||
- 记忆可检索、可注入、可删除、可关闭。
|
||||
2. 质量层:
|
||||
- 有基础指标与日志,支持问题排查。
|
||||
- 有失败兜底与降级路径。
|
||||
3. 叙事层:
|
||||
- 3 分钟能讲清架构。
|
||||
- 5 分钟能演示端到端效果。
|
||||
- 能回答核心取舍与后续演进。
|
||||
|
||||
## 16. 本轮执行顺序建议
|
||||
|
||||
1. 先做 Day 1 的表结构与事件接线,不进入复杂抽取细节。
|
||||
2. 再做 Day 2 的读取注入,优先 MySQL 结构化记忆。
|
||||
3. 最后补 Day 3 的 Milvus 与指标,确保面试讲述闭环。
|
||||
|
||||
---
|
||||
|
||||
本文件定位为“落地执行蓝图”。后续每完成一块能力,建议在本文件追加“已落地清单 + 待办差距”,持续收敛为真实实施记录。
|
||||
|
||||
Reference in New Issue
Block a user