NTT DATA TECH
🤖

【APIでも画像は取れる!】OpenAI Agents SDKのCode Interpreterでグラフ画像生成【文字化けも防げる!】

に公開

はじめに

こんにちは!NTTデータの@kouiwaです。
皆さんはOpenAI Agents SDKのCode Interpreterをご存じですか?入力したPythonコードをサンドボックス環境で実行し、データ処理やグラフ描画まで行ってくれる、データ分析において革命的な力を秘めたツールです。

この記事では、OpenAI Agents SDKのCode Interpreterを用いて文字化けのないグラフ画像を生成し、取得する方法を解説します。同様にAIエージェント開発に挑戦するエンジニアの方々にとって、少しでも参考になれば幸いです。

想定読者

  • OpenAI Agents SDKを活用した開発経験のある方
  • OpenAI Agents SDKを活用してエージェントを構築してみたい方
  • Code Interpreterを利用したことがある、もしくは利用に興味がある方
  • 自然言語の命令を基にデータ分析や可視化を自動化する仕組みに関心がある方

そもそもなぜCode Interpreterなの?

開発のきっかけ

「コーディング知識のないユーザーが、自然言語で命令するだけでcsvデータを分析し、グラフで可視化してくれるAIエージェントを作ろう」というプロジェクトに参加したことが、この開発のきっかけでした。
例えば、ユーザーが「生命保険の契約が一番多かった月を教えて」「この商品のプロモーションを行うならどんな層にアプローチすればいい?」といった指示を投げかけると、自動でPythonコードを生成・実行してデータとグラフを回答してくれるようなエージェントです。

このアイデアを実現するために採用したのが、OpenAI SDKのAgent機能とCode Interpreter(コード実行環境)です。

Code Interpreter vs Python REPL

LLMで生成したPythonコードの実行環境として、Code InterpreterではなくLangChainのPython REPLを使ったことのある方もいるかもしれません。ただ、データ分析の自動化エージェントを構築するうえではCode Interpreterの方が適しています

以下の表をご覧ください。

カテゴリ 比較項目 Code Interpreter Python REPL
機能 ライブラリの豊富さ
機能 セッション管理 ×
非機能 起動時間
安全性 隔離環境か ×
運用 ログ出力

起動時間以外の点ではCode Interpreterに軍配が上がります。詳しく説明します。

  • ライブラリの豊富さ:Code Interpreterにはmatplotlibなど、データ分析や結果の可視化に必要な多くのライブラリが用意されています。しかし、Python REPLはホストマシン上でPythonを実行するツールであるため、必要なライブラリを自分でホストマシン環境にインストールする必要があります。逆に、Code Interpreter上に存在しないライブラリを使用したい場合は、ライブラリを自由に追加できるPython REPLの方が適しています。
  • セッション管理:OpenAI Agents SDKはセッションの保持が可能です。詳細は省きますが、previous_response_idという設定を使用することで、以前の実行結果を引き継いでコードやグラフを修正することができます。データ分析においては一度抽出したデータを様々な角度で分析したいこともあるかと思うので、そうしたときに便利です。
  • 隔離環境か:Code InterpreterはPythonコードの実行をサンドボックス上で行います。そのため、ファイルが必要な時はアップロードする必要がありますが、関係のないファイルを誤って削除することはありません。Python REPLの場合はローカル環境上でコードを実行するため、ファイルのアップロードは不要ですが、生成したコードのバグによって意図しないファイルを書き換えたり削除したりする可能性があります。

実装の前に

全体の設計について説明します。
また、アプリケーションを実装する前に、OpenAI Agents SDKを実行するのに必要なAPIキーを発行しておきましょう。

処理の流れ・設計

処理全体を以下のように設計しました。

  1. アプリケーションがユーザ指示を含んだプロンプトを生成する。Code Interpreter、Agentを初期化する。
  2. アプリケーションがAgentを実行する。
  3. AgentがLLMを呼び出して、自然言語からpandasやmatplotlibなどのライブラリを用いたPythonコードを生成する。
  4. AgentがCode Interpreterを呼び出し、コードを渡す。
  5. Code Interpreterがコードを実行し、結果データと画像を取得する。
  6. Agent経由でユーザにデータと画像、LLMで生成した質問への回答を返却する。

図に表すとこんな感じです。

全体フロー図

APIキーの発行

