見出し画像

OpenAI Responses API 入門 (6) - ストリーミング

「Responses API」の「ストリーミング」ついてまとめました。

Streaming API responses


前回

1. ストリーミング

「OpenAI API」にリクエストを行うと、デフォルトでは、出力全体を生成してから、単一の応答で返します。長い出力を生成する場合、応答を待つのに時間がかかることがあります。ストリーミングを使用すると、応答を生成しながら、出力を返すことができます。

2. OpenAI API の準備

「Google Colab」での「OpenAI API」の準備手順は次のとおりです。

(1) パッケージのインストール。
「Responses API」と「Agent SDK」のパッケージをインストールします。

# パッケージのインストール
!pip install -U openai openai-agents

(2) 環境変数の準備。
左端の鍵アイコンで「OPENAI_API_KEY」を設定してから、以下のセルを実行してください。

import os
from google.colab import userdata

# 環境変数の準備 (左端の鍵アイコンでOPENAI_API_KEYを設定)
os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")

(3) クライアントの準備。

from openai import OpenAI

# クライアントの準備
client = OpenAI()

3. ストリーミングの実行

(1)  メッセージリストの準備。

# メッセージリストの準備
messages = [
    {
        "role": "user",
        "content": "早口言葉を言ってください。",
    }
]

(2) 推論の実行。
ストリーミングを行うには、stream=True を指定します。

# 推論の実行
stream = client.responses.create(
    model="gpt-4o",
    input=messages,
    stream=True,
)
for event in stream:
    print(event)
ResponseCreatedEvent(response=Response(id='resp_67d5f51fec6881918b309bc3509757480b9e6b9341010ff3', created_at=1742075167.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, max_output_tokens=None, previous_response_id=None, reasoning=Reasoning(effort=None, generate_summary=None), status='in_progress', text=ResponseTextConfig(format=ResponseFormatText(type='text')), truncation='disabled', usage=None, user=None, store=True), type='response.created')
ResponseInProgressEvent(response=Response(id='resp_67d5f51fec6881918b309bc3509757480b9e6b9341010ff3', created_at=1742075167.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, max_output_tokens=None, previous_response_id=None, reasoning=Reasoning(effort=None, generate_summary=None), status='in_progress', text=ResponseTextConfig(format=ResponseFormatText(type='text')), truncation='disabled', usage=None, user=None, store=True), type='response.in_progress')
ResponseOutputItemAddedEvent(item=ResponseOutputMessage(id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', content=[], role='assistant', status='in_progress', type='message'), output_index=0, type='response.output_item.added')
ResponseContentPartAddedEvent(content_index=0, item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, part=ResponseOutputText(annotations=[], text='', type='output_text'), type='response.content_part.added')
ResponseTextDeltaEvent(content_index=0, delta='もちろん', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='!', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='こちら', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='は', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='どう', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='です', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='か', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='?\n\n', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='「', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='生', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='麦', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='生', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='米', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='生', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='卵', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='。」\n\n', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='速', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='く', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='言', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='って', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='み', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='て', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='ください', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDeltaEvent(content_index=0, delta='!', item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, type='response.output_text.delta')
ResponseTextDoneEvent(content_index=0, item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, text='もちろん!こちらはどうですか?\n\n「生麦生米生卵。」\n\n速く言ってみてください!', type='response.output_text.done')
ResponseContentPartDoneEvent(content_index=0, item_id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', output_index=0, part=ResponseOutputText(annotations=[], text='もちろん!こちらはどうですか?\n\n「生麦生米生卵。」\n\n速く言ってみてください!', type='output_text'), type='response.content_part.done')
ResponseOutputItemDoneEvent(item=ResponseOutputMessage(id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', content=[ResponseOutputText(annotations=[], text='もちろん!こちらはどうですか?\n\n「生麦生米生卵。」\n\n速く言ってみてください!', type='output_text')], role='assistant', status='completed', type='message'), output_index=0, type='response.output_item.done')
ResponseCompletedEvent(response=Response(id='resp_67d5f51fec6881918b309bc3509757480b9e6b9341010ff3', created_at=1742075167.0, error=None, incomplete_details=None, instructions=None, metadata={}, model='gpt-4o-2024-08-06', object='response', output=[ResponseOutputMessage(id='msg_67d5f52045608191bb7707be7b5a0e140b9e6b9341010ff3', content=[ResponseOutputText(annotations=[], text='もちろん!こちらはどうですか?\n\n「生麦生米生卵。」\n\n速く言ってみてください!', type='output_text')], role='assistant', status='completed', type='message')], parallel_tool_calls=True, temperature=1.0, tool_choice='auto', tools=[], top_p=1.0, max_output_tokens=None, previous_response_id=None, reasoning=Reasoning(effort=None, generate_summary=None), status='completed', text=ResponseTextConfig(format=ResponseFormatText(type='text')), truncation='disabled', usage=ResponseUsage(input_tokens=34, output_tokens=26, output_tokens_details=OutputTokensDetails(reasoning_tokens=0), total_tokens=60, input_tokens_details={'cached_tokens': 0}), user=None, store=True), type='response.completed')

(3) 差分テキストのみ出力。

# 推論の実行
stream = client.responses.create(
    model="gpt-4o",
    input=messages,
    stream=True,
)
for event in stream:
   if event.type == 'response.output_text.delta':
        print(event.delta, end='')

もちろん!これはどうですか?
「生麦生米生卵」
または、
「隣の客はよく柿食う客だ」

Colab上では数文字ずつストリーミング出力されます。

4. イベント

「Responses API」は、ストリーミング応答で「イベント」を返します。「イベント」は type でイベント種別を識別することもできます。

主なイベント種別は次のとおりです。

・response.created : モデル応答が開始した時に発行。
・response.output_text.delta
: 追加のテキスト差分が発生した時に発行。
・response.completed
: モデル応答が完了した時に発行。
・error
: エラーが発生した時に発行。

イベント種別完全なリストについては、「ストリーミングのAPIリファレンス」を参照してください。

type StreamingEvent = 
	| ResponseCreatedEvent
	| ResponseInProgressEvent
	| ResponseFailedEvent
	| ResponseCompletedEvent
	| ResponseOutputItemAdded
	| ResponseOutputItemDone
	| ResponseContentPartAdded
	| ResponseContentPartDone
	| ResponseOutputTextDelta
	| ResponseOutputTextAnnotationAdded
	| ResponseTextDone
	| ResponseRefusalDelta
	| ResponseRefusalDone
	| ResponseFunctionCallArgumentsDelta
	| ResponseFunctionCallArgumentsDone
	| ResponseFileSearchCallInProgress
	| ResponseFileSearchCallSearching
	| ResponseFileSearchCallCompleted
	| ResponseCodeInterpreterInProgress
	| ResponseCodeInterpreterCallCodeDelta
	| ResponseCodeInterpreterCallCodeDone
	| ResponseCodeInterpreterCallIntepreting
	| ResponseCodeInterpreterCallCompleted
	| Error

次回



いいなと思ったら応援しよう!