跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScriptNode.jsAI算法

OpenClaw 3.7 ContextEngine 插件接口源码拆解:AI Agent 上下文管理

综述由AI生成OpenClaw 3.7 引入 ContextEngine 插件接口重构上下文管理。旧架构中上下文处理逻辑硬编码分散,维护困难且无法灵活替换策略。新方案定义七个生命周期钩子,涵盖消息摄入、组装、压缩及子 Agent 场景,实现核心运行时与具体实现的解耦。通过 LegacyContextEngine 采用绞杀者模式保证向后兼容,利用全局注册表和 Slot 机制支持动态切换引擎。开发者可快速编写自定义上下文处理插件,如基于向量数据库的 RAG 检索替代暴力截断。该设计体现了接口分离、配置驱动及渐进式重构的工程模式,适用于各类 AI Agent 系统的上下文管理优化。

魔尊发布于 2026/4/9更新于 2026/5/2216 浏览

OpenClaw 3.7 ContextEngine 插件接口源码级拆解

一、AI Agent 的上下文管理为什么是'最头疼的问题'?

在与 AI 对话时,每一轮历史消息都作为'上下文'发送给模型。模型需要看到之前的对话内容才能理解当前问题。

核心问题在于:模型的上下文窗口有大小限制。

即使拥有 128K token 的窗口,聊了 200 轮加上工具调用返回值,token 也很容易超限。此时系统必须决策:

  • 哪些消息保留?最近的肯定要,但边界在哪?
  • 旧消息如何处理?直接截断还是压缩成摘要?
  • 组装顺序如何?系统提示 + 历史 + 工具 Schema 的顺序和内容怎么组合?

这就是上下文管理(Context Management)。每个实用的 AI Agent 框架都绕不开此问题。

关键在于:这些处理逻辑一旦写死在核心代码里,日后每次调整都是高风险操作。

二、旧架构痛点分析

逆向 OpenClaw v2026.3.7 之前的源码可见,旧的上下文管理是一条完全硬编码的流水线,散布在 5+ 个文件中,没有任何抽象层:

用户消息进来 → SessionManager 写入 JSONL → 加载全部历史消息 → sanitize(清洗) → validate(校验) → limit(截断) → repair(修复) → 送给模型

2.1 Sanitize 复杂度

函数 sanitizeSessionHistory() 内部塞了大约 10 个处理步骤:

  1. 标注跨会话用户消息
  2. 清理/缩放图片(不同模型有不同图片限制)
  3. 删除 <thinking> 块
  4. 规范化工具调用的名称和 ID
  5. 修复孤立的 tool_use → tool_result 配对
  6. 删除工具返回值中的冗余字段
  7. 清理过期的 assistant usage 快照
  8. 记录模型快照元数据
  9. 降级 OpenAI 推理格式(仅 OpenAI Responses API)
  10. 修复 Google/vLLM 的轮次顺序约束(仅 Google provider)

2.2 压缩逻辑重复

当 token 快要超限时,系统有个'压缩'机制——把旧对话总结成摘要,删掉原始消息。

这个逻辑在 compact.ts(约 763 行),是最大的痛点。因为压缩需要调模型来生成摘要,而调模型需要完整的上下文环境,于是 compact.ts 把 attempt.ts 里大量环境构建代码复制了一遍:

compact.ts 独立重复的逻辑:
├── resolveModel() — 模型解析
├── resolveModelAuthMode() — 认证模式
├── resolveSandboxContext() — 沙箱配置
├── resolveChannelCapabilities() — 频道能力
├── sanitize→validate→limit→repair — 完整管线
└── ...更多重复代码

改一处忘了改另一处?上线即 bug。

2.3 三种压缩触发方式全是硬编码

旧架构有三种压缩触发方式:

  • SDK 自动检测:Agent 运行期间检测到即将超限,主动触发
  • 用户手动 /compact:用户在对话中输入命令
  • Overflow 重试:模型返回 token 超限错误,自动重试(最多 3 次)

