SandboxesPython の実行

LLM が生成したコード向けのコードインタープリター API

コードインタープリターは、言語モデルが書いたコードを実行し、その出力をモデルに返します。Lizard (lizard.build) では、Lizard SDK の CodeSandbox がこれを sandbox(Firecracker microVM)の中で行うため、コードはアプリや手元のマシンから切り離されて実行されます。CodeSandbox は、Python と一般的なデータ処理ライブラリを含む interpreter テンプレートを起動します。Python は呼び出し間で変数を保持する Jupyter カーネルで実行され、各結果には出力されたテキスト、最後の式の値、image/png 形式のグラフ、そしてエラーがあればそのトレースバックが含まれます。

状態を保持してコードを実行する

import { CodeSandbox } from '@lizard-build/sdk';
 
const sandbox = await CodeSandbox.create({ projectId: 'proj_123', timeoutMs: 300_000 });
try {
  await sandbox.runCode('import pandas as pd\ndf = pd.DataFrame({"x": [1, 2, 3]})');
  const run = await sandbox.runCode('df.x.sum()');
  console.log(run.results[0]?.data); // "6"
  const bad = await sandbox.runCode('1/0');
  console.log(bad.error?.name); // "ZeroDivisionError"
} finally {
  await sandbox.kill();
}
from lizard import CodeSandbox
 
with CodeSandbox.create(project_id="proj_123", timeout_ms=300_000) as sandbox:
    sandbox.run_code('import pandas as pd\ndf = pd.DataFrame({"x": [1, 2, 3]})')
    run = sandbox.run_code("df.x.sum()")
    print(run.results[0].data)  # "6"
    print(sandbox.run_code("1/0").error.name)  # "ZeroDivisionError"

コードのエラーは結果の一部であり、例外ではありません。不正な API キーなど、リクエスト自体の失敗は例外を投げます。

runCode は何を返しますか?

runCode(Python では run_code)は Execution を返します:

フィールド内容
stdout、stderrコードが出力したすべてのテキスト
resultsテキスト以外も含む出力: text/plain 形式の最後の式の値、base64 の image/png 形式の matplotlib のグラフと画像、HTML、その他の形式。それぞれ mime と data を持ちます
errorコードが例外を発生させたときの name、message、traceback
executionCountカーネルのセルカウンター(Python では execution_count)

コードの実行中に出力を受け取るには、onStdout、onStderr、onResult、onError(Python では on_stdout など)を渡します。

LLM ツールとして使う

モデルに 1 つのツールを与えます。たとえば、文字列の引数 code を 1 つだけ取る run_python です。モデルがこれを呼び出したら、sandbox でコードを実行し、そのテキストをツールの結果として返します。モデルの変数が呼び出し間で保持されるように、会話全体で 1 つの sandbox を使い続けてください。

import { CodeSandbox } from '@lizard-build/sdk';
 
const sandbox = await CodeSandbox.create({ projectId: 'proj_123', timeoutMs: 600_000 });
 
// Call this when the model asks to run Python. Return the text to send back to it.
async function runPython(code: string): Promise<string> {
  const run = await sandbox.runCode(code, { timeoutMs: 30_000 });
  if (run.error) return `${run.error.name}: ${run.error.message}\n${run.error.traceback}`;
  const values = run.results.flatMap((r) =>
    r.type === 'result' && r.mime === 'text/plain' ? [r.data] : [],
  );
  return [run.stdout, ...values].filter(Boolean).join('\n');
}
 
// When the conversation ends:
await sandbox.kill();
from lizard import CodeSandbox
 
sandbox = CodeSandbox.create(project_id="proj_123", timeout_ms=600_000)
 
def run_python(code: str) -> str:
    """Call this when the model asks to run Python. Return the text to send back to it."""
    run = sandbox.run_code(code, timeout_ms=30_000)
    if run.error:
        return f"{run.error.name}: {run.error.message}\n{run.error.traceback}"
    values = [r.data for r in run.results if r.mime == "text/plain"]
    return "\n".join(filter(None, [run.stdout, *values]))
 
# When the conversation ends:
sandbox.kill()

ここでの sandbox の有効期間は 10 分です。長い会話では setTimeout(Python では set_timeout)で延長するか、timeoutMs: 0 を設定して sandbox を自分で停止してください。

グラフ

コードが matplotlib のグラフを描画すると、results には mime が image/png で、data に base64 の画像を持つ項目が入ります:

import base64
from pathlib import Path
 
run = sandbox.run_code("import matplotlib.pyplot as plt\nplt.plot([1, 4, 9])\nplt.show()")
png = next(r.data for r in run.results if r.mime == "image/png")
Path("chart.png").write_bytes(base64.b64decode(png))

コンテキスト、タイムアウト、言語

  • コンテキスト。 createContext()(Python では create_context())は、独自の変数と作業ディレクトリを持つ別のカーネルを起動します。これを context として runCode に渡します。listContexts()、restartContext(ctx)、deleteContext(ctx) で管理します。コンテキストを指定しない場合、各言語はそれぞれの既定のコンテキストを使います。
  • タイムアウト。 timeoutMs(Python では timeout_ms)は 1 回の呼び出しの上限で、既定は 60 秒です。上限を超えた Python カーネルは中断されますが、状態は保持されます。結果の error.name は TimeoutError になります。
  • その他の言語。 JavaScript と bash は呼び出しのたびに新しいプロセスとして実行されるため、状態は引き継がれず、results は空のままです。JavaScript には node が必要ですが、interpreter テンプレートには含まれていません。
  • 一時停止とスナップショット。 カーネルは sandbox の中で動作するため、pause() と resume() は変数を保持し、snapshot() と fork() は変数をコピーします。

コマンドを実行する

process.exec はコマンドごとに別のプロセスを起動するため、Python の変数は呼び出し間で保持されません。pip install などのシェルの手順に使い、後で必要な結果はファイルに保存してください。コマンドの実行 と SDK リファレンス を参照してください。

信頼できないコード

モデルが書いたものはすべて信頼できないものとして扱ってください。コードが Lizard CLI を必要としない限り、lizardToken を渡さないでください。sandbox 内のどのプロセスもこれを読み取れるため、渡す場合はワークスペース単位のキーを使ってください。その他のシークレットは envs に入れないでください。Pod ベースのボリュームを使う sandbox はホストのカーネルを共有します。ランタイムとテンプレート を参照してください。

保存と課金

変数とファイルは sandbox が存在する間だけ残ります。課金と保存のルールは 保存と課金 に、サイズと料金は 制限 にあります。

Sandboxes の概要 · クイックスタート · SDK リファレンス · Persistent Volumes · Sandboxes 製品ページ

更新日