technical architecture plugins contextengine

ContextEngine 徹底解説:OpenClaw 2026.3.7 はコンテキスト管理をどうプラグインに変えたか

OpenClaws.io Team

OpenClaws.io Team

@openclaws

March 9, 2026

10 分で読める

ContextEngine 徹底解説:OpenClaw 2026.3.7 はコンテキスト管理をどうプラグインに変えたか

AI エージェントが役に立つ答えを返すか、それっぽいデタラメを自信満々に返すか。その差を一番大きく左右するのが、コンテキスト管理だ。会話の履歴、ツールの出力、外部の知識——これらをモデルの限られたコンテキストウィンドウにどう詰め込むか。OpenClaw 2026.3.7 まで、このロジックはコアにハードコードされていた。今はプラグインだ。

何が問題だったか

これまでの OpenClaw はスライディングウィンドウ圧縮を使っていた。会話が長くなりすぎたら、古いメッセージを要約して新しいメッセージのスペースを作る。動くには動く。でも不満もあった:

  • 圧縮すれば情報が落ちる。コーディングエージェントが50ターン前に書いた関数を参照したい?要約には残っていない。
  • セッションを閉じたら全部忘れる。エージェントにセッション間の記憶がない。
  • 戦略を変えられない。RAG ベースの組み立て、会話ブランチ、カスタムトークン予算——どれもきれいに差し込む方法がなかった。hack するしかない。

コミュニティは色々やった。内部モジュールの monkey-patch、コアの fork、外部オーケストレーション層の構築。どれも長くは持たなかった。

ContextEngine:コンテキスト管理を差し替え可能なプラグインに

2026.3.7 はシンプルに、コンテキストのライフサイクル全体をインターフェースに抽出して、プラグインスロットとして開放した。プラグインを書いて、このインターフェースを実装すれば、エージェントのコンテキストロジックを丸ごと引き受けられる。

どう組み込むか

プラグイン 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 をラップして、スライディングウィンドウの挙動をそのまま維持する。既存ユーザーには影響ゼロ。

7つのフック——コンテキストの一生を管理する

ContextEngine インターフェースは7つのフックを提供する。エンジンの起動からサブエージェントの完了まで、重要なポイントすべてに介入できる:

1. `bootstrap()`

エンジンの初期化時に一度だけ呼ばれる。ベクトル DB への接続、グラフ構造の構築、前回保存した状態の読み込みはここで。

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

2. `ingest(message: Message)`

新しいメッセージが来るたびに発火する——ユーザーの入力、アシスタントの応答、ツールの出力、何でも。ここで保存方法とインデックス方法を決める。

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 がトークン予算を持ってやってくる。「コンテキストを組み立ててくれ」。返したものが、そのままモデルに見える。

エンジンごとにまったく違うアプローチが取れる:

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()`

コンテキストがハードリミットを超えた。ダイエットの時間だ。デフォルトエンジンは古いメッセージを要約する。プラグインならグラフから関連度の低いノードを刈り込む、ベクトルストアに逃がす、あるいは assemble() で既に収まっているなら何もしない——選択肢はいくらでもある。

5. `afterTurn(turn: Turn)`

1ターン完了——ユーザーが話し、エージェントが答えた。状態の永続化、インデックスの更新、バックグラウンドジョブの起動に最適。

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

エージェントがサブエージェントを生成しようとしている。コンテキストをどれだけ渡す? 全部渡せばトークン予算が爆発する。何も渡さなければ何もわからない。このフックで、子エージェントが持っていく情報を正確にコントロールできる。

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)`

サブエージェントが仕事を終えて戻ってきた。その結果を親コンテキストにどう取り込む? 全部マージ、要約、つまみ食い——決めるのはあなただ。

アーキテクチャ:スロットとフックの違い

ContextEngine はプラグインレジストリの中でスロットとして実装されている。フックじゃない。フックは加算的——10個のプラグインが同時に onMessage をリッスンできる。スロットは排他的。ContextEngine は一度に1つだけ。

