Hindsight 记忆系统设计指南:打造只记住“该记的事“的 AI Agent
2026/9/13 17:17:32 网站建设 项目流程

Hindsight 记忆系统设计指南:打造只记住"该记的事"的 AI Agent

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

本文基于 Hindsight 仓库中的设计指南 Designing AI Agents That Remember What Matters,系统讲解"选择性记忆"的设计原则:什么样的信息值得持久化、什么应保持局部、什么绝不该落盘,以及如何在 Hindsight 的 retain API、recall API 和 Memory Bank 上把这套原则落成可运行的保留策略、检索策略与质量评审流程。读完本文,你将掌握一套可直接复用的"记忆层设计 + 评估"方法论。

一、快速答案:记忆设计是选择性的,不是穷举式的

原文档给出的三条核心结论,也是整篇设计纪律的骨架:

  • 好的记忆设计是选择性的(selective),而非穷举的(exhaustive)——目标不是让 Agent 记住一切,而是让它记住"那些会改变未来结果的事情";
  • 系统应该保留能改善未来工作的事实、偏好和决策,其余的留在任务局部上下文中;
  • 有用的记忆层要同时平衡三件事:召回质量(recall quality)、作用域(scope)、操作控制(operational control)

这三点在 Hindsight 中有直接对应的实现机制:retain 侧的"提取模式 + mission"决定记什么,Memory Bank 与 tags 决定记在谁的范围里,recall 侧的 budget / max_tokens 决定取回多少。设计纪律不是口号,而是落到 API 参数上的。

二、为什么生产环境需要持久记忆

很多团队在还没有词汇描述它之前就已经遇到了这个问题:Agent 在单次会话里看起来能力很强,换到下一次会话却异常脆弱。这通常意味着系统依赖的是"提示词状态"(prompt state)而非"持久记忆"(durable memory)。

这正是从 demo 走向生产工作流时,"临时上下文"与"持久记忆"的区分至关重要的原因。一个实用的记忆层让 Agent 能够复用之前的工作成果,而不用把整个历史塞进每个提示词。原文档中提到的 Hindsight retain / recall 两个 API 就是这一模式的两端:

  • 想存储持久信号时,用 retain API;
  • 想稍后恢复正确的上下文时,用 recall API。

同一个模式在仓库的手把手示例中反复出现,例如 Claude Code 集成、Codex 集成 等,它们展示的是同一套"retain 存、recall 取"的工作流。

三、常见的三种记忆设计失败

原文档归纳了三类典型失败,它们单独看都不大,但会叠加放大:

  1. 什么都存,导致召回变吵(noisy recall):噪声记忆混进提示词,模型被无关上下文带偏;
  2. 保留得太少,连续性永远得不到改善:Agent 反复"重新入职"(repeated onboarding);
  3. 没人能解释记忆策略到底是什么:记忆层成为黑盒,出了问题无法定位是"没存进去"还是"没取回来"。

这些失败会层层堆叠:一点点的遗忘变成反复的重新入职,反复的重新入职变成返工(rework),返工最终变成信任度下降——用户不再相信 Agent 能把关键上下文带下去。

可验证的对应关系:Hindsight 把"策略可解释"做进了产品形态——保留策略是 bank 配置(retain_missionretain_extraction_mode等显式字段),召回行为可通过trace: true查看各检索臂的过程,这正是针对第 3 类失败的直接解法。

四、更好的记忆层:四条设计要点

更好的设计是选择性的:不试图永远保留每一个 token,而是聚焦"能改善未来工作的信号",并在需要时可被找回。好的系统通常包含这四件事:

  1. 在扩大存储之前先定义保留规则(retention rules);
  2. 区分个人、项目、团队三类记忆(personal / project / team scope);
  3. 构建与 Agent 提问方式匹配的检索(retrieval that matches the questions the agent asks);
  4. 用真实工作流例子评审记忆质量

所以架构比标签重要。一个产品可以宣称有"记忆",行为却像"挂了一个搜索引擎的长提示词"。真正有用的系统必须做到:存得好(retain well)、取得好(retrieve well)、并把结果干净地装回当前上下文

下面逐条对应到 Hindsight 的实现。

