福生无量摸鱼天尊

agent系列:(三)context and memory

2026/03/06
0
0

context其实跟infra息息相关,好的infra能支持非常多的idea并发的去实现,而pipeline的高度耦合使得infra变得屎山中的屎山,所以模块化的context infra势在必行。我们想构建一个context playload,一个有序、分层、可度量的 typed blocks 集合,是模型单次推理的输入态。

Context Engineering:围绕 payload 的三类能力:

  • Retrieval & Generation:从外部源/内部状态获取或生成上下文块(docs、tools、memory、state)。

  • Processing:对块做清洗、裁剪、压缩、去重、排序、结构化与对齐。

  • Management:生命周期(TTL/版本)、缓存、更新策略、权限边界、跨会话迁移与审计。

再往下推一步,如何让context在这些模块中observable,可以从如下进行考虑:

  • Coverage(覆盖):关键事实/约束是否都进入 payload(召回侧)。

  • Precision(精度):进入 payload 的内容对任务是否“必要且足够”(噪声侧)。

  • Freshness(新鲜度):是否显式编码时间戳/版本,避免过时证据主导。

  • Traceability(可追溯):生成结论能否回溯到证据块与来源 span。

  • Controllability(可控):块的优先级/权限/可变性是否稳定可预测。

但是这些都很难量化,特别是在多模态的当下,好的infra是很难构建的,我们尝试去讨论一下。

context block

每个 block 都有统一Header,再挂载Body。

Block {
  type: enum,            // Instruction / State / Evidence / Memory / ToolIO / Trace ...
  id: string,            // 稳定标识,便于去重/引用
  priority: int,         // 布局与截断策略使用
  scope: enum,           // system | tenant | user | session | task
  mutability: enum,      // immutable | append_only | mutable
  ttl: duration?,        // 生命周期(尤其 memory/evidence)
  source: {kind, uri?, doc_id?, tool_name?},
  provenance: {span?, page?, bbox?, timestamp?, hash?},
  trust: {level, rationale?},
  token_est: int,
  body: ...
}

Context Processing

Processing基本上都是一些操作,我们可以类似一种编译器装配的设置来配置系统:

def build_payload(state, user_input, budget):
    # 1) collect candidates
    cand = []
    cand += make_instruction_blocks(state.policies, state.tool_contracts)         # immutable
    cand += make_task_state_blocks(state.plan, state.progress, state.failures)    # append-only
    cand += retrieve_memory_blocks(state.user_id, user_input)                     # gated
    cand += retrieve_evidence_blocks(user_input, state.sources)                   # with provenance
    cand += recent_tool_io_blocks(state.tool_trace)                               # structured
    cand += minimal_conversation_trace(state.dialogue)                            # trimmed

    # 2) sanitize & normalize
    cand = sanitize_by_type(cand)             # 防注入:evidence 禁止携带指令语气/越权字段
    cand = normalize(cand)

    # 3) score & select
    cand = dedup(cand, key=("type","source","semantic"))
    cand = rank(cand, objective=state.objective)

    # 4) compress with alignment
    cand = compress(cand, budget=budget, keep_alignment=True)

    # 5) layout & validate
    payload = layout(cand, ordering_policy="instruction>state>memory>evidence>toolio>trace")
    validate(payload)                         # schema/permissions/citation anchors
    return payload

Processing 的操作集合

  • Normalize:统一编码/单位/时间格式;去 HTML 噪声;表格结构化。

  • Chunk & Align:切块同时保留 span 映射(后续引用必须回链)。

  • Dedup:语义去重(同义/复述)+ 来源去重(同文多处)。

  • Rank:多目标排序(相关性×可信度×新鲜度×覆盖增益×成本)。

  • Compress:在预算内保真压缩(保留可引用锚点)。

  • Layout:布局优化(层优先级 + 依赖关系 + 读者友好)。

  • Validate:检查 schema、引用可对齐、权限不越界。

压缩

  • Fidelity:关键实体/数值/条件不丢;引用锚点不丢。

  • Cost:token 降本,推理延迟可控。

  • Explainability:压缩后仍能解释“为什么保留这些”。

Retrieval

