 深度解析:事实抽取、实体消解与知识图谱构建的完整记忆写入链路)
Hindsight retain() 深度解析事实抽取、实体消解与知识图谱构建的完整记忆写入链路【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight本文以 Hindsight 开发者文档 retain.md 为主体系统讲解retain()如何将对话与文档转化为结构化、可检索的记忆包括富事实抽取保留情绪、动机与推理链、experience/world 双事实类型、实体识别与模糊消解、四类知识图谱连接实体/时间/语义/因果、双时间维度建模以及retain_mission定向抽取、观察整合Observation Consolidation与 Memory Defense 安全拦截。读完并对照仓库源码后你可以理解每一次retain()调用在引擎内部经过的完整处理阶段并能正确配置抽取模式、实体标签与召回过滤做出可验证的调优决策。retain() 在 Hindsight 中的定位当你调用retain()时Hindsight 会将你的内容转化为保留语义与上下文的、结构化且可检索的记忆。原文档给出的整体流程是一个五步流水线从源码结构看这条流水线落在 retain 编排模块中该模块共 4000 余行负责协调分块、事实抽取、实体处理、链接创建、预算控制等全部 retain 子模块。其目录 engine/retain/ 下的文件与文档中的概念一一对应文档概念对应源码模块Extract Facts事实抽取fact_extraction.py、fact_storage.pyIdentify Entities实体识别/消解entity_processing.py、entity_labels.py、entity_resolver.pyBuild Connections建立连接link_creation.py、link_utils.py嵌入与向量索引embedding_processing.py分块与文档存储chunk_storage.py富事实抽取不只记录说了什么Hindsight 的抽取不止于字面陈述——它同时捕获why动机、how方式与what it means含义。以原文档的示例retain Alice joined Google last spring and was thrilled about the research opportunitiesHindsight 会抽取三个层次的信息核心事实Alice 加入了 Google这件事发生在去年春天情绪与含义她非常兴奋thrilled这代表一个重要机会推理动机她是为了研究机会而选择的这种富抽取的价值在于你之后可以问 Why did Alice join Google?得到的不只是 she joined Google而是有意义的因果回答。源码印证五维抽取 Schema 与因果约束在 fact_extraction.py 中抽取结果由 Pydantic 结构化 Schema 约束每条事实ExtractedFact要求模型输出以下维度what— 完整、详细的描述COMPLETE, DETAILED description with ALL specificswhen/where/who— 时间与地点上下文fact_type— 事实类型见下文 experience/world 一节entities— 命名实体、对象以及抽象概念的纯字符串数组causal_relations— 因果链接每条事实最多 2 条且target_index必须小于当前事实的索引即只能指向更早的事实。# fact_extraction.py 中因果关系的 Schema 定义节选 causal_relations: list[FactCausalRelation] | None Field( defaultNone, descriptionCausal links to PREVIOUS facts only. target_index MUST be less than this facts position. Example: fact #3 can only reference facts 0, 1, or 2. Max 2 relations per fact., )同时抽取系统提示词明确定位为 Extract SIGNIFICANT facts ... Be SELECTIVE - only extract facts worth remembering long-term——即抽取是有选择的只保留值得长期记忆的事实。批量Batch API路径同样适用该 Schema保证直连调用与批量提交两种模式输出一致。保留完整叙事上下文传统系统会把信息打碎成碎片Bob 提出了 Summer VibesAlice 想要一些独特的他们选了 Beach Beats而 Hindsight 保留完整叙事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.这意味着搜索结果携带完整上下文而非互不相关的碎片。从源码结构看这一点由分块chunking层支撑retain 流水线在保留文档原文documents.original_text的基础上按语义边界切块且对 JSON 数组形式的对话有专门保护逻辑——orchestrator.py 中的merge_json_array_parts会把分片重新合并为单个合法 JSON 数组再交给分块器避免追加写入时 JSON 被换行拼接破坏而丧失说话人归属。两种事实类型experience 与 world每条事实都会按它从谁的角度被记录来分类——是拥有该 bank 的 agent 自己还是外部世界类型记录内容示例experiencebank 自己的 agent 在行动、观察或互动——它的第一人称历史I recommended Python to Aliceworld关于其他人、地点、事物和事件的事实Alice works at Google关键规则是划分依据是说话者身份而非语法。第一人称陈述只有当说话者就是该 bank 的 agent 时才是experience同样的话出自他人之口则是关于那个人的world事实Agent 自己的日志——I patched the auth bug →experienceagent 做的用户对 agent 说——I bought a Tesla →world关于用户的事实而非 agent 的。实操建议在每个条目的context字段中描述说话者身份以正确引导分类。保留转录或第三方内容时使用如Customer Maria is speaking的 context可确保她的第一人称陈述被存为关于 Maria 的world事实而不是误判为 agent 自身的 experience对 agent 自己的日志则用The assistant is speaking将其第一人称陈述归属为 agent 的experience。源码印证types.py 中ProcessedFact.fact_type的注释即为world, experience, observation其中observation是整合阶段派生的高层知识见观察整合一节。抽取提示词中还有对应的分类规则用户偏好、规则、修正、约束、特质等客观事实一律标world只有 agent 实际执行的动作或经历才标 agent 侧类型。注意观察Observations会在retain()操作完成后在后台自动整合。该整合过程把新事实中的模式合成到 bank 的知识库中。实体识别与消解Hindsight 会自动识别并跟踪实体——重要的人物、组织与概念。识别范围人物Alice、Dr. Smith、Bob Chen组织Google、MIT、OpenAI地点Paris、Central Park、California产品与概念Python、TensorFlow、machine learning从源码 Schema 看实体抽取的定义比文档更广entities字段要求包含命名实体、对象以及抽象概念如 friendship、career growth并明确要求抽取任何有助于把相关事实链接起来的内容。实体消解Entity Resolution同一实体被不同方式提及时通过模糊名称匹配统一并由共现与时间邻近性强化Alice Alice Chen Alice C. → 同一个人由于消解基于名称相似度相近变体会自动合并。名称不相似的例如昵称与一个无关的正式名不会仅凭名称统一尽管共享的共现实体仍可将它们关联起来。为什么重要你可以问 What do I know about Alice?即使她某些对话中被称为 Alice Chen也能检索到全部内容。消解是判断性决策也可能反向出错在历史很多的 bank 中一个新出现的短名称如果与既有实体相似——且与既有实体已关联的实体共同出现——可能被吸收进既有实体而不是成为独立实体。如果你发现关于新人的事实被挂到了无关实体上实体消解决策机制configuration 文档中 How entity resolution decides 小节解释了比较的是什么、以及哪个设置可以让匹配更严格。源码级实现细节仓库使用 PostgreSQL 的pg_trgm三元组相似度做候选预筛相关阈值在 config.py 中有明确定义与注释HINDSIGHT_API_ENTITY_TRGM_SIMILARITY_THRESHOLD默认0.15pg_trgm相似度下限控制名称需要多接近才会被视为候选更低召回更多近似匹配但 CPU 成本更高更高更严格更省资源HINDSIGHT_API_ENTITY_INTRABATCH_MERGE_SIMILARITY默认0.5同一批次内两个全新名称合并为同一实体的阈值——pg_trgm 忽略非字母数字字符因此装饰性变体得分约 1.0、大小写/后缀变体约 0.75而真正不同的名称约 0.300.5 恰好落在这个空档另有合并最小相似度门槛ENTITY_MERGE_MIN_SIMILARITY等防止新名称仅因共现/新鲜度分数就被并入无关既有实体源码注释指出这是针对把新人事实挂到错误实体问题的门禁。上下文感知消歧如果 Alice 多次与 Google 和 Stanford 一起出现那么一个新提到的 Alice 若也提及这些实体很可能就是同一个人。Hindsight 利用共现模式对常见名称做消歧。实体标签Entity Labels你可以定义一套受控的key:value分类标签词表例如pedagogy:scaffolding、engagement:active它们在 retain 时被抽取并作为实体存储。因为标签会成为实体它们会自动在知识图谱中链接相关记忆并同时改善语义检索与关键词检索。标签还可以可选地写入记忆单元的 tags从而在 recall 与 reflect 中支持标准基于标签的过滤。与普通实体不同标签实体从不按名称相似度合并——不同标签值必须保持不同因此它们只做精确匹配完全排除在模糊名称匹配之外。完整配置参见 bank 配置中的 entity_labels 小节。源码中 entity_labels.py 负责解析标签配置、构建标签抽取 Schema含map、multi-values、multi-text等嵌套字段类型并在 fact_extraction.py 的_extract_map_entities中递归把 LLM 返回的 map 实体展平为key:field:value字符串。构建连接知识图谱的四类边记忆不是孤立的——Hindsight 创建一个包含四类连接的知识图谱。link_creation.py 的模块文档明确写道Handles creation of temporal, semantic, and causal links between facts与文档描述一一对应实体连接Entity Connections所有提及同一实体的事实互相链接。支撑能力Tell me everything about Alice → 检索所有与 Alice 相关的事实。基于时间的连接Time-Based Connections时间上接近的事实被连接日期越近链接越强。支撑能力What else happened around then? → 找到上下文相关的事件。对应实现为create_temporal_links_batch按时间邻近批量建链。基于语义的连接Meaning-Based Connections语义相似的事实被链接即使措辞不同。支撑能力Tell me about similar topics → 找到主题相关信息。对应实现为create_semantic_links_batch基于嵌入向量的余弦相似度阈值建链并支持用预计算的 ANN近似最近邻结果替代事务内查询以提升性能。因果连接Causal Connections因果关系被显式追踪。支撑能力Why did this happen? → 追踪推理链。示例Alice felt burned out ← caused by ← She worked 80-hour weeks源码实现上因果边由 link_creation.py 的create_causal_links_batch写入注释说明 retain 只写入规范的caused_by关系类型数据库与检索路径同时识别历史因果类型保证导入的旧记忆仍可遍历。因果链接是否抽取由配置开关retain_extract_causal_links环境变量HINDSIGHT_API_RETAIN_EXTRACT_CAUSAL_LINKS控制启用后抽取提示词会追加 CAUSAL RELATIONSHIPS 小节教会模型以caused_by类型输出链接Lost job → couldnt pay rent → moved apartment。时间建模两个时间维度Hindsight 追踪两个时间维度事件发生时间When It Happened对事件会议、旅行、里程碑Hindsight 记录其发生时间Alice got married in June 2024 → occurred in June 2024对一般性事实偏好、特征没有具体发生时间Alice prefers Python → 持续性偏好何时得知When You Learned ItHindsight 同时追踪你告知它每条事实的时间。为什么两者都要设想 2025 年 1 月有人告诉你 Alice got married in June 2024历史查询可用What did Alice do in 2024? → 找到这场婚礼新鲜度排序可用近期提及在搜索中获得优先时间推理可用What happened before her marriage? → 找到更早的事件。若没有这种区分旧信息要么无法按日期检索要么被当作无关。源码印证fact_extraction.py 在每条事实落库时设置mentioned_at event_date即对话/文档发生的时间而事件的发生时间来自 LLM 抽取的occurred_start/occurred_end。此外还有一层兜底_infer_temporal_date在 LLM 未提供occurred_start时用正则识别 last night、yesterday、last week 等相对时间表达并换算为绝对日期避免时间信息静默丢失。记忆打标签Tagging Memories标签实现可见性范围控制visibility scoping——当一个 memory bank 服务多个用户、但每个用户只应看到相关记忆时非常有用条目标签Item tags用特定 scope 标记单条记忆文档标签Document tags将标签应用到批次内的所有条目标签过滤Tag filtering在 recall/reflect 时按标签过滤。代码示例参见 Retain API过滤选项参见 Recall API。源码中RetainBatchRequest数据类types.py同时携带contents各条目可带独立 tags与document_tags应用于全部条目的文档级标签对应上述两种粒度。retain() 完成后的产出一次retain()完成后你得到结构化事实——保留含义、情绪与推理统一实体——消解了不同名称变体知识图谱——含实体、时间、语义与因果链接时间锚定——同时支持历史查询与新鲜度查询可选标签——用于 recall 时的过滤。全部存储在你的隔离memory bank中随时可供recall()与reflect()使用。用 Mission 引导抽取默认情况下retain()会抽取内容中所有显著事实。你可以用retain missionretain_mission收窄这个焦点——一段自然语言描述这个 bank 应关注什么e.g. Always include technical decisions, API design choices, and architectural trade-offs. Ignore meeting logistics, greetings, and social exchanges.Mission 与内置规则一起注入抽取提示词——它引导 LLM 而不替换抽取逻辑并且对任意抽取模式concise、verbose、custom都有效。源码实现Mission 为什么放在用户消息里这是一个值得注意的工程决策。fact_extraction.py 中_retain_mission_preamble的注释解释每个 bank 的 mission 刻意不烘焙进系统提示词否则系统提示词将变成 bank 专属迫使 Gemini 上下文缓存为每个 mission 单独建缓存。因此系统提示词保持 bank 无关一份CachedContent服务所有 bankmission 通过_retain_mission_preamble()以 FOCUS — What to retain for this bank (takes priority over the general guidelines) 前导块的形式放在每次请求的用户消息中。抽取模式Extraction Mode需要更细粒度控制时可更换抽取模式模式适用场景concise默认通用——有选择、快速verbose需要更丰富的、带完整上下文与关系的事实custom想完全自己编写抽取规则通过 bank 配置 API 的 retain 配置小节 或环境变量HINDSIGHT_API_RETAIN_MISSION设置retain_mission与retain_extraction_mode。源码中对应配置项见 config.pyENV_RETAIN_MISSION HINDSIGHT_API_RETAIN_MISSION、ENV_RETAIN_EXTRACTION_MODE、ENV_RETAIN_CUSTOM_INSTRUCTIONScustom 模式使用的自定义指令与ENV_RETAIN_EXTRACT_CAUSAL_LINKS。另外注意一个隐藏模式当llm_provider设为none时配置层会强制retain_extraction_mode chunks并禁用 observations/consolidation仅做分块存储reflect 将返回 HTTP 400。当 Mission 排除了文档中的全部内容Mission 收窄的是什么会成为记忆——不产出任何事实的内容就完全不产出记忆。文档本身仍然被存储但recall和reflect检索的是记忆因此零记忆的文档两者都找不到。收紧 mission 因此牺牲的不只是事实创建还有原始来源的可检索性。这是正常结果而非错误retain 会成功操作会被报告为已完成。两个信号可以识别这种情况位置观察什么retain.completedwebhookdata.memory_unit_count: 0Metricshindsight.retain.documents.total{outcomeno_facts}也可以事后审计GET /documents会返回每个文档的memory_unit_count过滤出0即可列出当前不可达的所有文档。抽取并非完全确定——边界文档可能这次跑出事实、下次跑不出。请把零事实当作这份文档需要再处理一遍而不是永久定论。要恢复一份文档放宽 mission 后重新处理即可——已存储的文本会被重新抽取无需重新上传POST /v1/default/banks/{bank_id}/documents/{document_id}/reprocess观察整合Observation Consolidationretain()完成后Hindsight 自动在后台触发观察整合。该过程将新事实与既有观察比对分析当模式浮现时创建新观察用新证据精化既有观察追踪哪些事实支撑每条观察。这是异步发生的——retain()调用立即返回整合在后台运行。实现位于 engine/consolidation/ 目录详细机制参见 Observations 文档。Memory Defense 与来源溯源receipt_uri可选类型string。指向外部回执或共签co-signature系统的可选指针。按原样存储并在针对该条目的任何 Memory Defense 决策中通过security_events.receipt_uri暴露。422 —— Memory Defense 违规当目标 bank 启用了 Memory Defense 且批次中所有条目都被策略拦截时请求返回 422 并附违规列表{ detail: { violations: [ { index: 0, detector: prompt_injection, severity: high, message: ... } ] } }部分拦截的批次返回 200未被拦截的条目正常处理被拦截的条目从结果中静默丢弃其决策记录在security_events中。完整指南参见 Memory Defense。源码印证orchestrator.py 定义了BlockedViolation字段恰为index、detector、message与MemoryDefenseAllBlockedErrorall N items blocked by Memory Defense policy即 422 响应体的直接来源同文件的redact_document_body还处理了超大文档被切分时的一个安全细节——完整原文不经过逐条 screening因此切分前先用策略对全文做一次脱敏防止原文绕过审查直接落入documents.original_text。注释中明确强调这是一项安全控制fail-open 是错误的默认值。延伸阅读Observations — retain 之后知识如何被整合RetrievalRecall — 多策略搜索如何检索相关记忆Reflect — agent 循环如何使用观察Retain API — 完整参数与代码示例。【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考