はじめに
2025年のトレンドは「AIエージェント」らしいです。時代に取り残されないために「SmolAgents」というのを触ってみました。環境構築にはまりまくりました。今回は導入までの備忘録です。SmolAgentsで何ができるか?
もちろんいろいろなことができるらしいのですが、今回はPythonスクリプトを書いてもらい、さらにそれを実行してもらうことを目的としました。以前、「9 9 9 9」という4桁の数字の間に四則演算の記号を追加して計算結果が10になるようにして下さいという問題をClaude 3.5 Sonnetに出したことがあります。残念ながら解けなかったのですが、このような問題を解くPythonスクリプトを書いて下さいと頼むときちんと動くスクリプトを書いてくれました。こちらの記事です。touch-sp.hatenablog.com
プログラムを書いてそれを実行して回答を返すような言語モデルがあればよいのにとその時思いました。
「SmolAgents」がそれを可能にしてくれます。
実行環境
WSL2でUbuntu 24.04の環境を二つ用意しました。一つはvLLMで言語モデルを動かすだけの環境です。
もう一つは「SmolAgents」を実行する環境です。言語モデルが書いたPythonスクリプトを実行するのにセキュリティー上コンテナなどを用意するのが良いそうです。
「SmolAgents」を実行するUbuntu環境にDockerをインストールしてそのようにしました。vLLMでは「gemma-3-12b-it」をbitsandbytesで4bitに量子化したものを動かしています。その方法はこちらを参照して下さい。
ここからは「SmolAgents」を実行するUbuntuの設定を書きます。
こちらを参考にして書いています。
手順1(オプション)
venvで仮想環境を作りました。必ずしも必要はありません。以下はその仮想環境内で行っています。
手順2
smolagentsをインストールする。pip install smolagents[docker]
手順3
「Dockerfile」という名前のファイルを用意して中をこのようにする。FROM python:3.12-bullseye
# ビルド依存関係のインストール
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
python3-dev && \
pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir 'smolagents[openai]' && \
# pip install --no-cache-dir 'smolagents[litellm]' && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 作業ディレクトリの設定
WORKDIR /app
# 制限付き権限で実行
USER nobody
# デフォルトコマンド
CMD ["python", "-c", "print('Container ready')"]手順4
「sandbox.py」という名前のファイルを用意して中をこのようにする。import docker from typing import Optional class DockerSandbox: def __init__(self): self.client = docker.from_env() self.container = None def create_container(self): try: image, build_logs = self.client.images.build( path=".", tag="agent-sandbox", rm=True, forcerm=True, buildargs={}, # decode=True ) except docker.errors.BuildError as e: print("Build error logs:") for log in e.build_log: if 'stream' in log: print(log['stream'].strip()) raise # セキュリティ制約と適切なロギングでコンテナを作成 self.container = self.client.containers.run( "agent-sandbox", command="tail -f /dev/null", # コンテナを実行状態に保つ detach=True, tty=True, mem_limit="512m", cpu_quota=50000, pids_limit=100, security_opt=["no-new-privileges"], cap_drop=["ALL"], # ホストへのアクセスを可能にする extra_hosts={"host.docker.internal": "host-gateway"}, # ネットワークアクセスを許可 network_mode="bridge" ) 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], user="nobody" ) # 全ての出力を収集 return exec_result.output.decode() if exec_result.output else None def cleanup(self): if self.container: try: # コンテナを停止 self.container.stop() # コンテナを削除 self.container.remove() print("Container stopped and removed successfully") except docker.errors.NotFound: # コンテナはすでに削除されている(想定内の動作) print("Container not found, it may have been already removed") pass except Exception as e: print(f"Error during cleanup: {e}") finally: self.container = None # 参照をクリア
手順5
「agent_runner.py」という名前(名前は任意)のファイルを用意して中をこのようにする。# DockerSandboxクラスをインポート from sandbox import DockerSandbox # sandbox.pyからインポート # サンドボックスのインスタンスを作成 sandbox = DockerSandbox() try: # エージェントのコードを定義 agent_code = """ import os from smolagents import CodeAgent, OpenAIServerModel # エージェントの初期化 model = OpenAIServerModel( model_id="gemma-3-12b-it-4bit", api_base="http://host.docker.internal:8000/v1", api_key="EMPTY" ) agent = CodeAgent( model=model, tools=[] ) # エージェントの実行 response = agent.run("{prompt}") print(response) """ #prompt = "What's the 20th Fibonacci number?" prompt = "8.11と8.7はどちらが大きいですか?" # サンドボックス内でコードを実行 output = sandbox.run_code(agent_code.format(prompt=prompt)) print(output) finally: # 常に最後にクリーンアップする sandbox.cleanup() ''' model=LiteLLMModel( model_id="openai/gemma-3-12b-it-4bit", api_base="http://host.docker.internal:8000/v1", api_key="EMPTY" ) '''
手順6
あとは実行するだけです。python agent_runner.py
docker buildやdocker runなどは手動で実行する必要はありません。Pythonから実行するようになっています。
実行結果
What's the 20th Fibonacci number?

8.11と8.7はどちらが大きいですか?

補足
vLLMからのモデルの呼び出しは二通りあるようです。from smolagents import OpenAIServerModel model = OpenAIServerModel( model_id="gemma-3-12b-it-4bit", api_base="http://host.docker.internal:8000/v1", api_key="EMPTY" )
from smolagents import LiteLLMModel model=LiteLLMModel( model_id="openai/gemma-3-12b-it-4bit", api_base="http://host.docker.internal:8000/v1", api_key="EMPTY" )
どちらを使うかによってDockerfileが一部変わってきます。
上の例では一方をコメントアウトしています。