Retrieval一直是ReAct中非常难搞的一环,我认为把检索写成策略引擎而非固定流程,核心问题不是“取几段”,而是何时取、取什么、取到何时停

  • Trigger:是否检索

    • 条件例:置信度不足、需要外部事实、需要最新信息、发现冲突、引用要求。

  • Target:检索什么源

    • 文档库 / web / 工具观测(Tool results)/ 记忆库 / 代码与配置库。

  • Budget:检索多少

    • 自适应 TopK:先小 K 探测,再扩展;或按“信息增益”停止。

  • Stop:何时停止

    • stop rule = 证据覆盖达标 + 冲突可解释 + 引用可对齐 + token budget 可承受。

Pipeline

常见的Agentic RAG 的闭环都是retrieve → generate → critique → refine,我们可以把这种想法下沉到检索里,抽象成控制回路(control loop):

  • Planner:从 task state 产出查询计划(queries + hops + stop condition)。

  • Retriever/Reranker:多路召回 + 重排(相关性、可信度、时效性、去重)。

  • Critic/Verifier:检查回答是否被证据支持;是否存在冲突/注入/过时。

  • Updater:把新 evidence 写入 state(不是写入 memory,除非通过写入门控)。

这带来的好处就是信息是能够被掌控的,一旦出现检索失败,能定位到具体的位置:

  • 错误召回:相关但错误;需要可信度/来源权重与对照验证。

  • 噪声过大:召回太宽导致注意力稀释;需要压缩与布局策略(见第 3 节)。

  • 注入文档:检索到“指令伪装”的内容;需要层隔离 + 过滤规则(evidence 不得解释为 instruction)。

  • 过时内容:缺少 timestamp/版本;需要 freshness gating(新内容优先,或显式展示冲突)。

Memory

三类memory的形式

简单来说 agent memory 的实现形态归纳为:

  • Token-level memory:外部存储 + 检索/摘要/拼接(最常见、最工程化)

  • Parametric memory:把记忆写进模型参数(微调/适配/持续学习)

  • Latent memory:隐藏状态/缓存态(更接近“状态机/循环网络式”的隐式记忆)

要做“通用 memory 系统”,几乎必然以 token-level 为主干,按需求引入 parametric/latent 作为补充。

三类memory的分类

  • Working memory:当前任务的临时工作区(短期状态、计划、工具中间结果)

  • Experiential memory:经历/轨迹/案例/失败教训(长期数据,支持跨任务迁移)

  • Factual memory:事实、属性、配置、用户偏好(可沉淀的,可被明确表达或结构化)

LangGraph 的 conceptual guide 也用“人类记忆类比”强调:语义(facts)、情景(experiences)、程序(rules)都可能成为 agent 的长期记忆,并且关键问题是“何时写入(hot path vs background)”。

三个memory的操作

  • Formed(形成):从交互与环境中抽取“值得记住”的东西

  • Evolved(演化):合并、去重、纠错、更新、遗忘、压缩

  • Retrieved(检索):按任务需要把合适的信息装配回上下文

memory 系统质量主要就卡在这三步的 策略(写什么、怎么变、怎么取)与 约束(token budget、延迟、可解释、权限)。

主流Memory的框架

OS范式:MemGPT / Letta

核心思想:把 LLM 当成“有限 RAM”,把外部存储当“磁盘/慢层”,通过 paging/分层管理 在有限 context window 内实现“看起来无限”的上下文。

MemGPT 提出 virtual context management,并用层级 memory + 中断(interrupts)管理控制流;在文档分析和多会话聊天上验证能“记住、反思、随时间演化”。

Letta 对 memory 的工程抽象:

  • Memory blocks:始终可见、会被 prepend 到上下文(XML 格式),有 label/description/value/limit;相当于“固定注入的核心记忆区”(例如 persona、用户画像、长期目标)。

  • Archival memory:语义可检索的“档案记忆”(向量库);不自动注入上下文,需要 agent 通过工具调用检索;更像“慢层外存”。

  • Context hierarchy:在 Letta 中明确了 memory blocks、files、archival memory、外部数据库/工具(含 MCP)等层级化上下文来源。

  • Shared memory blocks:多 agent 共享同一块记忆,适合团队协作/一致策略。

