technical architecture plugins contextengine

ContextEngine 深度解析:OpenClaw 2026.3.7 如何把上下文管理变成插件

OpenClaws.io Team

OpenClaws.io Team

@openclaws

2026年3月9日

10 分钟

ContextEngine 深度解析:OpenClaw 2026.3.7 如何把上下文管理变成插件

AI agent 好不好用,很大程度上取决于一件你看不见的事:上下文管理。对话历史、工具调用的结果、外部知识——这些东西怎么塞进模型那个有限的窗口,决定了 agent 是真能帮上忙,还是一本正经地胡说八道。在 OpenClaw 2026.3.7 之前,这套逻辑是写死在核心里的。现在,它是一个插件。

原来有什么问题

OpenClaw 之前用的是滑动窗口压缩:对话太长了,就把旧消息摘要一下,给新消息让路。能跑,但很多人不满意:

  • 压缩必然丢信息。写代码的 agent 要引用 50 轮前写的函数?摘要里早就没了。
  • 关了就忘。会话一结束,上下文清零。agent 没有跨会话的记忆。
  • 想换策略没门。你想用 RAG、想做对话分支、想自己管 token 预算——没有正经的扩展口,只能 hack。

社区的解决办法五花八门:monkey-patch 内部模块、fork 核心代码、在外面套一层编排。折腾是折腾了,都不是长久之计。

ContextEngine:把上下文管理变成可替换的插件

2026.3.7 做了一件干脆的事:把上下文的整个生命周期抽成接口,开放为插件槽位(slot)。你写一个插件,实现这套接口,就能完全接管 agent 的上下文逻辑。

怎么接入

插件通过 API 注册自己的 ContextEngine:

typescript
// In your plugin's bootstrap
export default function myContextPlugin(api: PluginAPI) {
  api.registerContextEngine('my-engine', (config) => {
    return new MyCustomContextEngine(config);
  });
}

用户在配置里选一下就行:

yaml
plugins:
  slots:
    contextEngine: my-engine

什么都不配?照常用。OpenClaw 会套上 LegacyContextEngine,滑动窗口该怎么跑还怎么跑,老用户完全无感。

七个钩子,管住上下文的一生

ContextEngine 接口给了你七个钩子,从引擎启动到子 agent 收工,每个关键节点都能插手:

1. `bootstrap()`

引擎初始化时跑一次。连数据库、建图结构、加载上次存下来的状态,都在这儿干。

typescript
async bootstrap(): Promise<void> {
  this.vectorStore = await connectToVectorDB(this.config.dbUrl);
  this.sessionGraph = new DAG();
}

2. `ingest(message: Message)`

每来一条新消息就触发——不管是用户说的、agent 回的、还是工具吐出来的。你在这里决定怎么存、怎么建索引。

typescript
async ingest(message: Message): Promise<void> {
  // Add to the DAG
  const node = this.sessionGraph.addNode(message);
  // Index for retrieval
  const embedding = await embed(message.content);
  await this.vectorStore.upsert(node.id, embedding, message);
}

3. `assemble(budget: TokenBudget): AssembledContext`

最核心的一个。每次要调模型之前,OpenClaw 会带着 token 预算来问你:给我组装上下文。你返回什么,模型就看到什么。

不同的引擎在这里可以玩出完全不同的花样:

typescript
async assemble(budget: TokenBudget): AssembledContext {
  const recentMessages = this.sessionGraph.getRecent(budget.soft * 0.6);
  const relevantHistory = await this.vectorStore.query(
    this.currentQuery,
    budget.soft * 0.3
  );
  const systemContext = this.buildSystemPrompt(budget.soft * 0.1);

  return {
    system: systemContext,
    messages: [...relevantHistory, ...recentMessages],
    tokenEstimate: this.estimateTokens([systemContext, ...relevantHistory, ...recentMessages]),
  };
}

4. `compact()`

上下文撑破硬上限了,OpenClaw 喊你来瘦身。默认引擎会摘要旧消息;你的插件可以砍掉图里不相关的节点,可以往向量库里挪,也可以什么都不做——如果你在 assemble() 里就已经控制住了。

5. `afterTurn(turn: Turn)`

一轮对话结束(用户说完、agent 答完)。存个状态、写个持久化、跑个后台索引,都适合放这儿。

6. `prepareSubagentSpawn(parentContext: Context): SubagentContext`

