Blog
ブログ

2025年08月26日

【7月技術ブログ】OpenAIのAPIでの「ファイル出力」

こんにちは。新入社員のTです。
今回は、OpenAIのAPIを使って「ファイルとして出力させる」ことを試してみました。

あらかじめお伝えしておくと、かなり自己流の解釈を含んでいます。
内容に誤りがあればご容赦ください。

やりたいこと

  • 出力結果を安定してファイルとして扱いたい

APIに対して「ファイルとして出力してください」と指示するだけでは、ちゃんとファイル出力の指示を理解してくれることもあれば、普通にテキストで返ってくることもあります。

テキスト形式での出力を後からファイル化することは可能ですが、出力のフォーマットが一定でなかったり、マークダウンの装飾が混ざったりと、ちょっと面倒に感じる場面もあります。

ChatGPT(ブラウザ版)に同じように頼むと、もっと安定してファイル形式で出力してくれる気がしますが……。
API経由だとなぜうまくいかないのか、不思議です。

検証

  • promptやinstructionsでの指示
    OpenAI APIには、promptの他にinstructionsという設定項目があります。

prompt チャットなどで使われるユーザーからの入力 今年は令和何年?
instructions LLM(大規模言語モデル)のふるまい方を指定するためのもの あなたは数学の教師です。与えられた問題の解法を答えなさい。

一般的に、promptでの指示よりもinstructionsでの指示の方が強力だとされています。

ただし、必ずしも思った通りの挙動になるとは限らず、ファイル出力を期待していてもテキストで返されることもありました。

 

  • 関数呼び出しを使ってみる
    OpenAI APIの関数呼び出し機能では、自作関数を用意してLLMに定義を渡しておくことで、LLMがその関数を呼び出すように指示を出してくれます。

実際の関数の実行はローカル(ユーザー側)で行うため、LLMは引数のみを返してきます。

今回はこの引数を活用するというアプローチを取っています。

本来の使い方とは異なるかもしれませんが、「こういった使い方もできる」という参考程度にご覧いただければと思います。

def output_files(filename, text):
    file_path = f'./output_files/{filename}'
    with open(file_path, "w", encoding='utf-8') as f:
        f.write(text)

今回は上記のような関数を用意し、下記のようにResponsesAPIに渡してみました。

client = OpenAI(api_key=OPENAI_API_KEY)

# 用意した関数とその説明
tools=[
    {
        "type": "code_interpreter",
        "container": {"type": "auto"}
    },
    {
        "type": "function",
        "name": "output_files",
        "description": "指定されたファイル名とコードからファイルを作成します",
        "parameters": {
            "type": "object",
            "properties": {
                "filename": {
                    "type": "string",
                    "description": "ファイル名 例、sample.py"
                },
                "text": {
                    "type": "string",
                    "description": "ファイルに書き込むコード"
                }
            },
            "required": ["filename", "text"]
        }
    }
]

# 用意したtoolsを渡し回答の生成
response = client.responses.create(
    model=MODEL,
    tools=tools,
    input='pythonでフィボナッチ数列を計算するプログラムを作成してください',
    tool_choice="auto"
)

また、回答から実際にファイル出力させる際には下記のように引数を取得して実行します。
実行してみるとResponseAPIからは下記のような出力(一部抜粋、見やすいように\nを改行に変換したりしています)が得られました。

output=[
	ResponseFunctionToolCall(
		arguments='{
			"filename":"fibonacci.py",
			"text":"
def fibonacci(n):
if n <= 0:
	return []
	elif n == 1:
		return [0]
	elif n == 2:
		return [0, 1]
	else:
		fib_sequence = [0, 1]
		for i in range(2, n):
			next_fib = fib_sequence[-1] + fib_sequence[-2]
			fib_sequence.append(next_fib)
		return fib_sequence

# 使用例
if __name__ == \'__main__\':
	n = 10  # フィボナッチ数列の項数
	print(fibonacci(n))
			"
		}',
		call_id='call_xxxxxxxx',
		name='output_files',
		type='function_call',
		id='fc_xxxxxxxx',
		status='completed'
	)
]

下記関数を使用して実際にファイルを作成できました。

# 回答から関数の実行
import json
for item in response.output:
    try:
        args = json.loads(item.arguments)
        output_files(args["filename"], args["text"])
    except Exception as e:
        print(e)

現時点の考察

  • prompt や instructions による指示では、必ずしも意図通りに動作しないことがあった
    特にトークン数が増えると、指示が曖昧に扱われやすくなる傾向がありました
    (おそらく、トークン数の増加により各指示の重要度が相対的に下がるのかもしれません。※あくまで推測です)
  • 関数呼び出しを使えば、引数として欲しい情報を明示的に受け取ることができる
    今回のケースでは「ファイルとして保存してほしい」という意図を、関数の引数で指定する形にしています

このページの先頭へ