优势

  • 看似是OS的分页机制,实际上都是做成分层记忆架构:设定机制固定注入 + 外部档案按需检索。

  • 可把“memory=prompt 工程”显式化(blocks/limits/层级),更可控。

短板/风险

  • 最明显的就是需要一整套“写入准则 + 过期/冲突处理 + 安全过滤”。

  • 强依赖“agent 会自己管理内存”的策略质量;写入/更新不好会造成 prompt 污染。

CRUD + 摘要 + 向量检索范式:Mem0

Mem0 的论文给了一套非常清晰的 两阶段 pipeline:Extraction + Update

  • Extraction:新消息对(常见是一轮 user+assistant)进入后,结合两种上下文:

    • 数据库中的会话的摘要(通常会维护一个索引表,通过摘要来索引原文)

    • 最近 N 条消息;

    • 并用 异步摘要刷新模块保证摘要不会阻塞主链路

  • Update:对每个候选事实,先做 embedding 相似检索拿到 top 相似记忆,再让 LLM 通过 function calling 决定对记忆库执行四类操作:ADD / UPDATE / DELETE / NOOP(特别关键:DELETE 处理“新信息否定旧信息”的冲突)。

  • 论文还提出图记忆增强版:用 实体-关系有向图存记忆,底层用 Neo4j,并结合 embedding 做双路检索(实体导向 + triplet 语义相似)。

这类“有明确 CRUD 语义”的 memory,比“只做向量检索”更容易做一致性与可维护性。

优势

  • 写入/更新语义清晰(ADD/UPDATE/DELETE/NOOP)非常适合做通用 memory 引擎的核心接口。

  • 摘要的异步刷新是典型生产设计:把“昂贵但必要”的 consolidation 下沉到后台。

短板

  • 抽取事实与更新决策依赖 LLM 的稳定性;需要大量约束(schema、校验、阈值、审计)。

  • 图记忆对抽取质量敏感;图越大,治理越复杂。

Knowledge Graph 范式:Zep / Graphiti

Zep 的论文把核心点说得很明确:企业场景需要从 持续对话 + 业务结构化数据中动态整合知识,而不是只检索静态文档;因此 Zep 以 Graphiti(时间感知知识图引擎)为核心,维护关系随时间变化的图。

评测与性能亮点

  • 在 DMR(MemGPT 团队提出的 Deep Memory Retrieval)上,Zep 报告超过 MemGPT(94.8% vs 93.4%)。

  • 在更强调时间推理的 LongMemEval 上,Zep 报告最高可提升 18.5% accuracy,并把响应延迟降低 90%(相对 baseline 实现)。

项目侧(工程产品化)

  • Zep 仓库将其定位为 end-to-end “context engineering platform”:实时 ingest 聊天/业务数据/事件 → 构建时间 KG → 检索并组装“关系感知”的上下文块(强调 <200ms latency)。

优势

  • 多跳关系推理时间一致性更强(适合“跨 session 信息综合”“时间线问题”“知识更新”)。

  • 更贴近企业数据治理:结构化数据接入、历史关系保留。

短板

  • 系统复杂度高:抽取、对齐、消歧、时态边失效(invalidate)、权限、扩展性、图查询成本等。

  • 图抽取错误会“结构化放大”:一旦写错关系,影响面更大。

GraphRAG:把“知识图+社区摘要层级”用于 RAG/记忆检索

GraphRAG 的官方文档给出了非常工程化的流程:

Index 阶段

  1. 把语料切成 TextUnits;

  2. 从 TextUnits 抽取实体、关系、关键主张;

  3. 用 Leiden 做层级聚类得到社区结构;

  4. 自底向上生成社区摘要(形成多层抽象)。

Query 阶段

  • Global Search:利用社区摘要回答“全局/整体性问题”;

  • Local Search:围绕特定实体向邻居扩展;

  • DRIFT Search:局部搜索叠加社区信息;

  • 也保留 Basic Search(标准 top-k 向量检索)。

GraphRAG 明确指出 baseline RAG 的痛点:难以“connect the dots”、难以对大集合做整体概念理解。

