Retain: How Hindsight Stores Memories
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
导读
retain()是 Hindsight 记忆管线的入口:一次调用即可把对话、文档等原始内容转译为结构化的、可检索的长期记忆。本文将围绕 retain.md 展开,逐层拆解事实抽取、实体识别与消解、知识图谱构建、双时间维度、标签体系、抽取任务(Mission)与观测合并(Consolidation)等机制,并结合仓库源码与配置(fact_extraction.py、entity_labels.py、config.py)给出可落地的参数与示例。读完你将掌握如何让 Hindsight 记住"什么、为何、何时",以及如何用抽取模式与 Entity Labels 精确控制记忆的质量与检索效果。
What Retain Does
当调用retain()时,Hindsight 会完成一次完整的记忆写入管线。原文档给出了如下工作流:
四个阶段依次为:抽取事实(Extract Facts)→识别实体(Identify Entities)→构建连接(Build Connections)→写入记忆库(Memory Bank)。从源码结构看,这一管线对应 engine/retain/ 目录中的若干模块:
fact_extraction.py:调用 LLM 从文本中抽取语义事实、实体与时间信息;entity_processing.py:对抽取出的实体进行规范化与去重;link_creation.py/link_utils.py:构建实体、时间、语义、因果四类图连接;fact_storage.py/chunk_storage.py:将事实与分块写入记忆库;orchestrator.py:编排整个 retain 流程。
retain()不是简单的"存原文",而是把内容转译成保留了含义(meaning)与上下文(context)的结构化记忆,为后续recall()与reflect()提供高质量素材。
Rich Fact Extraction:不只记下"说了什么"
Hindsight 保存的不只是"谁说了什么",而是为什么(why)、怎样(how)以及它意味着什么(what it means)。
What Gets Captured
以原文档的示例为例,当输入
Alice joined Google last spring and was thrilled about the research opportunities.
Hindsight 会抽取:
- 核心事实(core facts):Alice 加入了 Google;这件事发生在去年春天。
- 情感与含义(emotions and meaning):她很兴奋(thrilled);这对她是一个重要机会。
- 推理(reasoning):她选择 Google 是为了研究机会。
这种富抽取带来的直接收益是:之后你可以问"Alice 为什么加入 Google?",得到的不是干巴巴的"她加入了 Google",而是包含动机与情感的有意义答案。从源码看,这得益于抽取响应模型中的what字段——在 fact_extraction.py 中其定义为Core fact - concise but complete (1-2 sentences),即核心事实既要精炼又必须完整,同时 verbose 模式下还有更丰富的字段定义(对应ExtractedFactVerbose)。
Preserving Context:保存完整叙事
传统系统会把信息切碎:
- "Bob suggested Summer Vibes"
- "Alice wanted something unique"
- "They chose Beach Beats"
Hindsight 保留完整叙事:
Alice and Bob discussed naming their summer party playlist. Bob suggested 'Summer Vibes' because it's catchy, but Alice wanted something unique. They ultimately decided on 'Beach Beats' for its playful tone.
这意味着搜索结果自带完整上下文,而不是一堆互相割裂的片段。这也是 Hindsight 区别于朴素 RAG 切块检索的核心之一(可对比 rag-vs-hindsight.md 中的定位阐述)。
Two Types of Facts:world 与 experience
Hindsight 区分两类事实:
| Type | Description | Example |
|---|---|---|
| world | 关于他人、地点、事物的客观事实 | "Alice works at Google" |
| experience | 对话与事件(第一人称经历) | "I recommended Python to Alice" |
- world facts描述外部世界的稳定属性;
- experience facts描述"我"参与的对话和发生的事件。
Note(原文提示):retain()操作完成后,观测合并(observation consolidation)会在后台自动运行。该过程把新事实中涌现的模式综合进记忆库的知识体系。这一点将在本文后面的 Observation Consolidation 一节展开。
Entity Recognition:实体识别与消解
Hindsight 会自动识别并持续追踪实体——那些重要的人、组织与概念。
What Gets Recognized
- 人物(People):"Alice"、"Dr. Smith"、"Bob Chen"
- 组织(Organizations):"Google"、"MIT"、"OpenAI"
- 地点(Places):"Paris"、"Central Park"、"California"
- 产品与概念(Products & Concepts):"Python"、"TensorFlow"、"machine learning"
Entity Resolution:同实体归一
同一个实体以不同方式出现时会被统一:
- "Alice" + "Alice Chen" + "Alice C." → 同一个人
- "Bob" + "Robert Chen" → 同一个人(昵称消解)
为什么重要:你可以问"What do I know about Alice?",即使某些对话里她只被写作"Alice Chen",也能拿到关于她的全部信息。在实现层面,实体归一依赖 entity_processing.py,它会将 LLM 输出的实体列表做规范化与去重处理。
Context-Aware Disambiguation:上下文感知消歧
如果"Alice"多次与"Google"和"Stanford"共同出现,那么一个同样提及这些组织的"Alice",很可能是同一个人。Hindsight 使用**共现模式(co-occurrence patterns)**来消解常见姓名。这种消歧同时服务于知识图谱构建——共用实体的所有事实会被自动连接。
Entity Labels:受控词表标签
你可以为记忆库定义一组key:value形式的受控分类标签(例如pedagogy:scaffolding、engagement:active),它们会在 retain 时被抽取并作为实体存储:
- 因为标签本身是实体,它们会在知识图谱中自动关联相关记忆(两条都带
pedagogy:scaffolding的记忆会被链接); - 同时提升语义检索与关键词(BM25)检索的效果;
- 标签还可以(可选)写入记忆单元的 tags,从而在 recall / reflect 阶段启用标准标签过滤。
完整配置细节见 memory-banks.mdx 中的 entity_labels 章节。
Entity Labels 的源码实现
在 entity_labels.py 中,标签体系被建模为:
EntityLabelsConfig:一组LabelAttribute(label group),即一个分类维度;- 每个 group 有
key(前缀)、type(value/multi-values/text/map)、values(枚举值)、fields(map 字段)、optional、tag等属性; build_labels_lookup()为枚举型标签构建小写key:value的快速查找集合,抽取时超出词表的值会被静默丢弃(anything outside the list is silently dropped);is_label_entity()判断某个实体串是否属于已配置的标签组;label_tag_keys()返回tag: true的 group 键,这些键的值会被投影(_inject_label_tags)到事实的tags数组,从而支持tags/tags_match过滤。
在 fact_extraction.py 中,_build_labels_prompt_section()会把标签词表渲染进抽取 prompt:枚举型列出可取值及描述,文本型(text/multi-text)提示 LLM 自由书写,map 型则要求按key:field:value结构输出(例如person:name:Alice),并复用既有实体存储,无需变更 schema。
Building Connections:四类知识图谱连接
记忆不是孤立的——Hindsight 会创建一个知识图谱,包含四类连接:
Entity Connections(实体连接)
所有提及同一实体的事实被互相链接。能力:"Tell me everything about Alice" → 取回所有与 Alice 相关的事实。
Time-Based Connections(时间连接)
时间上接近的事实会被连接,日期越近连接越强。能力:"What else happened around then?" → 找到上下文相关的事件。
Meaning-Based Connections(语义连接)
语义相似的事实被连接,即使它们用了不同的词。能力:"Tell me about similar topics" → 找到主题相关的信息。
Causal Connections(因果连接)
因果关系被显式追踪。能力:"Why did this happen?" → 追溯推理链。示例:"Alice felt burned out" ← caused by ← "She worked 80-hour weeks"
从源码看,连接构建集中在 link_creation.py 与 link_utils.py。仓库测试 test_causal_relation_offsets.py、test_causal_relations.py 等验证了因果关系的抽取与存储细节;测试目录下还有test_link_expansion_*、test_observation_expansion_*等用例覆盖连接相关的检索行为。
Understanding Time:双时间维度
Hindsight 追踪两个时间维度,这是它支持时间语义检索的基础。
When It Happened(事件发生时间)
- 对于事件(会议、旅行、里程碑),记录其发生时间:
- "Alice got married in June 2024" → occurred 于 2024 年 6 月
- 对于一般事实(偏好、特征),没有特定发生时间:
- "Alice prefers Python" → 持续偏好
在实现上,事实模型带有occurred_start/occurred_end字段,并强制使用 ISO 时间戳格式(见 fact_extraction.py 中的_with_iso_timestamp_pattern)。此外还有一个兜底机制_infer_temporal_date()(同文件 L83 起):当 LLM 没有给出结构化时间,而事实文本中出现 "last night"、"yesterday"、"next week" 等相对时间表达时,会结合 retain 发生时的事件日期自动推算绝对日期。
When You Learned It(学习时间)
Hindsight 同时记录你告诉它每个事实的时间。
为什么两者都要?假设 2025 年 1 月有人告诉你"Alice 在 2024 年 6 月结婚":
- 历史查询生效:"What did Alice do in 2024?" → 能找到那场婚礼;
- 时效排序生效:最近的提及在搜索中优先级更高;
- 时间推理生效:"What happened before her marriage?" → 找到更早的事件。
如果失去这个区分,旧信息要么无法按日期检索,要么会被当作无关内容处理。这一设计对应源码中的created_at/observed_at类时间字段与时间连接构建逻辑,也是 retrieval.md 中时间窗过滤与时效排序能力的基础。
Tagging Memories:标签与可见性范围
标签用于可见性范围控制(visibility scoping)——当一个记忆库服务多个用户、而每个用户只应看到自己相关的记忆时尤其有用。
- Item tags:给单条记忆打上特定范围标签;
- Document tags:给一批(batch)中的全部条目统一打标签;
- Tag filtering:在 recall / reflect 阶段按标签过滤。
代码示例见 Retain API,过滤选项见 Recall API(对应 retrieval.md)。在实现上,标签会写入记忆单元(memory unit)的 tags 字段,并支持tags/tags_match过滤语义;Entity Labels 中tag: true的分组也会把抽取出的key:value标签投影为单元标签,与手动标签在同一套过滤机制下工作。
What You Get:retain() 完成后的产物
当retain()完成后,你得到:
- 结构化事实(Structured facts)——保留含义、情感与推理;
- 统一实体(Unified entities)——解析了不同名称变体;
- 知识图谱(Knowledge graph)——含实体、时间、语义、因果四类连接;
- 时间锚定(Temporal grounding)——同时支持历史查询与时效排序;
- 可选标签(Optional tags)——供 recall 阶段过滤使用。
所有这些都存储在**相互隔离的记忆库(memory bank)**中,随时可供recall()与reflect()使用。每个 bank 独立配置(retain_mission、retain_extraction_mode、entity_labels等均按 bank 生效,见 memory-banks.mdx)。
Steering Extraction with a Mission:用任务引导抽取
默认情况下,retain()会抽取内容中所有重要事实。你可以用retain mission(retain_mission)收窄关注范围——用自然语言描述"这个记忆库应该关注什么":
e.g. Always include technical decisions, API design choices, and architectural trade-offs. Ignore meeting logistics, greetings, and social exchanges.mission 的注入方式:它被注入到抽取 prompt 中,与内置规则并列生效——它引导(steer)LLM 的注意力,但不替换抽取逻辑。它适用于任何抽取模式(concise、verbose、custom)。
从源码看,这一机制位于 fact_extraction.py 的_retain_mission_preamble():mission 以用户消息前缀(user message preamble)的形式注入,而不是写进系统 prompt——这样系统 prompt 保持与 bank 无关,Gemini 等 provider 可以跨 bank 复用同一个缓存上下文(cache),不必为每个 mission 单独建缓存(见同文件 L1485-L1490 的注释说明)。
抽取模式(Extraction Modes)
| Mode | 何时使用 |
|---|---|
concise(default) | 通用场景——有选择地抽取,速度快 |
verbose | 需要更丰富的事实、完整上下文与关系时 |
custom | 想完全自定义抽取规则时 |
配置方式:
- 通过bank config API设置
retain_mission和retain_extraction_mode(见 memory-banks.mdx 的 retain 配置章节); - 或通过环境变量
HINDSIGHT_API_RETAIN_MISSION(见 configuration.md 的 retain 章节)。
环境变量与默认值(仓库实测)
在 config.py 中可确认:
ENV_RETAIN_EXTRACTION_MODE = "HINDSIGHT_API_RETAIN_EXTRACTION_MODE"(L738);ENV_RETAIN_MISSION = "HINDSIGHT_API_RETAIN_MISSION"(L739);DEFAULT_RETAIN_EXTRACTION_MODE = "concise",合法取值集合为("concise", "verbose", "custom", "verbatim", "chunks")(L1503-L1504);DEFAULT_RETAIN_MISSION = None(L1505);DEFAULT_ENABLE_AUTO_CONSOLIDATION = True——retain 后自动合并默认开启(L1571)。
也就是说,除文档提到的三种模式外,代码还保留了verbatim(逐字抽取)与chunks(分块相关)两种模式,前者在 fact_extraction.py 中对应VERBATIM_FACT_EXTRACTION_PROMPT,后者用于长内容分块抽取。配置示例:
export HINDSIGHT_API_RETAIN_MISSION="Focus on technical decisions, architecture choices, and team member expertise. Deprioritize social or personal information." export HINDSIGHT_API_RETAIN_EXTRACTION_MODE=verbose在custom模式下,还可以配合retain_custom_instructions完全替换内置抽取规则——只有当retain_extraction_mode为custom时才生效(见 configuration.md 附近的环境变量示例)。另外 configuration.md 还介绍了按策略覆盖(per-strategy overrides):retain_extraction_mode、retain_chunk_size、entity_labels、entities_allow_free_form、retain_mission等字段都可以在不同命名策略下分别配置(L954 起),适合"同一服务器服务多种工作负载"的场景。
Observation Consolidation:retain 后的自动合并
retain()完成后,Hindsight 会在后台自动触发观测合并(observation consolidation)。这一过程:
- 将新事实与既有观测(observations)做比对分析;
- 当新模式涌现时创建新的观测;
- 用新证据精炼已有观测;
- 追踪每条观测由哪些事实支撑。
整个合并异步进行——retain()调用立即返回,合并随后在后台完成。
从配置与源码看:
- 服务级开关
HINDSIGHT_API_ENABLE_AUTO_CONSOLIDATION(默认true,见 config.py); - bank 级开关
enable_observations(关闭后该 bank 不做任何合并)与enable_auto_consolidation(关闭后只在显式调用 consolidate 端点时合并,见 memory-banks.mdx); - 仓库测试 test_consolidation_*.py 系列(如
test_consolidation_retry_budget.py、test_consolidation_reschedule_after_round.py、test_consolidation_round_limit.py、test_consolidation_temporal_merge.py)覆盖了合并的轮次限制、失败重试、跨轮刷新、时间合并等细节,说明合并是带预算与重试机制的成熟后台任务。
观测合并的完整机制详见 observations 文档。
Next Steps:继续深入
- Observations——retain 之后知识如何被合并成观测;
- Recall——多策略搜索如何取回相关记忆;
- Reflect——Agentic 循环如何使用观测;
- Retain API / Bank 配置——
retain_mission、retain_extraction_mode、entity_labels、retain_chunk_size等参数的完整配置说明与代码示例; - Configuration——
HINDSIGHT_API_RETAIN_MISSION等环境变量的完整清单与分层配置说明; - 源码参考:fact_extraction.py、entity_labels.py、engine/retain/ 目录、config.py;
- 测试参考:test_retain.py、test_entity_labels.py、test_consolidation_*.py 系列。
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考