Files

609 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LongTermMemory issue planning
本文档记录 `langbot-longterm-memory` 插件后续优化计划。当前阶段的原则是:
- 不要求修改 LangBot core 或 langbot-plugin-sdk。
- 保持 LongTermMemory 作为插件实现,而不是把 memory 变成 LangBot 内置业务功能。
- 保留当前 L1/L2 两层架构,不急于引入 L0 goals、L3 procedural memory 等新事实层。
- 优先补齐生产可用性:写入策略、召回质量、治理、审计、整合、透明性。
## Current position
当前实现已经满足核心长期记忆闭环:
- L1 core profile:稳定事实,存储在 plugin storage,并注入 system prompt。
- L2 episodic memory:事件记忆,写入 vector DB,通过 metadata 做 scope 隔离和检索。
- 自动注入:PromptPreProcessing 阶段注入 L1,并基于当前消息自动召回少量 L2。
- 主动工具:`remember`、`recall_memory`、`update_profile`、`forget`。
- 管理命令:`!memory`、`!memory profile`、`!memory search`、`!memory list`、`!memory forget`、`!memory export`。
- 纠正机制:带 `correction` / `fact-update` / `clarification` 标签的新 episode 会尝试 supersede 相似旧 episode,降低旧记忆权重。
当前不会自动删除记忆:
- L1 profile 只有显式 `remove` 或覆盖才会改变。
- L2 episode 只有 `forget` 或 `!memory forget <episode_id>` 才会删除。
- superseded episode 仍然保留在 vector DB 中,只是 importance 降低并标记 `superseded_by`。
- time decay 只影响召回排序,不会硬删除。
## Design stance
不建议把插件对外模型扩成多层:
- 不新增 L0 Active Goals 作为 LongTermMemory 的核心层。
- 不新增 L3 Procedural Memory 作为 LongTermMemory 的核心层。
- goals / tasks 更像独立任务管理或个人助手插件,不应塞进长期记忆插件第一阶段。
- procedural knowledge 可暂时用 L1 notes、session profile 或 L2 summary episode 表达。
推荐模型仍然是:
- L1:当前稳定事实和画像。
- L2:可追溯事件和历史。
- Derived views:摘要、索引、候选记忆、归档状态、审计日志、supersede 关系。这些是派生能力,不是新的事实来源。
## Issue 1: Clarify L1/L2 memory write policy
Priority: P0
### Background
当前工具已有基本说明,但模型仍可能过度调用 `remember`,或把稳定偏好写成 L2 episode,把短期事件写进 L1 profile。长期运行后,这会带来噪声和画像污染。
### Goal
明确告诉模型:
- 什么时候写 L1。
- 什么时候写 L2。
- 什么时候不要写。
- 纠正类信息如何同时处理 profile 和 episode。
### Scope
更新以下位置:
- `components/tools/remember.yaml`
- `components/tools/update_profile.yaml`
- `components/tools/recall_memory.yaml`
- `components/event_listener/memory_injector.py` 中的注入说明
- `README.md` 的 usage / best practices 部分
### Suggested policy
L1 profile 只保存稳定、低频、当前有效的事实:
- 姓名、身份、长期偏好。
- 长期沟通风格。
- 群聊或会话长期约定。
- 用户明确纠正后的当前事实。
L2 episode 保存有时间语义、历史价值或上下文价值的事件:
- 计划、行程、近期状态。
- 某次对话中的决定。
- 用户明确希望以后参考的事件。
- 纠正发生的历史记录。
不要保存:
- 一次性寒暄。
- 临时情绪,除非用户明确希望记住。
- 敏感隐私、密钥、凭证、一次性验证码。
- 可能伤害用户或他人的推断性标签。
- 未经确认的群聊八卦或第三方隐私。
### Acceptance criteria
- Tool prompts 对 L1/L2 边界表述一致。
- README 中有 `When to write L1 / L2 / nothing` 小节。
- 至少包含中文群聊和个人私聊示例。
- 纠正类信息的处理规则清晰:更新 L1,必要时写 L2 保留历史。
### Non-goals
- 不引入自动记忆提取。
- 不修改 LangBot core 或 SDK。
## Issue 2: Add memory KB health check
Priority: P0
### Background
L2 依赖 metadata filter 做隔离。README 已说明 Milvus / pgvector 等固定 schema 后端不适合当前实现,但用户仍可能误配。仅靠文档提醒不够。
### Goal
增加运行时健康检查,帮助用户确认当前 pipeline 的 memory KB 是否可用且隔离可靠。
### Proposed command
```text
!memory health
```
### Checks
- memory KB 是否已创建。
- memory KB 是否绑定到当前 pipeline。
- embedding model 是否配置。
- 当前 vector backend 是否能在写入后通过 `user_key` metadata filter 正确检索。
- `vector_list` / `vector_delete` 是否能按 filter 工作。
### Suggested probe
写入两条临时 probe:
- `user_key = health_probe_a`
- `user_key = health_probe_b`
然后分别按 user_key 查询,确认不会串 scope。最后删除 probe。
### Acceptance criteria
- `!memory health` 输出 `OK`、`WARN` 或 `ERROR`。
- filter probe 失败时,明确提示该后端不适合 LongTermMemory。
- health check 不会留下 probe 记忆。
- README 安装步骤中增加 health check 建议。
### Non-goals
- 不要求 LangBot core 暴露 vector backend capability API。
- 不强制阻止用户使用不兼容后端,但要给出明确风险提示。
## Issue 3: Improve episodic recall with hybrid search and exact-match fallback
Priority: P1
### Background
当前 L2 recall 主要依赖 vector similarity,再叠加 recency、importance、speaker、update signal。中文群聊、人名、ID、短标签、episode_id 等场景下,纯向量召回可能不稳定。
LangBot 的 vector API 已支持 `search_type`、`query_text`、`vector_weight`。Chroma 和 SeekDB 已有 hybrid / full-text 能力,插件可以优先使用,不需要改 core。
### Goal
提升 recall 稳定性,特别是:
- 中文短词。
- 人名和群成员名。
- sender_id / sender_name。
- tags。
- episode_id。
- 精确事件名称。
### Scope
- `MemoryStore.search_episodes`
- `LongTermMemoryEngine.retrieve`
- `recall_memory` tool 参数和文档
- `memory_engine.yaml` retrieval / creation schema
### Proposed config
```yaml
retrieval_strategy: vector | hybrid | auto
vector_weight: 0.7
exact_match_boost: true
```
Default should be `auto`:
- use hybrid when backend supports it or when it succeeds;
- fallback to vector when hybrid fails.
### Acceptance criteria
- Automatic L2 recall and `recall_memory` both support the configured strategy.
- Hybrid mode passes `query_text` to `vector_search`.
- Exact match on episode_id can find the episode without relying on embedding.
- Exact match on sender_name / tags can boost or include results.
- If hybrid search fails, recall falls back to vector and logs warning.
### Non-goals
- 不实现新的 CJK tokenizer。
- 不引入外部 search service。
## Issue 4: Add episode lifecycle status
Priority: P1
### Background
当前只有 delete 和 supersede。旧记忆不会自动删除,superseded episode 仍可能被召回,只是权重降低。长期运行后,需要更清晰的生命周期。
### Goal
为 L2 episode 增加生命周期状态,避免默认召回过期或被整合的记忆,同时保留可追溯性。
### Proposed statuses
```text
active
superseded
archived
deleted
```
Recommended semantics:
- `active`: 默认可召回。
- `superseded`: 默认不召回,或强降权;管理命令可查。
- `archived`: 普通 recall 不返回;管理命令可查。
- `deleted`: 物理删除或 tombstone,取决于审计策略。
### Scope
- episode metadata 增加 `status`
- auto-supersede 时设置旧 episode `status=superseded`
- recall 默认过滤 `status=active`
- 管理命令支持 `--include-superseded` 和 `--include-archived`
### Acceptance criteria
- 新写入 episode 默认 `status=active`。
- superseded episode 默认不会进入自动 recall。
- `!memory list --include-superseded` 可以查看旧记忆。
- `!memory search` 默认只查 active,可选包含 archived/superseded。
### Non-goals
- 不默认自动物理删除。
- 不要求历史数据迁移;缺失 status 的旧数据可视为 active。
## Issue 5: Add L2 export/import and scoped deletion commands
Priority: P1
### Background
当前 `!memory export` 只导出 L1 profiles。L2 可通过 KB UI 导入 JSON,但没有完整的命令式 export/import 和批量治理能力。
### Goal
让管理员可以迁移、备份、清理当前 scope 的 L2 episodes。
### Proposed commands
```text
!memory export l2
!memory import l2 <json>
!memory delete --speaker <sender_id>
!memory delete --tag <tag>
!memory delete --before <timestamp>
!memory delete --status archived
```
### Export fields
```json
{
"id": "episode_id",
"content": "...",
"tags": ["..."],
"importance": 2,
"timestamp": "2026-05-15T00:00:00Z",
"user_key": "...",
"sender_id": "...",
"sender_name": "...",
"source": "agent",
"status": "active",
"superseded_by": ""
}
```
### Acceptance criteria
- Export 只导出当前 `user_key` scope。
- Import 默认写入当前 scope,不允许导入覆盖其他 scope。
- 批量删除必须 scope-safe。
- 删除前返回 preview 或至少返回将删除数量。
- 批量删除写 audit log。
### Non-goals
- 不实现跨 scope 全量备份。
- 不开放任意 raw metadata filter 给模型。
## Issue 6: Add memory audit log
Priority: P1
### Background
长期记忆涉及误写、误删、隐私和治理。当前 delete / update 没有独立 audit 记录,排查困难。
### Goal
记录所有会改变 memory state 的操作。
### Operations to audit
- `remember`
- `update_profile`
- `forget`
- L2 import
- L2 export
- bulk delete
- consolidation apply
- archive / restore
### Storage
Audit log 存 plugin storage,不进入 L2 vector DB。
### Proposed command
```text
!memory audit [page]
!memory audit export
```
### Audit fields
```json
{
"audit_id": "...",
"operation": "remember",
"scope_key": "...",
"user_key": "...",
"sender_id": "...",
"sender_name": "...",
"target_type": "episode",
"target_id": "...",
"summary": "...",
"timestamp": "2026-05-15T00:00:00Z",
"query_id": 123
}
```
### Acceptance criteria
- 单条删除和批量删除都有 audit。
- profile 更新记录 field / action / scope。
- audit list 支持分页。
- audit export 不泄漏其他 scope。
### Non-goals
- 不做不可篡改日志。
- 不要求外部 SIEM 集成。
## Issue 7: Expose superseded and archived memories
Priority: P1
### Background
supersede 机制已经存在,但管理员看不到旧记忆为什么不再优先召回,也不容易验证 correction 是否生效。
### Goal
增强可解释性和可调试性。
### Proposed commands
```text
!memory show <episode_id>
!memory superseded [page]
!memory archived [page]
!memory restore <episode_id>
!memory archive <episode_id>
```
### Acceptance criteria
- `!memory show` 显示 status、superseded_by、timestamp、tags、importance、sender。
- `!memory superseded` 能列出被替代的 episode。
- `!memory restore` 可以把 archived episode 恢复为 active。
- 所有 status 修改写 audit log。
### Non-goals
- 不实现复杂合并 UI。
## Issue 8: Add consolidation preview and apply commands
Priority: P1
### Background
当前只有 time decay,没有主动整合。长期运行后,L2 会积累重复、过期、被纠正的 episode。OpenClaw 的 dreaming 和 Hermes 的 post-turn sync 都说明长期记忆需要整理,但自动后台直接改写记忆风险较高。
### Goal
增加安全的、管理员可控的 memory consolidation 流程。
### Proposed commands
```text
!memory consolidate preview
!memory consolidate run
```
### Recommended first version
第一版只做 preview-first,不默认后台自动执行。
Candidate selection:
- older than configured age, e.g. 7 days;
- same tag / high similarity duplicate;
- already superseded;
- low importance and rarely recalled;
- many episodes from the same speaker/topic.
Preview output:
- candidate episode IDs;
- suggested summary episode;
- suggested L1 profile updates;
- suggested archive list;
- risk notes.
Apply output:
- write summary episode;
- update L1 profile if configured;
- archive selected old episodes;
- write audit log.
### Proposed config
```yaml
consolidation_enabled: false
consolidation_min_age_days: 7
consolidation_max_candidates: 20
consolidation_apply_profile_updates: false
```
### Acceptance criteria
- Preview does not modify memory.
- Run modifies only current scope.
- Run writes audit log for every profile update / episode archive / summary write.
- Archived episodes are excluded from default recall.
### Non-goals
- 不做后台 cron。
- 不默认自动整合。
- 不新增 L3 procedural memory。
## Issue 9: Add optional post-turn memory candidate extraction
Priority: P1 / P2
### Background
当前 memory 写入主要依赖模型主动调用 `remember` / `update_profile`。这可控,但容易漏记。直接自动写入又可能污染 memory。
### Goal
增加一个默认关闭的候选记忆提取流程,让管理员观察模型会建议记什么,再决定是否自动化。
### Proposed behavior
Post-turn extraction 生成 candidates:
- candidate L1 profile update
- candidate L2 episode
- ignore reason
默认只记录候选,不写入正式 memory。
### Proposed commands
```text
!memory candidates [page]
!memory candidate accept <candidate_id>
!memory candidate reject <candidate_id>
```
### Proposed config
```yaml
candidate_extraction_enabled: false
candidate_auto_apply: false
candidate_max_per_turn: 3
```
### Acceptance criteria
- 默认关闭。
- 开启后不自动污染 L1/L2。
- accept 后走正常 `update_profile` / `remember` 路径,并写 audit log。
- reject 后候选可查。
### Non-goals
- 不要求修改 LangBot core 增加 TurnCompleted event。
- 第一版可以基于现有 `NormalMessageResponded` 事件或命令触发。
## Issue 10: Add richer memory management commands
Priority: P2
### Background
OpenClaw / Hermes 的文件式 memory 透明性较强。LongTermMemory 不应改成文件主存储,但需要提供足够透明的管理入口。
### Goal
用命令增强替代直接编辑 Markdown 文件的透明性。
### Proposed commands
```text
!memory show <episode_id>
!memory edit <episode_id>
!memory profile edit session
!memory profile edit speaker <sender_id>
!memory stats
!memory export all-current-scope
```
### Acceptance criteria
- 管理员能查看和编辑 L1/L2。
- 所有 edit/delete/archive 操作写 audit。
- 不允许跨 scope 操作,除非未来单独设计全局管理员权限。
### Non-goals
- 不做 Web UI 第一版。
## Issue 11: Design optional global speaker profile scope
Priority: P2 design only
### Background
当前 L1 speaker profile 绑定在 session_key 下。这适合群聊隔离,但不支持“同一个用户跨多个群共享画像”。
### Goal
做设计预研,不急着实现。
### Questions
- 是否需要 `speaker_global` scope?
- 如何识别同一个用户在不同平台/群的身份?
- 默认是否关闭?
- 用户如何授权跨群共享?
- 与 `bot` L2 isolation 如何组合?
- 冲突时 session speaker profile 和 global speaker profile 谁优先?
### Acceptance criteria
- 输出设计文档。
- 明确默认关闭。
- 明确隐私边界和迁移策略。
### Non-goals
- 不在第一阶段实现。
## Suggested implementation order
1. Issue 1: write policy.
2. Issue 2: health check.
3. Issue 4: lifecycle status.
4. Issue 6: audit log.
5. Issue 5: L2 export/import/delete.
6. Issue 7: superseded / archived visibility.
7. Issue 3: hybrid recall and exact-match fallback.
8. Issue 8: consolidation preview/apply.
9. Issue 9: post-turn candidates.
10. Issue 10 / 11: management UI and global profile design.
## What not to do yet
- Do not move memory implementation into LangBot core.
- Do not require LangBot SDK changes as a prerequisite.
- Do not add active goals into this plugin in the first phase.
- Do not add procedural memory as a first-class layer yet.
- Do not automatically delete old memories by default.
- Do not run background consolidation that modifies memory without preview or audit.