跳转至

上下文引擎

上下文引擎控制 OpenClaw 如何为每次运行构建模型上下文:包含哪些消息、如何总结较早的历史,以及如何跨子代理边界管理上下文。

OpenClaw 自带内置的 legacy 引擎,并默认使用它。仅当您希望获得不同的组装、压缩或跨会话召回行为时,才需要安装并选择插件引擎。

快速开始

1. 检查当前生效的引擎

openclaw doctor
# or inspect config directly:
cat ~/.openclaw/openclaw.json | jq '.plugins.slots.contextEngine'

2. 安装插件引擎

上下文引擎插件的安装方式与任何其他 OpenClaw 插件相同。
openclaw plugins install @martian-engineering/lossless-claw
openclaw plugins install -l ./my-context-engine

3. 启用并选择引擎

// openclaw.json
{
  plugins: {
    slots: {
      contextEngine: "lossless-claw", // must match the plugin's registered engine id
    },
    entries: {
      "lossless-claw": {
        enabled: true,
        // Plugin-specific config goes here (see the plugin's docs)
      },
    },
  },
}

安装并配置后,重启网关。

4. 切换回 legacy(可选)

将 contextEngine 设置为 "legacy"(或直接删除该键——"legacy" 是默认值)。

工作原理

每次 OpenClaw 运行模型提示词时,上下文引擎都会在四个生命周期节点参与其中:

1. 摄取(Ingest)

当新消息添加到会话时调用。引擎可以在自己的数据存储中存储或索引该消息。

2. 组装(Assemble)

在每次模型运行之前调用。引擎返回一组有序的消息(以及可选的 systemPromptAddition),使其符合 token 预算。

3. 压缩(Compact)

当上下文窗口已满或用户运行 /compact 时调用。引擎会总结较早的历史记录以释放空间。

4. 回合结束后(After turn)

在运行完成后调用。引擎可以持久化状态、触发后台压缩或更新索引。

引擎还可以实现可选的 maintain() 方法,用于在引导、成功回合或压缩之后进行转录维护(通过 runtimeContext.rewriteTranscriptEntries() 进行安全重写)。将 info.turnMaintenanceMode: "background" 设置为以延迟工作的方式运行,而不是阻塞回复。

当排队的预算压缩接受后台维护时,它会使已准备的运行时在维护、合并重跑和引擎销毁期间保持存活。接受并不表示清理已完成。请从引擎方法和 dispose() 返回异步工作,以便宿主在释放其资源之前可以等待其完成。

逻辑回合同样会在引擎销毁期间保留其受管理的供应注册表。当该注册表从另一次检查中复制了运行时引擎时,其记录的捐赠方依赖关系可以在捐赠方检查退役后保持引擎可用。供应注册表一经退役,仍会拒绝新的逻辑回合;现有的引擎工作会保留其物理资源,直到清理完成。原始注册的生命周期仍由调用方所有。

对于随附的非 ACP Codex 运行框架(harness),OpenClaw 通过将组装后的上下文映射到 Codex 开发者指令和当前回合提示词中,应用相同的生命周期。Codex 仍然拥有其原生的线程历史和原生压缩器。

子代理生命周期(可选)

OpenClaw 会调用两个可选的子代理生命周期钩子:

prepareSubagentSpawn method (path)
在子运行开始前准备共享的上下文状态。该钩子接收父/子会话键、contextMode(isolated 或 fork)、可用的转录 ID/文件及可选的 TTL。如果它返回一个回滚句柄,OpenClaw 会在准备成功但生成失败时调用它。请求 lightContext 并解析为 contextMode="isolated" 的原生子代理生成会故意跳过此钩子,以便子代理从轻量级引导上下文开始,不包含上下文引擎管理的生成前状态。
onSubagentEnded method (path)
在子代理会话完成或被清理时执行清理。

系统提示词附加内容

assemble 方法可以返回一个 systemPromptAddition 字符串。OpenClaw 会将其前置到本次运行的系统提示词之前。这样,引擎无需静态工作区文件即可注入动态召回指南、检索指令或上下文感知提示。

legacy 引擎