五、保留规则:retain 侧的参数化记忆纪律

5.1 retain 请求的关键字段

Hindsight 的 retain API 一次调用接受一个或多个items,每个 item 是一段原始内容(对话、文档、笔记)。关键在于:原始文本不会被原样存储,Hindsight 会切块、送 LLM 提取结构化事实,存的是"事实"而不是"文本"——这正是"选择性"的第一层实现。

最小调用(取自 examples/api/retain.py 中docs:retain-basic片段):

client.retain( bank_id="my-bank", content="Alice works at Google as a software engineer" )

对话场景则作为单个 item保留,消息格式化为姓名 (时间戳): 内容,让 LLM 能把事实归因到正确的说话人、并解析跨消息的时间引用:

# 取自 examples/api/retain.py 的 docs:retain-conversation 片段 conversation = "\n".join([ "Alice (2024-03-15T09:00:00Z): Hi Bob! Did you end up going to the doctor last week?", "Bob (2024-03-15T09:01:00Z): Yes, finally. Turns out I have a mild peanut allergy.", "Alice (2024-03-15T09:02:00Z): Oh no! Are you okay?", "Bob (2024-03-15T09:03:00Z): Yeah, nothing serious. Just need to carry an antihistamine.", "Alice (2024-03-15T09:04:00Z): Good to know. We'll avoid peanuts at the team lunch.", ]) client.retain( bank_id="my-bank", content=conversation, context="team chat", timestamp="2024-03-15T09:04:00Z", document_id="chat-2024-03-15-alice-bob", )

逐字段拆解(均对应 RetainRequest 源码模型):

字段作用与取值
content唯一必填字段。Hindsight 将其切块并送 LLM 提取事实,存储的是结构化事实而非原文。
timestamp三种形态:省略/null→ 取入库时间;ISO 8601 字符串 → 使用给定时间;"unset"不带任何时间戳,适合参考文档、书籍等"无真实事件时间"的内容。时间戳会被注入事实提取提示词,作为解析"上周一"这类相对时间表达的锚点;也决定了时间召回查询(如"去年春天发生了什么")能否正确工作。
context简短的来源/场景标签,如"team meeting""slack""support ticket"。它会直接注入 LLM 提示词,主动塑造事实提取的方向。同一句话在不同 context 下会产生不同的记忆。文档明确说:一致地提供 context 是提高记忆质量杠杆率最高的手段之一
metadata任意键值对(字符串存储),如{"source": "slack", "channel": "engineering"}。既参与提取提示词,也会挂在每个记忆单元上、随 recall 结果原样返回,可做客户端过滤。值为null的键会被丢弃。
document_id让 retain幂等的钥匙:若同 ID 文档已存在,旧文档及其关联记忆会先被删除再重新处理。会话增长后只需带着完整新内容和相同document_id再次 retain,即可安全覆盖而不会积累重复记忆。
operation_id客户端提供的 UUID,用作异步 retain 操作的身份标识:相同 ID 重提只返回原操作、不产生新工作,避免超时重试造成重复入库;复用他人 ID 返回 409。

5.2 提取模式:从"默认精选"到"零 LLM 成本"

原文档的第一条设计要点——"先定义保留规则再扩大存储"——在 Hindsight 中对应 bank 级的 retain 配置(环境变量与层级覆盖详见 configuration):

  • retain_mission:一段自然语言,描述"这个 bank 在提取时应该关注什么"。它与内置提取规则一起注入提示词,引导聚焦但不替代提取逻辑。文档给出的示例风格是:

    e.g. Always include technical decisions, API design choices, and architectural trade-offs. Ignore meeting logistics, greetings, and social exchanges.

    这就是"什么算值得记、什么该忽略"的策略落点。

  • retain_extraction_mode:控制事实提取的激进程度,五种模式各对应一种"存储 vs 成本 vs 选择度"的取舍:

模式说明
concise(默认)选择性提取——只保留值得长期记忆的事实
verbose每条事实捕获更多细节;更慢、消耗更多 token
custom通过retain_custom_instructions完全替换内置规则,适合需要严格"包含/排除"逻辑的场景
verbatim每个 chunk 原文存为一条记忆;LLM 只提取实体、日期等元数据
chunks每个 chunk 存为一条记忆且完全不调用 LLM,零 LLM 成本;只有调用方提供的实体可用

