Hindsight Retain 深度解析:如何把对话与文档转化为可检索的结构化记忆

发布时间:2026/9/15 12:19:10
Hindsight Retain 深度解析:如何把对话与文档转化为可检索的结构化记忆 Hindsight Retain 深度解析如何把对话与文档转化为可检索的结构化记忆【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight导读retain()是 Hindsight 记忆系统的写入入口调用它之后对话记录与文档会被转化为结构化、可搜索、保留语义与上下文的记忆单元memory unit最终沉淀进独立的 Memory Bank供后续recall()检索与reflect()反思使用。本文以官方开发者文档 skills/hindsight-docs/references/developer/retain.md 为骨架结合仓库源码hindsight-api-slim/hindsight_api逐步拆解 retain 的抽取、实体解析、知识图谱构建、时间维度、内联附件、提取使命与观测整合机制读完你既能掌握retain()的完整心智模型也能用配置项和 API 对记忆写入做精细化控制。Retain 做什么从原始内容到记忆银行的五步流水线文档用一张流程图概括了 retain 的整体链路Extract Facts从内容中抽取值得长期记住的事实而非逐字存储Identify Entities识别并统一实体人物、组织、地点、概念Build Connections在实体、时间、语义、因果四个维度上建边形成知识图谱Memory Bank全部写入隔离的记忆银行等待recall()与reflect()消费。在源码层面这条流水线对应 hindsight-api-slim/hindsight_api/engine/retain 目录下的orchestrator.py编排、fact_extraction.py抽取、fact_storage.py落库与link_creation.py建边等模块其中事实抽取与链接创建是下面重点展开的两个环节。富事实抽取存下为什么与意味着什么Hindsight 不只是记录说了什么还捕获原因why、方式how与含义what it means。以文档中的例子为准对Alice joined Google last spring and was thrilled about the research opportunities抽取结果为核心事实Alice 加入了 Google这件事发生在去年春天情绪与含义她很兴奋这代表一个重要的机会推理她选择 Google 是因为研究机会。正是因为抽取阶段同时保留了情绪与动机之后才能回答Why did Alice join Google?这样的问题而不是只能得到一句干巴巴的 she joined Google。上下文的保存完整叙事而非碎片传统系统会把一段对话拆成互不相连的碎片Bob suggested Summer VibesAlice wanted something uniqueThey chose Beach BeatsHindsight 则保留完整叙事Alice and Bob discussed naming their summer party playlist. Bob suggested Summer Vibes because its catchy, but Alice wanted something unique. They ultimately decided on Beach Beats for its playful tone.于是搜索结果返回的是带完整上下文的事实而不是脱离语境的碎片。这一特性在抽取提示词中也有对应设计基础提示模板要求抽取时做共指消解coreference resolution例如把 my roommate Emily 统一为 Emily (users roommate)把 the manager Sarah 统一为 Sarah (the manager)从而在事实文本层面消除指代歧义。相关实现见 fact_extraction.py 的_BASE_FACT_EXTRACTION_PROMPT。抽取的五个字段文档中提到的核心事实结构在提示模板中对应五个字段详见 fact_extraction.py字段含义要求what核心事实简洁但完整1–2 句话以内when时间信息未提及则为 N/A尽量给出具体日期where地点无相关地点则为 N/Awho相关人物及关系仅针对一般信息时为 N/Awhy背景/重要性仅当重要时填写明显时省略模板还强调选择性——只抽取值得长期记住的显著事实Be SELECTIVE - only extract facts worth remembering long-term并用一句好句子胜过三句平庸句子来约束简洁度。两种事实类型experience 与 world每条事实都会按照视角被分类——它记录的是拥有该记忆银行的智能体自己还是外部世界类型捕获内容示例experience智能体自身的行为、观察、交互——它的第一人称历史我向 Alice 推荐了 Pythonworld关于其他人、地点、事物与事件的事实Alice 在 Google 工作归属由说话者决定而非语法关键点判断依据是谁在说话而不是句子是否用了第一人称。只有当第一人称陈述的说话者就是银行所属的智能体时它才是experience同样的措辞出自他人之口则是对该人的world事实智能体自己的日志I patched the auth bug →experience智能体做的用户对智能体说I bought a Tesla →world关于用户的事实与智能体无关。在源码的抽取提示词中这一规则体现为fact_type的分类指令fact_extraction.pyworld客观/外部事实包括用户的偏好、规则、纠正、约束、计划、特质与上下文。即使用户在与助手交互时陈述这些内容也保持为world如 User prefers browser_navigate over web_searchassistant助手/智能体实际执行的动作、经验与观察如 I changed X、I discovered Y。用于智能体做、尝试、学习、决定、推荐、回应等行为。用 context 引导分类要正确归类应在每个条目的context中描述说话者保留转写稿或第三方内容时使用类似Customer Maria is speaking的上下文确保她的第一人称陈述被存为关于 Maria 的world事实而不是被误认为智能体自己的经历处理智能体自己的日志时使用类似The assistant is speaking的上下文将其第一人称陈述归属为智能体的experience事实。源码中的 narrator 机制与此完全对应当提供agent_name时提示词会追加 Narrator 段声明第一人称陈述默认归为assistant智能体自身但当context指明不同的第一人称说话者如转写稿中的用户/客户时Context 优先将其归为world见 fact_extraction.py。注retain()完成后观测observations会在后台自动整合——该过程把新事实中浮现的模式综合进银行的知识库见后文Observation Consolidation一节。实体识别与解析让同一实体多种叫法归一Hindsight 自动识别并持续追踪实体entities——那些重要的人物、组织与概念人物Alice、Dr. Smith、Bob Chen组织Google、MIT、OpenAI地点Paris、Central Park、California产品与概念Python、TensorFlow、machine learning实体解析Entity Resolution同一个实体以不同方式出现时通过模糊名称匹配fuzzy name matching统一并以共现co-occurrence与时间邻近作为强化信号Alice Alice Chen Alice C. → 归并为同一个人。因为解析依赖名称相似度近似变体会被自动合并而名称毫不相似的写法如昵称与完全无关的正式名不会仅凭名称合并但共享的共现实体仍可能把它们关联起来。为什么重要你可以问What do I know about Alice?即使她在某些对话里被写作 Alice Chen也能拿到关于她的全部信息。解析本质上是一种判断因此也可能走向另一方向在一个历史很长、实体很多的银行里一个新出现的短名称若与既有实体相似、且总与该实体已关联的实体一同出现就可能被吸收进既有实体而不是自成新实体。如果你发现新人物的事实被挂到了不相关的实体上可参阅 How entity resolution decides 了解它比较了什么、哪个设置能让匹配更严格。上下文感知消歧Context-Aware Disambiguation如果 Alice 多次与 Google、Stanford 一同出现那么一个新提及 Alice 的上下文若同样包含这些实体则很可能就是同一个人——Hindsight 利用共现模式来消解常见名字的歧义。实体标签Entity Labels受控分类词表你可以定义一组受控的key:value分类标签词表如pedagogy:scaffolding、engagement:active在 retain 时被抽取出来并作为实体存储。由于标签也是实体它们会自动把相关记忆在知识图谱中链接起来两条带pedagogy:scaffolding的记忆会被互链同时提升语义检索与 BM25 关键词检索的效果标签还可以选择性地写入记忆单元的 tags从而在 recall 与 reflect 阶段支持标准的标签过滤。与普通实体不同标签实体永远不会按名称相似度合并——不同的标签值必须保持彼此不同因此它们只按精确匹配解析并被完全排除在模糊名称匹配之外。配置入口是 entity_labels in the bank config。每一条entity_labels是一个标签组一个分类维度支持多种取值类型详见 memory-banks.md{ entity_labels: [ { key: engagement, description: Student engagement level during the session, type: value, optional: true, values: [ { value: active, description: Student is actively participating }, { value: passive, description: Student is listening but not participating } ] }, { key: pedagogy, description: Teaching strategies used, type: multi-values, values: [ { value: scaffolding, description: Breaking complex tasks into smaller steps }, { value: direct_instruction, description: Explicit explanation by the teacher }, { value: socratic_questioning, description: Guiding through questions rather than answers } ] } ] }字段默认值说明key—标签组标识成为key:value实体map类型则为key:field:value的前缀description展示给 LLM 的说明用于引导标签指派typevaluevalue单选枚举multi-values多选text自由文本multi-text任意数量自由文本map带命名字段的结构化分组构建连接四类边构成知识图谱记忆不是孤立的——Hindsight 用四种类型的连接构建知识图谱。对应源码实现集中在 hindsight-api-slim/hindsight_api/engine/retain/link_creation.py其中create_temporal_links_batch、create_semantic_links_batch、create_causal_links_batch分别负责三类边的批量创建语义链接基于事实的 embedding 计算余弦相似度并施加最低阈值threshold。实体连接Entity Connections所有提及同一实体的事实被互相链接。启用Tell me everything about Alice → 取回全部与 Alice 相关的事实。时间连接Time-Based Connections时间上相近的事实被连接日期越近的链接越强。启用What else happened around then? → 找到语境上相关的事件。语义连接Meaning-Based Connections语义相似的事实被链接即使它们用了不同的措辞由 create_semantic_links_batch 基于 embedding 相似度阈值实现。启用Tell me about similar topics → 找到主题相关的信息。因果连接Causal Connections因果关系被显式追踪。启用Why did this happen? → 追溯推理链示例Alice felt burned out ← 起因 ← She worked 80-hour weeks。因果关系的抽取与写入由HINDSIGHT_API_RETAIN_EXTRACT_CAUSAL_LINKS环境变量控制见 config.py每条事实可携带causal_relations列表最终由 create_causal_links_batch 写入。理解时间双重时间维度Hindsight 追踪两个时间维度这是它支持历史查询与新鲜度排序的基础。事件发生时间When It Happened对于事件会议、旅行、里程碑记录其发生时间Alice got married in June 2024→ 发生于 2024 年 6 月对于一般事实偏好、特质没有具体的发生时间Alice prefers Python→ 持续性偏好。源码的提示模板对此有精细约束fact_extraction.py用输入中的 Event Date 作为相对日期的参照必须把全部相对时间表达转换为绝对日期写入事实文本yesterday → 写出解析后的日期而不是 yesterday 这个词事件需同时设置occurred_start与occurred_end点事件两者相同粗粒度日期只说了年份或月份要覆盖整个区间in 2015 → 2015-01-01 至 2015-12-31in March 2026 → 2026-03-01 至 2026-03-31绝不能坍缩到区间首日或 Event Date对话类conversation事实不设置occurred 日期。得知时间When You Learned ItHindsight 同时记录你是什么时候告诉它每条事实的。为什么两者都要假设 2025 年 1 月有人告诉你Alice got married in June 2024历史查询What did Alice do in 2024? → 能找到这场婚礼新鲜度排序最近被提及的条目在搜索中获得更高权重时间推理What happened before her marriage? → 能找到更早的事件。如果没有这个区分旧信息要么无法按日期检索要么会被当成无关内容处理。为记忆打标签可见性范围控制标签用于可见性范围控制visibility scoping——当一个记忆银行服务多个用户、而每个用户只应看到相关记忆时尤为有用条目标签Item tags为单条记忆打上特定范围的标签文档标签Document tags为一批中的所有条目统一打标签标签过滤Tag filtering在 recall/reflect 阶段按标签过滤。代码示例见 Retain API过滤选项见 Recall API。内容中的图片与文件内联附件文档中大量信息其实存在于图片里——说明按钮位置的截图、承载升级路径的示意图、本身就是数据的图表。content接受有序的块blocks列表让图片待在它该在的位置而不是事先被压缩成一句说明文字{ content: [ {type: text, text: To reset the VPN, click the button shown:}, {type: image, source: {type: base64, media_type: image/png, data: ...}}, {type: text, text: ...then reconnect.} ] }纯字符串仍然和以前完全一样——纯文本 retain 的行为没有任何改变。关键在于位置position。抽取时文本与图片一起、按顺序发送给模型模型会在引入某句话的截图旁边读到该截图。Click the button shown below 单独看毫无意义配图之后它就变成一条能说出按钮名称的事实。这一点在抽取提示词中有显式强化含附件时会注入 ATTACHMENTS 段落要求模型阅读每个内联展示的附件并规定只存在于附件中的事实——按钮标签、表格中的数值、示意图的方框——与正文中的事实一样可抽取且要求把每个附件归属到它周围的句子上见 fact_extraction.py。返回什么事实读起来很自然——[image: image/png]只标记附件曾经所在的位置不是内容哈希——每条记忆携带它抽取自哪些附件并给出可拉取的 URL{ text: The escalation path for a stuck sync begins by contacting Tier 3 Platform., attachments: [ {id: c414cd0e204d, kind: image, media_type: image/png, url: /v1/default/banks/my-bank/attachments/c414cd0e204d} ] }来自正文的事实没有attachments即使同一份文档满是图片。因此某条记忆旁出现附件意味着模型确实看过它才产出该记忆——这是证据不是装饰。抽取时每条事实携带的from_attachments编号正是这一机制的落点提示词要求只有事实离开该附件就无法陈述时才列出该附件。图表与表格会怎样携带结构化数据的附件会被转写transcribed而非摘要summarised每一行、每个柱、每个带标签的值都变成独立的事实带着它的标签、数值以及绘制方式。因此一张图表产生的记忆远多于一张截图——这是刻意为之一张二十个柱的图表的摘要只保留三个值、静默丢掉其余十七个而后续提问往往针对被丢掉的那几个。相应地抽取提示词要求数据就是文档为每一行/柱/扇区/带标签的值各建一条事实带上标签、精确数值与绘制方式颜色、在序列中的位置并且不要摘要整个序列还要记录图表整体信息标题、单位、覆盖周期、条目数量、图例每种颜色代表什么。非常密集的页面仍采用采样而非穷尽单次抽取遍过一张列了一百个条目的信息图会捕获其中很大一部分而非全部。前置要求需要一个能读图的模型。默认是银行的 retain 模型但并非必须HINDSIGHT_API_VLM_MODEL指定一个视觉槽位vision slot只用于真正携带附件的那些块所有纯文本块仍走 retain LLM。因此文档以散文为主的银行可以保留便宜的文本模型只在确有可看内容时才为视觉付费环境变量定义见 config.py如果该模型不能读图——或者 Hindsight 无法判断混合目录的后端网关即属此类——retain 会以422拒绝而不是静默丢弃附件。相关开关是HINDSIGHT_API_LLM_VISION该变量为三态布尔True/False直接覆盖模型自身判断见 config.py批量 retainHINDSIGHT_API_RETAIN_BATCH_ENABLED不能携带附件同样以422拒绝。这区别于POST /files/retain后者把整个文件转成 markdown 作为独立文档。扫描件想被解析时它仍然是正确工具——但它把文件与提及它的正文分开了而这正是内联附件要避免的。存储、大小限制与接受的媒体类型见 Inline attachments in retain相关环境变量包括HINDSIGHT_API_RETAIN_ATTACHMENT_MAX_SIZE_MB、HINDSIGHT_API_RETAIN_ATTACHMENT_MAX_COUNT、HINDSIGHT_API_RETAIN_MAX_ATTACHMENTS_PER_CHUNKconfig.py。你得到什么retain()完成后产出包括结构化事实保留含义、情绪与推理统一实体消解不同名称变体知识图谱实体、时间、语义、因果四类链接时间锚定同时支持历史查询与新鲜度排序可选标签供 recall 阶段过滤。以上全部存入你的隔离记忆银行随时可供recall()与reflect()使用。用 Mission 引导抽取默认情况下retain()抽取内容中所有显著事实。你可以用retain missionretain_mission收窄关注点——用自然语言描述这个银行应该关注什么e.g. Always include technical decisions, API design choices, and architectural trade-offs. Ignore meeting logistics, greetings, and social exchanges.mission 会被注入抽取提示词与内置规则并列它引导 LLM 而不替换抽取逻辑。它适用于所有基于 LLM 的抽取模式concise、verbose、verbatim、custom在chunks模式下被忽略。在源码中mission 作为retain_mission_section占位符被注入基础提示模板fact_extraction.py。抽取模式Extraction Modes需要更细粒度控制时可以切换抽取模式模式适用场景concise(默认)通用场景——有选择性、速度快verbose需要带完整上下文与关系的更丰富事实custom想完全自定义抽取规则verbatim需要保留原始块文本同时由 LLM 抽取实体、日期等元数据chunks原样存储块不做任何 LLM 调用、不抽取元数据源码中允许的模式集合为(concise, verbose, custom, verbatim, chunks)默认concise非法的模式值会被校验并回退到默认值config.py、config.py。custom模式读取retain_custom_instructionsHINDSIGHT_API_RETAIN_CUSTOM_INSTRUCTIONSverbose/verbatim模式各自使用专属的响应 Schemafact_extraction.py。配置方式retain_mission与retain_extraction_mode可通过 bank config API 配置也可用环境变量HINDSIGHT_API_RETAIN_MISSION与HINDSIGHT_API_RETAIN_EXTRACTION_MODE两者分别对应 config.py 中的ENV_RETAIN_EXTRACTION_MODE与ENV_RETAIN_MISSION在 config.py 读取。当 mission 排除了文档中的一切mission 收窄了什么能成为记忆——而产不出事实的内容就不会产生任何记忆。文档本身仍被存储但recall与reflect检索的是记忆因此一份零记忆的文档无法被两者中的任何一个找到。收紧 mission 因而牺牲的是对原始来源的检索能力而不只是事实创建。这是正常结果不是错误retain 成功操作被报告为已完成。两个信号能告诉你它发生了位置看什么retain.completedwebhookdata.memory_unit_count: 0Metricshindsight.retain.documents.total{outcomeno_facts}你还可以事后审计GET /documents按文档返回memory_unit_countapi/http.py过滤为0即可列出当前全部不可达的文档。抽取并非完全确定——一份边缘文档可能这次跑出事实、下次跑出零条。把零视为这份文档需要再跑一遍而不是永久判决。要恢复文档放宽 mission 后重新处理即可——存储的文本会被重新抽取无需重新上传POST /v1/default/banks/{bank_id}/documents/{document_id}/reprocess该端点对应源码中的reprocess_document操作api/http.py。Observation Consolidation后台观测整合retain()完成后Hindsight 会自动在后台触发观测整合observation consolidation。该过程将新事实与既有观测对比分析模式浮现时创建新观测用新证据精化既有观测追踪每条观测由哪些事实支撑。整个过程异步进行——你的retain()调用立即返回整合在后台运行。细节见 Observations。记忆防御与来源溯源Memory Defense and Source Provenancereceipt_uri可选类型string。指向外部收据或共同签名系统的可选指针。原样存储并会出现在该条目任何 Memory Defense 决策的security_events.receipt_uri中。422 —— Memory Defense 违规当目标银行启用了 Memory Defense、且批次中每条条目都被策略拦截时请求返回422并携带违规列表{ detail: { violations: [ { index: 0, detector: prompt_injection, severity: high, message: ... } ] } }部分拦截的批次返回200未拦截的条目照常处理被拦截的条目静默地从结果中移除其决策记录在security_events中。完整指南见 Memory Defense。下一步Observations—— retain 之后知识如何被整合Recall—— 多策略检索如何取回相关记忆Reflect—— 智能体循环如何使用观测Retain API—— 代码示例与参数说明【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考