OpenAI Chat APIチートシート – 同期・非同期でのやり方(ストリーム処理も解説)

OpenAIのChatGPTをAPI(正式名は、Chat Completion API)で呼び出す方法を同期・非同期・ストリームあり・なしでそれぞれ説明しようと思います。以下では、openai-pythonパッケージを使って紹介します。
それぞれのユースケースに合わせて、参照していただければと思います。

同期(NOストリーム)でChat Completionを使用する方法

ストリームなしで同期でAPIを呼び出す方法を一番簡単な方法です。ただし、徐々に回答が生成されるストリーム処理を使わない分、レスポンスが遅いです。そのため、ユーザーは待たされている様に感じるかもしれません。また、同期なので、マルチスレッドや非同期にしなければ、他の処理にCPUを使えません。

openai.ChatCompletion.createの引数であるstream=Falseとすることで設定できます。(デフォルトでも、stream=Falseです。)
プロンプトにもよりますが、以下のプロンプトでは、レスポンスが返ってくるまでに約14秒かかっています。

import openai 
import time 

# OPENAI APIキー
openai.api_key = ''

def call_sync_chat_completion_no_stream(messages, 
                                        temperature=0.0,
                                        max_tokens=1000,
                                        model='gpt-3.5-turbo'):
    start_request_time = time.perf_counter()
    response = openai.ChatCompletion.create(
        messages=messages,
        temperature=temperature,
        model=model,
        max_tokens=max_tokens,
        stream=False
    )
    end_request_time = time.perf_counter()
    print(f'処理時間: {end_request_time-start_request_time} sec')
    return response

result = call_sync_chat_completion_no_stream([{'role': 'user', 'content': '生成AIについて教えて'}])
print(result['choices'][0]['message']['content'])

## 出力
# 処理時間: 13.684446917002788 sec
# AI(Artificial Intelligence、人工知能)は、コンピューターシステムが人間の知能を模倣する技術や理論のことを指します。AIは、機械学習、深層学習、自然言語処理、コンピュータービジョンなどのさまざまな技術を組み合わせて実現されます。

# AIの目的は、人間の知能を持つようなタスクを自動化することです。例えば、画像認識や音声認識、自動運転、機械翻訳、推薦システムなどがあります。AIは、大量のデータを学習し、パターンやルールを抽出して問題を解決する能力を持ちます。

# AIの開発には、データの収集と前処理、モデルの設計とトレーニング、評価と改善のサイクルが含まれます。データの品質や量、モデルの選択やパラメータの調整など、多くの要素がAIの性能に影響を与えます。

# AIの利点は、高速で正確な処理、大量のデータの解析、人間の能力を超えるタスクの実行などです。しかし、倫理的な問題やプライバシーの懸念、人間の仕事の自動化などの課題も存在します。

# AIの将来の展望は、より高度な自己学習能力や推論能力の開発、人間との自然な対話や協働、倫理的な観点からのAIの開発などがあります。AIは、私たちの生活や社会のさまざまな分野において大きな影響を与えることが期待されています。

同期(ストリーム)でChat Completionを使用する方法

こちらは、同期処理で徐々に回答を生成するストリーム処理を入れたものです。徐々に回答が出力され、最初の回答が返ってくる時間がストリーム無しと比べて早いため、ユーザーの待たされている感は軽減されると思います。ただ、同期なので、複数処理を同時に動かすためには、マルチスレッド処理が必要になります。

openai.ChatCompletion.createの引数であるstream=Trueとすることで設定できます。
この関数の戻り値(レスポンス)は、ジェネレータとして返ってきます。このジェネレータをfor文を使うことにより、徐々に回答を生成します。また、ストリーム処理では、回答の終了を判断をしなければいけません。終了判断は、finish_reasonを見ることで行います。
また、最初の回答が得られるまでの時間は、約1秒とストリーム無しと比べて、短くなっていることがわかります。

def call_sync_chat_completion_stream(messages, 
                                        temperature=0.0,
                                        max_tokens=1000,
                                        model='gpt-3.5-turbo'):
    start_request_time = time.perf_counter()
    response = openai.ChatCompletion.create(
        messages=messages,
        temperature=temperature,
        model=model,
        max_tokens=max_tokens,
        stream=True
    )

    # レスポンスは、ジェネレータで返ってくる
    whole_content = ''
    is_first_response = True
    for chunk in response:
        finish_reason = chunk['choices'][0]['finish_reason']
        
        if finish_reason == 'stop':
            # finish_reason=stopで、終了判断
            break
        else:
            if is_first_response:
                end_request_time = time.perf_counter()
                print(f'最初の回答時間: {end_request_time-start_request_time} sec')
                is_first_response = False
            content = chunk['choices'][0]['delta']['content']
            whole_content += content
            print(content, end='')
    return whole_content

result = call_sync_chat_completion_stream([{'role': 'user', 'content': '生成AIについて教えて'}])

## 出力
# 最初の回答時間: 1.1622102910041576 sec
# AI(Artificial Intelligence、人工知能)は、コンピューターシステムが人間の知能を模倣する技術や理論のことを指します。AIは、機械学習、深層学習、自然言語処理、コンピュータービジョンなどのさまざまな技術を組み合わせて実現されます。