OpenAI Platformから、APIを使用するのに必要なAPIキーを発行しましょう。発行時の設定はお好みで。

APIキーの発行

アプリ側の実装

いよいよ、OpenAI Agents SDKのCode Interpreterを用いて文字化けのないグラフ画像を生成し、取得するための実装について説明していきます。以下のコードでCode InterpreterをToolとして持たせたAgentを実行し、結果画像を取得できます。

import io

from agents import Agent, CodeInterpreterTool, ModelSettings, Runner, set_default_openai_key
from openai import OpenAI
from PIL import Image

# OpenAI APIのAPIキー
secret = "XXXXX"
client = OpenAI(api_key=secret)

# 日本語グラフ描画用のフォントファイル
font_path = "/XXX/NotoSansJP-Bold.zip"
with open(font_path, "rb") as f:
    resp = client.files.create(file=f, purpose="assistants")
    file_id = resp.id

ci = CodeInterpreterTool(
    tool_config={
        "type": "code_interpreter",
        "container": {
            "type": "auto",
            "file_ids": [file_id],
        },
    }
)

agent = Agent(
    name="Data Analyzer",
    model="gpt-4.1",
    instructions="グラフを生成してください。",
    tools=[ci],
    model_settings=ModelSettings(
        tool_choice="required",
        parallel_tool_calls=False,
    ),
)

prompt = f"""
y=xのグラフを生成してください。
グラフを作成する際は、ラベルを含めてフォントは必ず<フォントファイル>で示したzipファイルを解凍し、得られるttfファイル(NotoSansJP-Bold.ttf)を使用してください。
ax.annotate、ax.set_title、ax.set_xlabel、ax.set_ylabel、
ax.set_xticklabels、ax.set_yticklabels、ax.text、ax.bar、colorbar.set_labelすべてにfontpropertiesを指定してください。
plt.legendにはtitle_fontpropertiesを指定してください。
ax.pieにはtextpropsを指定してください。
<フォントファイル>
{f"/mnt/data/{file_id}"}
</フォントファイル>
"""

result = await Runner.run(agent, prompt)

# 画像取得
for response in result.raw_responses:
    if hasattr(response, "output"):
        for item in response.output:
            if hasattr(item, "type") and item.type == "code_interpreter_call":
                container_id = item.container_id
container_files = client.containers.files.list(container_id)  # type: ignore
for file in container_files.data:
    if file.path.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".svg")):
        print(f"画像ファイルを取得します: {file.path}")
        content = client.containers.files.content.retrieve(file.id, container_id=container_id)  # type: ignore
        pil_image = Image.open(io.BytesIO(content.content))
        display(pil_image)

結果画像はこんな感じです。文字化けのない画像が取得できました。

結果画像


1つ1つ解説していきます。

文字化けのないグラフ画像の生成

まず、画像生成部分です。
以下でclientを定義し、フォントファイルをアップロードしています。
secretには、先ほど取得したOpenAI APIキーを入力します(実際にはAPIキーをハードコードするのはやめましょう)。
また、matplotlibのデフォルトフォントを使用するとグラフの日本語が文字化け(いわゆる豆腐文字になって)してしまうため、商用利用可能な無償フォント「NotoSansJP」をclientにアップロードし、グラフに使用しています。ただし、clientにアップロードできるファイルの拡張子には制限があるため、フォントを圧縮してzip形式でアップロードし、グラフ生成時には解凍してから使用するようプロンプトで指示しています。

# OpenAI APIのAPIキー
secret = "XXXXX"
client = OpenAI(api_key=secret)

# 日本語グラフ描画用のフォントファイル
font_path = "/XXX/NotoSansJP-Bold.zip"
with open(font_path, "rb") as f:
    resp = client.files.create(file=f, purpose="assistants")
    file_id = resp.id

続いて、Agentとそこから呼び出せるCodeInterpreterToolを定義します。
CodeInterpreterTooltool_configfile_idsを渡すことによって、先ほどclientにアップロードしたファイルをCodeInterpreterToolが参照できるようになります。
また、Agentmodel_settingstool_choicerequiredにしていることによって、Agentが応答を返す際、必ず定義したtoolのいずれか(ここではCodeInterpreterToolのみ)を使用するよう指定しています。

ci = CodeInterpreterTool(
    tool_config={
        "type": "code_interpreter",
        "container": {
            "type": "auto",
            "file_ids": [file_id],
        },
    }
)

