fix(memory): initialize candidates and enforce lifecycle rollout
Memind CI / Test, build, and release guards (push) Successful in 3m29s

This commit is contained in:
john
2026-07-21 22:55:19 +08:00
parent 0dd9331e5b
commit bfb1f6fea9
8 changed files with 207 additions and 14 deletions
+10 -1
View File
@@ -79,7 +79,16 @@ The additive user-scoped management API is:
Lifecycle workers are disabled by default. When explicitly enabled they run
expiration, conservative compaction observation, candidate promotion, and
reflection observation according to the rollout mode; none of these operations
blocks the chat path.
blocks the chat path. `off` creates no worker scope and performs no lifecycle
mutation, `canary` is always user-scoped to the configured rollout IDs, and only
`active` permits an unscoped global worker run.
When candidate persistence is enabled, the Portal runtime idempotently creates
`h5_memory_v2_candidates` before enabling the MySQL candidate store. DDL failure
is fail-open for Portal chat and falls back to bounded in-memory candidates;
`memind_adm` uses the same schema helper during bootstrap and fails startup rather
than serving a permanently broken candidate API. The table is additive and must
not be dropped as part of an application rollback.
The pgvector adapter does not create tables or generate embeddings. It only defines the adapter contract for a future semantic memory backend and requires explicit `enabled: true`, an injected PostgreSQL pool, and either an input embedding or an injected `embedQuery(...)` function.
+1
View File
@@ -10,6 +10,7 @@
| [mindspace-remote-page-sync-and-thumbnail.md](./mindspace-remote-page-sync-and-thumbnail.md) | ① remote 模式 public HTML 入库 sync ② storage 缺失时缩略图/读页回退 workspace HTML |
| [page-data-delivery-contract.md](./page-data-delivery-contract.md) | 数据集注册、绑定、真实 page UUID 与交付验收 |
| [h5-session-stream-replay.md](./h5-session-stream-replay.md) | Portal/Goose SSE 游标映射、断线续播与 Finish 终态恢复 |
| [memory-v2-candidate-and-lifecycle.md](./memory-v2-candidate-and-lifecycle.md) | 候选记忆表幂等初始化、Portal fail-open、生命周期 off/canary/active 作用域 |
## 自动化
@@ -0,0 +1,45 @@
# Memory V2 候选表与生命周期灰度守卫
## 已知故障
生产曾出现 `h5_memory_v2_candidates` 不存在:候选记忆写入和后台查询报错,
但 Agent 召回仍在 shadow 模式执行,因此表现为“有召回事件、回答未注入、个人记忆状态降级”。
同时,生命周期 worker 只检查了 worker 开关;`expire()` 没有检查 rollout
作用域。若忘却开关开启,即使 `MEMORY_LIFECYCLE_ROLLOUT_MODE=off`,定时器也可能
归档全量用户的过期记忆。
## 必须保留的行为
1. `MEMORY_CANDIDATE_PERSISTENCE_ENABLED=1` 且 MySQL 可用时,Portal 必须先执行
`ensurePersonalMemoryCandidateSchema()`,再创建候选记忆 store。
2. 候选表 DDL 失败不能阻塞 Portal 聊天启动;Portal 应记录告警并退回 bounded-memory。
3. memind_adm 必须在创建候选 store 前完成同一幂等建表;初始化失败时后台启动失败,
不允许以“页面可用但候选接口持续报错”的半初始化状态运行。
4. 生命周期 rollout 语义必须严格一致:
- `off`:不得执行任何写操作,也不得启动 worker 定时器。
- `canary`:只允许 `MEMORY_LIFECYCLE_ROLLOUT_USER_IDS` 中的用户。
- `active`:才允许无 user scope 的全局任务。
5. `forgetMemory()``expire()` 都必须同时满足功能开关与 rollout 作用域。
## 回归检查
```bash
node --test memory-v2-personal-store.test.mjs memory-v2-lifecycle.test.mjs memory-v2-runtime.test.mjs
npm test
```
memind_adm 同时执行:
```bash
node --test server/personal-memory-candidate-store.test.mjs
npm test
```
灰度上线后还必须验证:候选表存在、候选写入成功、后台可查询,以及一个测试用户完成
“保存 → 候选 → 晋升 → 新会话召回 → 回答注入”的闭环。未通过前不得切换全量 active。
## 回滚
代码可以按标准 runtime/release 回滚;候选表是加法结构,回滚时保留空表或已有数据,
不得为了代码回滚直接删除表。先把候选、生命周期和 Agent 注入模式切回 shadow/off。