# AIの目的は、人間の知能を持つようなタスクを自動化することです。例えば、画像認識や音声認識、自動運転、機械翻訳、音声アシスタントなどがあります。AIは、大量のデータを学習し、パターンやルールを抽出して問題を解決することができます。

# AIの開発には、データの収集と前処理、モデルの設計とトレーニング、評価と改善のサイクルが含まれます。データは、正確で多様なものであることが重要です。モデルの設計では、機械学習アルゴリズムやニューラルネットワークの構造を選択します。トレーニングでは、データを使用してモデルを学習させます。評価では、モデルの性能を評価し、必要に応じて改善を行います。

# AIの応用は広範であり、医療診断、金融取引、製造業、交通管理、エネルギー効率など、さまざまな分野で利用されています。AIの進歩により、より高度なタスクの自動化や効率化が可能になり、私たちの生活や社会に大きな影響を与えることが期待されています。

非同期(NOストリーム)でChat Completionを使用する方法

以下のコードは、ストリームなしで非同期で回答を取得するものです非同期で実装されているため、CPUのリソースをブロックすることなく、APIを読んでOpenAIからのレスポンスを待っている間、他の処理に回すことができます。
非同期処理は、ウェブアプリのバックエンドで使う場合は、ほぼ必須です。例えば、pythonのwebフレームワークであるFastAPIではネイティブでasyncioをサポートしております。これは、同期関数で実装すると、同時にユーザーがアクセスされた時に一つの処理しか一回で捌けないためです。(マルチスレッドやワーカー数を増やすことに対応はできますが)

関数定義の最初に、asyncをつけて、この関数が非同期に実装されていることを示します。そして、chatcompletionの非同期関数であるopenai.ChatCompletion.acreateを使います。createではなく、acreateを使っている点に気をつけてください。また、非同期で関数を呼び出すためにawaitをopenaiの関数の先頭につけています。
asyncioを使って、この非同期関数を2つ同時に呼び出すことができます。出力結果を見ると、それぞれが並行的に動いていることがわかります。また、処理時間も3秒ほど差がありますが、直列(1回目の処理が終わった後に次の関数を実行する方法)で実行した場合は12秒+12秒ですが、並行処理だと関数番号2の結果が得られるまでの時間15秒で結果を得られます。

import asyncio

async def call_async_chat_completion_no_stream(messages, 
                                               func_number,
                                               temperature=0.0,
                                               max_tokens=1000,
                                               model='gpt-3.5-turbo',
                                               ):
    print(f'関数番号{func_number} スタート')
    start_request_time = time.perf_counter()
    response = await openai.ChatCompletion.acreate(
        messages=messages,
        temperature=temperature,
        model=model,
        max_tokens=max_tokens,
        stream=False
    )
    end_request_time = time.perf_counter()
    print(f'関数番号{func_number} 処理時間: {end_request_time-start_request_time} sec')
    return response

messages = [{'role': 'user', 'content': '生成AIについて教えて'}]
result1, result2 = await asyncio.gather(call_async_chat_completion_no_stream(messages, 1), call_async_chat_completion_no_stream(messages, 2))

## 出力
# 関数番号1 スタート
# 関数番号2 スタート
# 関数番号2 処理時間: 12.042620416992577 sec
# 関数番号1 処理時間: 15.627812833001371 sec

非同期(ストリーム)でChat Completionを使用する方法

ストリームありで、非同期で処理を実行する場合は、非同期(ストリームなし)で、acreate関数の引数でstream=Trueとすることでできます。これで実行すると、非同期ジェネレータ(AsyncGenerator)が返ってきます。非同期ジェネレータの説明については、こちらのサイトなどがおすすめです。
非同期ジェネレータからチャンクを受け取るには、async for でループを回して受け取ります。

async def call_async_chat_completion_stream(messages, func_number,
                                            temperature=0.0,
                                            max_tokens=1000,
                                            model='gpt-3.5-turbo'
                                           ):
    print(f'関数番号{func_number} スタート')
    start_request_time = time.perf_counter()
    async_generator = await openai.ChatCompletion.acreate(
        messages=messages,
        temperature=temperature,
        model=model,
        max_tokens=max_tokens,
        stream=True
    )
    
    # レスポンスは、ジェネレータで返ってくる
    whole_content = ''
    is_first_response = True
    async for chunk in async_generator:
        finish_reason = chunk['choices'][0]['finish_reason']
        
        if finish_reason == 'stop':
            # finish_reason=stopで、終了判断
            break
        else:
            if is_first_response:
                end_request_time = time.perf_counter()
                print(f'関数番号{func_number} 最初の回答時間: {end_request_time-start_request_time} sec')
                is_first_response = False
            content = chunk['choices'][0]['delta']['content']
            whole_content += content
    end_process_time = time.perf_counter()
    print(f'関数番号{func_number} 回答時間: {end_process_time-start_request_time}')
    return whole_content

messages = [{'role': 'user', 'content': '生成AIについて教えて'}]
result1, result2 = await asyncio.gather(call_async_chat_completion_stream(messages, 1), call_async_chat_completion_stream(messages, 2))

## 出力
# 関数番号1 スタート
# 関数番号2 スタート
# 関数番号2 最初の回答時間: 0.9983667080086889 sec
# 関数番号1 最初の回答時間: 1.0379899170075078 sec
# 関数番号2 回答時間: 12.028665375008131
# 関数番号1 回答時間: 14.01881449999928

コメント

タイトルとURLをコピーしました