Hindsight:LLM 终于能长记性了?
2026.06.14 10:00Hindsight:LLM 终于能长记性了?
论文:Hindsight is 20/20
代码:https://github.com/vectorize-io/hindsight
金鱼的记忆
现在的 LLM 就像金鱼——聊完就忘。你上周说”我喜欢川菜,不吃香菜”,今天它可能给你推荐一家香菜管够的泰国菜。
RAG 试图解决这个问题:把对话历史存进向量库,查询时检索相关片段塞进 prompt。但这只是给金鱼配了个便签本。它仍然分不清”你说过什么”和”它猜你喜欢什么”,跨会话的信息堆在一起像垃圾场,你问”为什么推荐这个”它只会说”因为检索到了 top-k”。
Hindsight 的思路是:别再把记忆当便签本,把它当成 Agent 的一部分。
记忆分类:我一开始搞混了
Hindsight 把记忆分成四个网络。我一开始以为这很简单,直到被问了一个问题:
你说”我觉得 Rust 比 Go 痛苦”,又说”Rust 的内存安全让我少了很多 bug”——这两句话分别该进哪个网络?
我脱口而出:第一句进 Opinion,第二句进 World。错了。
“我觉得 Rust 痛苦”是你说过的客观事实(你真的这么说了),所以进 Experience。如果 Agent 后来形成观点”Rust 学习曲线陡峭”,那才是 Opinion。
“内存安全减少 bug”也是你说过的客观事实,同样进 Experience。但如果 Agent 把它当作普遍规律”Rust 比 Go 更安全”,那才是 World 或 Opinion。
这个区分很微妙:“用户说过 X”是事实,“X 是正确的”是推断。我花了好一会儿才内化这个区别。
四个网络的具体分工:
- World:客观世界事实,与 Agent 无关。“Alice 在 Google 的 AI 团队工作”
- Experience:Agent 自己的经历,第一人称。“上周我向 Alice 推荐了 Yosemite”
- Opinion:Agent 自己形成的判断,带置信度。“Python 更适合数据科学(0.85)”
- Observation:对实体的中性摘要,综合 World 和 Experience 得出。“Alice 是 Google 的 ML 工程师”
Observation 的异步生成:代码实现中,Observation 不是实时生成的。当你 retain 一条新事实时,系统立即返回(低延迟),然后在后台异步重新生成相关实体的 Observation。这类似于数据库的”物化视图”——读时快,写时延迟更新。当新事实提到某个实体时,后台任务会被触发,基于更新后的事实集合重新生成摘要。
论文花了很大篇幅讲这个区分,我觉得有点啰嗦,但确实重要。混淆证据和推断是现在很多 Agent 胡说的根源。
Retain:不只是存,还要理解
Hindsight 存记忆不是简单 dump 原始对话,而是做几件事:
- 把多轮对话提炼成自包含的叙事片段
- 把”上周三”这种相对时间转成绝对时间戳
- 把”Emily”、“我室友”、“那个女生”链接到同一个实体
- 建立时间、语义、实体、因果四种关联
举个例子,你和 Agent 聊了几轮关于命名一个播放列表,最后选了 “Beach Beats”。传统 RAG 可能存成几条碎片,Hindsight 会存成一个完整的叙事单元:“Alice 和 Bob 讨论夏日派对播放列表,Bob 提议 Summer Vibes 和 Sunset Sessions,Alice 喜欢 Beach Beats 的 playful tone,最终选了 Beach Beats”。
这种自包含的叙事在后续检索时更有用,因为上下文都在一个单元里,不会因为切片而丢失。
实体解析的加权公式:把”Emily”、“我室友”、“那个女生”链接到同一个实体,不是简单的字符串匹配。Hindsight 用三个信号的加权组合:字符串相似度(Levenshtein 距离)、共现相似度(是否出现在同一句话)、时间邻近度(同一对话中提到的更可能是同一人)。公式是 score = α·sim_str + β·sim_co + γ·sim_temp,比纯字符串匹配更鲁棒。
Recall:四路检索,比一路好
检索不是简单的向量相似度搜索。Hindsight 同时跑四种策略:
- 语义向量:找概念相关的
- BM25 关键词:找精确匹配的,比如 API 名、人名
- 图传播:从种子节点扩散,发现间接关联(“Alice 的朋友”→“Bob”)
- 时序过滤:回答”上周发生了什么”
然后用 RRF(Reciprocal Rank Fusion) 融合,再用交叉编码器重排序,最后按 token 预算截断。
我一开始觉得四路是不是太多了,会慢。但论文说这些可以并行跑,实际延迟可控。而且不同策略互补:语义强在概念关联,弱在精确名词;BM25 正好反过来。合在一起比单一策略召回率高很多。
RRF 的巧妙之处:它不看原始分数,只看排名位置。公式是 score = Σ 1/(60 + rank_i)——在某个策略里排第 1 得 1/61 分,排第 10 得 1/70 分,排第 100 得 1/160 分。被多个策略都排得高的记忆自然上浮。这解决了不同检索策略分数尺度不统一的问题(余弦相似度 0-1,BM25 可能上千),直接融合会失真,但排名是无量纲的。
交叉编码器的作用:语义检索用的是”双编码器”——查询和文档分别编码成向量,然后比余弦相似度。快,但查询和文档没有交互。交叉编码器把查询和文档拼接在一起输入模型,让 token 之间直接交互。慢(不能预计算),但精度高。Hindsight 先用双编码器快速召回 top-100,再用交叉编码器精排 top-20,是工业界标准的”粗排+精排”架构。
Token 预算过滤:Hindsight 不返回固定 top-k,而是按 token 预算动态截断。比如预算 2000 token,可能返回 5 条长记忆或 15 条短记忆。这确保不浪费上下文窗口——Agent Memory 与普通搜索的关键差异:搜索只关心相关性,Agent Memory 还要关心”能不能塞进 prompt”。
Reflect:Agent 也有性格
这是 CARA 组件做的事。Agent 不是机械拼接检索结果,而是加载一个”行为画像”来生成回答:
- Skepticism(怀疑度):1=容易相信,5=杠精
- Literalism(字面度):1=灵活解读,5=死抠字眼
- Empathy(共情度):1=冷漠,5=暖男
- Bias strength:性格影响程度,0=完全客观,1=性格决定一切
论文举了个例子。同样的远程工作证据:
- 低怀疑+灵活+高共情的 Agent:“远程工作释放了创造力和自发创新”
- 高怀疑+严格+低共情的 Agent:“远程工作缺乏维持稳定绩效所需的结构和问责制”
同样的输入,不同的性格,不同的输出。这个设计挺有意思,但我有个疑问:实际产品中用户真的需要调节这么多参数吗?还是直接提供几个预设性格(“谨慎顾问”、“创意伙伴”)更实用?
观点会演化:但我有个疑问
论文里我最喜欢的部分是 Opinion 的置信度更新。Agent 的观点不是静态标签,而是轨迹:
初始:("Python 是数据科学最佳语言", 0.70)
↓ 新证据:Python 在 AI/ML 生态占主导
更新:("Python 是数据科学最佳语言", 0.85)
↓ 新证据:Julia 和 Rust 在某些领域性能更好
更新:("Python 在数据科学很强但有取舍", 0.55)更新规则:支持 +α,削弱 -α,矛盾 -2α。
但这里我卡住了。当被问到”为什么用置信度而不是简单的对错标签”时,我只想到”不同场景准确性不同”。后来才理解到更深层的点:
置信度让 Agent 可以表达不确定性。 “我觉得可能…(0.6)“和”我确信…(0.95)“是完全不同的语气。低置信度时 Agent 可以直接说”我不确定”,而不是硬给答案。
另一个我没想到的点:用户会不会觉得 Agent 反复无常? 今天说 Python 最好,明天说 Julia 更好,用户可能觉得”这 Agent 不靠谱”。
论文的解法是给每个 Opinion 加时间戳和证据来源,让用户看到”为什么变了”。但我觉得实际落地还需要更多设计——比如只在置信度变化超过阈值时才通知用户,否则太吵。这个细节论文没讲,是我自己的思考。
实验:架构比模型大小重要
论文在 LongMemEval 上测试(500 问,约 50 轮对话):
| 系统 | 准确率 |
|---|---|
| Full-context OSS-20B(无记忆架构) | 39.0% |
| Hindsight + OSS-20B | 83.6% |
| Hindsight + OSS-120B | 89.0% |
| Hindsight + Gemini-3 | 91.4% |
同样的 20B 小模型,加上 Hindsight 后从 39% 跳到 83.6%,甚至超过了 Full-context GPT-4o(60.2%)。
这个对比很能说明问题。给模型更好的记忆架构,比单纯堆参数更有效。就像给学生一个整理好的笔记本和检索系统,比让他死记硬背所有内容更实用。
代码与论文的差异:为什么 Opinion 被砍了
我翻了 Hindsight 的 GitHub 代码,发现论文发表后架构有改动。最意外的是 Opinion 类型被移除了——代码里只剩 world、experience、observation 三种 fact_type。
这让我困惑。论文花了那么多篇幅讲 Opinion Network 和置信度更新,怎么代码里直接砍了?
看了迁移记录才理解:
“Remove the deprecated ‘opinion’ fact type… drop the confidence_score column (was only used for opinions, always NULL otherwise)”
confidence_score 列只为 opinion 使用,其他类型永远是 NULL。 这意味着 opinion 在数据库层面是”特殊公民”,增加了维护复杂度。而且每次 retain 新事实都要检查是否影响现有 Opinion,参数 α、阈值怎么调都是问题。
代码的解决方案是:不存观点,reflect 时动态生成。 这样观点总是基于最新信息,省去了持续更新的麻烦。
这个改动让我想到一个工程原则:能计算的不存储,能动态生成的不持久化。 论文里的四个网络在代码中简化为三个,TEMPR 和 CARA 也不再是独立组件,而是融入 MemoryEngine 的方法。说明作者在实际落地时做了务实的取舍。
但我也有保留:动态生成观点虽然简单,但失去了”观点演化轨迹”这个论文的核心卖点。用户看不到”我之前认为 Python 最好,现在降到 0.55 了”,只能看到当前的回答。这个 trade-off 值不值,取决于应用场景。
对我自己的启发
我的个人知识管理系统(鸢-信息流)目前流程是:收集链接 → 生成 Clip → 归档到 Obsidian → 每日晨报。
Hindsight 给我几个直接可用的想法:
第一,给 Clip 加类型标签。现在所有 Clip 是统一格式,其实可以区分:
#type:fact— 客观事实#type:experience— 我的个人行动/经历#type:opinion— 我的主观判断#type:summary— 对某人/某公司的中性摘要
这样以后查询”我对 AI 的观点”时,不会混在一堆事实里。
第二,观点动态生成。与其记录”我认为 Python 最好”,不如在需要时基于所有相关 Clip 实时生成观点。这样观点总是最新的,也避免了旧观点过时的尴尬。
第三,检索多策略。现在主要靠关键词搜索,可以加入语义相似度和时间过滤。比如”最近三个月关于 OpenAI 的所有内容”。
最后:学习过程比结论更重要
这篇博客不只是论文笔记,也是我的学习轨迹。
我一开始以为 Hindsight 只是又一个向量数据库包装。被问到 RAG 和 full-context 的区别时,我用”图书馆”类比解释,觉得自己懂了。但继续深入才发现——懂和能应用是两回事。
比如四个网络的分类,我最初觉得简单,直到被问到”Rust 那句话进哪个网络”时直接答错。这才意识到”用户说过 X”和”X 是正确的”是完全不同的东西。这个错误让我印象很深,比直接被告知正确答案深刻得多——被提问逼着你思考,而不是被动接收。
还有 Opinion 的置信度。我最初只想到”不同场景准确性不同”,被引导后才理解到更深层的点:
最意外的收获是代码与论文的差异。论文里的 Opinion Network 在代码中被砍了,因为 confidence_score 列只为 opinion 使用,其他类型永远是 NULL,成了数据库里的”特殊公民”。这个改动让我理解到论文是理想,代码是现实,好的工程需要在优雅和可维护之间找平衡。
类比很重要。把 Agent Memory 比作人类记忆——World 是常识,Experience 是经历,Opinion 是判断,Observation 是印象——这个类比帮我快速建立了直觉。
Why 比 What 更重要。知道 Hindsight 有 4 个网络没用,知道”为什么区分事实和推断”才有用。因为混淆二者会导致 Agent 把猜测当作真理,这是现在很多 Agent 胡说的根源。
架构比规模重要。20B 模型 + 好架构 > GPT-4o + 无架构。这提醒我们:在工程设计中,结构化的组织方式往往比 raw power 更有效。但论文到代码的演进也说明——好的架构不是把所有功能都做进去,而是知道什么该做、什么该砍。
参考
- Hindsight is 20/20 — Latimer et al., 2025
- Hindsight GitHub
- LongMemEval — Wu et al., 2024
- LoCoMo — Maharana et al., 2024
痕迹
没有过去,就没法认定现在的自己
