はじめに#
OpenAI Codexは、ChatGPTのWebインターフェースやCodex CLIから利用できるだけでなく、Responses APIを通じてプログラマティックに呼び出すことも可能です。APIを使用することで、開発ワークフローの自動化、CI/CDパイプラインへの統合、カスタムツールの構築など、より高度なユースケースを実現できます。
本記事では、Codex APIの基礎となるcodex-mini-latestモデルを使った自動化開発の方法を解説します。基本的なAPIリクエストの構造から、ストリーミングレスポンスの処理、そして本番環境で必須となるエラーハンドリングとリトライ戦略までを実践的に学びます。
codex-mini-latestモデルとは#
codex-mini-latestは、Codex CLI向けに最適化されたo4-miniベースの推論モデルです。API経由でコード生成や分析タスクを実行する際に利用できます。
モデルの特徴#
| 項目 |
値 |
| コンテキストウィンドウ |
200,000トークン |
| 最大出力トークン |
100,000トークン |
| 知識カットオフ |
2024年6月1日 |
| 入力料金 |
$1.50 / 1Mトークン |
| キャッシュ入力料金 |
$0.375 / 1Mトークン |
| 出力料金 |
$6.00 / 1Mトークン |
対応エンドポイント#
codex-mini-latestは以下のエンドポイントで利用できます。
| エンドポイント |
対応状況 |
Responses API (v1/responses) |
対応 |
Chat Completions (v1/chat/completions) |
対応 |
Realtime API (v1/realtime) |
対応 |
Assistants API (v1/assistants) |
対応 |
Batch API (v1/batch) |
対応 |
本記事では、OpenAIが推奨する最新のResponses APIを使用した実装を紹介します。
前提条件#
Codex APIを利用するには、以下の準備が必要です。
| 要件 |
詳細 |
| OpenAIアカウント |
API利用のためのアカウント登録 |
| APIキー |
プロジェクト設定で生成したAPIキー |
| 課金設定 |
従量課金のためのクレジット設定 |
| Python環境 |
Python 3.10以降(本記事のサンプル実行用) |
APIキーの取得#
OpenAI Platformにログインし、プロジェクトのAPIキーを生成します。
1
2
|
# APIキーを環境変数に設定
export OPENAI_API_KEY="sk-proj-..."
|
Python SDKのインストール#
OpenAIの公式Pythonライブラリをインストールします。
基本的なAPIリクエストの構造#
Responses APIの概要#
Responses APIは、OpenAIが提供する最新のモデル応答生成インターフェースです。テキストと画像の入力をサポートし、ファイル検索やWeb検索などのビルトインツールとの連携も可能です。
sequenceDiagram
participant Client as クライアント
participant API as Responses API
participant Model as codex-mini-latest
Client->>API: POST /v1/responses
API->>Model: リクエスト転送
Model-->>API: レスポンス生成
API-->>Client: Response オブジェクト最初のAPIリクエスト#
Pythonを使用した基本的なリクエストの例を示します。
1
2
3
4
5
6
7
8
9
10
|
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="codex-mini-latest",
input="Pythonで素数判定を行う関数を作成してください。"
)
print(response.output_text)
|
このコードを実行すると、Codexがコードを生成して返します。
リクエストパラメータの詳細#
Responses APIでは、様々なパラメータを指定してリクエストをカスタマイズできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="codex-mini-latest",
input=[
{
"role": "user",
"content": "TypeScriptで配列の重複を除去するユーティリティ関数を作成してください。"
}
],
instructions="あなたは熟練したソフトウェアエンジニアです。コードは必ずコメント付きで記述してください。",
temperature=0.2,
max_output_tokens=4096,
store=True
)
# レスポンスの詳細を確認
print(f"Response ID: {response.id}")
print(f"Status: {response.status}")
print(f"Model: {response.model}")
print(f"Output: {response.output_text}")
|
主要なパラメータ#
| パラメータ |
説明 |
デフォルト値 |
model |
使用するモデルID |
必須 |
input |
入力テキストまたはメッセージ配列 |
必須 |
instructions |
システムメッセージ(開発者指示) |
なし |
temperature |
出力のランダム性(0-2) |
1 |
max_output_tokens |
最大出力トークン数 |
モデル依存 |
store |
レスポンスを保存するか |
true |
stream |
ストリーミングを有効にするか |
false |
レスポンスオブジェクトの構造#
APIから返されるResponseオブジェクトには、生成結果に関する詳細情報が含まれています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="codex-mini-latest",
input="FizzBuzz問題をPythonで解いてください。"
)
# レスポンスオブジェクトの構造を確認
print(f"ID: {response.id}")
print(f"Object: {response.object}")
print(f"Created at: {response.created_at}")
print(f"Status: {response.status}")
print(f"Completed at: {response.completed_at}")
# 出力内容
for item in response.output:
if item.type == "message":
for content in item.content:
if content.type == "output_text":
print(f"Text: {content.text}")
# トークン使用量
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")
print(f"Total tokens: {response.usage.total_tokens}")
|
ステータスの種類#
レスポンスには以下のステータスが設定されます。
| ステータス |
説明 |
completed |
正常に完了 |
failed |
エラーで失敗 |
in_progress |
処理中 |
cancelled |
キャンセル済み |
queued |
キュー待ち |
incomplete |
不完全な終了 |
マルチターン会話の実装#
Responses APIでは、previous_response_idを使用して会話の状態を維持できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
from openai import OpenAI
client = OpenAI()
# 最初のリクエスト
response1 = client.responses.create(
model="codex-mini-latest",
input="Pythonでシンプルなクラスベースのカウンターを作成してください。"
)
print("=== 初回レスポンス ===")
print(response1.output_text)
# フォローアップリクエスト
response2 = client.responses.create(
model="codex-mini-latest",
input="このカウンターにリセット機能を追加してください。",
previous_response_id=response1.id
)
print("\n=== フォローアップレスポンス ===")
print(response2.output_text)
# さらに続ける
response3 = client.responses.create(
model="codex-mini-latest",
input="最大値を設定できるようにしてください。最大値に達したらエラーを発生させます。",
previous_response_id=response2.id
)
print("\n=== 追加機能レスポンス ===")
print(response3.output_text)
|
この方法により、前回の会話コンテキストを保持しながら、段階的にコードを改善していくことができます。
ストリーミングレスポンスの処理#
長い応答を生成する場合、ストリーミングを使用することで、生成されたテキストをリアルタイムで受信できます。
基本的なストリーミング実装#
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from openai import OpenAI
client = OpenAI()
stream = client.responses.create(
model="codex-mini-latest",
input="Express.jsを使用したREST APIサーバーの完全な実装例を作成してください。",
stream=True
)
# ストリーミングイベントを処理
for event in stream:
print(event)
|
テキストデルタの抽出#
実用的なアプリケーションでは、テキストの差分(デルタ)を抽出してリアルタイム表示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
from openai import OpenAI
client = OpenAI()
stream = client.responses.create(
model="codex-mini-latest",
input="ReactでTodoアプリを作成してください。useState, useEffectを使用します。",
stream=True
)
full_text = ""
for event in stream:
# テキストデルタイベントを処理
if event.type == "response.output_text.delta":
delta_text = event.delta
full_text += delta_text
print(delta_text, end="", flush=True)
# 完了イベント
elif event.type == "response.completed":
print("\n\n=== 生成完了 ===")
print(f"Total tokens: {event.response.usage.total_tokens}")
|
ストリーミングイベントの種類#
ストリーミング中に発生する主要なイベントは以下の通りです。
| イベント |
説明 |
response.created |
レスポンス生成開始 |
response.in_progress |
処理中 |
response.output_text.delta |
テキストの差分出力 |
response.output_text.done |
テキスト出力完了 |
response.completed |
全体の処理完了 |
error |
エラー発生 |
非同期ストリーミング#
asyncioを使用した非同期実装も可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import asyncio
from openai import AsyncOpenAI
async def stream_response():
client = AsyncOpenAI()
stream = await client.responses.create(
model="codex-mini-latest",
input="Node.jsでファイルアップロード機能を実装してください。",
stream=True
)
async for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
print("\n完了")
# 実行
asyncio.run(stream_response())
|
エラーハンドリング#
本番環境でAPIを使用する際は、適切なエラーハンドリングが必須です。
エラーの種類#
OpenAI APIで発生する主なエラーは以下の通りです。
| エラーコード |
原因 |
対処法 |
| 401 |
認証エラー |
APIキーを確認 |
| 429 |
レート制限超過 |
リクエスト頻度を下げる |
| 500 |
サーバーエラー |
リトライを実行 |
| 503 |
サービス過負荷 |
時間をおいてリトライ |
基本的なエラーハンドリング#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import openai
from openai import OpenAI
client = OpenAI()
def generate_code(prompt: str) -> str | None:
try:
response = client.responses.create(
model="codex-mini-latest",
input=prompt
)
return response.output_text
except openai.AuthenticationError as e:
print(f"認証エラー: APIキーを確認してください - {e}")
return None
except openai.RateLimitError as e:
print(f"レート制限エラー: リクエスト頻度を下げてください - {e}")
return None
except openai.APIConnectionError as e:
print(f"接続エラー: ネットワーク設定を確認してください - {e}")
return None
except openai.APITimeoutError as e:
print(f"タイムアウトエラー: リトライを検討してください - {e}")
return None
except openai.BadRequestError as e:
print(f"リクエストエラー: パラメータを確認してください - {e}")
return None
except openai.InternalServerError as e:
print(f"サーバーエラー: 時間をおいてリトライしてください - {e}")
return None
except openai.APIError as e:
print(f"APIエラー: {e}")
return None
# 使用例
result = generate_code("Pythonでバブルソートを実装してください。")
if result:
print(result)
|
リトライ戦略#
一時的なエラーに対しては、適切なリトライ戦略を実装することで、システムの信頼性を向上させます。
指数バックオフの実装#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import time
import random
import openai
from openai import OpenAI
def exponential_backoff_retry(
func,
max_retries: int = 5,
base_delay: float = 1.0,
max_delay: float = 60.0,
jitter: bool = True
):
"""指数バックオフを使用したリトライ関数"""
retries = 0
delay = base_delay
while retries < max_retries:
try:
return func()
except (openai.RateLimitError, openai.APITimeoutError,
openai.InternalServerError) as e:
retries += 1
if retries >= max_retries:
raise e
# ジッターを追加してサンダリングハード問題を回避
if jitter:
delay = min(delay * 2, max_delay) + random.uniform(0, 1)
else:
delay = min(delay * 2, max_delay)
print(f"リトライ {retries}/{max_retries}: {delay:.2f}秒後に再試行...")
time.sleep(delay)
except openai.APIError as e:
# リトライ不可能なエラーは即座に例外を発生
raise e
return None
# 使用例
client = OpenAI()
def make_request():
return client.responses.create(
model="codex-mini-latest",
input="クイックソートをPythonで実装してください。"
)
try:
response = exponential_backoff_retry(make_request)
print(response.output_text)
except openai.APIError as e:
print(f"リトライ後もエラー: {e}")
|
デコレータパターンによるリトライ#
再利用可能なリトライロジックをデコレータとして実装します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import time
import random
import functools
import openai
def retry_with_backoff(
max_retries: int = 5,
base_delay: float = 1.0,
max_delay: float = 60.0,
retryable_exceptions: tuple = (
openai.RateLimitError,
openai.APITimeoutError,
openai.InternalServerError
)
):
"""リトライ機能を提供するデコレータ"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
delay = base_delay
last_exception = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except retryable_exceptions as e:
last_exception = e
if attempt < max_retries - 1:
sleep_time = delay + random.uniform(0, 1)
print(f"試行 {attempt + 1} 失敗: {sleep_time:.2f}秒後にリトライ")
time.sleep(sleep_time)
delay = min(delay * 2, max_delay)
raise last_exception
return wrapper
return decorator
# デコレータの使用例
from openai import OpenAI
client = OpenAI()
@retry_with_backoff(max_retries=3)
def generate_with_retry(prompt: str) -> str:
response = client.responses.create(
model="codex-mini-latest",
input=prompt
)
return response.output_text
# 実行
try:
result = generate_with_retry("マージソートをPythonで実装してください。")
print(result)
except openai.APIError as e:
print(f"最終的にエラー: {e}")
|
実践的なコード生成の自動化#
ここでは、Codex APIを使用した実践的なコード生成自動化の例を紹介します。
コードレビュー自動化#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
from openai import OpenAI
client = OpenAI()
def review_code(code: str, language: str = "Python") -> str:
"""コードをレビューして改善点を提案する"""
prompt = f"""以下の{language}コードをレビューしてください。
\`\`\`{language.lower()}
{code}
\`\`\`
以下の観点でレビューを行い、改善点を具体的に指摘してください:
1. バグや潜在的な問題
2. コードの可読性
3. パフォーマンスの改善点
4. ベストプラクティスへの準拠
5. 改善版のコード(必要な場合)
"""
response = client.responses.create(
model="codex-mini-latest",
input=prompt,
instructions="あなたは経験豊富なシニアエンジニアです。建設的なフィードバックを提供してください。",
temperature=0.3
)
return response.output_text
# 使用例
sample_code = """
def find_duplicates(lst):
duplicates = []
for i in range(len(lst)):
for j in range(i + 1, len(lst)):
if lst[i] == lst[j] and lst[i] not in duplicates:
duplicates.append(lst[i])
return duplicates
"""
review_result = review_code(sample_code)
print(review_result)
|
テストコード自動生成#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
from openai import OpenAI
client = OpenAI()
def generate_tests(code: str, framework: str = "pytest") -> str:
"""コードに対するテストを自動生成する"""
prompt = f"""以下のPythonコードに対するテストを{framework}を使用して作成してください。
\`\`\`python
{code}
\`\`\`
要件:
- 正常系のテストケース
- エッジケースのテスト
- 異常系のテスト(エラーハンドリング)
- 境界値テスト
"""
response = client.responses.create(
model="codex-mini-latest",
input=prompt,
instructions="テストは網羅的で、実行可能なコードを出力してください。",
temperature=0.2
)
return response.output_text
# 使用例
target_code = """
def calculate_discount(price: float, discount_percent: float) -> float:
if price < 0:
raise ValueError("Price cannot be negative")
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Discount must be between 0 and 100")
return price * (1 - discount_percent / 100)
"""
tests = generate_tests(target_code)
print(tests)
|
バッチ処理による大量タスクの実行#
複数のコード生成タスクを効率的に処理する方法です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import asyncio
from openai import AsyncOpenAI
async def generate_batch(prompts: list[str]) -> list[str]:
"""複数のプロンプトを並列処理する"""
client = AsyncOpenAI()
async def single_request(prompt: str) -> str:
response = await client.responses.create(
model="codex-mini-latest",
input=prompt,
temperature=0.3
)
return response.output_text
# 並列実行
tasks = [single_request(prompt) for prompt in prompts]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
# 使用例
async def main():
prompts = [
"Pythonでスタックを実装してください。",
"Pythonでキューを実装してください。",
"Pythonでリンクリストを実装してください。",
"Pythonで二分探索木を実装してください。"
]
results = await generate_batch(prompts)
for i, result in enumerate(results):
print(f"\n=== タスク {i + 1} ===")
if isinstance(result, Exception):
print(f"エラー: {result}")
else:
print(result[:500] + "..." if len(result) > 500 else result)
asyncio.run(main())
|
コスト管理のベストプラクティス#
APIを本番環境で使用する際は、コスト管理が重要です。
トークン使用量の監視#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
from openai import OpenAI
from dataclasses import dataclass
@dataclass
class UsageTracker:
total_input_tokens: int = 0
total_output_tokens: int = 0
total_requests: int = 0
# codex-mini-latestの料金(2026年1月時点)
INPUT_COST_PER_1M = 1.50 # ドル
OUTPUT_COST_PER_1M = 6.00 # ドル
def add_usage(self, input_tokens: int, output_tokens: int):
self.total_input_tokens += input_tokens
self.total_output_tokens += output_tokens
self.total_requests += 1
def get_estimated_cost(self) -> float:
input_cost = (self.total_input_tokens / 1_000_000) * self.INPUT_COST_PER_1M
output_cost = (self.total_output_tokens / 1_000_000) * self.OUTPUT_COST_PER_1M
return input_cost + output_cost
def report(self):
print(f"総リクエスト数: {self.total_requests}")
print(f"総入力トークン: {self.total_input_tokens:,}")
print(f"総出力トークン: {self.total_output_tokens:,}")
print(f"推定コスト: ${self.get_estimated_cost():.4f}")
# 使用例
client = OpenAI()
tracker = UsageTracker()
def tracked_request(prompt: str) -> str:
response = client.responses.create(
model="codex-mini-latest",
input=prompt
)
tracker.add_usage(
response.usage.input_tokens,
response.usage.output_tokens
)
return response.output_text
# 複数リクエストを実行
prompts = [
"Hello Worldをpythonで出力してください。",
"FizzBuzzをPythonで実装してください。",
"バイナリサーチをPythonで実装してください。"
]
for prompt in prompts:
result = tracked_request(prompt)
print(f"生成完了: {len(result)} 文字")
# レポート出力
print("\n=== 使用量レポート ===")
tracker.report()
|
プロンプトキャッシュの活用#
同じプロンプトプレフィックスを繰り返し使用する場合、キャッシュを活用してコストを削減できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from openai import OpenAI
client = OpenAI()
# prompt_cache_keyを設定して類似リクエストのキャッシュヒット率を向上
response = client.responses.create(
model="codex-mini-latest",
input="Pythonでユーティリティ関数を作成してください。",
prompt_cache_key="user_123_utility_functions",
prompt_cache_retention="24h" # 24時間キャッシュを保持
)
# キャッシュされたトークンを確認
cached = response.usage.input_tokens_details.cached_tokens
print(f"キャッシュされたトークン: {cached}")
|
まとめ#
本記事では、Codex APIのcodex-mini-latestモデルを使用した自動化開発の基礎を解説しました。
学んだ内容#
- Responses APIの基本的なリクエスト構造とパラメータ
- マルチターン会話による段階的なコード改善
- ストリーミングレスポンスによるリアルタイム出力
- 本番環境で必須のエラーハンドリング手法
- 指数バックオフを使用したリトライ戦略
- コスト管理とトークン使用量の監視
次のステップ#
Codex APIの基礎を習得したら、次は以下のトピックに進むことをお勧めします。
- カスタム開発ワークフローの構築: Issue自動トリアージ、PRレビュー自動化などの実装
- CI/CDパイプラインへの統合: GitHub ActionsやJenkinsとの連携
- Function Callingの活用: 外部システムとの連携によるエージェント機能の拡張
Codex APIを活用することで、開発プロセスの自動化を実現し、より創造的な作業に時間を使えるようになります。
参考リンク#