拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Hindsight Recall 检索管线全解析:TEMPR 四路召回、RRF 融合与跨编码器精排

Hindsight Recall 检索管线全解析TEMPR 四路召回、RRF 融合与跨编码器精排【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight本文基于 Hindsight 官方开发者文档 Recall: How Hindsight Retrieves Memories深入讲解recall()的完整检索管线四路并行召回策略语义 / 关键词 / 图谱 / 时序、Reciprocal Rank Fusion 融合、Cross-Encoder 重排与三段式乘性加分、token 预算管理与budget/max_tokens调优策略。文中所有关键结论均结合 search/fusion.py、search/reranking.py 与 config.py 等源码印证读完后你既能按官方推荐配置调参也能理解每个分数在管线中的确切来源。记忆召回面临的挑战当你调用recall()时Hindsight 会使用多种搜索策略并行查找最相关的记忆而无需你精确措辞。但不同形态的查询对检索方式的要求完全不同Alice works at Google→ 需要精确的名字匹配Where does Alice work?→ 需要语义理解What did Alice do last spring?→ 需要时间推理Why did Alice leave?→ 需要因果关系追溯没有任何单一检索方法能同时擅长这四类查询。Hindsight 的解法是TEMPR—— 四种互补的策略并行运行最终通过融合与重排得到统一排序四种检索策略语义搜索Semantic它做什么理解词语背后的含义而非词语本身。最适合概念匹配Alices job → Alice works as a software engineer同义改写Bobs expertise → Bob specializes in machine learning近义词meeting 匹配 conference、discussion、gathering为什么重要你可以自然地问问题而不必匹配完全一致的关键词。关键词搜索Keyword / BM25它做什么精确命中术语与专有名词即使其拼写独特。最适合专有名词Google、Alice Chen、MIT技术术语PostgreSQL、HNSW、TensorFlow唯一标识符URL、产品名、特定短语为什么重要确保不会漏掉提到特定名称或术语的结果——哪怕它们在语义上离查询很远。BM25 后端可插拔Hindsight 提供五个可插拔的 BM25 后端通过环境变量HINDSIGHT_API_TEXT_SEARCH_EXTENSION选择。这一点在 config.py 中有直接印证合法取值被硬校验为(native, vchord, pg_textsearch, pgroonga, pg_search)默认值为native。后端底层实现是否兼容 CitusnativePostgreSQL 原生tsvectorts_rank_cd注意是 TF-IDF并非真正的 BM25是vchordvchord_bm25扩展否pg_textsearchTimescale 的pg_textsearch扩展否pgroongaPGroongaGroonga全文扩展使用TokenBigram多语言分词器否pg_searchParadeDBpg_search扩展分词器可配置如jieba、chinese_compatible、ngram通过HINDSIGHT_API_TEXT_SEARCH_EXTENSION_PG_SEARCH_TOKENIZER指定是如果你的集群是水平扩展的 PostgresCitus而你又需要真正的 BM25 排序pg_search是唯一选项。仓库中提供了可直接使用的示例pg_search docker-compose 示例其中 Dockerfile 构建了带pg_search扩展的 Postgres 镜像。相关集成测试见 test_knowledge_bm25_dispatch.py 与 test_pg_extensions.py。图谱遍历Graph Traversal它做什么沿着实体之间的连接找到间接相关的信息。最适合间接关系What does Alice do? → Alice → Google → Google 的产品实体探索Bobs colleagues → Bob → 同事 → 共同项目多跳推理Alices teams achievements为什么重要能够检索到那些在语义或词汇上与查询都不相似、但通过知识图谱结构上相连的事实。示例即使 Alice 和她的经理从未在同一个句子里被提到图谱遍历也能通过共享项目或团队关系找到她的经理。时序搜索Temporal它做什么理解时间表达按事件发生的时间过滤。最适合历史查询What did Alice do in 2023?时间范围What happened last spring?相对时间What did Bob work on last year?先后关系What happened before Alice joined Google?工作机制当查询包含时间引用时Hindsight 会将其解析为一个日期窗口然后检索时间与该窗口重叠的记忆。窗口内的候选选择以对查询的语义相关性为准——而不是以新近程度为准——因此最相关的窗口内记忆不会因为别的事实恰好更新而被丢弃。随后选择结果会沿窗口范围铺开窗口被划分成若干时间桶每个非空桶优先取其最强匹配。这样2023 年发生了什么这类查询能覆盖全年而不是聚集在最密集的那一段。时间同时也是一个打分信号——越靠近窗口中心的记忆获得小幅加分见下文时序接近度信号。为什么重要支持既精确又有代表性的历史查询。对于时间戳密集聚集的记忆库例如大批量数据以同一日期入库依然保持快速且有意义由于选择以相关性优先即使时间窗口覆盖了库中大部分记忆返回的也仍是最佳匹配而非任意切片。结果融合Result Fusion四路策略运行完毕后结果会被融合出现在多个策略中的记忆排名更高共识机制排名比分数更重要对不同打分体系鲁棒最终结果由一个理解查询-记忆交互的神经网络模型重排融合为什么重要既语义相似又提到了正确实体的事实会排在仅语义相似的事实之前。为什么需要多种策略以查询What did Alice say about Python last spring?为例语义找到关于 Alice 编程观点的事实关键词确保 Python 确实被提到图谱连接 Alice → 编程语言 → 相关实体时序过滤到去年春天这一时间段四者的融合让你恰好拿到想要的结果——而任何单一策略都做不到。Token 预算管理Hindsight 是为 AI Agent 而非人类设计的。传统搜索系统返回 top-k 条结果但 Agent 不按条数思考——它们按 token 思考。Agent 的上下文窗口以 token 度量Hindsight 也正是用 token 来度量结果。工作方式优先选取排名靠前的记忆直到 token 预算耗尽为止你指定上下文预算Hindsight 用最相关的记忆填满它你控制的参数max_tokens返回多少记忆内容默认4096 tokensbudget搜索深度档位low / mid / hightypes按 world、experience、observation 或全部过滤tags按可见性标签过滤记忆tags_match标签匹配方式完整选项见 Recall API扩展上下文Chunks记忆是蒸馏后的事实——简洁但可能丢失细节。当 Agent 需要更深的上下文时可以可选地取回原始材料Chunks返回生成每条记忆所用的原文——当蒸馏后的事实丢失了关键细节时特别有用Memory: Alice prefers Python over JavaScript Chunk: Alice mentioned she prefers Python over JavaScript, mainly because of its data science ecosystem, though she admits JS is better for frontend work and shes been learning TypeScript lately.配合max_chunk_tokens使用include_chunksTrue来控制 chunk 的 token 预算。在需要逐字引用或对语境敏感的场景如的项目上Alice 到底是怎么说的中非常有用。调优 Recall质量 vs 延迟不同用例需要在召回质量与响应速度之间做不同取舍。两个参数控制这一点Budget搜索深度控制 Hindsight 探索记忆库的彻底程度——影响图谱遍历深度、候选池大小和 Cross-Encoder 重排Budget最适合权衡low快速查找、简单查询快可能漏掉间接连接mid大多数查询均衡覆盖良好速度合理high需要深度探索的复杂查询彻底但更慢示例What did Alices managers team work on? 受益于 high budget因为它需要遍历多跳Alice → 经理 → 团队 → 项目并评估更多候选。Max Tokens上下文窗口大小控制返回多少记忆内容Max Tokens约相当于最适合权衡2048~2 页聚焦回答、快速 LLM记忆更少、更快4096默认~4 页均衡上下文覆盖良好、标准8192~8 页全面上下文记忆更多、LLM 更慢示例Summarize everything about Alice 受益于更高的 max_tokens 以纳入更多事实。两个独立维度Budget 与 max_tokens 控制 recall 的不同侧面参数控制什么延迟影响示例Budget探索记忆的彻底程度搜索耗时High budget 找到 Alice → 经理 → 团队 → 项目Max Tokens返回多少上下文LLM 处理耗时更高 tokens 向 Agent 返回更多记忆二者相互独立。常见组合BudgetMax Tokens用例highlow深度搜索只返回最佳结果lowhigh快速搜索返回找到的所有结果highhigh综合性研究查询lowlow快速聊天机器人响应推荐配置用例BudgetMax Tokens原因聊天机器人回复low2048快速响应聚焦上下文文档问答mid4096覆盖与速度均衡研究查询high8192全面多跳推理实时搜索low2048最小化延迟打分与排序深潜Scoring Ranking Deep Dive本节精确说明 Hindsight 如何把原始检索结果变成最终排序列表。管线分三个阶段RRF 融合、Cross-Encoder 重排、综合打分乘性加分。阶段 1Reciprocal Rank FusionRRF所有策略并行运行后结果使用 Reciprocal Rank Fusion 合并。RRF 通过奖励在多策略中都排名靠前的条目来合并多个排序列表且不依赖原始分数不同检索方法的分数不可比。实现位于 fusion.py公式score(d) Σ 1 / (k rank_i(d)) i其中k 60平滑常数——防止榜首条目独占rank_i(d) 文档d在策略i中的位置1-indexed求和遍历所有d出现的策略在源码中四条检索臂的固定命名依次为[semantic, bm25, graph, temporal]见 fusion.py每个 doc 都会记录source_ranks中各臂的排名供后续 trace 与策略加权使用。RRF 内部四路策略等权—— 融合只看排名位置不看来源因此没有任何策略获得隐式乘数。不过你可以用HINDSIGHT_API_RECALL_STRATEGY_BOOSTS刻意偏向某一来源——该 boost 作用于两个独立阶段重排预过滤截断之前和重排之后而不在上述 RRF 融合内部。从源码 recall_boost.py 的模块注释可以看到设计细节第一阶段在排名空间中提升被加权臂把其排名除以档位除数再参与 RRF 贡献1/(k rank/divisor)而非直接放大分数——早期按分数乘权重的做法会与紧随其后的硬截断RERANKER_MAX_CANDIDATES严重冲突导致加权臂填满全部候选槽位。档位与权重的对应关系在 recall_boost.py 中固化low2.0/0.05、medium4.0/0.2、high8.0/0.5分别是 rank_divisor 与 additive。为什么用 RRF 而不是原始分数合并每种检索策略产生的分数尺度不同余弦相似度、BM25 tf-idf、图谱激活度。这些分数不可比——BM25 分数 12.5 和余弦相似度 0.85 含义完全不同。RRF 只使用排名位置绕开了这个问题无需校准即可在任何打分体系上保持鲁棒。示例某记忆在语义中排 #1、在 BM25 中排 #5RRF score 1/(601) 1/(605) 0.0164 0.0154 0.0318某记忆仅在语义中排 #1RRF score 1/(601) 0.0164第一条记忆排名更高因为它有跨策略的共识。阶段 2Cross-Encoder 重排RRF 给出的是不错的初始排序但它基于位置而非对查询-文档的深度理解。Cross-Encoder 把每个候选与查询作为一对进行评估产出相关性分数。实现位于 reranking.py。预过滤重排前候选先按 RRF 分数裁剪到前300条以控制计算成本。这由HINDSIGHT_API_RERANKER_MAX_CANDIDATES配置环境名常量见 config.py。若设置了HINDSIGHT_API_RECALL_STRATEGY_BOOSTSboost 在此之前生效使受偏好的来源的候选更可能存活截断。该 boost 在排名空间中提升受偏好的臂其排名先除以档位除数再计算 RRF 贡献而非放大分数因此即使合并池远超上限它也能深入到该臂更深处而不挤掉其他臂的头部命中。当请求trace: true时rerank_prefilter阶段会报告保留/丢弃的候选数、生效的上限、当前 boosts 以及幸存者的逐臂构成。为什么 RRF 之后还要重排RRF 是基于位置的——它知道某条记忆在多策略中排名都不错但从未真正同时读过查询和记忆。Cross-Encoder 做的是把查询与每个候选成对输入基于二者的完整交互给出相关性分数。它能抓住基于位置的融合漏掉的细节例如一条记忆因为在关键词搜索中命中了某个常见词而排 #1实际上与查询意图无关。一个值得注意的实现细节送入 Cross-Encoder 的文档文本会被增强——若记忆带上下文拼接为context: text若带发生日期还会前置[Date: June 5, 2022 (2022-06-05)]双格式日期前缀见 reranking.py让模型具备时间感知。分数归一化Cross-Encoder 输出原始 logits可能为负。已经落在 [0, 1] 区间内的分数——由校准过的外部 API 重排器如 Cohere、SiliconFlow、ZeroEntropy、Alibaba、Jina返回——会被原样透传以保留其绝对置信度落在 [0, 1] 之外的原始 logits 则用 sigmoid 归一化到 [0, 1]CE_normalized 1 / (1 e^(-raw_logit))源码中该判断位于 reranking.py若所有分数都在 [0,1] 内则直接透传否则逐条过 sigmoid。此外 NaN 分数会被清零避免经 Pydantic 序列化为 JSONnull破坏客户端。批处理候选分批打分——本地重排器每批32 对TEI 每批128 对。提示没有 Cross-Encoder 怎么办在不带 Cross-Encoder 的环境例如 slim 镜像且无外部重排器中系统回退到基于 RRF 的分数候选根据其 RRF 排名被分配 [0.1, 1.0] 区间内的合成分数见 reranking.py 中is_passthrough_reranker分支——按 RRF 排名线性映射到 [0.1, 1.0]使下文的综合打分加分依然有意义。阶段 3综合打分乘性加分 Boosts归一化后的 Cross-Encoder 分数会被三个乘性加分调整纳入 Cross-Encoder 看不到的信号新近度recency、时序接近度temporal proximity与证据强度proof count。核心实现是 apply_combined_scoring。为什么用乘性而不是加性加性加分如CE 0.1 × recency会给每个候选相同的绝对加成与相关性无关。一条几乎不相关的记忆可能仅凭新近度就跃过一条高度相关的记忆。乘性加分让调整量与基础相关性成正比——对高相关记忆 10% 的绝对变化大于对低相关记忆 10%。这保证次要信号永远不会压倒主要的相关性判断。公式final_score CE_normalized × recency_boost × temporal_boost × proof_count_boost每个加分以 1.0中性为中心由 alpha 限定其摆动幅度boost 1 α × (signal - 0.5)加分项α最大调整奖励什么新近度 Recency0.2±10%新近记忆优于陈旧记忆时序接近度 Temporal0.2±10%靠近查询时间窗口的记忆证据数 Proof count0.1±5%有更多证据支撑的观察这三个 alpha 常量在源码中明确定义于 reranking.py_RECENCY_ALPHA 0.2、_TEMPORAL_ALPHA 0.2、_PROOF_COUNT_ALPHA 0.1。新近度信号Recency signal默认按 365 天窗口从记忆发生日期线性衰减recency clamp(1.0 - days_ago / 365, 0.1, 1.0)查询时间戳当天的记忆 recency 为 1.010% 加分早于查询时间戳 6 个月的记忆约 0.5中性超过一年的记忆为 0.1-8% 惩罚。未提供query_timestamp时使用服务器当前时间没有日期的记忆得 0.5中性——不加分也不减分。源码 compute_recency_decay 显示该衰减函数实际支持三种模式linear为默认另有exponential指数衰减与none完全禁用且线性窗口天数默认 365与指数半衰期默认 90 天均可配置。另一个值得留意的细节对粗粒度日期如2015 年被存为 2015-01-01 → 2015-12-31 的完整日历周期的记忆源码会从周期的末端起算新近度并封顶在中性值避免把2026 年的峰会这类仍在进行中的周期误判为过期见 reranking.py。时序接近度信号Temporal proximity signal仅当查询包含时间引用如 last spring、in 2023时生效。它度量记忆日期与查询时间窗口中心的距离temporal_proximity 1.0 - min(days_from_center / (window_days / 2), 1.0)位于窗口中心的记忆得 1.010% 加分位于边缘的得 0.0-10% 惩罚。非时序查询中所有记忆得 0.5中性。证据数信号Proof count signal对 observation 类记忆按对数曲线奖励有更强证据支撑的记忆proof_norm clamp(0.5 ln(proof_count) / 10, 0.0, 1.0)证据数proof_norm加分10.5中性30.611.1%100.732.3%1501.05%上限对应源码见 reranking.pyproof_count为 None非 observation 类事实时取中性 0.5使 world/experience 类事实的该乘子坍缩为 1.0。最大合成区间当所有加分都取到极端时最佳情况×1.10 × 1.10 × 1.05 ≈27%最差情况×0.90 × 0.90 × 0.95 ≈-23%这些加分刻意保守——它们只微调排序不会推翻 Cross-Encoder 的相关性判断。阶段 4Token 截断打分完成后结果按final_score排序自顶向下选取直到max_tokens预算耗尽。只有记忆文本计入预算——元数据免费。如果某条结果超出剩余预算会被跳过打包继续尝试下一条因此单条超长事实不会让你丢失排在它后面的更短结果。若一条都放不下排名第一的结果仍会被完整返回——匹配到了东西的查询永远不会得到空列表。Budget 如何映射到管线参数budget参数low/mid/high控制搜索深度——每个策略考虑多少候选。每档映射为一个贯穿所有管线阶段的recall budget数值其默认值在 config.py 中定义BudgetRecall budgetfixed 模式环境变量覆盖low100HINDSIGHT_API_RECALL_BUDGET_FIXED_LOWmid300默认HINDSIGHT_API_RECALL_BUDGET_FIXED_MIDhigh1000HINDSIGHT_API_RECALL_BUDGET_FIXED_HIGH该 recall budget 在管线中的流转方式管线阶段recall budget 的用法语义搜索SQL 中LIMIT recall_budget同时 ANN 候选列表按查询同步调整大小pgvector 上即hnsw.ef_search受该设置上限 1000 约束BM25 搜索SQL 中LIMIT recall_budget图谱遍历最多探索 recall_budget 个节点时序铺开最多通过链接激活 recall_budget 个节点结果筛选取 top recall_budget × 2 条结果进入 token 过滤重排预过滤300 条候选独立于budget——它是单独的旋钮HINDSIGHT_API_RERANKER_MAX_CANDIDATES。自适应预算Adaptive budgeting另一种预算模式让 recall budget 随max_tokens缩放而非使用固定值recall_budget clamp(max_tokens × ratio, min, max)Budget比率环境变量覆盖lowmax_tokens 的 2.5%HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_LOWmidmax_tokens 的 7.5%HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_MIDhighmax_tokens 的 25%HINDSIGHT_API_RECALL_BUDGET_ADAPTIVE_HIGH结果被截断在20HINDSIGHT_API_RECALL_BUDGET_MIN下限与2000HINDSIGHT_API_RECALL_BUDGET_MAX上限之间。默认模式为fixed可用HINDSIGHT_API_RECALL_BUDGET_FUNCTIONadaptive启用 adaptive取值校验逻辑见 config.py。图谱打分细节Graph Scoring Detail图谱遍历link expansion对每个候选加性组合三个独立信号信号打分公式取值范围实体重叠 Entity overlaptanh(shared_entity_count × 0.5)[0, ~1.0]语义链接 Semantic link预计算的 kNN 链接权重[0.7, 1.0]因果链接 Causal link因果链接权重[0, 1.0]graph_score entity_score semantic_score causal_score ∈ [0, 3]加性组合奖励证据汇聚——通过多种信号类型连接到查询的记忆比仅靠单一强信号连接的排名更高。实体分为什么用 tanh原始共享实体数是无界的——user 这类高扇出实体可能产生 50 的计数淹没另外两个信号。tanh(count × 0.5)自然饱和前几个共享实体权重很大1→0.462→0.763→0.91之后的贡献递减使实体信号与语义、因果分数一起保持在 [0, 1] 内。这里为什么用加性而不是乘性与综合打分中的乘性加分不同图谱信号是独立的证据通道而不是对基础分数的调整。一条记忆可能仅通过因果链接与查询相连无共享实体、无语义相似——乘性组合会直接把它清零。加性打分让每个信号独立贡献跨策略的排序由外层 RRF 融合负责。实体分示例与查询共享 1 个实体的记忆得分 tanh(0.5) ≈ 0.46共享 2 个得 tanh(1.0) ≈ 0.763 个及以上饱和在 0.91 以上。小结从查询到最终列表的完整链路综合文档与源码一次recall()的完整数据流为四路并行召回semantic / bm25 / graph / temporal各自受 recall budget 约束RRF 融合k60等权按排名位置合并见 fusion.py策略 boost可选HINDSIGHT_API_RECALL_STRATEGY_BOOSTS先于截断、后于重排两处生效见 recall_boost.pyCross-Encoder 重排预过滤至 300 条候选sigmoid 归一化或 [0,1] 透传见 reranking.py乘性加分recency / temporal / proof count最大合成区间约 27% / -23%Token 预算截断自顶向下填充max_tokens超预算者跳过而非整批放弃。想进一步深入可以继续阅读官方文档的Retain记忆如何被存储、Reflectdisposition 如何影响推理、Recall API代码示例、参数与标签过滤以及 配置文档 中上述所有环境变量的完整说明。【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门