问题不在于触发方式不够多,而在于——你无法通过插件替换这些策略。想自定义'什么时候该压缩'的判断逻辑?只能改核心代码。

2.4 痛点总结

痛点具体表现
逻辑散落散布在 sanitize、validate、limit、repair、compact 5+ 个文件
大量重复compact.ts 复制了 attempt.ts 的模型解析、沙箱配置等大量代码
无法替换想换个压缩算法?必须改核心代码
策略硬编码三种压缩触发方式全硬编码在核心代码
无后处理Agent 一轮结束后没有任何钩子做后续优化
子 Agent 隔离父子 Agent 之间没有上下文共享/清理机制

三、ContextEngine 的核心思路:把'做什么'和'怎么做'分开

OpenClaw 3.7 的 ContextEngine 接口(PR #22201,44 个文件改动),解决方案其实很经典——定义一组接口,让核心运行时只关心'要做什么',具体'怎么做'交给可替换的引擎。

翻译成人话:以后换个上下文处理算法,像换插件一样简单。

3.1 七个生命周期钩子

ContextEngine 定义了 7 个钩子,覆盖了上下文的完整生命周期:

会话开始 → ① bootstrap() — 初始化引擎状态 → ② ingest() — 每条消息摄入 → ③ assemble() — 组装模型上下文 → 调用模型 → ④ afterTurn() — 回合后处理 → ⑤ compact() — 需要压缩时执行
子 Agent 场景: → ⑥ prepareSubagentSpawn() — 准备子 Agent 上下文 → ⑦ onSubagentEnded() — 清理子 Agent 上下文
从前(硬编码)现在(ContextEngine 钩子)意味着什么
消息直接写入 JSONLingest() — 引擎决定怎么存可以存到向量库、做预处理
sanitize→validate→limit→repairassemble() — 引擎决定怎么组装可以用 RAG、用检索、用任何算法
三种硬编码压缩方式compact() — 引擎决定怎么压可以不压、可以自定义摘要策略
回合结束什么都不做afterTurn() — 引擎做后处理可以异步优化上下文
子 Agent 完全独立prepareSubagentSpawn() + onSubagentEnded()父子 Agent 可以共享上下文

3.2 接口定义(精简版)

interface ContextEngine {
    readonly info: ContextEngineInfo;
    // 3 个必选 = 上下文管理的三件核心事
    ingest(params: { sessionId, message }): Promise<IngestResult>;
    assemble(params: { sessionId, messages, tokenBudget? }): Promise<AssembleResult>;
    compact(params: { sessionId, sessionFile, tokenBudget?, ... }): Promise<CompactResult>;
    // 4 个可选 = 增强能力
    bootstrap?(params): Promise<BootstrapResult>;
    afterTurn?(params): Promise<void>;
    prepareSubagentSpawn?(params): Promise<SubagentSpawnPreparation | undefined>;
    onSubagentEnded?(params): Promise<void>;
}

3 个必选方法覆盖了上下文管理最本质的三件事:消息怎么存、上下文怎么组、太多了怎么压。4 个可选方法提供增强能力,不实现也不影响基本运转。

四、向后兼容的艺术:LegacyContextEngine 与绞杀者模式

引入新接口最怕什么?Breaking change。

OpenClaw 用了一个经典的架构模式——Strangler Fig(绞杀者模式):在旧系统外面包一层新接口,旧逻辑原封不动跑在新接口里。

LegacyContextEngine 就是这个包装器:

方法实际做了什么效果
ingest()返回 { ingested: false },什么都不做SessionManager 照旧处理
assemble()原样返回 messages,不改动旧管线照旧跑
compact()调用原来的压缩函数压缩逻辑完全不变
afterTurn()空操作没有副作用

结果:不配置任何 context engine 插件时,系统行为和升级前 100% 一致。零风险升级。

五、注册与解析机制:怎么做到'换插件一样简单'?

5.1 进程全局注册表

// 通过 Symbol.for() 挂在 globalThis 上
// 为什么?monorepo 构建可能把模块打包成多份拷贝
// Symbol.for() 确保不管有几份 js 文件,注册表只有一个
const REGISTRY = Symbol.for("openclaw.contextEngineRegistryState");
registerContextEngine(id, factory);
getContextEngineFactory(id);
listContextEngineIds();

5.2 Slot 机制

{
  "memory": "memory-core",
  "contextEngine": "legacy"
}

一个坑位只能站一个人。两个插件都声明 kind: "context-engine",只有被配置选中的那个加载。

5.3 一行配置搞定切换

{
  "plugins": {
    "slots": {
      "contextEngine": "lossless-claw"
    }
  }
}

不配这一行 → 默认 "legacy" → 行为不变。

这就是'像换插件一样简单'的实现原理。

六、实战:10 分钟写一个自定义 Context Engine

这不是空谈,看代码:

export default function register(api) {
    api.registerContextEngine("my-rag-context", () => ({
        info: {
            id: "my-rag-context",
            name: "RAG Context",
            ownsCompaction: true
        },
        async ingest({ sessionId, message }) {
            // 每条消息写入向量数据库
            await vectorDb.insert(sessionId, message);
            return { ingested: true };
        },
        async assemble({ sessionId, messages, tokenBudget }) {
            // 不是暴力截断,而是 RAG 检索最相关的历史消息
            const relevant = await vectorDb.search(sessionId, latestQuery, tokenBudget);
            return { messages: relevant, estimatedTokens: countTokens(relevant) };
        },
        async compact({ sessionId }) {
            // RAG 模式下不需要压缩——assemble 时按需检索
            return { ok: true, compacted: false };
        },
    }));
}

三个核心方法,加起来不到 20 行。但效果是什么?你把上下文管理从'暴力截断'变成了'语义检索',而且没有改动 OpenClaw 的任何核心代码。

七、ContextEngine ≠ 记忆系统,别搞混了

很多人会混淆这两个概念。简单区分:

ContextEngine(上下文管理)Memory 插件(记忆系统)
管什么当前 session 内的消息流跨 session 的持久化知识库
核心问题哪些消息送给模型、何时压缩怎么索引、搜索、存取记忆
生命周期一个 session 内跨越所有 session
存储session JSONL 文件Markdown 文件 + SQLite/LanceDB

它们之间的桥梁是 Memory Flush——在 ContextEngine 执行压缩之前,先触发一个静默 Agent 回合,提醒 Agent 把值得保留的对话内容写入记忆文件。

上下文管理决定'模型这次能看到什么',记忆系统决定'Agent 下周还记得什么'。两者协同但独立。

八、从 ContextEngine 学到的架构思维

不管你用不用 OpenClaw,这次的 ContextEngine 设计有几个通用的工程模式值得带走:

8.1 绞杀者模式(Strangler Fig)

新接口包装旧逻辑,渐进式替换。不一口气重写,而是让新旧共存,逐步迁移。这在任何大型系统的重构中都适用。

8.2 接口 + 注册表 + 配置驱动

定义接口 → 工厂函数注册到全局注册表 → 配置文件选择用哪个引擎 → 运行时动态解析。这套模式在 Java 的 SPI、Python 的 entry_points、Rust 的 trait 中都有对应。

8.3 Best-effort 回调

子 Agent 生命周期回调失败只记日志不阻断主流程。在分布式系统中,'让非关键路径的失败不影响主路径'是基本原则。

8.4 必选 + 可选方法分离

3 个必选方法保证核心能力,4 个可选方法扩展增强能力。实现者可以从最简开始,逐步增加功能。

九、行业背景与对比

当前开源社区对主流 AI Agent 框架的记忆系统进行了广泛分析,涵盖了多种设计哲学:

框架语言特点
OpenClawTypeScript插件架构、混合搜索、优雅降级
nanobotPython极简主义,2 个 Markdown 文件搞定
NullClawZig10 种存储后端、9 阶段检索管线
OpenFangRust知识图谱、记忆衰减、统一 SQLite 底座

从 2 个 Markdown 文件到 10 种存储后端、从暴力截断到 9 阶段检索管线——4 种完全不同的设计哲学,让你一次看透 AI Agent 记忆系统的全貌。

适合以下人群参考:

  • 正在构建 AI Agent 的工程师:直接参考已经验证过的架构
  • 想理解'记忆'到底怎么实现的研究者:源码级拆解,不是概念堆砌
  • 想在 LangGraph / LangChain / 自研框架中做上下文管理的开发者:每个框架都有复刻指南

十、总结

OpenClaw 3.7 的 ContextEngine 接口,核心价值用一句话概括:

以后换个上下文处理算法,像换插件一样简单,不用再担心改一处崩全程。

它不是一个'多了个功能'的小更新,而是一次架构层面的升级——把 AI Agent 中最头疼的上下文管理问题,从'硬编码在核心代码里'变成了'可插拔的标准接口'。

7 个生命周期钩子、绞杀者模式的向后兼容、全局注册表 + Slot 机制的引擎管理——这些设计模式不仅适用于 OpenClaw,同样适用于你正在做的任何 AI Agent 系统。

不论你用什么技术栈,上下文管理的核心问题是一样的。理解它、借鉴它、在你的项目中用好它。

目录

  1. OpenClaw 3.7 ContextEngine 插件接口源码级拆解
  2. 一、AI Agent 的上下文管理为什么是“最头疼的问题”?
  3. 二、旧架构痛点分析
  4. 2.1 Sanitize 复杂度
  5. 2.2 压缩逻辑重复
  6. 2.3 三种压缩触发方式全是硬编码
  7. 2.4 痛点总结
  8. 三、ContextEngine 的核心思路:把“做什么”和“怎么做”分开
  9. 3.1 七个生命周期钩子
  10. 3.2 接口定义(精简版)
  11. 四、向后兼容的艺术:LegacyContextEngine 与绞杀者模式
  12. 五、注册与解析机制:怎么做到“换插件一样简单”?
  13. 5.1 进程全局注册表
  14. 5.2 Slot 机制
  15. 5.3 一行配置搞定切换
  16. 六、实战:10 分钟写一个自定义 Context Engine
  17. 七、ContextEngine ≠ 记忆系统,别搞混了
  18. 八、从 ContextEngine 学到的架构思维
  19. 8.1 绞杀者模式(Strangler Fig)
  20. 8.2 接口 + 注册表 + 配置驱动
  21. 8.3 Best-effort 回调
  22. 8.4 必选 + 可选方法分离
  23. 九、行业背景与对比
  24. 十、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 前端实时推送与 WebSocket 面试题(2026 版)
  • AI 新趋势:智能体(Agent)与产品经理的机遇
  • GitHub开源项目日报:AI代理与本地助手热榜 (2026-02-19)
  • 双指针算法实战:移动零与复写零详解
  • 数据结构:堆与优先级队列
  • LangChain 智能体执行引擎 AgentExecutor 详解与实战
  • Stable Diffusion 模型原理与本地部署实践
  • CppCoro C++ 协程异步编程实战指南
  • FossFLOW:开源等距图表工具,构建立体技术文档
  • 前端文件下载实战:从原理到最佳实践
  • Arduino BLDC 机器人 IMU 角度读取与 PID 互补滤波控制
  • iPad 端 Obsidian 结合 Gitee 实现 Git 同步实战
  • FAIR plus 机器人全产业链接会:聚焦具身智能与全球协作
  • OpenClaw Skills 核心原理与开发实战
  • C++ 手写线程池:基于策略模式实现日志模块
  • 两个月从入门到独立进行漏洞挖掘的渗透测试指南
  • C++ STL list 容器底层实现分析
  • Web3 开发入门:概览与开发环境搭建
  • PyCharm 安装 Python 模块失败?常见 pip 报错原因与解决方案
  • Node.js 在 Windows 上的安装与配置详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online