52
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Python】ChatGPT APIでウェブサイト版のように返答を逐次受け取る方法

Last updated at Posted at 2023-03-08

はじめに

ChatGPT APIの公開後、瞬く間に様々なサービスが誕生していますね。
私も少し触ってみましたが、マルコフ連鎖的に文章を紡ぐ仕組み上、レスポンスがやや遅い点が気になります。
PythonからAPIを叩いている多くの方はOpenAIのライブラリを使用しているかと思いますが、
一般的にHTTPリクエストに使用されるrequestsを使用すれば、ウェブサイト版のChatGPTとお話するときのように、逐次返答を受け取ることができます。

追記
openaiライブラリでも普通にできました。後半で解説します。

方法

requestspost()からAPIを叩いて、その際、引数とペイロードのstreamTrueにすればおkです。
以下の実装例の後半部分が該当箇所になります。

import requests, json

API_URL = "https://api.openai.com/v1/chat/completions"
API_KEY = "sk-************************************************"


def chat(text,
         messages=None,
         settings="",
         max_tokens=2000,
         temperature=1.,
         top_p=.1,
         presence_penalty=0.,
         frequency_penalty=0.):

    # やり取りの管理
    messages = messages if messages is not None else []
    if settings and not messages:
        messages.append({"role": "system", "content": settings})
    messages.append({'role': 'user', 'content': text})

    # ヘッダ
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_KEY}"
    }

    # ペイロード
    payload = {
        "model": "gpt-3.5-turbo",
        "messages": messages,
        "max_tokens": max_tokens,
        "temperature": temperature,
        "top_p": top_p,
        "presence_penalty": presence_penalty,
        "frequency_penalty": frequency_penalty,
        "stream": True,
    }

    # APIを叩く、streamをTrueに
    resp = requests.post(API_URL, headers=headers, json=payload, stream=True)

    # 返答を受け取り、逐次yield
    response_text = ''
    for chunk in resp.iter_lines():
        try:
            j = json.loads(chunk.decode()[6:])
            content = j['choices'][0]['delta'].get('content')
            if content:
                response_text += content
                yield content
        except:
            ...
    else:  #
        messages += [{'role': 'assistant', 'content': response_text}]

追記 openaiライブラリでの方法

openai.ChatCompletion.create()の引数streamTrueにしてあげればおkです。
以下の実装例では上のrequestsの場合と同じ内容の関数にしてあります。

import openai

openai.api_key = "sk-************************************************"

def chat(text,
         messages=None,
         settings="",
         max_tokens=2000,
         temperature=1.,
         top_p=.1,
         presence_penalty=0.,
         frequency_penalty=0.):

    # やり取りの管理
    messages = messages if messages is not None else []
    if settings and not messages:
        messages.append({"role": "system", "content": settings})
    messages.append({'role': 'user', 'content': text})


    # APIを叩く、streamをTrueに
    resp = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=messages,
        max_tokens=max_tokens,
        temperature=temperature,
        top_p=top_p,
        presence_penalty=presence_penalty,
        frequency_penalty=frequency_penalty,
        stream=True
    )
    
    # 返答を受け取り、逐次yield
    response_text = "" 
    for chunk in resp:
        if chunk:
            content = chunk['choices'][0]['delta'].get('content')
            if content:
                response_text += content
                yield content
    else:  #
        messages += [{'role': 'assistant', 'content': response_text}]

使い方

上記実装例ではyieldを使用し、ジェネレーターとして定義しているので、

messages = []
for talk in chat('こんにちは。自己紹介してください。', messages):
    print(talk, end='')

のようにすれば↓のように訥々と喋ってくれます(言い方)。
stream.gif

さいごに

これを読んだ方が面白いサービスを作ってくれることを期待しています(他力本願)!

52
47
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?