内置的 legacy 引擎保留了 OpenClaw 的原始行为:

  • 摄取(Ingest):无操作(会话管理器直接处理消息持久化)。
  • 组装(Assemble):透传(运行时中现有的 sanitize → validate → limit 管道负责上下文组装)。
  • 压缩(Compact):委托给内置的摘要压缩,它会从较早的消息创建单一摘要,并保持最近的消息完整。
  • 回合结束后(After turn):无操作。

legacy 引擎不会注册工具,也不会提供 systemPromptAddition。

当未设置 plugins.slots.contextEngine(或将其设置为 "legacy")时,将自动使用此引擎。

禁用或拒绝所选插件会保留插槽偏好,但当引擎的注册所有者标识出该插件、或引擎 ID 与插件 ID 匹配时,通常会使用 legacy。在无注册的冷启动之后,不同的引擎 ID 无法标识其所属插件:解析过程仍会报告缺失的引擎。全局禁用插件时,无论此所有者映射如何,都会使用 legacy。当运行时注册可用时,重新启用插件会恢复保留的选择。已启用但缺失或失败的选择仍遵循下面的故障隔离行为。

插件引擎

插件可以使用插件 API 注册上下文引擎:

import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";

// `buildContext`, `countTokens`, and `commitAcceptedTurn` below are your own
// plugin's helpers to implement. They are not part of the plugin SDK.
// `buildMemorySystemPromptAddition` is real SDK surface, imported above.

export default function register(api) {
  api.registerContextEngine("my-engine", (ctx) => ({
    info: {
      id: "my-engine",
      name: "My Context Engine",
      ownsCompaction: true,
      acceptedHostParams: ["sessionKey", "runtimeContext"],
      transcriptSemantics: {
        currentTurnFence: "before-current-turn-entry-v1",
        turnAdvancementIdempotency: "atomic-idempotent-v1",
      },
    },

    async ingest({ sessionId, message, isHeartbeat }) {
      // Store the message in your data store
      return { ingested: true };
    },

    async assemble({
      sessionId,
      sessionKey,
      messages,
      tokenBudget,
      availableTools,
      citationsMode,
    }) {
      // Return messages that fit the budget
      return {
        messages: buildContext(messages, tokenBudget),
        estimatedTokens: countTokens(messages),
        systemPromptAddition: buildMemorySystemPromptAddition({
          availableTools: availableTools ?? new Set(),
          citationsMode,
          agentSessionKey: sessionKey,
        }),
      };
    },

    async compact({ sessionId, force }) {
      // Summarize older context
      return { ok: true, compacted: true };
    },

    async commitTurn({ advancementKey, messages }) {
      // Atomically store the accepted turn and advancementKey. Return
      // "duplicate" when that exact key was committed by an earlier retry.
      return await commitAcceptedTurn({
        advancementKey,
        messages,
      });
    },
  }));
}

工厂 ctx 包含可选的 config、agentDir 和 workspaceDir 值,以便插件可以在首次生命周期调用之前初始化按代理或按工作区划分的状态。在非旧版 assemble() 调用之前,宿主会完成已注册的异步记忆提示准备工作。同步的 buildMemorySystemPromptAddition(...) 辅助函数读取该不可变的运行快照;请将所提供的工具、引用、代理和会话上下文原样传递。

然后在配置中启用它:

{
  plugins: {
    slots: {
      contextEngine: "my-engine",
    },
    entries: {
      "my-engine": {
        enabled: true,
      },
    },
  },
}

ContextEngine 接口

必选成员:

成员 种类 用途
info 属性 引擎 ID、名称、版本、接受的宿主参数,以及它是否拥有压缩能力
ingest(params) 方法 存储单条消息
assemble(params) 方法 为模型运行构建上下文(返回 AssembleResult)
compact(params) 方法 总结/缩减上下文

设置 info.acceptedHostParams 以限制引擎接收的由宿主添加的生命周期字段。当前键为 sessionKey、prompt、runtimeSettings、sessionTarget、runtimeContext 和 abortSignal。OpenClaw 会将该声明与每个生命周期方法可用的字段取交集,因此未声明或未知的键永远不会被注入。abortSignal 控制 maintain() 的可选协作式取消;现有的压缩操作中止信号始终保留。未声明此属性的引擎会接收所有当前宿主字段;当引擎验证更窄的输入形状时,应声明一个显式列表(包括 [])。