对应环境变量为HINDSIGHT_API_RETAIN_EXTRACTION_MODE,取值concise/verbose/verbatim/chunks/custom(默认concise),custom模式需搭配HINDSIGHT_API_RETAIN_CUSTOM_INSTRUCTIONS。从 configuration 文档 可见,这些配置还可以按 retain strategy 分层覆盖——不同来源的内容用不同的保留策略,是"个人/项目/团队"之外的第四维细分。

  • retain_chunk_size(默认3000)与retain_structured_chunk_size(默认继承前者):控制切块粒度。chunk 越大 LLM 调用越少但可能降低长文本提取质量;对 JSONL 日志、聊天记录这类"单条记录不应被切开"的结构化内容,可把retain_structured_chunk_size调大以保持整条记录完整。

  • entity_labels:定义 retain 时提取的受控标签词表(key:value形式的实体)。标签成为实体后会自动在知识图谱里把同类记忆连起来,同时改善语义/BM25 检索;对设置了tag: true的组,提取出的标签还会写为记忆单元上的 tag,可在 recall 里用tags/tags_match过滤。

从源码结构看,这些 bank 配置字段与请求模型都在 hindsight-api-slim/hindsight_api/api/http.py 中定义,配置解析与校验集中在 API 层,保证了"策略必须显式、可审计"这一设计前提。

5.3 "绝不该持久化"的边界:Memory Defense

原文档说记忆设计需要一条规则:"什么绝不该持久化"。Hindsight 的 Memory Defense 模块就是这条边界的落地:它用一套 45 条正则模式从 retain 内容中清除密钥与 PII,每个命中项被替换为[REDACTED:type]标记后才进入记忆单元与文档正文,且按 bank 配置、默认关闭

最小策略(配置在 bank 的memory_defense字段,创建时设置或之后通过PATCH /v1/{tenant}/banks/{bank_id}/config更新):

{ "memory_defense": { "enabled": true, "rules": [ { "on": "sensitive_data", "action": "redact" } ] } }

开源版提供两种动作:

  • redact:把命中的密钥替换为[REDACTED:type]标记,存储清洗后的记忆——之后所有 recall、导出、reflect 都看不到原始秘密;
  • block:整条丢弃任何包含命中的 item;若一次 retain 的所有 item 都被 block,调用返回422

覆盖类别包括 AI/LLM 提供商密钥(anthropic_keyopenai_keygoogle_api_key等)、云厂商凭据(aws_access_key等)、源码管理与 CI token(github_tokengitlab_patnpm_token等)、支付处理方密钥(stripe_secret等)。

两个必须知道的边界:策略只影响设置后新的 retain 调用,不会回溯扫描已有记忆;若某 bank 订阅了对应 webhook,被 redact / block 的 item 会触发memory_defense.triggered事件(payload 含动作、document ID、命中的模式标签),可接 SIEM 或 Slack 告警。

六、作用域:个人 / 项目 / 团队记忆的隔离与共享

第二条设计要点——区分 personal、project、team 记忆——在 Hindsight 中由两层机制实现:

6.1 Memory Bank:硬隔离

Memory Bank 是完整隔离的存储单元,内含记忆、文档、实体、实体间关系(知识图谱)和 Directives(reflect 时 Agent 必须遵循的硬规则)。不同 bank 之间完全不可见——一个 bank 里存的记忆,另一个 bank 永远看不到。

两个实操细节值得注意:

  • bank 无需预创建,首次写入时按默认配置自动创建;
  • 读取不存在的 bank 返回404(而非空结果)——bank_id拼错、改名或删除会被明确报错,而不是伪装成"这个 bank 没有记忆"。对监控系统尤其重要:对缺失 bank 的GET .../stats会响亮地失败,而不是返回一组看起来健康实为空银行的零值计数器。

典型映射:个人记忆 → 每个用户一个 bank;项目记忆 → 每个项目一个 bank;团队共享 → 团队级 bank

6.2 Tags 与 tags_match:bank 内部的软作用域

