Hindsight 深度拆解:Agent 怎样从经历中形成有证据的认识?

沿着 retain、recall、reflect,区分事实、经历、归纳与心智模型,理解证据追踪和检索预算背后的工程取舍。

本文目录

Memory Agent 深度拆解 · 第 5 / 8 篇。查看系列目录

“用户昨天拒绝了这个方案”,是一件发生过的事。

“用户通常更在意可维护性”,是从多次互动中得到的认识。

“这次应该怎么推荐”,则需要把过去的认识放回当前情境,重新判断。

很多记忆系统把这三种内容混在一堆文本里。Hindsight 的设计值得看,是因为它给事实、归纳和临时推理安排了不同的位置。

本文依据 2026 年 9 月 22 日的提交 680406b3dd9c 与官方文档,未复现实验。项目的基准成绩不作为本文技术判断的直接结论。

1. 三个动词,划出三条不同工作链路

Hindsight 的核心接口是 retainrecallreflect。它们分别负责形成记忆、查找记忆,以及结合记忆进行推理。官方项目说明

这三个动作不应该混着测。

假设业务只需要取回用户过去的偏好,recall 返回证据后,由应用自己的模型回答就够了。如果还需要系统综合多次经历、按特定指令组织建议,才涉及 reflect 的额外循环。

把反思接口当普通数据库查询,会低估其延迟和成本;只用检索成功来证明系统能从经验中学习,又会高估效果。

2. retain:先保存可以被解释的事实

写入阶段会把输入整理成可检索记忆,区分外部世界的事实与 Agent 自己的经历。比如用户说“我改了数据库配置”,这是关于用户的事实;Agent 的执行记录说“我修改了配置并得到成功回执”,才是该 Agent 的经历。官方 Retain 说明

区分依据不是句子里有没有“我”,而是说话者是谁。因此输入的角色、上下文和工具结果不能在拼接时丢掉。

源码把这条写入链进一步分为事实提取、实体处理、embedding、事实存储和关联等模块,由 orchestrator 组织。源码:Retain 编排事实提取

这给调试提供了拆分点:某件事没找回来,先检查提取结果,再查索引和搜索。不能看到原始文档已保存,就假设一定产生了对应事实。

3. observations:从若干事实中整理认识

写入之后,后台 consolidation 会处理 observations。它可以基于新证据创建或更新归纳内容,并记录支撑来源。

在代码里,source_memory_ids 维护来源记忆集合,proof_count 表达支持记忆的数量;更新路径还会检查来源是否仍然有效存在。源码:Consolidator

这与写一段“用户画像总结”有个重要区别:归纳结果有机会回到具体证据,而不是只留下一句越来越抽象的标签。

但支持条数并不等于结论概率。十次重复导入同一段材料,不该被理解为十个独立证据;用户连续三次选择便宜方案,也不能自动推出他在所有情境都只看价格。

所以我会把 observation 理解为可更新的认识,而不是永久事实。它应该允许随着条件变化而修正,保留“在哪些情境下观察到”的边界。

4. recall:四种线索怎样汇到一起?

Hindsight 的检索考虑语义、关键词、图关联和时间线索。源码中的融合模块使用 RRF,把不同结果列表中的名次转换为可合并的分数。官方 Recall 机制源码:融合模块

RRF 的形式可以写为:

一个候选的融合分数 = 各检索列表中 1 / (k + 名次) 的和

它利用排名,而非直接把不同尺度的原始分数相加。一个候选如果在多条路径中都靠前,就容易得到较高的综合位置。

这对普通检索很有意义,却不一定适合所有内部任务。代码还提供 interleave 形式的融合,用轮流取各路靠前结果的方式,防止某一路非常重要的命中被其他信号压到预算之外。

源码注释解释了一个归纳去重场景:几乎相同的旧 observation 可能只在语义路排名很高,缺少其他路支持。若它被融合截断丢掉,模型没看到它,就可能再创建一份重复归纳。

这给出一个值得借鉴的思路:为回答问题做检索,与为更新记忆寻找重复项,优化目标可以不同。

5. 候选预算比“搜得越多”更重要

融合模块还提供每个来源的候选数量限制,避免某一路扩展太多,在总预算裁剪时挤掉其他路径。后续重排再评价查询与候选的相关性。源码:候选融合重排实现

这是一种资源分配问题:总共只能看几十条材料时,每条检索路径应该获得多少席位?

我的做法是按问题类型检查缺失证据。如果名字和错误码经常漏,优先看关键词链路;如果“去年变更前”答错,检查时间候选;如果因果关系缺一环,再看图关联与扩展。

直接提高总 token 上限,有时只是让更多无关材料挤进模型,同时提高延迟。

6. reflect:一次有预算的取证循环

反思阶段可以先找 mental models,再找 observations,必要时回到原始事实并展开上下文。mental model 可以承载针对常见问题提前整理的内容,不能直接等同于本轮临时回答。官方 Reflect 说明

源码中的反思 Agent 维护工具调用、迭代和上下文预算。尤其有一个细节:同一轮并行调用多个工具时,会按剩余上下文额度限制每次调用的请求预算,并保留最低可用下限。源码:反思 Agent

为什么不能只在下一轮开始前检查总长度?因为这轮多个工具可能同时返回大量内容,等检查时早已超过预算。

把控制前移到请求参数,可以减少这种超量。它不是“无限思考直到满意”,而是一段有迭代、证据和资源约束的过程。

7. 归纳变旧,怎样避免一直引用?

长期系统必然面对派生内容变旧的问题:原始事实变了,但总结还没重做。

反思代码中有检查 mental model 是否明确新鲜且内容非空的分支。缺失新鲜度标记,不会直接按“确定新鲜”处理。归纳模块也维护来源 ID,给证据检查和后续更新提供依据。源码:新鲜度检查

从工程角度看,这是一张依赖网:事实支撑归纳,归纳又可能进入更高层总结。删除或更正事实时,不仅要考虑原始记录,还要检查哪些派生内容受影响。

这些代码表明系统在处理此类边界,但本文没有验证所有删除传播与并发更新路径。真实上线时,仍应加入“归纳完成后撤回原始证据”的测试。

8. 什么场景值得用这套结构?

如果你的 Agent 会反复经历类似任务,并需要从反馈中形成可解释的偏好或经验,Hindsight 的分层值得研究。

如果只是实时语音中的一句事实查询,我会优先让 recall 服务当前回答,把较重的归纳放到后台;若使用 reflect,则独立测量它带来的答案收益、额外调用与等待。

一个最小测试故事可以包含三次方案选择和一次偏好改变。先检查各次事实,再检查归纳是否过度概括,最后观察新条件下的建议是否回到证据。全部过程用同一组固定问题重复运行。

Hindsight 最值得学习的,是让“过去发生过什么”“我从中形成什么认识”“这次应该怎么做”保持可区分。记忆要能积累,也要能解释自己为什么这样理解。

系列阅读

返回系列目录与全景总览

上一篇:OpenViking 深度拆解

下一篇:Cognee 深度拆解