对于持久化的已准入轮次,请同时声明以下转录语义:

  • currentTurnFence: "before-current-turn-entry-v1"
  • turnAdvancementIdempotency: "atomic-idempotent-v1"

并将 commitTurn(...) 实现为以 advancementKey 为键的原子性、幂等性写入。首次写入返回 { status: "committed" },当宿主重试携带已提交的键时返回 { status: "duplicate" }。messages 载荷仅包含从已准入的用户条目到已接受的终止条目的闭区间范围。在引导或重建期间需要更早转录的引擎,应通过转录游标 API readSessionTranscriptVisibleMessageDelta(...) 读取。在引导、维护、组装和重试期间进行的轮次前转录读取,将看到已准入用户消息之前的精确转录前缀。宿主仅对已接受的成功轮次调用 commitTurn;失败或中止的轮次不会推进上下文引擎的状态。

在已接受轮次的最终确认返回 committed 或 duplicate 之后,宿主还会通过同一维护调度器提供 maintain()。声明 turnMaintenanceMode: "background" 的引擎会运行延迟维护。后台工作会保留该逻辑轮次的引擎及所提供资源,直到其稳定下来,而不会让回复完成等待维护。声明 "foreground" 或省略该模式的引擎会以内联方式运行维护:已接受轮次的最终确认和回复完成都会等待其稳定。内联维护接收已提交的会话目标、提供商/模型/Token 预算、LLM 能力和转录重写能力,但不具备后台压缩权限。转录重写会重新打开持久化目标,而不需要活动的会话管理器。失败的提交保持排队状态,不会触发此交接。运行前的发件箱恢复会在引导和组装之前调和摄取;它不会启动并发的后台维护。维护是尽力而为的,并非针对每个已提交轮次的崩溃持久化任务,并且单次调用不保证引擎会处理完所有待处理的压缩。

对于这些已准入的轮次,嵌入式工具循环 assemble() 会接收当前轮次之前的历史记录,其 Token 预算会为待处理的用户和工具消息预留空间。宿主会在下一次模型请求之前将这些待处理消息追加到组装好的历史记录中,因此它们保持可见而无需进入引擎的存储。

如果缺少完整的声明和方法,OpenClaw 会为整个逻辑轮次(包括重试)使用旧版上下文路径。已配置的上下文引擎槽位不会改变,OpenClaw 会在下一个逻辑轮次再次尝试该配置的引擎。如果声明的围栏因精确的已准入消息缺失、被重写或已被转录游标越过而无法被满足,则同样会应用这种轮次本地的降级。

assemble 返回一个包含以下内容的 AssembleResult:

messages Message[] (path) 必选
要发送给模型的有序消息。
estimatedTokens number (path) 必选
引擎对组装上下文中总 Token 数的估计。OpenClaw 使用它进行压缩阈值决策和诊断报告。
systemPromptAddition string (path)
前置附加到系统提示词中。
promptAuthority "assembled" | "preassembly_may_overflow" (path)
控制运行器使用哪个 Token 估计来进行预防性溢出预检查。默认为 "assembled",这意味着对于不拥有压缩能力的引擎,仅检查组装后提示词的估计值。设置 ownsCompaction: true 的引擎会自行管理提示词准入,因此 OpenClaw 默认跳过通用的提示词前预检查。仅当你的组装视图可能掩盖底层转录中的溢出风险时,才设置 "preassembly_may_overflow";此时运行器会保持通用预检查处于活动状态,并在决定是否预防性压缩时,取组装估计值与组装前(无窗口)会话历史估计值中的较大者。无论哪种方式,你返回的 messages 仍然是模型所见的内容——promptAuthority 仅影响预检查。
contextProjection ContextEngineProjection (path)
可选投影生命周期,适用于具有持久后端线程的主机(例如 Codex app-server)。带有稳定 epoch 的 mode: "thread_bootstrap" 要求主机在每个 epoch 注入一次已组装的上下文,并复用后端线程直到 epoch 改变,而不是每轮都重新投影。对于正常的逐轮投影,请省略此字段。

