Skip to content

[algo] lazy_forget 在 LLM 补全失败时不可逆地覆盖原文,只留一条 warn 日志 #79

Open

Description

@dynamder

🤖 本 issue 由审阅 agent 手动提交(File agent issue workflow 尚未进入 main),结论未经维护者核实,不代表维护者判断。

问题

// crates/soul-mem-algo/src/algo/forget/decay_revise.rs:117-133
Err(error) => {
    log::warn!("记忆补全失败,降级为仅遮罩(kind={}, unavailable={}): {}", ...);
    set_summary(node, &masked_text);          // ← 用遮罩文本覆写节点内容
    ForgetAction::MaskOnly { missing_degree: md, masked_count: ..., masked_text }
}

set_summary 会把 SpecificSituation.narrative 或 SemMemory.content 原地替换为遮罩文本(:140-148)。LLM 失败(超时、限流、断流——恰恰是最常见的失败)时,原始内容就此永久消失,只剩下一条日志,且没有任何重试入口:下一次 lazy_forget 面对的已经是遮罩文本。

代码注释本身也承认这一点:「节点上留下遮罩文本,原始文本已不可逆」。但同仓库的滑动窗口刚刚做过一次方向相反的重构——从「先出队再总结」改成「只有成功才移除」(sliding_window.rs:10-24),明确把「失败即静默丢历史」判为缺陷。遗忘路径保留了同类问题。

可达性说明:目前只有 soul-tune 的遗忘流水线会调用 lazy_forget,但它是 soul-mem-algo 的公开算法入口,会被后续编排(docs/architecture/orchestration.md 的 🔲 遗忘流程)继承。

最小复现

let mut node = specific_situation_note("张三昨天在上海的一家酒馆里喝醉了");

// 引擎返回可重试失败(Transport 等)
let action = lazy_forget(&mut node, now, &jieba, None, Some(&failing_engine)).await;

assert!(matches!(action, ForgetAction::MaskOnly { .. }));
assert_eq!(get_summary(&node).unwrap(), "张三昨天在 [masked] 的一家酒馆里喝醉了");
// ↑ 原文已丢失;引擎恢复后也无法重试,因为原文不再存在

建议修法

失败时保留原文,把「需要补全」变成可重试的状态,例如:

  • 不覆写内容,仅在返回值/节点上标记待补全(ForgetAction::MaskOnly 里区分「已写入」与「仅计算」);
  • 或把遮罩结果与原文一并保存在节点上,仅当 LLM 成功时才用补全结果替换原文。

如果「失败即丢弃原文」确实是有意设计(「遗忘」语义上就该不可逆),请把它写进模块注释,并让返回值显式表达「原文已不可恢复」,避免调用方以为可以重试。

位置

  • crates/soul-mem-algo/src/algo/forget/decay_revise.rs:106-148
  • crates/soul-mem-algo/src/algo/forget/llm_completion.rs:78-92
  • 对照原则:crates/soul-mem-runtime/src/working_memory/sliding_window.rs:10-24

环境

分支 dev(efaf5cb);仅静态分析——沙箱为只读,未运行 cargo。

Activity

  1. added
    agent-report由审阅 agent 自动提交,未经核实
    bugSomething isn't working
    questionFurther information is requested
    on Oct 14, 2026
  2. dynamder commented on Oct 14, 2026

    @dynamder
    OwnerAuthor

    这是正确的语义,被遮罩的文本在遮罩的那一刻已经判定为丢失,llm失败时保留遮罩没有造成额外的丢失。遗忘的目的正是不可逆的修改原文,来模拟人类的记错过程。
    滑动窗口是短期记忆,因此我们要保证短期记忆的连续性而不能静默丢失。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-report由审阅 agent 自动提交,未经核实bugSomething isn't workingquestionFurther information is requested

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions