PiにおけるCodemode:ツール呼び出しをオーケストレーションするためのJavaScriptサンドボックス

概要 – Codemodeは、LLMがハーネス内で短いJavaScriptスクリプトを実行し、ツール呼び出しをオーケストレーションできるようにします

Codemodeは、Piのハーネス側(bashツールが実行される実行サンドボックス内ではありません)に存在する、サンドボックス化されたJavaScript環境(WASMランタイム上のQuickJS)です。小さなスクリプトを書くことで、LLMは複数のツール呼び出しを構成し、中間状態を保存し、すべての中間出力をモデルのトークンウィンドウに送信することなくMCP APIを呼び出すことができます。これにより、ターン数が削減され、コンテキストのオーバーフローが回避され、複雑なワークフロー(画像生成、分類、サブエージェントループなど)が実現可能になります。


Codemodeの仕組み – 脳と手をつなぐ言語レベルのブリッジ

  • 脳と手 – 脳(信頼されたハーネス)と手(bashツールが実行されるUnixサンドボックス)は、異なる信頼レベルを持つ分離されたファイルシステムです。Codemodeは脳内で実行され、LLMにハーネスへのプログラム可能なインターフェースを提供します。
  • JavaScript関数としてのツール呼び出し – サンドボックス内で、LLMは tools.bash、models.generateImages、models.classify などを通常の非同期関数として呼び出すことができます。サンドボックスは生のテキストではなく構造化されたオブジェクト(JSON)を返すため、モデルはクリーンなデータを受け取ります。
  • 状態の永続化 – store(key, value) への呼び出しはセッションのトランスクリプトにデータを書き込みます。後の呼び出しで load(key) を使用してそれを取得できます。これにより、大きなペイロードを再送信することなく、多段階の推論が可能になります。
  • 並行処理 – 環境がJavaScriptであるため、LLMは Promise.all を起動して最大4つの同時ツール実行(Piの組み込み制限)を行い、その結果を処理できます。

利点 – LLM駆動型エージェントにとってCodemodeが重要な理由

  1. コンテキスト使用量の削減 – 完全な中間ツールログではなく、最終的なスクリプト出力のみがモデルに注入されます。
  2. 構成可能なワークフロー – LLMはループ、条件分岐、並列呼び出しを記述でき、アドホックなツール呼び出しを適切なプログラムに変えることができます。
  3. 直接的なMCPアクセス – 通常のツールとして公開されていないMCP API(画像生成、分類器など)をスクリプトから呼び出せるようになり、特注の拡張機能が不要になります。
  4. 決定論的なデータ交換 – models.* およびMCP呼び出しからの構造化されたJSONにより、壊れやすいテキスト解析を回避できます。
  5. サブエージェントオーケストレーションの改善 – スクリプトは外部のCLIラッパーに頼ることなく、サブエージェントを生成および調整できます。

例:Codemodeによる画像生成

const [painter] = await models.getAvailableOfType("image");
const result = await models.generateImages(painter, {
  input: [{ type: "text", text: "A cute little puppy sitting on a grassy lawn, soft natural light, photorealistic" }]
});
if (result.stopReason !== "stop") return result.errorMessage;
for (const block of result.output) {
  if (block.type === "image") image(block); // sends image back to LLM
  else text(block.text);
}

このスクリプトは内部の画像モデルを呼び出し、画像バイトを含むオブジェクトを受け取り、単一の image() 呼び出しでそれをLLMに転送します。


例:GitHub Issueのバルク感情分類

const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
const r = await tools.bash({
  command: "gh issue list --state open --limit 100 --json number,title,body,comments"
});
const issues = JSON.parse(r.output);
const results = await Promise.all(issues.map(async issue => {
  const res = await models.classify(jev, {
    state: {
      title: issue.title,
      body: (issue.body || "").slice(0,4000),
      comments: issue.comments.slice(-5).map(c => c.body.slice(0,800))
    },
    questions: { /* sentiment, frustration, kind */ }
  });
  return { n: issue.number, title: issue.title, ...res.answers };
}));
store("sentiment_results", results);
return results.filter(r=>!r.error).sort((a,b)=>b.frustration.score-a.frustration.score).slice(0,12)
  .map(r=>`#${r.n} ${r.frustration.score.toFixed(2)} [${r.kind.choice}] ${r.title}`);