优势

  • 对“跨文档、跨主题、需要综合”的问题更强;天然提供层级摘要作为压缩记忆。

  • 适合做“组织级知识记忆”(KB + 社区概念层)。

短板

  • Index 成本高(抽取+聚类+摘要),增量更新也更复杂。

  • 摘要层可能引入“摘要漂移/信息丢失”,需要评测与校验。

层级摘要检索(RAPTOR):树化总结 + 多粒度检索(文档记忆非常实用)

RAPTOR 的核心机制:递归地 embedding → 聚类 → 摘要,构建自底向上的“摘要树”;推理时从树的不同抽象层级检索并整合信息,以提升对长文整体理解与多步推理。

优势

  • 很适合做“文档型长期记忆”的压缩与多粒度召回(局部细节 + 全局概念)。

  • 与 GraphRAG 在“层级摘要”思想上互补:RAPTOR 偏纯文本树,GraphRAG 偏图结构+社区。

短板

  • 摘要正确性是关键风险点;需要“证据回溯”(回答引用叶子块证据)来降低漂移影响。


F. 反思/经验沉淀范式:Generative Agents、Reflexion、Meta-Policy Reflexion

这条线解决的是:仅存事实不够,agent 还需要把经历变成更高层策略/规则(更接近 experiential & procedural memory)。

Generative Agents(2023,奠基性)

  • 架构:记录完整经历(memory stream),随时间综合成更高层反思(reflections),并动态检索来规划行为。

  • 其记忆检索会综合 相关性/时间新近性/重要性 来打分,把“对当下决策最有用”的记忆浮出水面。

Reflexion(2023)

  • 提出让 agent 通过自我反思把经验写入“动态记忆”,增强推理轨迹与行动选择能力。

Meta-Policy Reflexion(2025)

  • 进一步把反思沉淀成 可复用规则:引入外部 Meta-Policy Memory,把反思 insights consolidation 成结构化规则供未来复用。

优势

  • 能让 memory 不止“记住”,而是“学到”:形成可迁移的策略/规则(procedural memory)。

  • 很适合复杂任务 agent(编程、运维、投研)做长期自我改进。

短板

  • 反思与规则抽取的正确性/可控性更难;容易生成过拟合或错误规则。

  • 需要强评测与回滚机制(规则版本化、A/B、灰度)。


G. 框架级 Memory API:LangGraph/LangChain、LlamaIndex(“把记忆系统做成可插拔基础设施”)

LangGraph / LangChain:把“短期状态 + 长期 store + 持久化”做成一等能力

  • 短期记忆(thread-scoped):LangGraph 把对话历史与状态放在 agent state 中;通过 checkpointer 将 state 持久化到数据库,使 thread 可随时恢复;并且 state 在每一步(含工具调用)后更新、每步开始读取。

  • 长期记忆(跨 session):以 store 保存 JSON 文档;每条 memory 放在自定义 namespace(类似文件夹)+ key(类似文件名)下,支持层级组织与跨 namespace 搜索/过滤。

  • LangGraph 明确提出“何时写入记忆”的两种路径:hot path(主链路写) vs background(后台异步生成)

  • Persistence 文档还说明 checkpointer 将每个 super-step 的 state 存为 checkpoint,存入 thread,从而支持 memory、time travel、fault-tolerance 等能力。

LlamaIndex:提供可定制 memory.put/get 与多种 memory 组合

  • 文档明确:agent 运行时会调用 memory.put() 存信息、memory.get() 取信息。

  • 其(旧)memory types 示例很典型:

    • ChatMemoryBuffer(token 限制内的最近消息)

    • ChatSummaryMemoryBuffer(超限时周期性总结)

    • VectorMemory(向量库相似检索,不保证顺序)

    • SimpleComposableMemory(组合多种 memory,例如 Vector + Buffer)

优势

  • 作为“通用 memory infra”的骨架很合适:状态持久化、namespace、多租户、可插拔 store、可组合 memory。

  • 易与其它 memory layer(Mem0/Zep/自研)集成。

短板

  • 框架提供的是能力而非策略:真正难的是“写什么/怎么更新/怎么检索/怎么拼 prompt”。