跳转至

第 7 章:检索、压缩与后台更新

1. 如果你不会把正确内容重新取出来,记忆就没用

当你已经把短期、长期和画像记忆分开之后,下一个现实问题就是:智能体到底应该怎样把需要的记录重新带回提示?

很多系统就是从这里开始退化的:

  • 提示里塞进太多不相关上下文;
  • 检索返回的是“相似”,却不是“有用”;
  • 摘要越来越长,却没有更清晰;
  • 每一次新迭代都只让上下文变得更重。

也就是说,问题已经不再是“有没有记忆”,而是记忆已经变得太吵、太贵、太难读了。

2. 检索不是“找回所有相似内容”,而是“挑出对当前任务有帮助的内容”

检索有一个坏习惯:如果你不限制它,它就会变得过于慷慨。结果进入提示的,不是最有帮助的内容,而是嵌入或关键词重叠上最相似的内容。

对生产系统来说,更有用的思路是:

  • 检索不需要返回很多;
  • 检索必须是可解释的;
  • 检索必须尊重租户、来源和信任边界;
  • 检索必须服从上下文预算。

一个正常的检索流水线,几乎总会同时考虑:

  • 租户隔离;
  • 记忆类别;
  • 近因性;
  • 置信度;
  • 来源;
  • 策略过滤器。

2.1. 用户和知识层之间的语义鸿沟是真实存在的

这里还有一个在演示里不太明显的问题:用户通常用口语提问,但文档和知识记录往往是正式的、技术化的,或者带有内部系统术语。

因此,检索失败并不一定是因为数据不存在,也可能只是因为用户查询和语料库之间存在语义鸿沟。

从工程上看,这意味着检索查询往往值得先做一层塑形:

  • 规范化实体名称和内部状态名;
  • 把查询改写成更像文档的表达;
  • 做受控的查询扩展;
  • 在部分场景中使用 HyDE,也就是先生成一个假想的文档式答案,再据此检索。

但这里有一个很重要的纪律:这种查询辅助手段不能变成新的“事实”。HyDE 或查询改写只是检索工具,而不是有依据答案的替代品。

2.2. 大多数情况下,应先从 RAG 开始,而不是训练

如果问题在于智能体缺少新知识,或者拿不到内部文档,最实用的第一步通常不是训练,而是把检索层做扎实。

原因很直接:

  • RAG 更新更快;
  • 检索更容易审计和加约束;
  • 知识漂移更容易通过更新语料库修复,而不是重新训练模型;
  • 持续变化的文档和可变知识源更适合放在检索,而不是塞进模型权重。

同时,最好把两种不同任务分开看:

  • 持续预训练主要用于适配知识分布;
  • SFT 主要用于适配行为、风格和决策模式。

一个实用规则通常是:先把检索做到足够像样,再判断系统是否真的撞到了上限,以至于需要训练。

这里还有一个很有用的运营信号:如果一个支持智能体长期工作正常,后来却在提示和模型路由都没明显变化的情况下退化,优先怀疑陈旧检索语料库、索引漂移或数据新鲜度问题,而不是先假设出现了某种神秘的模型退化。

贯穿案例:应该取回什么

在支持分诊案例里,检索不应该简单地把客户过去的所有互动都塞回提示。对当前运行真正有用的上下文,是最近仍然打开的工单、经过验证的用户画像事实(比如偏好语言),以及当前版本支持 playbook 的相关摘录。旧草稿、未经审查的投诉和过期摘要,要么进入后台压缩流程,要么完全不要进入提示。

3. 一个好的提示爱的是高密度信号,不是完整性

人很容易以为“上下文越多,智能体越聪明”。实践里,情况往往相反:你塞进提示的噪音越多,模型越难抓住真正的优先级。

所以检索回答的应该不是“我们能取出什么”,而是“什么能在此刻提高正确决策的概率”。

一个很有用的实用规则:

  • 3 条高度相关记录,通常比 20 条勉强相似记录更好;
  • 一个带来源的小摘要,通常比长原始文档更好;
  • 一个画像提示,通常比整段偏好历史更好;
  • 空检索,通常比不可信且不可解释的检索更好。

4. 压缩不是美容,而是保持系统可工作的手段

如果记忆层只会增长,那么提示组装迟早会变成一个没有规则的垃圾收集器。这就是为什么压缩必须是架构的一部分,而不是某次专项清理工程。

压缩可以意味着很多事:

  • 把几条记录压成一个摘要;
  • 删除过期的工作笔记;
  • 合并重复项;
  • 把大块数据替换成规范化记录加来源链接;
  • 让旧记录降权,而不是永远留在前景里。

更适合把检索和压缩看成一整个记忆维护循环

flowchart TD
    A["新运行"] --> B["查询记忆"]
    B --> C["应用过滤器和排序"]
    C --> D["组装提示上下文"]
    D --> E["模型 + 工具"]
    E --> F["创建新记忆候选"]
    F --> G["后台压缩与审查"]
    G --> H["规范化记忆存储"]
    H --> B

5. 不是所有记忆更新都应该发生在热路径

这是智能体系统里最有价值的架构转变之一。起初,几乎所有人都想把记忆做成“立刻就好”:智能体看见了什么,就立刻重写摘要、更新画像、保存知识。

但这通常既昂贵又危险。