agent = Agent(
    name="Data Analyzer",
    model="gpt-4.1",
    instructions="グラフを生成してください。",
    tools=[ci],
    model_settings=ModelSettings(
        tool_choice="required",
        parallel_tool_calls=False,
    ),
)

ユーザプロンプトにグラフ生成用の指示を与えます
先ほど説明したとおりフォントファイルは解凍して使うことを記載したうえで、グラフ生成時のあらゆる命令においてフォントを指定するよう指示します。これにより、グラフ生成時に文字化けが発生することを防ぐことができます。
また、アップロードしたファイルはclient上のサンドボックス/mnt/data/{file_id}に格納されます。プロンプト上にこのパスを記載することで、CodeInterpreterToolにアップロードしたフォントファイルを参照させることができます。
このプロンプトとエージェントをRunner.runで実行すると、resultに結果が格納されます。

prompt = f"""
y=xのグラフを生成してください。
グラフを作成する際は、ラベルを含めてフォントは必ず<フォントファイル>で示したzipファイルを解凍し、得られるttfファイル(NotoSansJP-Bold.ttf)を使用してください。
ax.annotate、ax.set_title、ax.set_xlabel、ax.set_ylabel、
ax.set_xticklabels、ax.set_yticklabels、ax.text、ax.bar、colorbar.set_labelすべてにfontpropertiesを指定してください。
plt.legendにはtitle_fontpropertiesを指定してください。
ax.pieにはtextpropsを指定してください。
<フォントファイル>
{f"/mnt/data/{file_id}"}
</フォントファイル>
"""

result = await Runner.run(agent, prompt)

グラフ画像の取得

生成した画像の取得部分です。resultから生成したグラフを取得します
Code Interpreterが生成したグラフは、サンドボックス環境(container)内に一時的に保存されます。そのため、まずグラフが保存されているcontainer_idを特定し、そこに保存されているファイル一覧を取得して目的の画像を探す必要があります。

  1. resultに存在するcontainerを走査し、CodeInterpreterToolを呼び出したときのcontainerを取得します。このcontainercontainer_idを記録しておきます。
  2. client上に存在するファイルはすべてclient.containers.filesに格納されています。該当container_idに保存されているファイルの一覧listを、client.containers.files.list(container_id)で取得します。
  3. listを走査し、拡張子が画像と思しきファイルの情報を取得します。
  4. client.containers.files.content.retrieve(file.id, container_id=container_id)で該当ファイルのIDとcontainer_idの情報を渡し、画像データを取得します。
  5. データを変換後、画像として表示します。
for response in result.raw_responses:
    if hasattr(response, "output"):
        for item in response.output:
            if hasattr(item, "type") and item.type == "code_interpreter_call":
                container_id = item.container_id
container_files = client.containers.files.list(container_id)  # type: ignore
for file in container_files.data:
    if file.path.lower().endswith((".png", ".jpg", ".jpeg", ".gif", ".svg")):
        print(f"画像ファイルを取得します: {file.path}")
        content = client.containers.files.content.retrieve(file.id, container_id=container_id)  # type: ignore
        pil_image = Image.open(io.BytesIO(content.content))
        display(pil_image)

まとめ

本記事では、OpenAI Agents SDKのCode Interpreterを用いて文字化けのないグラフ画像を生成し、取得する方法について解説しました。

本記事のポイント

  • Code Interpreterの優位性: Python REPLと比較して、豊富なライブラリ、ファイル管理、セッション管理の面でCode Interpreterが優れています。
  • 日本語対応: フォントファイルをアップロードし、プロンプトで適切に指示することで日本語の文字化けを防止できます。
  • 画像取得の実装: container_idを特定し、client.containers.files経由で生成された画像ファイルを確実に取得できます。

今後の展開

本記事で紹介した実装パターンを応用すれば、複雑なデータ分析タスクや、マルチエージェントシステムへの統合も可能です。あなたのエージェントにも組み込んでみてくださいね!

助けていただいた方々

以下の方々のおかげで本記事を執筆できました。ありがとうございました!

@hua_wei_hanagi:結果画像取得の方法など、実装にあたってたくさんのヒントをいただきました!
@shinya_fujita:本記事のレビューを担当いただきました!

NTT DATA TECH
NTT DATA TECH
設定によりコメント欄が無効化されています