OpenAI Agents SDK で出力形式の指定を試してみた
OpenAI-Agents-SDK
結論
多少込み入った形式でも、プロンプトを丁寧に書けば意図したとおりの出力形式が得られる。
OpenAI Agents SDK の output_type を指定したときの出力結果は以下のとおり。
- ◯: 意図したとおりの出力形式が得られた
- △: 意図したとおりの出力形式が得られることもある
- ✗: 意図したとおりの出力形式が得られなかった
- -: 実行できず(API エラーが発生)
| LLM | 簡単なプロンプト | 丁寧なプロンプト |
|---|---|---|
| openai/gpt-oss-20b | ✗ | ◯ |
| openai/gpt-oss-120b | ✗ | ◯ |
| openai/gpt-5.1 | ◯ | ◯ |
| anthropic/claude-3.5-haiku | ✗ | ◯ |
| anthropic/claude-3.7-sonnet | ✗ | △ |
| anthropic/claude-sonnet-4.5 | - | - |
| anthropic/claude-opus-4.5 | - | - |
| google/gemini-2.5-flash | ✗ | ✗ |
| google/gemini-2.5-pro | ✗ | ✗ |
| google/gemini-3-pro-preview | ✗ | ✗ |
※ 具体的なプロンプトは後述。
ソースコード
https://github.com/gnkm/try.openai-agents-sdk
背景
OpenAI Agents SDK では、output_type を使って出力の形式を指定することができる。
これで本当に指定した形式で出力されるのか試してみたかった。
やりたいこと
構造化された文書を生成させて、そのメタデータを取得したい。 例えば以下の文書が出力されたとする。
# コンテンツ
## 導入
ここでは導入をおこなう。
## メインコンテンツ
メインコンテンツを扱う。
### サブコンテンツ 1
サブコンテンツ 1 を扱う。
### サブコンテンツ 2
サブコンテンツ 2 を扱う。
## まとめ
ここではまとめをおこなう。
このとき、以下の出力を得たい。
{
'contents': [
{
'level': 2,
'text': '導入',
'children': [
{
'content': 'ここでは導入をおこなう。'
},
],
},
{
'level': 2,
'text': 'メインコンテンツ',
'children': [
{'content': 'メインコンテンツを扱う。'},
{'level': 3, 'text': 'サブコンテンツ 1', 'children': [{'content': 'サブコンテンツ 1 を扱う。'}]},
{'level': 3, 'text': 'サブコンテンツ 2', 'children': [{'content': 'サブコンテンツ 2 を扱う。'}]},
],
},
{
'level': 2,
'text': 'まとめ',
'children': [
{
'content': 'ここではまとめをおこなう。'
},
],
}
]
}
やったこと
環境
pyproject.toml
requires-python = ">=3.13"
dependencies = [
"dspy>=3.0.4",
"openai-agents[litellm]>=0.6.1",
"pydantic>=2.12.4",
"rich>=13.0.0",
"typer>=0.20.0",
]
システムプロンプト
2 種類のシステムプロンプトを準備した。
システムプロンプト(1)
ユーザーの要求に基づいて、構造化されたマークダウン文書を生成してください。
文書は見出し(Heading)と本文(Content)で構成され、見出しは階層構造を持つことができます。
見出しのレベル(1-6)を適切に設定し、各セクションに適切な本文を配置してください。
見出しには子要素として、さらに下位の見出しや本文を含めることができます。
システムプロンプト(2)
ユーザーの要求に基づいて、構造化されたマークダウン文書を生成します。
文書は見出し(Heading)と本文(Content)で構成され、見出しは階層構造を持つことができます。
見出しのレベル(1-6)を適切に設定し、各セクションに適切な本文を配置してください。
見出しには子要素として、さらに下位の見出しや本文を含めることができます。
出力は必ず JSON 形式で、以下の構造に従ってください:
{
"contents": [
{
"level": 2,
"text": "見出しテキスト",
"children": [
{"content": "本文テキスト"},
{
"level": 3,
"text": "サブ見出し",
"children": [{"content": "サブセクションの本文"}]
}
]
}
]
}
準備
出力の型を定めるクラスを定義する。
class Content(BaseModel):
"""コンテンツ"""
content: str = Field(
...,
description="本文のテキスト",
examples=["ここでは導入をおこなう。", "メインコンテンツを扱う。"],
)
class Heading(BaseModel):
"""見出しのメタデータ"""
level: int = Field(
...,
description="見出しのレベル(例: 1, 2, 3)",
examples=[2, 3],
)
text: str = Field(
...,
description="見出しのテキスト",
examples=["導入", "まとめ"],
)
children: list[Union[Content, Heading]] = Field(
...,
description="子要素のリスト",
examples=[
# ...
]
)
class MarkdownDocument(BaseModel):
"""マークダウン文書の構造を定義します。"""
contents: list[Union[Content, Heading]] = Field(
...,
description="本文のリスト",
examples=[
# ...
],
)
OpenAI Agents SDK の output_type を使う
OpenAI Agents SDK では、output_type を使って出力の形式を指定することができる。
これを使うと、出力の形式を厳密に指定することができる。
まず、出力の型を定めるクラスを定義する。
出力を output_type で指定する。
agent = Agent(
name="Markdown Generator",
instructions=system_prompt,
model=LitellmModel(model=llm, api_key=api_key),
model_settings=ModelSettings(temperature=temperature),
output_type=MarkdownDocument,
)
実行して得られた出力の一部を抜粋する。
{
"level": 2,
"text": "生成AIの種類と機能",
"children": []
},
{
"level": 3,
"text": "テキスト生成AI",
"children": []
},
{
"content": "GPTシリーズに代表されるテキスト生成AIは、与えられたプロンプトに基づいて、自然な文章を生成します。記事の執筆、メール作成、コード生成、翻訳、要約など、その用途は多岐にわたります。人間のような対話能力を持つことで、カスタマーサポートや教育分野での活用も期待されています。"
},
children の中に入っていてほしいものが外にある。
Gemini 特有の現象
https://github.com/openai/openai-agents-python/issues/1575
Gemini を使うと、output_type を使っても意図したとおりの出力形式が得られないという現象があるらしい。
あとがき
Anthropic の新しいモデルで軒並み API エラーになってしまって使えなかったのが残念。 またあらためて試してみたい。