Skip to content

[蒸馏/dream] 5 处 prompt 未围栏:巩固把 46K 清单读成「没人给我下指令」,约 43 万 token/天落库 0 条(#493 的另一半) #501

Closed

Description

@modusensus

是什么

#493 修的是「模型把会话转录当成自己的发言」,围栏只装在了 autoSummarize 那一处。同一个形状在 dream / sleep 侧还有 5 处,0.9.4 一处未覆盖:user 段把清单裸放,system 之后没有任何分隔与「你不是这份清单的对话者」的约束。

社区报告(#493 追加,2026-10-21 18:34)的现场:48 小时审计里 dream_consolidate 72 成功 / 9 失败,失败的 8 次输入 47K→55K tokens、输出只有 212–907 tokens;日志原文是模型把 46K 的记忆清单读成「有人丢给我一包检索结果但没下指令」,于是开始寒暄。约 43 万 token/天,落库 0 条。他还给了对照实验:只加 system 约束的臂从 2/2 掉到 0/1,围栏 + 角色重申的臂仍 1/1。

五处位置(对照 main 已核)

src/dream.js 叙述条 / 巩固 / 摘要三条 user 消息;src/dream/sleep.js 冲突裁决与模式提炼两条。grep transcriptFence src/ 当时只命中 summarize.js。

修法

  1. lang.js 新增 memoryFenceOpen/Close 与 dreamRoleReminder(不复用「会话转录」那对名字——喂的是记忆清单,标错名字会误导读审计的人)。角色声明落在 user 段开头:[0.9.3] 解析失败后确定性无限重试同一窗口:转录裸放 user 消息会被模型当成「该我回话了」 #493 实测判断由 user 消息决定。
  2. src/user-settings.js 之外新增 fenceListBody(language, text)(住 dream.js,sleep.js 复用),纯函数:重试收到的 prompt 与首次逐字节相同([0.9.3] 解析失败后确定性无限重试同一窗口:转录裸放 user 消息会被模型当成「该我回话了」 #493 的「同输入同错」诊断依据靠这条);正文里的 <<</>>> 用零宽空格断链——否则一条写着闭合标记的记忆就能提前闭合围栏。
  3. 失败行的审计 metadata 不再是 null:runAuditedLlm / auditedSleepLlm 增加 spec.metadataFrom(result),dream 侧写 parse_failure / raw_len / raw_head / raw_has_bracket。注:dream 的 warn 日志本来就带这些,缺的是可查询的那一面。

顺带修的一处测试脆弱

多个 mock 靠「user 段以 id= / 候选冲突 开头」认阶段。围栏让首行变成角色声明;而败者行的溯源注释里也会出现「候选冲突」,所以改成认清单形状(| type=)与 options.purpose。这不是迁就测试:dream-candidate-hybrid.test.js 早就注释说过前缀判据在语义预分组下会误判,只能绕开共用 mock——现在那个绕法不再需要。

不在本条范围

type 枚举把窗口滤空那一形态另立 issue(#493 19:12 追加):JSON 合法、条目被 VALID 滤空、却报成 invalid summary JSON 并反复重放。两者是不同的失效路径,不该混在一条里发版。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug功能异常、崩溃、数据错误(agent 诊断 → 人工确认)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions