このページはOpenAIのAPIを使うを書き直したものです。
[2023-06-14] 新しいモデル "gpt-3.5-turbo-0613"
、"gpt-3.5-turbo-16k-0613"
、"gpt-4-0613"
、"gpt-4-32k-0613"
が来ました。価格も若干改訂され、function calling(関数呼び出し)の機能が付きました。Function calling and other API updates 参照。GPT-3.5の16k版はすぐに使えましたが、GPT-4の32k版はまだ私には降ってきていません。
[2023-03-18] GPT-4のAPIが私のところにも来ました。以下のコードで "gpt-3.5-turbo"
を "gpt-4"
(8192トークン版)あるいは "gpt-4-32k"
(32768トークン版)にするだけで使えます。値段は1桁以上高くなって、入力は8k版が $0.03/1k、32k版が $0.06/1k、出力はその倍の値段です。
2023年3月2日(米国時間では1日)、OpenAI の ChatGPT の API が公開されました(Introducing ChatGPT and Whisper APIs)。費用は従来の text-davinci-003 の1/10の0.0002ドル/1000トークンと、非常にお値打ちです。
従来のAPIを使っていた人は何もせずに使えますが、そうでない場合は、まずこちらで登録してAPIキーを発行してもらわなければなりません。
APIの概要はOpenAIの Introduction および Chat をご覧ください。規約については API data usage policies をご覧ください。APIで送られたデータは学習用に使われることはありません。不正使用の監視のために30日間保持され、特に問題なければ消去されるようです。
Pythonのパッケージは pip install openai
でインストールできます。
APIキーは、プログラムに直接書き込まず、環境変数に設定しておくのが安全・便利です。MacやLinuxでは、ターミナルに
export OPENAI_API_KEY="sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
と打ち込めば環境変数が設定されます。.bashrc
か .zshenv
等に書き込んでおけばシェル起動時に設定されます。APIキーを書き込んだファイルは他人に見られないようにパーミッションを正しく設定しておきましょう。
Google Colab などの環境では上の方法がうまくいきませんので、プログラムの先頭部分に次のように直接入れればいいでしょう:
import openai openai.api_key = "sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
使い方の基本は次の通りです:
import openai # もし環境変数が使えないなら次のように書き込む: # openai.api_key = "sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" res = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "あなたは賢いAIです。"}, # 役割設定(省略可) {"role": "user", "content": "1たす1は?"} # 最初の質問 ], temperature=1 # 温度(0-2, デフォルト1) ) print(res["choices"][0]["message"]["content"]) # 答えが返る
ChatGPTのAPIは、質問・応答の履歴を記憶しません。以前の質問・応答を前提としたい場合は、次のように履歴を与えた上で質問をします:
res = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "あなたは賢いAIです。"}, # 役割設定(省略可) {"role": "user", "content": "1たす1は?"}, # 最初の質問 {"role": "assistant", "content": "2です。"}, # 最初の答え {"role": "user", "content": "それを3倍して。"} # 次の質問 ], temperature=1 ) print(res["choices"][0]["message"]["content"]) # 答えが返る
履歴と質問、答えを合わせて、4096トークンが上限です。トークンはほぼ単語に相当するものです(後述)。トークン数を表示するには、次のようにします:
print(res["usage"]["prompt_tokens"], # (履歴と)質問のトークン数 res["usage"]["completion_tokens"], # 答えのトークン数 res["usage"]["total_tokens"]) # 合計
合計が4096を超えることはありません。延々と会話を続けるには、不要な履歴を削除する必要があります。削除も含めて、簡単な会話を続けるには、例えば次のようにすればいいでしょう:
import openai msg = [{"role": "system", "content": "あなたは賢いAIです。"}] while True: prompt = input("> ").strip() if prompt in ["quit", "exit"]: break msg.append({"role": "user", "content": prompt}) res = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=msg) ans = res["choices"][0]["message"]["content"].strip() print(ans) msg.append({"role": "assistant", "content": ans}) if res["usage"]["total_tokens"] > 3000: msg.pop(1) msg.pop(1)
Web 版 ChatGPT のように文字単位(単語単位)で出力するようにしてみましょう。
import openai class Chatbot: def __init__(self, model="gpt-4", messages=None, temperature=0, stream=True): self.model = model # "gpt-4" or "gpt-4-32k" or "gpt-3.5-turbo" self.messages = messages or [] self.temperature = temperature self.stream = stream def chat(self, prompt, temperature=None, stream=None): prompt = prompt.strip() # print(len(prompt), "letters,", len(prompt.split()), "words") self.messages.append({"role": "user", "content": prompt}) stream = stream or self.stream res = openai.ChatCompletion.create(model=self.model, messages=self.messages, temperature=temperature or self.temperature, stream=stream) if stream: ans = [] for chunk in res: content = chunk["choices"][0]["delta"].get("content", "") print(content, end="") ans.append(content) ans = "".join(ans).strip() else: ans = res["choices"][0]["message"]["content"].strip() print(ans) u = res["usage"] print(u["prompt_tokens"], u["completion_tokens"], u["total_tokens"]) self.messages.append({"role": "assistant", "content": ans}) def get_messages(self): return self.messages
これを使うには例えば
chatbot = Chatbot() chatbot.chat("こんにちは!")
のようにします。会話を続けるには
chatbot.chat(""" ○○について説明してください。 """)
のようにします。コンテクストが溢れた場合は、とりあえず
chatbot = Chatbot(messages=chatbot.get_messages()[2:])
のようにして新しいチャットのインスタンスを作ってください。
IPython または下のような Jupyter Notebook/Lab でお試しください。
上で作った Chatbot()
クラスの応用として、URLを与えてページを要約するアプリを作ってみましょう。ここでは Trafilatura というライブラリを使ってWebページのテキストを取り出しています。
import trafilatura URL = "https://....." text = trafilatura.extract(trafilatura.fetch_url(URL)) chatbot = Chatbot(model="gpt-3.5-turbo-16k") chatbot.chat(prompt="Provide a long and detailed summary of what follows:\n\n" + text)
トークンはほぼ単語に相当する概念で、gpt-3.5-turbo や gpt-4 では tiktoken の cl100k_base というエンコーディングが使われています(→ OpenAI 言語モデルごとのエンコーディング一覧)。頻出単語は1トークン、そうでない単語は2トークン以上に分割されます。日本語の場合は、2〜3文字が1トークンになることも、逆に1文字が2〜3トークンに分割されることもあります。平均してトークンあたり日本語0.9170文字程度です(OpenAI 言語モデルで日本語を扱う際のトークン数推定指標)。
pip install tiktoken
して試してみましょう:
import tiktoken enc = tiktoken.get_encoding("cl100k_base")
次の例で試してみましょう(山本義隆『熱学思想の史的展開』(現代数学社,1987年)より):
s = "「何人ものニュートンがいた(There were several Newtons)」と言ったのは,科学史家ハイルブロンである.同様にコーヘンは「ニュートンはつねに二つの貌を持っていた(Newton was always ambivalent)」と語っている."
e = enc.encode(s) for i in e: c = enc.decode([i]) if len(c) == 1 and ord(c) == 65533: # 65533は「�」 print(i, end="|") else: print(c, end="|") print()
次のように88トークンに分割されていることがわかります:
「|何|人|も|の|ニ|ュ|ート|ン|が|い|た|(|There| were| several| Newton|s|)|」|と|言|っ|た|の|は|,|科|学|5877|110|家|2845|237|イ|ル|ブ|ロ|ン|で|あ|る|.|同|162|100|246|に|コ|ー�|246|ン|は|「|ニ|ュ|ート|ン|は|つ|2243|255|に|二|つ|の|80631|234|を|持|って|い|た|(|Newton| was| always| amb|ivalent|)|」|と|45918|252|って|い|る|.|
英語はだいたい1語1トークンですが、直前のスペースも含めてトークンになっていることがけっこうあります。日本語はだいたい1文字1トークンですが、「様」のようにUTF-8の3バイトがそれぞれトークンになっている場合や、「ーヘ」の「ヘ」の最初の2バイトが「ー」とくっついて1トークンになっているような場合もあります。平均して10トークンが日本語9文字少々という感じです。
ちなみに、同じ文字列をOpenAIの Tokenizer に入れると、GPT-3では120トークンになります。