AI Navigate

プロンプトから実ファイルへ: AIファイル生成の開発者ガイド

Dev.to / 2026/3/17

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • 投稿は、現代の LLM が、テキストのみを返す代わりにサンドボックス環境でコードを実行することで、PDF やスプレッドシートといった実際のダウンロード可能な成果物を生成できる方法を示しています。
  • Claude、OpenAI、Gemini に共通する普遍的な三段階のパターン:コード実行を有効化するためのツール宣言、事前インストール済みライブラリを備えたサンドボックス実行、そして生成された成果物を取得するファイル取得手段。
  • このワークフローを実装するためのベンダー別のガイダンスと実行可能なコードを提供しており、code_execution ツールを有効にし、ファイルを取得するには Files API または同等の手段を使用します。
  • この成果物生成機能が、自然言語プロンプトから直接エンドツーエンドの文書作成を可能にすることで、開発者のワークフローを拡張することを強調しています。

ChatGPT に「売上レポートの PDF を作成し、収益のチャートを含めてください」を依頼する。1年前なら、いくつかのマークダウンを貼って幸運を祈るだけだったでしょう。今日では、サンドボックス化された Python 環境を起動し、reportlabmatplotlib を実行して、実際にダウンロード可能な PDF ファイルを手渡してくれます。

これは、テキスト生成 から アーティファクト生成 への移行です -- そして主要な LLM ベンダーのすべてが API 経由でこの機能をサポートしています。Claude、OpenAI、Gemini はいずれも、開発者に LLM へプロンプトを送信し、実際のファイル(PDF、スプレッドシート、チャート、スライドデッキ、Python で作成できるあらゆるもの)を返してくれる方法を提供します。

この投稿は、ファイル生成の普遍的なパターンを解説し、その後、各ベンダーごとにそれを正確に実行する方法を示します — 動作するコードも含まれています。

普遍的なパターン

API が異なるにもかかわらず、3社はいずれも同じ3ステップのアーキテクチャに従います。

各ベンダー固有の実装は、このフローのバリエーションです。詳細は異なりますが、3つの概念はどこでも繰り返されます:

  1. ツール宣言 -- API リクエストに特定のツールを含めることで、コード実行を選択します。デフォルトで有効にはなっていません。
  2. サンドボックス実行 -- LLM のコードは、インターネット接続のない分離されたコンテナ内で実行されます。一般的なライブラリ(pandas、matplotlib、reportlab)は事前にインストールされています。
  3. ファイル取得 -- 各ベンダーには、バイト列を取得するための異なる仕組みがあります。ダウンロード用のファイル ID を返すものもあれば、バイト列をインラインで返すものもあります。

このパターンを身につければ、任意のベンダーの API を学ぶことは、これら3つのステップに対応づけるだけです。

Claude: Code Execution + Files API

Claude のファイル生成は、文書作成の中で最も機能豊富なオプションです。永続的なコンテナを提供し、フル bash アクセス、豊富な事前インストール済みのドキュメントライブラリ、アップロードとダウンロードのためのクリーンな Files API を備えています。

プロンプトから PDF を生成する

code_execution_20250825 ツールを有効にし、プロンプトを送信して、レスポンスからファイル ID を抽出し、Files API を通じてそれらをダウンロードします。

import anthropic

The code snippet is omitted for brevity.

レスポンスのコンテンツブロックにはネストされた構造があります。bash_code_execution_tool_result ブロックを探し、それは bash_code_execution_result オブジェクトを含み、さらにその中のアイテムには file_id 属性があります。files.download() 呼び出しで生のバイト列が得られ、retrieve_metadata() によって元のファイル名が得られます。

なぜ bash_code_executioncode_execution_20250825 ツールを含めると、Claude は実際には 2 つのサブツールを取得します: bash_code_execution(シェルコマンドを実行)と text_editor_code_execution(ファイルを作成・編集)。ファイルを生成するには、Claude は通常、テキストエディタのサブツールで Python スクリプトを書き、それを bash で実行します。結果ブロックは、出力を生成したサブツールの名前が付けられます。そして、最終ファイルを作成するのは bash 実行なので、解析するブロックのタイプはそれになります。これが、他のベンダーと異なり Claude がフル Bash アクセスを持つ理由でもあります。_20250825 ツールバージョンは、この bash/テキストエディタの分割を導入し、以前の _20250522 バージョン(Python のみ)を置き換えました。

CSV のアップロード、チャート + PDF の取得

自分のデータを処理するには、まず Files API でファイルをアップロードし、次にそのファイルをコード実行ツールとともにプロンプトに添付します。

import anthropic

The code snippet is omitted for brevity.

レスポンスのコンテンツブロックにはネストされた構造があります。bash_code_execution_tool_result ブロックを探し、それは bash_code_execution_result オブジェクトを含み、さらにその中のアイテムには file_id 属性があります。files.download() 呼び出しで生のバイト列が得られ、retrieve_metadata() によって元のファイル名が得られます。

なぜ bash_code_executioncode_execution_20250825 ツールを含めると、Claude は実際には 2 つのサブツールを取得します: bash_code_execution(シェルコマンドを実行)と text_editor_code_execution(ファイルを作成・編集)。ファイルを生成するには、Claude は通常、テキストエディタのサブツールで Python スクリプトを書き、それを bash で実行します。結果ブロックは、出力を生成したサブツールの名前が付けられます。 そして、最終ファイルを作成するのは bash 実行なので、解析するブロックのタイプはそれになります。これが、他のベンダーと異なり Claude がフル Bash アクセスを持つ理由でもあります。_20250825 ツールバージョンは、この bash/テキストエディタの分割を導入し、以前の _20250522 バージョン(Python のみ)を置き換えました。

CSV のアップロード、チャート + PDF の取得

自分のデータを処理するには、まず Files API でファイルをアップロードし、次にそのファイルをプロンプトに添付します。

import anthropic

The code snippet is omitted for brevity.

レスポンスのコンテンツブロックにはネストされた構造があります。bash_code_execution_tool_result ブロックを探し、それは bash_code_execution_result オブジェクトを含み、さらにその中のアイテムには file_id 属性があります。files.download() 呼び出しで生のバイト列が得られ、retrieve_metadata() によって元のファイル名が得られます。

# 入力ファイルをアップロード uploaded = client.beta.files.upload(file=open("sales_data.csv", "rb")) # ファイル + コード実行のプロンプトを送信 response = client.beta.messages.create( model="claude-sonnet-4-6", betas=["files-api-2025-04-14"], max_tokens=4096, messages=[{ "role": "user", "content": [ { "type": "text", "text": "この販売CSVを分析します。地域別の収益の棒グラフを作成してください。" }, {"type": "container_upload", "file_id": uploaded.id}, ], }], tools=[{"type": "code_execution_20250825", "name": "code_execution"}], ) # 生成されたすべてのファイルをダウンロード for block in response.content: if block.type == "bash_code_execution_tool_result": result = block.content if result.type == "bash_code_execution_result": for item in result.content: if hasattr(item, "file_id"): content = client.beta.files.download(item.file_id) metadata = client.beta.files.retrieve_metadata(item.file_id) content.write_to_file(metadata.filename) print(f"Downloaded: {metadata.filename}")

1つのプロンプトで複数のファイルを生成できます。この場合、PNG のチャートと PDF レポートの両方が得られます。常に完全なレスポンスを反復してください。単一ファイルを前提としてはいけません。

コンテナの再利用: 反復ワークフローの鍵

Claude コンテナは 30日間持続します。最初のリクエストでコンテナを作成すると、レスポンスには container.id が含まれます。以降の呼び出しへそれを渡すと、Claude は前回の続きをすぐに再開します。前回のリクエストのすべてのファイルはまだディスク上にあります。

# 最初の呼び出しでコンテナを作成します
response1 = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    messages=[{"role": "user", "content": "Generate a sales report PDF."}],
    tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
)
container_id = response1.container.id

# 後続の呼び出しは同じコンテナを再利用します response2 = client.messages.create( container=container_id, model="claude-sonnet-4-6", max_tokens=4096, messages=[{"role": "user", "content": "ページ2のチャートを円グラフに置き換えてください。"}], tools=[{"type": "code_execution_20250825", "name": "code_execution"}], )

これは「対話型ファイル編集」を可能にします -- ユーザーはデータを再アップロードすることなく、文書を反復的に編集できます。

OpenAI: Responses API + Code Interpreter

OpenAIのResponses API(旧Assistants APIの後継)は、ファイル生成のためにCode Interpreterツールを使用します。 Claudeと似たパターンですが、応答の構造とファイル取得の仕組みは異なります。

Code InterpreterでCSVを生成する

code_interpreterツールを有効にし、応答からcontainer_file_citation注釈を解析して生成されたファイルを見つけます。

from openai import OpenAI

client = OpenAI()

