こんにちは、みねです。
AIワーカーに「ここは触るな」と言っても、事故は起きます。言葉による制約には限界がある。
今回はWASM(WebAssembly)を使って、AIワーカーが触れる範囲を技術的に制限する方法を解説します。
この記事でできるようになること
- WASMをサンドボックスとして活用する設計パターンを理解できる
- AIワーカーの変更範囲をコンポーネント境界で強制できる
- ロールバックを高速化し、障害時の復旧時間を短縮できる
対象読者
- AIワーカーの「事故半径」を小さくしたい
- プラグインやジョブの安全な分離に興味がある
- 「技術的に不可能にする」ガードレールを求めている
この記事でやらないこと
- WASMランタイム(Wasmtime、Wasmer等)の詳細な導入手順
- 本番環境でのWASM最適化
1. WASM/WASIの現在地
1.1 何が"できるようになりつつある"か
The State of WebAssembly 2025-2026によると、WASMは以下の領域で成熟しています:
┌─────────────────────────────────────────────────┐
│ WASM/WASIの進化(2025-2026) │
├─────────────────────────────────────────────────┤
│ ✅ 安定化済み │
│ - WASI Preview 1: ファイルI/O、環境変数 │
│ - Component Model: モジュール間インターフェース│
│ - 主要言語サポート: Rust, Go, C/C++, Zig │
│ │
│ 🔄 進行中 │
│ - WASI 0.3: 非同期I/O、ネットワーク │
│ - Component Model: ストリーミング │
│ - JavaScript/TypeScript: jco, componentize-js│
│ │
│ ⏳ 将来 │
│ - WASI 0.3+: より豊富なシステムアクセス │
│ - 標準ライブラリの充実 │
└─────────────────────────────────────────────────┘
1.2 サーバーサイドでのWASM
ブラウザだけでなく、サーバーサイドでもWASMが使えるようになっています:
- Wasmtime: Bytecode Allianceによるリファレンス実装
- Wasmer: エッジコンピューティング向け
- WasmEdge: クラウドネイティブ向け
サーバーサイド全般での Wasm 採用判断(コンテナ/V8 isolate との比較、適用領域)は Wasmはサーバー実装で本当に効くのか で整理しています。
2. AI時代の"サンドボックス"としての価値
2.1 なぜWASMなのか
┌────────────────────────────────────────────────┐
│ サンドボックス手法の比較 │
├────────────────┬────────────┬──────────────────┤
│ 手法 │ 分離レベル │ オーバーヘッド │
├────────────────┼────────────┼──────────────────┤
│ Dockerコンテナ │ OS │ 中〜大 │
│ VM │ ハードウェア│ 大 │
│ WASM │ ランタイム │ 小 │
│ プロセス分離 │ OS │ 中 │
└────────────────┴────────────┴──────────────────┘
WASMの特徴:
- 軽量: 起動が速い(ミリ秒オーダー)
- ポータブル: どこでも同じ動作
- Capability-based Security: 明示的に許可したリソースだけアクセス可能
2.2 AIワーカー文脈での価値
┌─────────────────────────────────────────────────┐
│ 従来: 「触るな」は言葉の約束 │
├─────────────────────────────────────────────────┤
│ handoff.md: 「secrets/は触らないでください」 │
│ │
│ → AIが無視すれば事故 │
│ → 人間のレビューで防ぐしかない │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ WASM: 技術的に不可能にする │
├─────────────────────────────────────────────────┤
│ WASMモジュール: secrets/へのアクセス権限なし │
│ │
│ → アクセスしようとしてもランタイムがブロック │
│ → 事故が構造的に起きない │
└─────────────────────────────────────────────────┘
3. 小さな導入:周辺タスクの隔離
3.1 まずは周辺から
いきなりコアロジックをWASM化しない。周辺タスクから始めます:
┌────────────────────────────────────────────────┐
│ WASM化に向くタスク(周辺から) │
├────────────────────────────────────────────────┤
│ 1. ログ整形 │
│ - 入力: 生ログ │
│ - 出力: 整形済みログ │
│ - 副作用: なし │
│ │
│ 2. 静的解析 / Lint │
│ - 入力: ソースコード │
│ - 出力: 解析結果 │
│ - 副作用: なし │
│ │
│ 3. コード生成 │
│ - 入力: テンプレート + データ │
│ - 出力: 生成コード │
│ - 副作用: なし │
│ │
│ 4. 設定ファイル生成 │
│ - 入力: パラメータ │
│ - 出力: 設定ファイル │
│ - 副作用: なし │
└────────────────────────────────────────────────┘
3.2 実装例:Linterのwasm化
// lint-plugin/src/lib.rs
use wit_bindgen::generate;
generate!({
path: "wit",
world: "linter",
});
struct LintPlugin;
impl Guest for LintPlugin {
fn run(source: String) -> Vec<LintError> {
let mut errors = Vec::new();
// 例: console.logを検出
for (line_num, line) in source.lines().enumerate() {
if line.contains("console.log") {
errors.push(LintError {
line: line_num as u32 + 1,
message: "console.log found".to_string(),
});
}
}
errors
}
}
// wit/world.wit
package local:linter;
world linter {
record lint-error {
line: u32,
message: string,
}
export run: func(source: string) -> list<lint-error>;
}
4. 運用:AIワーカーにはWASMタスクだけ許可
4.1 アーキテクチャ
┌─────────────────┐
│ Root Planner │
│ (人間 or AI) │
└───────┬─────────┘
│ タスク割り当て
▼
┌─────────────────────────────────────────────────┐
│ WASM Sandbox │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Linter │ │ Formatter │ │ Generator │ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ 許可: stdin/stdout のみ │
│ 禁止: ファイルシステム、ネットワーク │
└───────────────────────────────────────────────┼─┘
│
▼
┌─────────────────────────────────────────────────┐
│ Host Application │
│ │
│ - WASMの出力を受け取る │
│ - ファイル書き込みはHostが制御 │
│ - 変更はHostがレビュー後に適用 │
└─────────────────────────────────────────────────┘
4.2 権限の制御
# wasm-runtime-config.yaml
plugins:
linter:
module: plugins/linter.wasm
capabilities:
# stdin/stdoutのみ許可
stdin: true
stdout: true
# 以下は明示的に禁止
filesystem: false
network: false
env: false
formatter:
module: plugins/formatter.wasm
capabilities:
stdin: true
stdout: true
# 読み取り専用ファイルアクセス(限定パス)
filesystem:
read_only: true
paths:
- "src/**"
- "tests/**"
5. 失敗例:境界設計が雑だと逆に複雑化
5.1 アンチパターン
[!WARNING] いきなり全部WASM化 「安全だから」と全機能をWASM化すると、オーバーヘッドとデバッグ困難で破綻
❌ Bad: 全部WASM
├── core-logic.wasm # 巨大、デバッグ困難
├── database-client.wasm # WASI制限でDB接続困難
├── http-client.wasm # ネットワーク制限
└── file-processor.wasm # 大量ファイル処理で遅い
✅ Good: 周辺だけWASM
├── src/ # 通常のコード
├── plugins/
│ ├── linter.wasm # 副作用なし、隔離向き
│ ├── formatter.wasm # 副作用なし、隔離向き
│ └── validator.wasm # 入力検証、隔離向き
└── core/ # 通常のコード
5.2 段階的導入
Week 1-2: 評価
├── 1つのプラグインをWASM化して検証
├── オーバーヘッド計測
└── デバッグ手法確立
Month 2: 拡張
├── 2-3個のプラグインを追加
├── CI統合
└── エラーハンドリング整備
Month 3+: 安定運用
├── 新プラグインはWASM前提で設計
├── ドキュメント整備
└── チーム展開
測れる仮説と検証
仮説
- H1: プラグイン/ジョブをWASM化して隔離すると、ロールバックが高速化し、障害時の復旧時間が短縮
- H2: AIワーカーの「変更上限」をコンポーネント境界で強制できる
KPI
| 指標 | 測定方法 | 目標 |
|---|---|---|
| 変更影響範囲 | 対象ファイル数 | WASMプラグイン内に限定 |
| ロールバック時間 | 障害発生→復旧完了 | 5分以内 |
| 事故発生率 | WASMモジュール起因の本番障害 | 0件/月 |
今日から一歩
まずは今日、「副作用のないタスク」を1つ特定してください。
# ai/wasm-candidates.md
## WASM化候補(副作用なし)
1. ログ整形スクリプト
- 入力: 生ログファイル
- 出力: 整形済みログ
- 現在: Node.jsスクリプト
2. 設定バリデーション
- 入力: config.yaml
- 出力: 検証結果
- 現在: シェルスクリプト
そこから、WASM化の実験を始められます。
シリーズ記事
- AI自動運転を"ベンチマーク思考"で検証する
- MCPで"運転席と作業者"を分離して事故率を下げる
- LLM/AI開発のObservabilityを"eBPF × OTel"で最小実装する
- WASM Component Modelで"安全な拡張"を作る(本記事)
- 2026年の「AIコーディング自動運転」最前線
参考リンク
- The State of WebAssembly 2025-2026 - Uno Platform
- WASI - WebAssembly System Interface
- Component Model - WebAssembly
シリーズ全体に戻る: 親記事
