OpenClaw 的缓存子系统是一个多级、多维度的复杂架构,旨在解决多 Agent 并发场景下的性能瓶颈、大模型 Token 成本控制,以及长期记忆的快速召回问题,个人觉得项目的成功该模块的贡献占一半。

一、缓存架构概览

整个缓存系统由以下四个相对独立但互相配合的子系统构成:

  1. Bootstrap 启动缓存 (Bootstrap Cache):负责极速加载 Agent 环境与初始化配置,避免重复的文件系统 IO。
  2. 上下文摘要与状态压缩 (Token Compaction):在 Agent 对话轮次叠加时,动态控制上下文窗口大小,并丢弃/压缩历史对话。
  3. 长期记忆与向量缓存 (Memory & Embedding Operations):负责知识库或高价值历史的持久化混合检索,同时对第三方 Embedding 接口(如 OpenAI/Gemini)提供防刷接口的向量哈希缓存。
  4. 大模型 Prompt 缓存追踪 (Cache Tracing):专门用于对接 Anthropic、OpenAI 等支持 Prompt Caching (提示词缓存) 的大模型,分析缓存命中命中率、提供日志级诊断追踪。

二、四大缓存流程与实现细节

2.1 Agent 启动缓存数据流 (src/agents/bootstrap-cache.ts)

业务场景:每当一个用户在群聊或私聊唤醒 Agent 时,引擎需要加载该 Agent 的工作空间(Workspace)配置文件(如 HEARTBEAT.mdINSTRUCTIONS.md)。

工作流程

  1. 触发挂载:外部调用 getOrLoadBootstrapFiles(config, sessionKey, workspaceDir, agentId)
  2. 命中内存:由于使用了全局的 Map<string, WorkspaceBootstrapFile[]>,引擎直接以 sessionKey 查询内存。若命中,则直接跳过磁盘读取步骤。
  3. 未命中回退 (Cache Miss)
    • loadWorkspaceBootstrapFiles 访问底层文件系统。
    • 读取后经过 Hook (如 applyBootstrapHookOverridesbootstrap-hooks.ts 中拦截调整文件结构)。
    • 最后将结果推入全局 Map 中供下一次极速启动。

2.2 上下文摘要压缩流 (src/agents/compaction.ts)

业务场景:对话越来越长,达到大模型 32K 或 128K 阈值边界。

工作流程

  1. Token 估算器:利用 estimateMessagesTokensestimateAdaptiveTokenLength 来预测消息流占用的 Token。
  2. 动态滑动窗口计算:通过 computeAdaptiveChunkRatio 和大模型自身的 contextWindow,计算出本次留给历史消息的安全边际(SAFETY_MARGIN)。
  3. 超量切分与大块替换
    • 若某条单独的日志/消息超大,核心库会触发分块:chunkMessagesByMaxTokens
    • 系统调用 summarizeWithFallback,尝试用轻量级 LLM 将一段冗长的对话压缩为简短总结。
    • 若总结失败或触及更底层的防线,它会执行冷酷截断,并用诸如 [Large message (~10K tokens) omitted] 的占位符替换原本的巨量文本。
  4. 不可丢失参数锁定 (Identifier Preservation):即使处于严重压缩抛弃阶段,OpenClaw 通过独特的声明 (IDENTIFIER_PRESERVATION_INSTRUCTIONS) 强势保护会话中出现的 URL、IP地址、哈希值、文件路径,防止 Agent 变“失忆”但依然看似接得上话的伪状态。

2.3 记忆混合检索与 Embedded 向量缓存机制

负责模块:src/memory/manager.ts, src/memory/manager-embedding-ops.ts, src/memory/qmd-manager.ts
底座设施:具有原生向量和全文引擎的 SQLite 数据库 (chunks_vec, chunks_fts)

业务场景:用户问:“三个月前关于重构支付网关的讨论细节是什么?”。此时 Agent 需要通过向量在海量归档对话中捞出关键片段。

工作流程

  1. 内容重组与分块 (Indexing)
  2. 向量接口级防刷层 (EMBEDDING_CACHE_TABLE)
    • OpenClaw 为了避免昂贵的远程 Embedding 调用(如向 Voyage 或 OpenAI POST 数据),通过 hashText 计算 Chunk 文本的哈希(结合 provider.model + providerKey 防止换模型产生脏命中)。
    • 使用 loadEmbeddingCache 到 SQLite 的 embedding_cache 表去查是否已经为相同的字符串生成过向量。
    • 如果全部命中,根本不会发送网络请求,直接内存补齐向量数组。
  3. 并发调用与后置更新
    • 遭遇未命中的缺口 (missing) 时,组装成 Batch Requests(例如 runOpenAiEmbeddingBatchesrunGeminiEmbeddingBatches)。
    • 成功后,通过 upsertEmbeddingCache 回写缓存表。同时系统具备清理机制 (pruneEmbeddingCacheIfNeeded) 保证体积不超标。
  4. 混合召回策略 (Hybrid Search)
    • Agent 请求回忆时,调用 search。首先执行纯降级 FTS(基于关键字)。然后同时进行 searchVector(Cosine 向量逼近)。
    • 调用 mergeHybridResults 去重归并分数,然后组装好并注入到 System Prompt 提供给 LLM。

2.4 Prompt Cache 追踪落盘体系 (src/agents/cache-trace.tsqueued-file-writer.ts)

业务场景:开发者想要度量目前 Agent 是否有效地命中了例如 Anthropic 的原生 Context Caching。

工作流程

  1. 埋点拦截:通过包装原生推流入口 CacheTrace.wrapStreamFn 获取底层的元数据拦截点。
  2. 阶段指纹计算
    • record() 方法按照四个生命周期:session:loadedsession:sanitizedprompt:beforestream:context 运行。
    • 将每一次发送给 LLM 的系统级和对话级 prompt 进行分段 Hash (主要是 messagesDigestmessageFingerprints)。
  3. 队列落盘无阻塞
    • 采集到的指纹对象被传入底层的 getQueuedFileWriter (基于 Promise 链安全实现的单线异步队列落盘) 写入本地物理日志中。供外部监控系统抽检 Prompt Hit 的状态跳变情况。

总结

OpenClaw 并非采用暴力的 Redis 或者简单的全文全局变量来进行缓存,而是深刻根据 LLM Agent 的底层链路(从初始化 -> Token 超限应对 -> 长期记忆向量化 -> Prompt 追踪)构建了多层防护池:

  • 短期热点:通过内存 Map 拦截 (Bootstrap)。
  • 账单与速率防护:防撞库般的 Embedding SQLite 缓存拦截与重试退避。
  • 对话臃肿防御:极具策略性的 fallback Token 截断与总结机制。
  • 状态追踪:非侵入式的 Trace 排队写入,用于监控大模型后端的缓存表现。

这套架构高度聚焦于“防 API 限流、防 Token 超量、防重入计算”,是大型 Agent 基座典型的标准打法。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