はじめに
LLMの実行
llama.cppでGemma-3-12b-it-Q4_K_M.ggufを実行しています。./llama-server -m /home/hoge/Documents/models/gemma-3-12b-it/gemma-3-12b-it-Q4_K_M.gguf -ngl 40 -c 16384 --host 0.0.0.0
詳細はこちらを見て下さい。
使用するMCPサーバー
「Filesystem MCP Server」というPC内のファイルにアクセスするためのものです。github.com
dockerイメージの作成
Dockerfile
FROM python:3.12-bullseye
# Node.jsのインストール
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
python3-dev \
curl \
gnupg && \
# Node.jsの公式リポジトリを追加
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs && \
npm install -g npm@latest && \
# npmグローバルパッケージのインストール
npm i -g @modelcontextprotocol/server-filesystem && \
# Pythonパッケージのインストール
pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir 'smolagents[openai,mcp]' && \
# クリーンアップ
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 作業ディレクトリの設定
WORKDIR /app
# デフォルトコマンド
CMD ["python", "-c", "print('Container ready')"]docker build --force-rm=true -t agent-sandbox .
最初はセキュリティー上「USER」を指定していましたが、これを付けると後でnpxコマンドの実行に支障がでたため削除しました。
# 制限付き権限で実行 USER nobody
「sandbox.py」という名前のファイルを作成する
import docker from typing import Optional class DockerSandbox: def __init__(self, image_name="agent-sandbox"): self.client = docker.from_env() self.container = None self.image_name = image_name def create_container(self): try: # セキュリティ制約と適切なロギングでコンテナを作成 self.container = self.client.containers.run( "agent-sandbox", command="tail -f /dev/null", # コンテナを実行状態に保つ detach=True, tty=True, # ホストへのアクセスを可能にする extra_hosts={"host.docker.internal": "host-gateway"}, # ネットワークアクセスを許可 network_mode="bridge", # ボリュームをマウント: ホストの/home/hoge/dataをコンテナの/app/dataにマウント volumes={ "/home/hoge/data": {"bind": "/app/data", "mode": "rw"} } ) except docker.errors.ImageNotFound: raise Exception(f"イメージ '{self.image_name}' が見つかりません。事前にイメージを作成してください。") except Exception as e: raise Exception(f"コンテナ作成エラー: {e}") def run_code(self, code: str) -> Optional[str]: if not self.container: self.create_container() # コンテナ内でコードを実行 exec_result = self.container.exec_run( cmd=["python", "-c", code], stderr=True # 標準エラー出力も取得する ) # 結果をより詳細な形式で返す return { "exit_code": exec_result.exit_code, "output": exec_result.output.decode() if exec_result.output else None, "success": exec_result.exit_code == 0 } def cleanup(self): if self.container: try: self.container.stop() self.container.remove() print("Container stopped and removed successfully") except Exception as e: print(f"エラー: {e}") finally: self.container = None
コンテナ作成時の引数についてはこちらです。(Claude 3.7 Sonnetによる解説です)
1. `command="tail -f /dev/null"` - コンテナ内で実行するコマンド。この`tail -f /dev/null`はダミーのコマンドで、コンテナを終了させずに実行し続けるために使われています。何も出力せず、単にコンテナをバックグラウンドで実行し続けるための工夫です。 2. `detach=True` - コンテナをバックグラウンドで実行します。Trueにすると、コンテナを起動した後にプログラムが続行できます(フォアグラウンドでブロックされません)。 3. `tty=True` - 疑似TTY(ターミナル)を割り当てます。これにより対話的なコマンドが正しく動作します。
セキュリティ上の引数は今回省略しました。以下のようなものがあります。(こちらもClaude 3.7 Sonnetによる解説です)
1. `mem_limit="1024m"` - コンテナのメモリ使用量を512MBに制限します。これはセキュリティのための制約です。 2. `cpu_quota=50000` - CPUの使用率を制限します。この値は100,000を基準とした相対値で、50000は50%のCPU使用率を意味します。 3. `pids_limit=100` - コンテナ内で作成できるプロセスの数を100に制限します。これはプロセスのフォークボムなどの攻撃を防ぐためのセキュリティ対策です。 3. `security_opt=["no-new-privileges"]` - プロセスが新しい権限を取得することを防ぎます。これにより、コンテナ内のプロセスが追加の権限を取得して脱出するリスクを軽減します。 4. `cap_drop=["ALL"]` - すべてのLinuxケーパビリティ(特権)をドロップします。これにより、コンテナ内のプロセスができることを大幅に制限し、セキュリティを向上させます。
これらの引数は主にセキュリティと制御のために設計されており、コンテナが使用できるリソースを制限し、潜在的なセキュリティリスクを軽減しています。特にサンドボックス環境では、このような制約が重要です。
実行ファイルを作成する
今回は「agent_runner.py」という名前にしました。(名前は任意です。)IPアドレスは各自変更が必要です。同じPC上なら「localhost」で大丈夫です。
# DockerSandboxクラスをインポート from sandbox import DockerSandbox # sandbox.pyからインポート # サンドボックスのインスタンスを作成 sandbox = DockerSandbox() try: # エージェントのコードを定義 agent_code = """ import os from smolagents import CodeAgent, ToolCollection, OpenAIServerModel from mcp import StdioServerParameters # エージェントの初期化 model = OpenAIServerModel( model_id="gemma-3-12b-it-4bit", api_base="http://192.168.10.12:8080", api_key="EMPTY" ) server_parameters = StdioServerParameters( command="npx", args=["-y", "@modelcontextprotocol/server-filesystem","/app/data"], env={{}}, ) with ToolCollection.from_mcp(server_parameters) as tool_collection: agent = CodeAgent( model=model, tools=[*tool_collection.tools], add_base_tools=True ) # エージェントの実行 response = agent.run("{prompt}") print(response) """ prompt = "ローカルPCにアクセスした上で、ファイルの内容を要約して下さい。" # サンドボックス内でコードを実行 result = sandbox.run_code(agent_code.format(prompt=prompt)) if result["success"]: print("実行成功:") print(result["output"]) else: print(f"エラー発生 (終了コード: {result['exit_code']}):") print(result["output"]) # エラーメッセージを含む finally: # 常に最後にクリーンアップする sandbox.cleanup()
実行
プロンプトはローカルPCにアクセスした上で、ファイルの内容を要約して下さい。
「/home/hoge/data」フォルダに適当なテキストファイルを用意して実行するだけです。
Pythonに必要なのは「docker」ライブラリだけです。
python agent_runner.py