# 手順1: コードインタプリタを有効化してリクエスト
response = client.responses.create(
    model = "gpt-5.2",
    tools = [{"type": "code_interpreter", "container": {"type": "auto"}}],
    input = "Generate a CSV file named 'q1_report.csv' with 10 rows of financial data."
)

# 手順2: 注釈からファイル参照を抽出
# 応答構造は深くネストされています: output → message → content → output_text → annotations
for item in response.output:
    if item.type == "message":
        for content_block in item.content:
            if content_block.type == "output_text":
                for annotation in content_block.annotations:
                    if annotation.type == "container_file_citation":
                        # 手順3: コンテナエンドポイントからダウンロード
                        file_data = client.containers.files.content.retrieve(
                            file_id = annotation.file_id,
                            container_id = annotation.container_id
                        )
                        with open(annotation.filename, "wb") as f:
                            f.write(file_data.read())
                        print(f"Downloaded: {annotation.filename}")

The annotation traversal is the trickiest part. Don't try to shortcut it with response.output_text -- that gives you a plain string with citation markers, not the actual file references.

Uploading a File, Transforming It

標準のFiles APIを介してファイルをアップロードし、コンテナ設定にはファイルIDを渡します。

from openai import OpenAI

client = OpenAI()

# ファイルをアップロード
uploaded = client.files.create(
    file = open("sales_data.csv", "rb"),
    purpose = "user_data"
)

この長文の正確な翻訳はHTMLタグをそのまま保持しつつテキストのみ翻訳する必要があり、JSON形式での返却が非常に長くなる見込みです。分割してお届けしてもよろしいですか?最初の部分(見出しと前半部分)をすぐに翻訳して返します。client = genai.Client(api_key="YOUR_API_KEY") response = client.models.generate_content( model="gemini-2.5-flash", config=types.GenerateContentConfig( tools=[types.Tool(code_execution=types.ToolCodeExecution)] ), contents="Generate a bar chart of quarterly revenue: Q1=$2.1M, Q2=$2.8M, Q3=$3.2M, Q4=$3.9M." ) # Gemini returns results inline -- no separate download step for part in response.candidates[0].content.parts: if part.executable_code: print("Code ran:", part.executable_code.code[:80], "...") if part.code_execution_result: print("Output:", part.code_execution_result.output) if part.as_image() is not None: with open("revenue_chart.png", "wb") as f: f.write(part.as_image().image_bytes) print("Chart saved as revenue_chart.png")

No file IDs, no download endpoints. The image bytes are right there in the response. For text/data output, it shows up in code_execution_result.output.

CSV生成のための構造化出力

Geminiの最も強力なファイル生成パターンは実際には間接的です。構造化されたJSONデータを返してもらい、それを自分の好みのライブラリでローカルに整形します。

import json
import pandas as pd
from google import genai
from google.genai import types

client = genai.Client(api_key="YOUR_API_KEY")

# Ask for structured JSON outputresponse = client.models.generate_content(
    model="gemini-2.5-flash",
    config=types.GenerateContentConfig(response_mime_type="application/json"),
    contents="Return a JSON array of 10 tech companies with fields: name, ticker, market_cap, sector."
)

# Convert to CSV locally -- you control the formatting
data = json.loads(response.text)
df = pd.DataFrame(data)
df.to_csv("tech_companies.csv", index=False)
print(f"Saved {len(df)} rows to tech_companies.csv")

This "structured output" approach gives you 100% control over formatting and is the most reliable way to produce files from Gemini. Let the model do what it's good at (data generation), and handle the file formatting yourself.

30-Second Execution Timeout

Gemini's code execution sandbox has a hard 30-second timeout. This makes it ideal for quick chart generation and data transforms, but rules it out for heavy document creation tasks like multi-page PDF reports or complex PowerPoint decks. For those, use Claude or OpenAI.

どのAPIを何に使うべきか?