agent 要派子 agent 出去干活了。给多少上下文?全给会撑爆 token,不给又两眼一抹黑。这个钩子让你精确控制子 agent 带走哪些信息。

typescript
prepareSubagentSpawn(parentContext: Context): SubagentContext {
  // Give the subagent a focused slice of context
  const relevantNodes = this.sessionGraph.getSubtree(parentContext.taskId);
  return {
    messages: relevantNodes.map(n => n.message),
    metadata: { parentSessionId: this.sessionId },
  };
}

7. `onSubagentEnded(result: SubagentResult)`

子 agent 干完活回来了。它带回来的结果怎么合进父级上下文?全收、摘要、还是挑着要?你说了算。

架构细节:slot 不是 hook

ContextEngine 在插件体系里是个槽位,不是钩子。区别在哪?钩子是叠加的,十个插件可以同时监听 onMessage;槽位是排他的,同一时间只能有一个 ContextEngine 在跑。

┌─────────────────────────────────────┐
│           Plugin Registry           │
│                                     │
│  Hooks(可叠加):                    │
│    onMessage  → [plugin1, plugin2]  │
│    onTool     → [plugin3]           │
│                                     │
│  Slots(只能选一个):                │
│    contextEngine → my-engine        │
│    (默认: LegacyContextEngine)      │
└─────────────────────────────────────┘

启动时 OpenClaw 读 plugins.slots.contextEngine 的配置值,找到对应的工厂方法,实例化。找不到?直接报错退出,不会偷偷回退到默认引擎——这是个明确的设计选择,避免你以为在用自定义引擎其实跑的是默认的。

子 agent 之间的隔离靠 AsyncLocalStorage:每个子 agent 有自己的运行时作用域,插件状态不会串台。

已经有人在用了

Lossless-Claw

GitHub · Martian Engineering 出品

第一个有分量的 ContextEngine 插件。用基于 DAG 的摘要系统替掉了滑动窗口,做到了在 token 上限内保留每一条原始消息。

  • 每条消息变成有向无环图里的一个节点
  • 按话题相关性自动分组成"片段"
  • 预算超了,旧片段会被摘要——但原始消息还留在图里
  • 后面如果又聊到了旧内容,引擎直接取回原文,不用将就摘要

写代码的人应该能体会:100 轮前定义的函数,突然又要用了,滑动窗口早就把它压没了,Lossless-Claw 还能翻出来。

MemOS Cloud Plugin

GitHub · MemTensor 出品

一个很轻的插件,就干一件事:让 agent 记住你

  • 启动时从 MemOS Cloud 召回跟当前话题相关的记忆
  • 每轮对话结束后把新内容存回去
  • 组装上下文时把记忆注入进来

效果就是:上周聊过的事、你的偏好、你正在做的项目——agent 都记得,不用你每次重复交代。

接下来会冒出什么

翻翻 GitHub issue 和 Discord,社区已经在搞这些:

  • RAG 原生组装:不再拼消息历史,直接用检索到的文档片段组装上下文。OpenClaw 秒变对话式搜索引擎。
  • 多 agent 共享记忆:几个 agent 共用一张知识图谱,协作干活时不用重复同步信息。
  • Token 预算自动调配:根据你用的模型贵不贵、快不快,动态调整上下文怎么分配。
  • 对话分支:树状结构,一条对话岔出去探索不同方向,随时切回来,历史不丢。

这件事为什么重要

OpenClaw 之前能加渠道、加模型、加工具,但上下文这一层——说白了就是 agent 的"脑子怎么运转"——一直是个碰不得的黑盒。

ContextEngine 把这个盒子撬开了。往后的事情会一环扣一环:

  1. 1.插件开发者终于能在 agent 体验里最难也最值钱的地方发力了——上下文质量。
  2. 2.企业用户可以做合规:数据到模型之前先脱敏、留存策略强制执行、每个 prompt 输入了什么全程可审计。
  3. 3.研究者验证新的上下文策略,再也不用 fork 整个项目。
  4. 4.模型厂商可以出官方插件,针对自家架构优化上下文组装——长窗口模型和短窗口模型,策略本来就该不一样。

这就是从框架变成平台的样子。不靠改名,不靠发公告,靠的是一个实实在在的架构改动,让生态开始自己转起来。插件越多 → 用户越多 → 开发者越多 → 插件更多。飞轮一旦转起来,就很难停下了。

龙虾又长出了一只新钳子。

订阅更新

第一时间获取新功能和玩法。放心,不会发垃圾邮件。