第5章

Recall as Algebra——读路径的代数化与预算化召回

程乾 · 2026-05-24 · 约 8 分钟

5.1 为什么读路径需要被重新设计

前三章几乎全部聚焦于"写入侧"——记什么、怎么记、什么时候记、什么时候挤。但记忆系统的价值最终是在读取时兑现的。如果检索只有一个 search(query) 黑盒,写入侧再精妙的价值筛选也落不了地——精心选择"记什么"所保留的高价值 facet,如果无法被精确地捞进当前 prompt,其价值就是潜在的而非实现的。

现有方案的检索各有各的黑盒。mem0 的 search 是纯向量 top-k——语义近似是唯一维度。MemoryOS 按层拉取——层是固定时间窗口,与当前任务需求无关。GraphRAG 按问题路由社区层级——社区是 Louvain 算法的统计副产物,与"用户当前关心什么"没有必然联系。一旦你的需求稍微偏离设计者预想——比如"我只想看和公司 X 有关的、有争议的、过去一周新增的记忆"——你就发现所有检索 API 的参数都不够用。

这种困境的理论根源可以追溯到 Codd 在 1970 年提出的物理数据独立性原则。Codd 在关系模型的奠基论文中写道:"Future users of large data banks must be protected from having to know how the data is organized in the machine" [14]。关系代数的五个基本算子——σ(选择)、π(投影)、×(笛卡尔积)、∪(并)、−(差)——之所以强大,不是因为它们各自有多复杂,而是因为它们满足封闭性(closure):每个算子的输入和输出都是关系,因此可以任意组合嵌套。这种封闭性使得任意复杂的查询都可以由有限的基本操作组合而成,而查询优化器可以在逻辑表达式与物理执行计划之间自由转换——谓词下推(predicate pushdown)、连接重排序(join reordering)等优化技术,全都依赖于"逻辑查询与物理执行解耦"这一前提。

通用组件的检索,应该是一组可组合的算子,而不是一个预设的查询函数。这就是"代数"的含义:给你有限的基本操作,让任意检索需求都可以表达为它们的组合。

但仅有代数表达力还不够。认知心理学的研究告诉我们,人类的信息检索行为本身就不是"找到所有相关的"——Information Foraging Theory(Pirolli & Card, 1999)将人类的信息搜寻行为类比为动物的觅食行为:人类依据"信息气味"(information scent)来决定是否继续追踪某条线索,当气味变弱时就转向其他路径 [47]。信息觅食理论的核心预测是:信息搜寻者会在"继续在当前 patch 觅食"和"移动到新 patch"之间做成本-收益权衡——当当前 patch 的信息获取速率(information gain per unit cost)低于环境的平均获取速率时,搜寻者会切换 patch。映射到 Agent 的记忆检索:当 topk_semantic 返回的前几个结果的信息增益急剧下降("信息气味"变弱)时,系统应该自动切换到图扩散或时间过滤等其他检索路径——而不是继续在同一个 patch 里挖。这意味着检索的目标不应该是"穷尽所有相关结果",而是在有限的注意力预算下快速找到"足够好"的结果——这正是 Herbert Simon 在 1956 年提出的满意化(satisficing)原则:在有限理性下,决策者不会寻找最优解,而是寻找第一个满足最低标准的选项 [25]

此外,Tulving 和 Thomson(1973)的编码特异性原则(encoding specificity principle)指出:检索线索只有在与编码时的上下文匹配时才有效 [48]。Godden 和 Baddeley(1975)的经典实验——潜水员在水下学习的单词在水下回忆效果更好——证明了上下文依赖记忆(context-dependent memory)的显著效应,匹配与不匹配条件之间的回忆差异高达 30-40% [49]。这意味着代数化的读路径必须将查询上下文(当前任务、当前对话状态、当前时间)作为一等公民纳入算子参数,而不是假设"语义相似 = 检索有效"。一条记忆的"有用性"不仅取决于它的内容与查询的语义距离,还取决于当前情境与编码时情境的匹配度——这是一个多维的匹配问题,无法被单一的 embedding cosine similarity 捕获。