Feature Claude OpenAI Gemini
Sandbox Type 再利用可能なコンテナ(30日有効期限) 一時的なコンテナ(20分のアイドルタイムアウト) ステートレスサンドボックス(30秒タイムアウト)
Resources 5 GiB ディスク、5 GiB RAM、1 CPU 最大64 GB RAM(階層型) トークン制限付き(インライン出力)
Shell Access 完全な Bash Pythonのみ Pythonのみ
File Download ファイル API(files.download() コンテナエンドポイント(containers.files.content.retrieve() レスポンス内にインラインで(ダウンロード手順なし)
Best Use Case 複雑なドキュメント(PDF、DOCX、PPTX) 大量データ処理 + ファイル生成 素早いチャート作成とデータ変換
pip install Yes(bashアクセスあり) No(分離されたサンドボックス) No(分離されたサンドボックス)

短い要約:

  • 複雑なドキュメント(PDFレポート、スライドデッキ、書式設定されたWord文書): Claude。事前にインストールされた文書ライブラリと30日間のコンテナ永続性により、最適な選択となります。
  • 大規模データセットの処理(大容量のCSV処理、Excel変換): OpenAI。最大64 GBのRAMを要求できる能力は他に類がない。
  • 迅速な可視化(チャート、グラフ、簡易なデータ要約):Gemini。インライン画像の返却により、APIコールが減り、処理が高速化します。
  • 最大のフォーマット制御:任意のモデルの構造化出力モード。JSONデータを返し、ローカルで自分のライブラリを使ってレンダリングします。
  • セルフホスト型の代替案: 自分自身でサンドボックスを運用する

    上記の3つのベンダーAPIはすべて、それぞれのインフラストラクチャでコードを実行します。彼らのインフラストラクチャ内でコードを実行します。プロンプトを送信すると、彼らはコンテナを起動し、ファイルを返します。これは便利ですが、データがあなたのネットワークを離れ、各ベンダーのサンドボックス制限(30秒のタイムアウト、インターネット接続不可、固定ライブラリセット)に縛られ、実行ごとに料金を支払うことになります。

    4つ目の選択肢として、自分でサンドボックスを実行する方法があります。このパターンでは、ベンダーのコード実行ツールを有効にせずに任意のLLM APIを呼び出してコードを生成し、次にそのコードを自分のマシン上の分離された環境でローカルに実行します。同じ「プロンプトからファイルへ」というワークフローを得られますが、実行環境は自分で制御します。

    なぜセルフホストなのか?

    • データの所在。規制産業(医療、金融、政府)では、コードとデータを第三者のサンドボックスへ送ることはコンプライアンス要件に抵触する可能性があります。ローカルサンドボックスはすべてを自分のインフラストラクチャ上に保持します。
    • ベンダーのサンドボックス制限なし。タイムアウト、RAM、ディスク、インストール済みライブラリを自分で選択します。10分の実行時間が必要ですか?GPUですか?内部サービスへのネットワークアクセスですか?あなたのサンドボックス、あなたのルールです。
    • 大規模展開時のコスト。ベンダーのサンドボックスの料金はセッション単位または時間単位です。大量に発生する場合、独自の実行インフラを運用する方がかなり安くなります。
    • モデルの柔軟性。コードを生成することとコードを実行することを分離しているため、任意のLLMを使用できます――オープンソースモデル、ファインチューニング済みモデル、あるいはご自身のモデルを含む――が Pythonスクリプトを作成します。サンドボックスはコードの出所を気にしません。

    構築のためのツール

    2つのオープンソースプロジェクトが、サンドボックス化されたコード実行の主要な選択肢として登場しました。

    E2B は Firecracker マイクロVM(AWS Lambdaの背後にあるのと同じ技術)を使用して、各実行を専用カーネルを備えた独自の軽量VM内で分離します――Dockerコンテナよりも強力な分離性を実現します。E2Bはマネージドクラウドサービスを提供しますが、Terraformベースのデプロイメントを使用して、GCPやLinuxの自分のインフラストラクチャ上でセルフホストすることもできます。PythonとJavaScriptのSDKを使えば、サンドボックスを起動してコードを実行し、ファイルをプログラム的に取得することが容易です。

    exec-sandbox は完全にローカルなアプローチを取ります。信頼できないコードを、一時的なQEMUマイクロVMで、ハードウェアアクセラレーション(LinuxではKVM、macOSではHVF)を用いて実行します。クラウド依存はなく、コードは決してあなたのマシンを離れません。ウェームプールの待機遅延は1〜2msで、Python、JavaScript、シェルの実行をサポートします。外部サービスへコードを送信することを前提としないエアギャップ環境向けに設計されています。

    アーキテクチャの転換

    セルフホストの主な違いは、コード生成とコード実行を分離することです。ベンダーAPIでは、LLMがコードを作成し、それを1つのAPI呼び出しで実行します。セルフホストされたサンドボックスでは、これらを2つのステップに分割します:

    1. テキスト/コード生成のためにLLM APIを呼び出します(コード実行ツールは不要です)。
    2. レスポンスから生成されたPythonスクリプトを抽出します。
    3. それをあなたのローカルサンドボックス(E2B、exec-sandbox、または制限されたDockerコンテナでも可)で実行します。
    4. サンドボックスのファイルシステムから出力ファイルを取得します。

    以下は、サンドボックスとしてE2B、LLMとしてAnthropicを使用した具体例です。API呼び出しにはコード実行ツールが含まれていないことに注意してください。Claudeにスクリプトを書かせ、それを自分たちで実行します:

    import re
    from anthropic import Anthropic
    from e2b_code_interpreter import Sandbox
    
    # Step 1: Ask the LLM to generate a Python script (no code execution tool)
    client = Anthropic()
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4096,
        messages=[{
            "role: "user",
            "content": "Write a Python script that uses matplotlib to create a bar chart "
                       "of quarterly revenue (Q1=$2.1M, Q2=$2.8M, Q3=$3.2M, Q4=$3.9M) "
                       "and saves it as 'revenue_chart.png'. Return only the script, "
                       "no explanation."
        }]
    )
    
    # Step 2: Extract the Python code from the response
    code = response.content[0].text
    match = re.search(r"```
    
    python
    (.*?
    
    ```", code, re.DOTALL)
    if match:
        code = match.group(1)
    
    # Step 3: Execute it in an E2B sandbox
    with Sandbox.create() as sbx:
        execution = sbx.run_code(code)
    
    if execution.error: print(f\"Error: {execution.error.value}\") else: # Step 4: Download the generated file from the sandbox file_content = sbx.files.read(\"/home/user/revenue_chart.png\", format=\"bytes\") with open(\"revenue_chart.png\", \"wb\") as f: f.write(file_content) print(\"Saved: revenue_chart.png\")

    You can swap Anthropic for OpenAI, genai.Client, or any other LLM client -- the sandbox doesn't care where the code came from. You can also upload input files to the sandbox before execution using sbx.files.write(), mirroring the upload-then-process pattern from the vendor APIs.

    E2B's default code-interpreter template comes with matplotlib, pandas, numpy, scikit-learn, pillow, openpyxl, python-docx, seaborn, and dozens of other common libraries pre-installed -- similar to the vendor sandboxes. If you need additional packages, you can either install them at runtime with sbx.commands.run("pip install <package>"), or build a custom template with your dependencies baked in so every sandbox starts ready to go.

    This is more work to build, but it gives you full control over execution, security, and cost. It also means you can use Gemini or any other model that doesn't offer file artifacts -- you just need the model to write good Python, and your sandbox handles the rest.

    Production Tips

    If you're building file generation into a real product, a few hard-won lessons:

    1. Sanitize filenames. The LLM chooses the filename based on the prompt. A creative user (or an adversarial one) can craft prompts that produce filenames with path traversal characters. Always strip or validate filenames before writing to disk. os.path.basename() is your friend.

    2. Handle multi-file responses. A single prompt like "make a PDF report and an Excel spreadsheet of the raw data" can produce two or more files. Always iterate the full response -- never assume exactly one file comes back.

    3. Persist container IDs for edit workflows. Claude's 30-day containers enable a powerful pattern: users can say "update the chart on page 2" in a follow-up message, and the LLM picks up the original file from the persistent container. Store the container_id alongside the conversation thread in your database.

    4. Set timeouts generously. Code execution is significantly slower than text generation. Simple files might take 30-60 seconds; complex multi-file generation (especially PPTX with embedded charts) can take 5-15 minutes. Don't use your standard API timeout.

    5. All sandboxes are offline. None of the three vendors allow network access from within the sandbox. All data must be uploaded or included in the prompt. You can't pip install on OpenAI or Gemini (Claude is the exception -- it has bash access). You can't fetch URLs. Plan accordingly.

    Conclusion

    File generation via LLM APIs follows a universal pattern across all three major vendors:

    • Claude excels at complex document creation with its 30-day persistent containers, full bash access, and pre-installed document libraries.
    • OpenAI offers the most compute headroom with up to 64 GB of RAM, making it ideal for heavy data processing tasks.
    • Gemini is the fastest path to charts and visualizations, returning inline image bytes with no separate download step.

    Try it yourself: Build a CLI tool that takes a prompt and a desired output format, routes to the best vendor based on file type (PDFs to Claude, big data to OpenAI, charts to Gemini), and saves the result locally. You'll touch all three APIs and internalize the patterns in a single afternoon.

    Official Documentation