このスクリプトは100件のIssueを取得し、並行して分類し、完全な結果を保存し、簡潔なランキングを返します。


MCPとの相互作用 – 進歩的な発見と制限

  • 進歩的なツール発見 – Codemode内では、LLMは tools.mcp__sentry__find_organizations({}) を呼び出し、返された組織を反復処理してプロジェクトをクエリできます。ハーネスは structuredContent フィールドを提供し、さらなる呼び出しを決定論的にします。
  • 現在の課題 – 多くのMCPサーバーは依然としてJSONではなく自由形式のテキストを返すため、構造化データを期待するCodemodeの動作が損なわれます。一貫性のないバッチサイズや大きなバイナリサポートの欠如も失敗の原因となります。
  • 二重エスケープの問題 – MCPサーバー自体がCodemodeを実行する場合(CloudflareのMCPなど)、LLMはJavaScriptの中にJavaScriptをネストすることになり、二重のJSONエスケープが発生してデバッグが困難になります。

コミュニティのフィードバック – ユーザーの意見

"これとCloudflareの投稿の間には、言葉は多いが単純なシステムレベルの全体像がない。…Code modeは基本的に、LLMのためのホモアイコニシティとアクターセマンティクスを再発見しているようなものだ。" – @ssivark

"もしあなたが私と同じで、記事やコメントを読んでも『codemode』が何なのかまだ疑問に思っているなら:それは、ハーネスに小さなjsサンドボックスを与えて、ツール呼び出しを構成し、それらが返すデータをコンテキストに読み込む前に操作できるようにするものだ。" – @ohgodhelpplease

"なぜほとんどのcodemode実装がJavascriptを選択するのかわからない。私はcodemodeの言語としてbashを使用するエージェントをプロトタイプしたが、それは同様にうまく機能し、教える必要もなかった。" – @ylxdzsw

"Codemodeはターン数を約1桁削減する。" – @lionkor

"Piの約束は、基本的にbashさえあれば他のツールは必要ないということだった。最小限の侵襲的なアプローチは、ツール呼び出しを仮想bashコマンドとして注入することだっただろう。Code modeは不要だ。" – @dnlgrmm

これらのコメントは、ターン数削減に対する熱意と、言語の選択、複雑さ、そして純粋なbashワークフローという当初のビジョンに対する懸念の両方を浮き彫りにしています。


制限事項と未解決の疑問

  • 耐久性 – 長時間実行されるスクリプトにはチェックポイントが必要になる場合があります。現在の実装には組み込みのスナップショット機能がありません。
  • 言語の選択 – JavaScriptは便利ですが唯一の選択肢ではありません。一部のユーザーは、決定論的な実行のためにBashやStarlarkを使用して成功したと報告しています。
  • バイナリの処理 – MCPはまだバイナリブロブをストリーミングしないため、大きな画像やファイルには依然として回避策(事前署名付きURL)が必要です。
  • MCP発見のスケーラビリティ – 複数のMCPサーバー間でツール検索をファンアウトする標準化された方法がなく、創発的な動作が壊れやすくなる可能性があります。

今後の方向性 – CodemodeをLLMのためのファーストクラスのプログラミングレイヤーにする

  • 決定論的な構成言語 – StarlarkやLispスタイルの言語がJavaScriptに取って代わり、参照透過性を保証する可能性があります。
  • ワークフローエンジン – 耐久性のあるワークフローエンジン(Temporalなど)を統合することで、信頼性の高いスナップショットと再試行を提供できる可能性があります。
  • より良いMCPコントラクト – outputSchema と一貫したJSON応答を強制することで、Codemodeと外部サービス間のブリッジがスムーズになります。
  • 統合されたハーネス設計 – LLMを(トークン生成器ではなく)言語ランタイムの参加者として扱うことで、ホモアイコニックなオペレーティングシステムのビジョンに近い、新しいクラスの「コードモード対応」ハーネスが生まれる可能性があります。

Codemodeは銀の弾丸ではありませんが、モデルにハーネス内の軽量でサンドボックス化されたプログラミングサーフェスを提供することで、よりリッチでオーバーヘッドの低いLLM駆動型自動化への実用的な道を提供します。

Sources