Watkins(1975)的线索过载(cue overload)原理进一步指出:当一个检索线索关联了过多的记忆时,它对任何单条记忆的诊断性都会下降。一个过于泛化的标签(如"重要")作为检索线索几乎无效——它关联了太多记忆,无法区分彼此。这为过滤算子 σ 的设计提供了认知依据:过滤器的价值在于降低候选集的基数,从而让每个检索线索保持足够的诊断性。从信息论的角度,线索过载等价于检索线索的互信息(mutual information)随关联记忆数增加而递减——I(query; specific_memory | cue) 随 |memories linked to cue| 增大而趋零。

5.2 基本算子集

借鉴 Codd 关系代数的封闭性设计,读路径的基本算子集由四类构成:

过滤算子 σ(Selection)——按谓词缩小候选集:

检索算子 ρ(Retrieval)——从候选集中按相关性提取:

投影算子 π(Projection)——控制返回粒度:

投影算子的三级粒度(summary → facet → evidence)形成了一个信息密度递减、保真度递增的阶梯。在注意力紧张时使用 π_summary,在需要验证时使用 π_evidence——这与 Baddeley 工作记忆模型中中央执行系统的"注意力切换"功能相对应:根据当前任务需求动态调整信息粒度。

合并算子 ⊕(Merge/Budget)——在预算约束下合并多路结果:

代数化的核心性质是封闭性:每个算子的输入和输出都是同一类型的结构化记忆集(或流),因此算子可以任意组合。这使得查询优化成为可能——例如,过滤算子可以被"下推"到检索算子之前执行(减少检索的候选集规模),与关系数据库中的谓词下推是同一种优化思路。另一个优化方向是算子的短路求值:当 σ_modality 已经将候选集缩减到 3 个 facet 时,下游的 ⊕_budget 可以直接返回全部 3 个而跳过复杂的边际价值计算。

查询优化器的引入还意味着逻辑查询计划物理执行计划的分离。同一个逻辑表达式可以有多种物理实现:

逻辑: σ_entity(X) ∘ topk_semantic(q, k) ∘ π_summary

物理计划 A(过滤下推): 先按 entity 过滤全量 facet,再在子集上做向量检索
物理计划 B(检索先行): 先做向量检索拿到 top-k,再按 entity 过滤

当 entity="X" 的 facet 数量远小于总 facet 数时,计划 A 更优(向量检索的候选集更小);当 entity="X" 的 facet 数量接近总 facet 数时,计划 B 更优(避免了全量扫描的过滤成本)。优化器可以根据统计信息(每个 entity 的 facet 数量分布、向量索引的类型等)自动选择最优计划——这正是关系数据库查询优化器做了半个世纪的工作,现在被迁移到了记忆检索的语境中。

5.3 几个典型查询的代数表达

"用户当前可能关心的关于公司 X 的所有有争议事项"

σ_modality({disputed}) ∘ σ_entity(company_X) ∘ σ_type(Semantic) ∘ π_summary

三个过滤算子依次作用,最后投影为 Summary 文本。这个查询在 mem0 的 search API 里无法表达——它不知道什么是"有争议的",也不知道什么是"公司 X"。

"GraphRAG 风格的全局问答"

⊕_budget(B, scorer=relevance) ∘
  [ topk_semantic(q, k_global),
    expand_graph(topk_semantic(q, k_seed), hops=2) ]
  ∘ π_summary

向量检索和图扩散检索两路并行,结果按 relevance 合并,预算 B 控制总量。GraphRAG 的社区摘要可以表达为离线预派生的 reflective-type facet 存储在 L0 中,由 expand_graph 自然地检索到。

"过去一周新获得的对旧问题的反向证据"

σ_time(transaction_window=last_7d) ∘ σ_source(stance=contradict) ∘ π_evidence

"上下文敏感的回忆——与当前对话情境最相关的记忆"

⊕_budget(B, scorer=context_match) ∘
  [ topk_semantic(current_topic, k),
    temporal_proximity(now, window=1d) ∘ topk_semantic(current_topic, k) ]
  ∘ π_facet

这个查询同时利用语义相似度和时间上下文作为检索信号,体现了编码特异性原则的工程化:编码时的时间和情境上下文被存储为 facet 的元数据,检索时作为 temporal_proximity 的参数参与匹配。Godden 和 Baddeley 的实验表明,这种上下文匹配能带来 30-40% 的回忆提升。

"混合精确-语义检索——用关键词定位实体,用语义扩展上下文"

⊕_budget(B, scorer=diversity_weighted_relevance) ∘
  [ topk_bm25("Hans 付款周期", k),
    topk_semantic("客户付款条款变更", k) ]
  ∘ π_summary

BM25 锁定精确实体,语义检索扩展相关上下文——两路互补。这正是 Zep/Graphiti 混合检索策略 [20] 的代数表达:topk_bm25 + topk_semantic + expand_graph + σ_time 四路并行,由 ⊕_budget 按 diversity-weighted relevance 合并。

这些查询示例共同说明了一个关键性质:代数化使得查询意图的表达与查询执行的优化解耦。用户(或 L2 策略)只需要声明"我想要什么"——用算子的组合描述检索意图——而不需要关心"怎么找"——哪个索引先用、候选集如何缩减、结果如何排序。这种声明式(declarative)的检索范式,与 Codd 在 1970 年为关系数据库所倡导的哲学一脉相承:用户写 SQL 声明意图,优化器选择执行计划。五十年后,同样的哲学被迁移到了 Agent 记忆检索的语境中。

5.4 预算化召回:写路径价值函数的读路径对偶

⊕_budget(B, scorer) 是整个读路径的灵魂。Agent 的上下文窗口总是有限的,检索的真正目标不是"返回所有相关的",而是"在 B token 预算下返回边际价值最大的子集"。这与写路径的价值函数是同一个问题的两面:

两者的评分函数 scorer 是同构的——都由 R_task 驱动的预期边际贡献来定义。写路径的 forget_policy 淘汰价值最低的 facet,读路径的 ⊕_budget 优先装填价值最高的 facet。读写两端共享同一套价值标尺,记忆系统才真正实现了"有用的留下来,没用的被过滤"的闭环。

⊕_budget 的实现需要一个贪心的边际价值排序:对候选集中的每个 facet,计算其在"已有选中子集"条件下的边际贡献。这避免了简单 top-k 的冗余问题——如果前 k 个 facet 高度相似,它们的联合价值远低于一个多样化的 k 子集。具体而言:

⊕_budget(B, scorer):
  selected = ∅
  while token_budget(selected) < B and candidates ≠ ∅:
    m* = argmax_{m ∈ candidates} marginal_value(m, selected, scorer)
    selected = selected ∪ {m*}
    candidates = candidates \ {m*}
  return selected

其中 marginal_value(m, selected, scorer) = scorer(m) - redundancy(m, selected)。冗余度惩罚保证了选中子集的多样性——与已选 facet 高度重叠的新 facet,其边际价值被自动折减。这与信息论中的率失真理论(rate-distortion theory)有深层联系:在有限的"率"(token 预算)下最小化"失真"(任务相关信息的损失),⊕_budget 本质上是在记忆检索场景下求解率失真函数 R(D) 的一个贪心近似。

5.5 GraphRAG 被收编为一个算子

GraphRAG(Edge et al., 2024 [51])的工作流是"离线建实体-关系图 → 社区检测(Leiden 算法)→ 多层级摘要 → 检索时按问题路由层级"。它的核心假设是语料是给定的、静态的、彼此不矛盾的——没有一条假设在 Agent 的世界里成立。

在这个代数视角下,GraphRAG 不是被替代,而是被降级——或者说被收编。它的图检索能力被提取为 expand_graph 算子。它的社区摘要被表达为离线预派生的 reflective-type facet 存储在 L0 中。它的多层级摘要路由被表达为 σ_type(Reflective) + π_summary 的组合。

这意味着你可以同时使用 GraphRAG 风格的图检索 + bitemporal 时间过滤 + modality 模态过滤的复合查询——这些能力在 GraphRAG 的原生架构里是无法组合的,因为它们被封装在单一的 query 黑盒里。代数化之后,每种能力都是一个独立算子,按需组合。

更进一步,Zep/Graphiti 的 bitemporal 知识图谱架构 [20] 提供了另一种检索算子的实现范例。Graphiti 的混合检索策略——语义搜索 + BM25 关键词匹配 + 图遍历 + 时间过滤——可以被自然地表达为代数算子的组合:topk_bm25topk_semanticexpand_graphσ_time 四路并行,由 ⊕_budget 合并。这验证了代数化框架的表达能力:不同的检索系统不是不同的"产品",而是同一组算子的不同物理实现——正如 Codd 所设想的,逻辑查询与物理解耦。

代数化还带来了一个重要的工程红利:查询优化器的可实现性。当检索需求被表达为算子的组合时,系统可以自动选择最优的执行计划——例如,当 σ_modality(disputed) 的选择性很低(只有 5% 的 facet 是 disputed)时,优化器应该将这个过滤下推到向量检索之前执行,先缩减候选集再做 embedding 计算。当 expand_graph 的种子集很大时,优化器可以决定是否先做 σ_entity 缩减种子集再展开图。这种优化在黑盒式检索 API 中是不可能实现的——因为黑盒封装了执行细节,外部无法干预。

GraphRAG 被收编后,原生 GraphRAG 的一个核心功能——层级化社区摘要路由——在代数框架中有了更精确的表达。GraphRAG 的 global search 策略是"根据问题的抽象层级,选择对应层级的社区摘要回答"。在代数表达中,这被分解为两步:(1) 用 LLM 判断查询的抽象层级,映射为 σ_type(Reflective_level=L) 的过滤条件;(2) 在对应层级的 reflective facet 中检索和合并。这种分解的好处是:抽象层级的判断(一个分类问题)和内容的检索(一个排序问题)被解耦为两个独立的算子,各自可以被独立优化、替换或 A/B 测试——而原生 GraphRAG 将两者焊死在同一个 query 函数中。

5.6 本章小结

读路径的代数化补齐了记忆系统的"写入-读取"闭环。写入侧由统一价值函数驱动,精心选择"记什么、怎么记";读取侧由相同的价值标尺驱动,在注意力预算约束下择优装填。⊕_budget 是读路径上对写路径 forget_policy 的对偶——两者共同定义了记忆系统的"选择性"。

代数化的设计有三个理论根基:Codd 的关系代数提供了可组合性与物理数据独立性的范式;Information Foraging Theory 和 satisficing 解释了为什么"足够好"的贪心检索优于"完美但慢"的穷举检索;编码特异性原则和上下文依赖记忆说明了为什么查询上下文必须作为一等公民进入算子参数。GraphRAG 的图检索被收编为一个可组合算子,验证了代数化的表达能力。

读路径代数化与写路径价值函数的对偶关系,可以用一个简洁的类比来总结:写路径是"投资",读路径是"兑现"。写入侧的价值筛选(记什么、怎么记、什么时候记)是对未来检索收益的"投资"——投入存储和计算资源,保留预期最有价值的认知。读取侧的预算化召回(⊕_budget)是在有限的注意力窗口中"兑现"这些投资——选择边际价值最高的子集装入 prompt。投资与兑现共享同一把价值标尺(R_task 驱动的 scorer),整个系统才能实现"投入有回报、回报可衡量、衡量可优化"的闭环。至此,从 L0 的存储不变量、到 L1 的类型系统、到 L2 的策略平面、到读写的价值闭环,理论框架的骨架已经完整。但还有一些问题我们诚实地说还没解——这是第 6 章的任务。