┌─────────────────────────────────────┐
│           Plugin Registry           │
│                                     │
│  Hooks(加算的):                    │
│    onMessage  → [plugin1, plugin2]  │
│    onTool     → [plugin3]           │
│                                     │
│  Slots(排他的):                    │
│    contextEngine → my-engine        │
│    (default: LegacyContextEngine)   │
└─────────────────────────────────────┘

起動時に OpenClaw が plugins.slots.contextEngine の設定値を読み、登録済みのファクトリを見つけて、インスタンス化する。見つからなければ? 起動失敗、エラーメッセージ付き。サイレントフォールバックはしない——これは意図的な設計だ。自分がどのコンテキストエンジンを動かしているか、常に把握していてほしい。

サブエージェント間のアイソレーションは AsyncLocalStorage で実現。各子エージェントは独立したスコープのランタイムを持ち、プラグインの状態が他のエージェントに漏れることはない。

もう使われ始めている

Lossless-Claw

GitHub · Martian Engineering

最初の本格的な ContextEngine プラグイン。スライディングウィンドウ圧縮を DAG ベースの要約システムに置き換え、トークンリミット内で全メッセージの原文を保持する。

  • メッセージ1件が DAG の1ノードになる
  • トピックの関連性でノードが「エピソード」にまとまる
  • 予算オーバー時、古いエピソードは要約されるが原文はグラフに残る
  • 後のターンで古い内容に言及すると、要約ではなく原文を取り出せる

プログラマーなら実感があるはず。100ターン前に定義した関数が急に必要になる。スライディングウィンドウならとっくに消えている。Lossless-Claw なら掘り出せる。

MemOS Cloud Plugin

GitHub · MemTensor

やることは1つ。エージェントにセッションをまたぐ記憶を持たせる。

  • 起動時に MemOS Cloud から関連する記憶を呼び出す
  • 各ターン終了後、新しい内容を保存
  • コンテキスト組み立て時に記憶を注入

先週の会話、あなたの好み、進行中のプロジェクト——エージェントが全部覚えている。毎回説明し直す必要がない。

次に何が来るか

GitHub の issue と Discord を見ると、こんなものが動いている:

  • RAG ネイティブ組み立て:メッセージ履歴の代わりに検索したドキュメントチャンクでコンテキストを構成。OpenClaw が対話型検索エンジンに。
  • マルチエージェント共有メモリ:複数エージェントがナレッジグラフを共有。協調ワークフローで情報の重複同期が不要に。
  • トークン予算の自動最適化:使っているモデルの価格と性能に応じて、コンテキストの配分を動的に調整。
  • 会話ブランチ:ツリー構造のコンテキスト。別の方向に分岐して探索し、いつでも戻れる。履歴は失われない。

なぜこれが転換点なのか

OpenClaw はチャネルもモデルもツールも追加できた。でもコンテキスト——エージェントの「頭の中がどう動いているか」——はコアの中のブラックボックスだった。触れるのは fork する人だけ。

ContextEngine がその箱をこじ開けた。ここから先は連鎖する:

  1. 1.プラグイン開発者が、エージェント UX で一番難しく一番価値のある領域——コンテキスト品質——で勝負できるようになった。
  2. 2.エンタープライズユーザーはコンプライアンス対応ができる。データをモデルに渡す前にマスキング、保持ポリシーの強制適用、プロンプト入力の完全監査。
  3. 3.研究者は新しいコンテキスト戦略を試すのにプロジェクト全体を fork しなくていい。
  4. 4.モデルプロバイダーは自社アーキテクチャに最適化したプラグインを出せる。ロングコンテキストモデルとショートコンテキストモデルでは、そもそも戦略が違うべきだ。

フレームワークからプラットフォームへの移行とは、こういうことだ。リブランドでもない。ビジョンを語るブログ記事でもない。エコシステムが自己強化するループを生む、具体的なアーキテクチャの変更だ。プラグインが増える → ユーザーが増える → 開発者が増える → プラグインがもっと増える。このループが回り始めたら、止まらない。

ロブスターは新しいハサミを手に入れた。

最新情報を受け取る

新機能や連携情報をお届け。スパムなし、いつでも解除可能。