LLMにソースコードを直接実行させる危険性
AIにコード実行権限を与えることは絶大な力を生み出しますが、同時に最悪のセキュリティ悪夢にもなり得ます。
わずかなロジックの不具合や巧妙なプロンプトインジェクション(Prompt Injection)によって、モデルが誤ってos.system("rm -rf /")を実行してしまう恐れがあります。さらに危険なのは、データベースの認証情報を含む環境変数をすべて読み取られたり、無限ループによってサーバーのCPUリソースを100%消費されたりすることです。
実行環境を安全に分離(サンドボックス化)するため、エンジニアは通常次の3つのアプローチを検討します。
- Dockerコンテナの自前運用: リクエストごとにバックエンドからDockerデーモンを呼び出して新規コンテナを起動し、コードを実行した後にコンテナを破棄する方式。
- MicroVMの自前運用(Firecracker / gVisor): AWS Lambdaのアーキテクチャと同様に、ハードウェア仮想化レイヤーでカーネルを完全に分離する方式。
- 専用クラウドサンドボックス(E2B Code Interpreter): SDK経由でマネージドなMicroVMインフラを利用する方式。Linux仮想マシンが約150〜200msで高速起動し、PythonやJupyterカーネルがプリインストールされており、API経由で即座にファイルのアップロード/ダウンロードが可能です。
実戦比較:Docker、Firecracker、それともE2Bか?
私たちは最終的なソリューションを決定する前に、これら3つのアプローチをすべて検証しました。
1. Docker Engineのセルフホスト
メリット: ライセンス費用が不要で、既存サーバーをそのまま活用でき、社内ライブラリを含めたカスタムイメージのビルドも容易です。
デメリット: ライフサイクル管理の手間がかかります。Dockerのコールドスタートには通常1.5〜3秒ほど要します。100人のユーザーが一斉にコードを実行した場合、ディスクI/Oが瞬時にボトルネックになります。何より深刻なのは、DockerがホストとLinuxカーネルを共有している点です。CVE-2024-21626のようなコンテナブレイクアウトの脆弱性が1つあるだけで、攻撃者にバックエンドサーバー全体を掌握される危険性があります。
2. Firecrackerを用いたMicroVMクラスタの自前構築
メリット: ハードウェアレベルでの高いセキュリティ分離。50ms未満という驚異的な超高速起動。
デメリット: DevOpsの運用負荷が極めて高い点です。仮想ネットワークの制御、不要なディスククリーンアップの仕組み、IPプーリング、そして24時間365日の監視体制を自前で構築・維持する必要があります。チームが5人未満の場合、保守運用に膨大なリソースを奪われてしまいます。
3. E2B Code Interpreter
メリット: 導入が極めてスムーズです。サンドボックスは約150msで起動します。Jupyterランタイムがあらかじめ用意されており、標準出力(stdout)や標準エラー出力(stderr)、グラフ画像(PNG/SVGのBase64形式)を正確に抽出できます。カーネルのパッチ適用やインフラのスケーリングを気にする必要もありません。
デメリット: E2Bのクラウドサービスに依存します。医療データや金融データなど、100%オンプレミス環境での運用が必須となる場合は、Enterprise向けのセルフホストプランについて相談する必要があります。
どのソリューションを選ぶべきか?
社内の同僚数名が試す程度の社内用Botであれば、--memory=512m や --cpus=1.0 などの制限を厳格に設定したDockerコンテナで十分でしょう。
しかし、課金ユーザー向けに一般公開するプロダクションサービスとなると話はまったく別です。たった1件のセキュリティインシデントやダウンタイムが、プロジェクトの信頼を失墜させる原因になります。このようなケースにおいて、E2Bを利用すれば低レイテンシと堅牢なセキュリティを両立しつつ、数週間に及ぶインフラ構築の手間を省くことができます。
PythonでE2B Code Interpreterを導入するステップ
ステップ1:SDKのインストールとAPIキーの取得
e2b.dev で無料アカウントを作成してAPIキーを取得します。その後、作業環境にSDKをインストールします。
pip install e2b-code-interpreter python-dotenv
プロジェクトのルートディレクトリに .env ファイルを作成します。
E2B_API_KEY=e2b_your_actual_api_key_here
ステップ2:サンドボックス内で最初のコードを実行する
コンテキストマネージャ with Sandbox() を使用することで、コードの実行完了時にリソースが自動的にクリーンアップされます。
import os
from dotenv import load_dotenv
from e2b_code_interpreter import Sandbox
load_dotenv()
# 隔離されたサンドボックスを初期化
with Sandbox() as sandbox:
code = """
import math
def calculate_primes(limit):
primes = []
for num in range(2, limit):
if all(num % i != 0 for i in range(2, int(math.sqrt(num)) + 1)):
primes.append(num)
return len(primes)
count = calculate_primes(1000)
print(f'1000未満の素数が {count} 個見つかりました。')
"""
print("E2Bサンドボックスにコードを送信中...")
execution = sandbox.run_code(code)
# stdoutの結果を出力
for log in execution.logs.stdout:
print("Output:", log)
# 構文エラーや実行時エラーのキャッチ
if execution.error:
print("エラー:", execution.error.name, execution.error.value)
ステップ3:データファイルのアップロードと分析グラフの出力
AI Agentの実用例として、ユーザーがアップロードしたCSVファイルを読み込み、計算処理を行ってグラフを描画するケースがよくあります。E2Bは双方向のファイルI/Oに対応しているため、この処理を非常にスムーズに行えます。
import base64
from e2b_code_interpreter import Sandbox
with Sandbox() as sandbox:
# 1. CSVファイルをサンドボックスに直接書き込み
csv_content = "Thang,DoanhThu\n1,120\n2,150\n3,180\n4,220\n5,210\n6,290"
sandbox.files.write("data.csv", csv_content)
# 2. matplotlibを使用したグラフ描画コード
analysis_script = """
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('data.csv')
plt.figure(figsize=(8, 4))
plt.plot(df['Thang'], df['DoanhThu'], marker='o', color='royalblue')
plt.title('上半期の売上推移')
plt.xlabel('月')
plt.ylabel('売上(100万VND)')
plt.grid(True)
plt.show()
"""
print("データ分析を実行中...")
execution = sandbox.run_code(analysis_script)
# 3. PNG画像(Base64文字列として返却)を取得してディスクに保存
for i, result in enumerate(execution.results):
if result.png:
with open(f"chart_{i}.png", "wb") as f:
f.write(base64.b64decode(result.png))
print(f"グラフ画像の保存に成功しました: chart_{i}.png")
ステップ4:LLMのTool CallingループにE2Bを組み込む
インテリジェントなAgent(Claude 3.5 SonnetやGPT-4oなど)を完成させるには、E2Bをコード実行ツールとして定義します。基本的な動作フローは以下の5ステップです。
- ユーザーが「四半期ごとの売上グラフを作成して」とリクエストを送信。
- LLMがTool Callを介して対応するPythonコードを生成。
- バックエンドがコード文字列を受け取り、
sandbox.run_code()に渡して実行。 - コードが正常に実行された場合、stdoutの結果や画像ファイルをLLMに返却して最終的な回答を生成。
- コードがエラー(Traceback)を吐いた場合、そのエラーログをそのままLLMに送信。LLMがTracebackを解析してコードを自己修正し、再実行を試行(セルフデバッグ)。
本番環境(Production)運用における3つの重要ポイント
以下の3つの設定を確認・適用するまでは、本番環境へのデプロイを急がないでください。
- 必ず厳格なタイムアウトを設定する: AIが生成したコードは無限ループに陥りやすい傾向があります。
run_code(code, timeout=30)のように指定して30秒で自動終了させ、無駄なコンピュートコストの消費を防ぎましょう。 - セッション単位でサンドボックスを再利用する: 質問ごとにサンドボックスを新規作成するのではなく、ユーザーのチャットセッション中は同一の
sandbox_idを保持します。これにより、変数やDataFrame、アップロード済み一時ファイルの状態を引き継ぐことができます。 - サンドボックスを明示的に破棄(kill)する: ユーザーがブラウザタブを閉じた際やセッションタイムアウト時には、
sandbox.kill()を呼び出してください。不要な仮想マシンがバックグラウンドで稼働し続け、クラウドアカウントの予算を圧迫するのを防ぎます。
