记忆修复-Plan-Execute-Review 闭环
改了什么
- 一次召回 top3,不再只取 top1
server/app/Core/Memory.php:新增 recall()(纯召回,最多捞 50 条做候选池),search() 变成它的薄封装。打分加了「覆盖率 + 整句命中 + 历史命中」三项,比原来的「命中词数 × 权重」更稳。
默认注入条数从 5 降到 3(config.php 的 agent.memory_top_k)。
2. 检索后加 LLM 校验节点
新文件 server/app/Core/MemoryRetriever.php:Recall → Verify → Rewrite → Retry 管线,校验提示词让模型输出 {“relevant”:[编号],”enough”:bool,”reason”:””,”rewrite”:””}。
校验是 fail-open:调用失败 / 解析不了 / 超时(默认 20s)一律退回「按分数取 top3」,绝不会因为质检把记忆搞丢。
3. 不通过自动改写关键词重试,最多 2 次
优先用模型给的 rewrite;模型没给就剥口语词(帮我/查一下/是多少…)兜底。剥完只剩一两个字(会 LIKE 命中全库)时直接放弃改写,保持原候选。
4. 结构化存储 + 密码类分类过滤
memories 表加 category(credential/profile/preference/event/general)、title、data(键值对 JSON)。
自动迁移:首次读写时查 information_schema 缺列就 ALTER,并一次性把老数据里含”密码/账号/密钥/卡号/身份证/验证码”的回填成 credential(不回填的话升级后反而查不到老密码)。
问「密码/账号/密钥」时只在 credential 类里挑候选,普通偏好类记忆不会挤进来;这一类确实为空才退回全量,不硬过滤成空。
save_memory 工具新增 category / title / fields 参数,自动沉淀(distillMemory)也走结构化。
校验阶段还有一层确定性兜底:问密码类,但选中的记忆里一条真正的凭据都没有 → 判定不通过、触发重试,专治模型敷衍点头。
5. 后台新增独立菜单「召回过程」
侧栏「长期记忆」下面,?page=memory_trace。
上半部分是记忆库分类分布 + 当前召回策略(候选条数 / 校验开关 / 重试上限 / PER 开关)。
下半部分按运行分组,逐步还原:第 N 轮召回(检索词、分类过滤、候选池、每条候选的分数)→ 第 N 轮校验(通过/未通过、命中编号、理由)→ 关键词改写(旧→新)→ 最终注入(已校验/兜底),并可跳转完整运行轨迹。
「长期记忆」页也加了分类列、结构化字段展示、按分类筛选和分类选择框。
「运行记录 → 轨迹」页新增 plan / review / memory_* 步骤的配色与中文标签。
6. Plan-Execute-Review 闭环(按你说的留到后续,已预埋)
config.php 的 agent.plan_execute = false,默认关。打开后:先拆计划拼进 system prompt,答复后再自检一次,不通过带着意见重答一轮。流式对话只做 Plan 不做 Review(否则同一句话会在客户端推两遍)。