同一 bank 内部还需要更细的作用域,这由 item 级tags和 recall 侧的tags_match组合完成。retain 示例 中docs:retain-with-tags片段展示了给每条记忆打作用域标签:

client.retain_batch( bank_id="my-bank", items=[ { "content": "User Alice said she loves the new dashboard", "tags": ["user:alice", "feedback"], "document_id": "user_feedback_001" }, { "content": "User Bob reported a bug in the search feature", "tags": ["user:bob", "bug-report"], "document_id": "user_feedback_002" } ] )

RecallRequest 源码 中tags_match提供六种语义,这是"个人/项目/团队"三档在单 bank 内共享时的精确工具:

取值语义
any(默认)OR 匹配,包含无标签记忆
allAND 匹配,包含无标签记忆
any_strictOR 匹配,排除无标签记忆
all_strictAND 匹配,排除无标签记忆
exact完整标签集合相等;不带 tags(或空列表)+exact选中"空全局作用域",只匹配无标签记忆(即 observation_scopes=shared 写入的共享记忆)
tag_groups布尔复合过滤(AND 连接组,组内支持 and/or/not 叶子),还支持resolve='fuzzy'按三元组相似度模糊匹配,'typsecript'也能命中'typescript'

注意tagstag_groups互斥(模型校验器 直接拒绝同时提供),复合过滤场景只能用后者。

多 Agent 系统需要"共享但有作用域的上下文"——这正是原文档列举的典型工作流之一。落地形态是:一个团队 bank + 按用户/项目的 tag 作用域 + 需要全局共享的结论(如整合后的 observation)写无标签的全局作用域,再用exact精确取回。

七、与提问匹配的检索:recall 侧的参数化设计

第三条设计要点——"检索要匹配 Agent 会问的问题"——由 recall API 承担。recall 会并行运行四条检索臂——语义相似度、关键词(BM25)、图谱遍历、时间——再融合排序成单一排名列表,返回的是结构化事实而非原始文档。基本调用(取自 examples/api/recall.py):

response = client.recall(bank_id="my-bank", query="What does Alice do?") # response.results 中每条 RecallResult 含: # text(提取出的事实)、type(world/experience/observation)、 # context / metadata / tags / entities、 # occurred_start / occurred_end / mentioned_at、 # document_id / chunk_id

关键参数与源码 RecallRequest 的对应关系:

参数作用与默认值
query唯一必填。自然语言问题会同时驱动四条检索臂:嵌入后做语义检索、分词后做 BM25、作为图谱遍历的起点、解析时间表达。超过 500 token 的 query 会被拒绝。
types控制搜索哪类事实:world(客观事实)、experience(事件与对话)、observation(由多条记忆整合去重、有证据支撑的信念)。省略则三类全搜;每类独立跑完整四臂管线,缩小types同时降低结果集与查询成本。
prefer_observations默认false。开启后:若某 observation 由原始事实整合而来,则丢弃该原始事实、observation 取代它,腾出的名额由次优结果回填——"要全部"与"不重复"兼得。仅当同时请求observation与至少一个原始类型时生效。
budgetlow/mid(默认)/high,控制检索深度与广度。快速简单查找用low,日常用mid,需要间接关联或穷尽覆盖用high
max_tokens返回事实的合计 token 预算,默认4096,只对每条事实的text字段计数。重排后按相关度顺序装填直至预算耗尽;放不下剩余预算的事实会被跳过而不终止选择。设计哲学是"Hindsight 为 Agent 而生,Agent 以 token 思考而非结果条数"——max_tokens就是"你想把上下文窗口的多少分给记忆"。
tags/tags_match/tag_groups见上节 6.2,作用域过滤。
temporal_window显式指定时间臂的窗口(替代从 query 文本解析日期),适合日期选择器或 Agent 已自行解析"上季度"的场景。注意它是排序而非过滤:窗口外的记忆仍会返回。
min_scores分阶段分数下限(semantic/keyword/reranker/final)。rerankerfinal是重排后的硬过滤,适合做"拒答"判断;文档同时警告重排器绝对分数跨查询未校准,慎用。
tracetrue时返回各检索臂的中间过程,用于评审"为什么取回/没取回这条"。