compact 返回一个 CompactResult。当压缩改变活动会话身份时,result.sessionTarget(一个携带会话身份和存储范围的类型化 ContextEngineSessionTarget)标识下一次重试或轮次必须使用的后继会话;result.sessionId 镜像后继 id。

可选成员:

成员 类型 用途
bootstrap(params) 方法 为会话初始化引擎状态。当引擎第一次看到某个会话时调用一次(例如导入历史记录)。
maintain(params) 方法 在 bootstrap、成功轮次或压缩之后进行转录维护。使用 runtimeContext.rewriteTranscriptEntries() 进行安全重写。
ingestBatch(params) 方法 将已完成轮次作为批次摄取。在一次运行完成后调用,传入该轮次的全部消息。
afterTurn(params) 方法 运行后的生命周期工作(持久化状态、触发后台压缩)。
prepareSubagentSpawn(params) 方法 在子会话开始前为其设置共享状态。
onSubagentEnded(params) 方法 在子代理结束后进行清理。
dispose() 方法 当所属操作结束且任何保留的工作完成后,释放引擎实例资源。

宿主还会释放为独立压缩、Doctor 检查和子代理生命周期钩子解析的实例。排队中的子代理生成会保留其实例,直到调度成功或准备被回滚;返回排队接受并不会结束该生命周期。超时的压缩会保留其实例,直到底层插件工作稳定下来。 Gateway 关闭会释放排队的实例,但不会回滚它们的准备,因此持久化的排队工作可以在重启后恢复。显式取消仍会回滚准备。

前台引擎释放与代理清理截止时间共享:默认为 10 秒,可通过 OPENCLAW_AGENT_CLEANUP_TIMEOUT_MS 调整。清理停滞时会记录警告并让已完成的回复返回;它不会取消插件待处理的释放。清理失败和超时保留现有的一次性 CLI 清理失败结果;它们不保证资源已关闭。

运行时设置

在 OpenClaw 内部运行的生命周期钩子会收到一个可选的 runtimeSettings 对象。这是一个带版本、只读的内部生产者/消费者 API 表面:OpenClaw 为选定的上下文引擎生成它,上下文引擎在生命周期钩子内部消费它。它不会直接渲染给用户,也不会创建专门的报告表面。

  • schemaVersion:当前为 1
  • runtime:OpenClaw 宿主、运行时模式(normal、fallback 或 degraded)以及可选的 harness/运行时 ID
  • contextEngineSelection:选定的上下文引擎 ID 和选择来源
  • executionHost:调用钩子的表面的宿主 ID 和标签
  • model:请求的模型、已解析的模型、提供商以及可选的模型族
  • limits:已知时的提示词 token 预算和最大输出 token 数
  • diagnostics:已知时的关闭 fallback 和降级原因代码

可能未知的字段以 null 表示;判别器字段(如运行时模式和选择来源)保持非空。限制宿主参数并接受 runtimeSettings 的引擎必须在 info.acceptedHostParams 中包含它。

宿主要求

上下文引擎可以在 info.hostRequirements 上声明宿主能力要求。OpenClaw 在开始操作前检查这些要求,当选定的运行时无法满足时,会以描述性错误安全失败(fail closed)。

对于代理运行,当引擎必须通过 assemble() 控制实际模型提示词时,声明 assemble-before-prompt:

info: {
  id: "my-context-engine",
  name: "My Context Engine",
  hostRequirements: {
    "agent-run": {
      requiredCapabilities: ["assemble-before-prompt"],
      unsupportedMessage:
        "Use the native Codex or OpenClaw embedded runtime, or select the legacy context engine.",
    },
  },
}

原生 Codex 和 OpenClaw 嵌入式代理运行满足 assemble-before-prompt。通用 CLI 后端不满足,因此需要它的引擎会在 CLI 进程启动前被拒绝。

故障隔离

OpenClaw 将选定的插件引擎与核心回复路径隔离开来。如果非 legacy 引擎缺失、未通过契约验证、在工厂创建期间抛出异常,或在生命周期方法中抛出异常,OpenClaw 会隔离该引擎(针对当前 Gateway 进程),并将上下文引擎工作降级为内置的 legacy 引擎。错误会与失败的操作一起记录,以便操作员修复、更新或禁用插件,而不会让代理静默。