通常适合留在热路径里的:

  • 最小会话状态;
  • 简短工作笔记;
  • 带清晰 TTL 的安全瞬时记录;
  • 如果不更新,当前工作流就真的会断掉的内容。

通常更适合放到后台的:

  • 长会话的压缩;
  • 摘要重建;
  • 事实规范化;
  • 去重;
  • 持久写入前对记忆候选的复查。

后台更新让记忆变得整洁,而不只是“反应很快”。

6. 好系统会把检索查询和维护任务分开

在成熟架构里,几乎总会有两条不同路径:

  • 读取路径:为当前运行快速而安全地取回上下文;
  • 维护路径:不受延迟压力地慢慢改进记忆存储。

这不仅是性能问题,也是决策质量问题。当同一条链同时:

  • 执行任务;
  • 重建摘要;
  • 写画像记忆;
  • 清理重复项;
  • 更新排序元数据,

它很快就会变得脆弱且难以解释。

6.1. 前沿记忆正在走向自适应塑形,但生产仍然需要纪律

最新的记忆研究正在把架构继续往前推:不只是存记录、偶尔做压缩,而是试图根据真实使用模式逐步重塑整个记忆层。

这很有吸引力,因为它暗示着一种更聪明的系统:

  • 摘要可以持续演化;
  • 记忆类别可以变得更丰富;
  • 存储可以更贴近真实重复任务。

但也正是在这里,最容易跳过运营纪律。

在团队还没有稳定建立这些能力之前:

  • 来源规则;
  • 修订语义;
  • 可审查记忆写入;
  • 可追踪维护任务;
  • 针对派生产物的回滚路径,

自适应记忆塑形更适合被当作研究方向,而不是默认的生产模式。

从工程上讲,这意味着一件很朴素的事:演化记忆值得研究,但当前在线系统轮廓仍然应该建立在可解释检索、受控压缩和可验证来源之上。

7. 一个检索与后台更新的策略示例

下面是一个很实用的模板。它不追求万能,但很清楚地展示了哪些决定应该被写明。

retrieval:
  max_records: 5
  max_tokens: 1800
  allowed_classes:
    - short_term
    - long_term
    - profile
  require_tenant_match: true
  min_confidence: 0.75
  deny_sources:
    - raw_external_html
    - unreviewed_summary

compaction:
  run_mode: background_only
  summary_max_tokens: 400
  deduplicate: true
  merge_similar_records: true
  drop_expired_short_term: true

当这些规则是显式的,团队讨论记忆时就不再停留在感觉,而会讨论真实限制和取舍。

8. 一个提示组装前排序的简单代码示例

下面不是一个“聪明”的检索引擎,而是一个故意写得可读的例子。它展示了排序不应该只看相似度,还应该看信任度、新鲜度和重要性。

from dataclasses import dataclass


@dataclass
class RetrievedRecord:
    text: str
    similarity: float
    confidence: float
    recency_weight: float
    trusted: bool


def score(record: RetrievedRecord) -> float:
    trust_bonus = 0.15 if record.trusted else -0.2
    return (
        record.similarity * 0.5
        + record.confidence * 0.25
        + record.recency_weight * 0.1
        + trust_bonus
    )


def select_for_prompt(records: list[RetrievedRecord], limit: int = 3) -> list[RetrievedRecord]:
    ranked = sorted(records, key=score, reverse=True)
    return ranked[:limit]

这套逻辑很粗,但有一个重要优点:它是可以讨论、可以测试、也可以逐步替换成更精细方案的,同时不会丢掉可解释性。

9. 摘要应该帮助理解,而不是隐藏数据来源

团队常常把摘要当作“把更多记忆挤进更少 token”的手段。这没问题,但有一个陷阱:摘要不能变成新的匿名真相。

一个好的摘要:

  • 比原始记录更短;
  • 保留来源;
  • 不混租户;
  • 不丢掉关键限制;
  • 被标记为派生产物,而不是原始事实。

一个糟糕的摘要:

  • 听起来很自信,但没人知道它来自哪里;
  • 把冲突事实混在一起;
  • 丢失日期和数据负责人;
  • 被当作可信指令喂给模型。

10. 常见错误

这些问题通常会一遍遍重复:

  • 重复内容被塞进提示;
  • 检索不知道类别边界;
  • 排序不考虑信任度;
  • 摘要变得过于泛化;
  • 没有后台任务,记忆只会膨胀;
  • 没人知道为什么正是这个片段被取回。

最后这一点尤其重要。如果你已经无法解释为什么某段上下文进入了提示,那系统基本已经失去良好控制了。

11. 现在就该做什么

先过一遍这份短清单,把所有回答为“否”的地方单独记下来:

  • 有没有记录数量上限和 token 预算?
  • 排序是否不仅考虑相似度,也考虑置信度、近因性和信任度?
  • 读取路径和维护路径是否分开了?
  • 压缩是不是一个持续的维护过程,而不是偶尔手工清理?
  • 摘要能不能看到来源?
  • 有没有防止跨租户或错误类别的检索?

如果连续几个问题答案都是“否”,那说明你已经有了记忆,但还没有形成记忆纪律。

12. 下一步做什么

到这里,关于记忆的基础部分已经开始成形。接下来你可以继续深入保留和删除,也可以转去看工具和执行。