两个值得记住的边界行为(均来自 recall 文档 的说明):

  • 有命中就不会返回空:即使最相关事实也超出预算,它会完整返回并超预算,而不是截断到句子中间——因为"空列表"会被读成"这个 bank 没有这条记忆",而"截断的事实"是记忆从未做出过的陈述。唯一的例外是max_tokens=0,它表示"故意不要事实",是只取 chunks 的方式;
  • observations 的定位:observation 是后台在 retain 之后自动创建和维护的、带证据引用的整合信念,新证据到来时是"精化"而非"覆盖"。它是记忆系统"连续性随时间改善"的机制基础。

max_tokens与原文档评估框架第 5 条——"召回的上下文是否足够简洁,能帮助而非干扰"——是一一对应的:召回侧的简洁性由预算参数保证,而不是靠事后裁剪提示词

八、原文档列举的三类高价值工作流

原文档指出影响最明显的工作流场景,对照 Hindsight 的能力可以给出更具体的落地形态:

  1. 产品团队从零设计记忆架构:先定 bank 划分(personal/project/team),再写retain_mission与提取模式,最后调 recall 预算;
  2. 工程团队从"仅提示词"系统迁移到持久工作流:把每次会话的关键输出 retain 到项目 bank,下一次会话开始时 recall 注入,替换手工维护的"上下文接力";
  3. 需要共享但有限作用域上下文的多 Agent 系统:单 bank + tag 作用域 + 全局共享 observation 的组合(见第六节)。

九、五步评估框架:在自己的技术栈上验证

原文档给出的评估框架是全文最可操作的部分,逐条对应到 Hindsight 的验证手段:

  1. 找出"一件 Agent 应该明天还记得、因为今天学到了"的东西——确定最小验证信号;
  2. 决定这个信号属于个人、项目还是共享记忆——映射到 bank 选择或 tag 作用域;
  3. 验证系统能有意地(intentionally)保留它——retain 后检查返回的提取结果,或调trace确认事实确实落库、类型与时间正确;
  4. 测试它在正确的后续工作流中能被取回——用目标工作流里真实会问的 query 做 recall,检查排名与覆盖;
  5. 检查召回的上下文是否足够简洁,能帮助而非干扰——调budget/max_tokens直到注入提示词的体积可控,且不丢关键事实。

好的记忆系统之所以更容易被信任,正在于存储与召回模型清晰到可以被检查:策略是显式配置,行为可用trace复盘,作用域可用tags_match精确复现。

十、FAQ

Agent 应该记住多少?只记住"让未来工作变得更好或更正确"的信息。落到参数上就是retain_mission里写清包含/排除规则,配合concise这类选择性提取模式。

记忆设计应该从存储开始,还是从检索开始?从工作流开始,从"Agent 之后要回答的问题"开始。因为检索问题定义了保留什么才有意义——Hindsight 的文档组织也是如此:retain 架构 与 recall 架构 都以"信号 → 问题"为主线。

策略可以随时间演化吗?可以。强记忆系统会随着团队弄清"什么值得保留"而持续改进。Hindsight 的策略载体(bank 配置、retain strategy 分层覆盖、memory defense 规则)都是可热更新的配置,不需要重建存储。

结语:记忆层 = 保留策略 × 作用域 × 检索预算 × 可评审性

回看原文档的骨架,"记住重要的事"不是一个功能,而是一组可审计的纪律:先用 retain 侧的 mission、提取模式、memory defense 决定"记什么、不记什么";再用 bank 与 tag 作用域决定"记给谁";然后用 recall 的 budget、max_tokens、tags_match 决定"取回多少、取回哪份";最后用 trace 与真实工作流评审"记取得对不对"。这套纪律在 Hindsight 仓库中全部有显式的配置字段与 API 参数承载——策略写在配置里而不是散落在提示词里,正是它区别于"挂了搜索引擎的长提示词"的关键。

进一步阅读(均为仓库内路径):

  • Retain API 参考 / Recall API 参考
  • Retain 架构详解 / Recall 架构详解
  • Memory Banks 参考 / 配置与层级覆盖
  • Memory Defense / Quick Start
  • 请求模型源码:RecallRequest、RetainRequest
  • 可运行示例:examples/api/retain.py、examples/api/recall.py

【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询