Skip to content

会话记忆优化:实现与验证

调查基线:生产部署提交 3e385b016f368523496f409d72de2809a6495ecb;发布前已同步 mainaa0eb1959(仅项目说明更新)。 本地分支:codex/memory-reliability-optimization。 工作目录:C:/Users/peiji/Downloads/yumina-memory-optimization。 发布前验证已通过,生产数据库已添加本次需要的可空字段;代码将通过推送 main 触发 Railway 部署。原工作目录中的其他改动保留原样。

判断

保留清单记忆、剧情摘要、分层摘要的分工。本次先修复会话记忆更新的可靠性,并减少短对话中的重复重写。

截图对应会话的只读调查发现:最后三次记忆输出都接近 8192 tokens,连续失败后自动更新暂停;旧记忆仍然保留。更关键的是,旧实现开始更新时就推进处理位置,失败和队列合并可能让中间对话不再进入后续更新。

该会话当时累计 119 次记忆调用,约 131.6 mushies;剧情压缩约 36.6 mushies。两者合计约为聊天发送与重生成费用的 10.8%。累计 tokens 本身不能证明记忆过贵,三种开关开启也不等于三种后台任务都在收费。

已实现

  • 成功保存后推进位置。 从上次成功覆盖的位置开始,按时间顺序读取待处理对话,仍然排除最新一条助手回复,便于用户重新生成。失败、重启和队列合并留下的对话会在下次处理时再次读取。
  • 只保存完整且符合预算的生成结果。 正常结束但超过 7000 字符,也视为需要压缩。超长或达到输出上限时,从原始输入再生成一次,目标降至 3500 字符。生成结果不再通过截尾后保存来满足上限;手写及历史数据的既有读取限制不变。
  • 限制重试消耗。 压缩重试、旧模型输出上限兼容和重复文本模型回退共用更新器层面的两次生成尝试额度,供应商适配器内部的网络重试保持现状。沿用实际模型调用计费,不新增退款规则;连续三个失败任务后暂停自动更新。明确手动重试、换模型可重置失败计数。
  • 短对话合并更新。 已有记忆时,累计三条可处理的助手回复,或待处理文本达到约 4000 字符再更新。初次生成、失败恢复、已有记忆超预算和手动重试及时处理。每批最多读取 200 条消息,最多向记忆模型提供约 60000 字符对话,只跨完整交流推进位置;其余保留待处理。
  • 跨服务器并发保护。 数据库保存 15 分钟更新租约;认领时核对旧记忆与设置,提交时核对唯一任务所有者和源消息内容。过期任务不能覆盖新任务。历史中的消息被修改或删除时,旧结果不会保存。改写未参与记忆的最新回复不会使有效任务失效。
  • 明确恢复入口。 面板显示自动暂停、待处理轮数或旧数据覆盖范围不明,并提供“重试并补齐”。一次点击处理一批,仍有积压时继续显示余量。“重新生成”继续保留为独立的历史重建操作。

SQL 时间范围直接在数据库内比较,避免 JavaScript 时间精度丢失导致重复读取端点或遗漏最后一条。每个增量批次只读取端点的游戏状态快照,避免加载数百份大型历史状态。

发布复查还修复了设置保存与后台提交同时发生的竞争:设置接口在数据库更新语句中保留当前已提交的进度,不再把接口较早读取的旧进度写回;仍会清除旧版失败尝试的不可信进度。

旧数据兼容与边界

旧版失败记录中的处理位置可能指向未成功保存的尝试。修复时将其视为不可信,结合原有记忆、剧情摘要与最近最多 400 条历史消息重建,沿用最多 60000 字符的重建窗口。修复失败不会把旧位置认证为成功位置,也不会在每次自动重试时重置失败次数。原本已经暂停的会话继续暂停,等待用户明确重试。

这属于有限历史下的修复,不能保证找回旧版已经漏掉的全部事实。新的连续处理规则从成功建立的覆盖位置生效。对已写入记忆的更早历史进行编辑,仍沿用现有的过期提示及手动重建机制。超过单批输入预算的一整个交流会明确报错,保留旧记忆,不跳过后续内容。

本次未发起付费模型评测,也未进行浏览器验证;不能把控制流程测试解释成事实保留率或线上节费比例的实测结果。

验证

  • 会话记忆专项测试:55 项通过,包含真实 PGlite SQL 测试,覆盖失败后连续范围、时间精度、超长压缩、重试次数、并发所有权、历史改写、设置保存时保留并发提交进度、旧数据兼容和加列迁移幂等性。
  • 相关集成测试:32 项通过,包含记忆失效、固定记忆注入、会话分支、回退,以及新增的真实更新器入口测试。新增测试使用模拟供应商输出和假密钥,验证两次截断后保留旧记忆与待处理范围,手动重试补齐全部可处理交流,且不包含最新一轮;确认零网络请求。
  • 现有记忆面板检查:4 项通过。
  • pnpm buildpnpm typecheck 在最终修改后再次通过。
  • 全量前端测试:620 / 623 通过;其余三个移动导航样式断言失败。测试与其读取的 CSS、组件文件均与基线相同,本次没有修改它们。
  • 全量服务端测试未通过:隔离工作目录缺少测试环境配置,既有空数据库初始化流程也没有建立所有基础表。随后为上述相关集成测试在独立 PGlite 内存数据库中按当前 Drizzle schema 建表并显式关闭数据库,32 项测试正常结束、退出码为 0;没有连接生产数据库执行测试。全量服务端测试不能报告为全绿。

上线顺序

先在目标数据库执行新增的 packages/server/drizzle/0054_session_memory_claim.sql,再部署代码。迁移只新增一个允许为空的时间戳列,可重复执行;两条现有启动自愈路径也包含该列作为兜底。回退应用时该列可留存,无需删列。

发布前已在生产数据库执行这一条加列语句(锁等待上限 3 秒、语句执行上限 15 秒),并确认字段为 timestamp without time zone、允许为空。未运行整库 schema 同步,也未修改已有记忆。

上线后先检查少量长会话:失败恢复后的待处理轮数是否下降、成功位置是否推进、输出超限及重复生成是否减少、每轮平均后台调用数是否降低。以上线上验证尚未执行。