以下の内容はhttps://touch-sp.hatenablog.com/entry/2025/03/27/011648より取得しました。


HuggingFaceが開発している「SmolAgents」でMCPサーバーを使ってみる。

はじめに

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






以上の内容はhttps://touch-sp.hatenablog.com/entry/2025/03/27/011648より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14