进入工厂之前发生的宿主准入和资源所有权失败会直接传播,而不会隔离引擎。因调用方工作被取消而导致的工厂拒绝也会直接传播,不会进行隔离或回退。完成清理拥有独立的异步生命周期,因此关闭调用方作用域不会阻止其工厂运行。

Host requirement failures are different: when an engine declares that a runtime lacks a required capability, OpenClaw fails closed before starting the run. That protects engines that would corrupt state if they ran in an unsupported host.

主机要求失败有所不同:当引擎声明某个运行时缺少所需能力时,OpenClaw 会在开始运行前以失败关闭方式处理。这可以保护那些如果在不受支持的主机上运行会损坏状态的引擎。

ownsCompaction

ownsCompaction 控制 OpenClaw 运行时内置的尝试内自动压缩是否在该次运行中保持启用:

ownsCompaction: true

引擎拥有压缩行为。OpenClaw 会禁用 OpenClaw 运行时内置的自动压缩以及通用的提示前溢出预检查(针对该次运行),并且引擎的 compact() 实现负责处理 /compact、提供商溢出恢复压缩,以及它希望在 afterTurn() 中执行的任何主动压缩。当引擎从 assemble() 返回 promptAuthority: "preassembly_may_overflow" 时,OpenClaw 仍会运行提示前溢出保护。

ownsCompaction: false 或未设置

OpenClaw 运行时内置的自动压缩仍可能在提示执行期间运行,但活动引擎的 compact() 方法仍会被调用,用于 /compact 和溢出恢复。

Warning

ownsCompaction: false 并不意味着 OpenClaw 会自动回退到旧引擎的压缩路径。

这意味着存在两种有效的插件模式:

实现你自己的压缩算法,并设置 ownsCompaction: true。

设置 ownsCompaction: false,并让 compact() 调用来自 openclaw/plugin-sdk/core 的 delegateCompactionToRuntime(...),以使用 OpenClaw 的内置压缩行为。

对于活动的非拥有引擎,空操作 compact() 是不安全的,因为它会禁用该引擎槽位的常规 /compact 和溢出恢复压缩路径。

配置参考

{
  plugins: {
    slots: {
      // Select the active context engine. Default: "legacy".
      // Set to a plugin id to use a plugin engine.
      contextEngine: "legacy",
    },
  },
}

Note

该槽位在运行时是排他的——对于给定的运行或压缩操作,只会解析一个已注册的上下文引擎。其他已启用的 kind: "context-engine" 插件仍可以加载并运行其注册代码;plugins.slots.contextEngine 只选择当 OpenClaw 需要上下文引擎时解析哪个已注册的引擎 ID。

Note

插件卸载: 当你卸载当前被选为 plugins.slots.contextEngine 的插件时,OpenClaw 会将该槽位重置回默认值(legacy)。相同的重置行为也适用于 plugins.slots.memory。无需手动编辑配置。

与压缩和记忆的关系

压缩

压缩是上下文引擎的一项职责。旧引擎委托给 OpenClaw 的内置摘要功能。插件引擎可以实现任何压缩策略(DAG 摘要、向量检索等)。

记忆插件

记忆插件(plugins.slots.memory)与上下文引擎是分开的。记忆插件提供搜索/检索;上下文引擎控制模型看到的内容。它们可以协同工作——上下文引擎可能在组装期间使用记忆插件数据。希望使用活动记忆提示路径的插件引擎应使用来自 openclaw/plugin-sdk/core 的 buildMemorySystemPromptAddition(...),它会将主机准备好的记忆提示部分转换为可直接前置的 systemPromptAddition,而不会暴露记忆插件布局。

会话修剪

无论哪个上下文引擎处于活动状态,在内存中修剪旧工具结果仍会运行。

提示

  • 使用 openclaw doctor 验证你的引擎是否正确加载。
  • 如果切换引擎,现有会话将继续使用其当前历史。新引擎将接管未来的运行。
  • 引擎错误会被记录,并且所选插件引擎会在当前 Gateway 进程中被隔离。OpenClaw 会在用户轮次中回退到 legacy,以便回复可以继续,但你仍应修复、更新、禁用或卸载损坏的插件。
  • 对于开发,使用 openclaw plugins install -l ./my-engine 来链接本地插件目录,而无需复制。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw