このページではOpenAIのChat Completions APIについて解説します。OpenAIは新しいResponses APIに移行するつもりのようです(OpenAIのresponses APIを使う 参照)。しかし、Chat Completionsは他社のAPIもお手本にする基本的な方法なので、使ってみて損はしません。
まずこちらで登録してAPIキーを発行してもらいます。サブスクのChatGPTと異なり、料金は従量制で、百万トークンあたり何ドルという具合に課金されます。値段の比較はLLM API比較がわかりやすいと思います。
APIの概要はOpenAIの OpenAI developer platform からドキュメンテーション、APIレファレンスなどをご覧ください。APIで送られたデータは学習用に使われることはありません。不正使用の監視のために30日間保持され、特に問題なければ消去されるようです。
Pythonのパッケージは pip install openai でインストールできます。
APIキーは、プログラムに直接書き込まず、環境変数に設定しておくのが安全・便利です。MacやLinuxでは、ターミナルに
export OPENAI_API_KEY="sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
と打ち込めば環境変数が設定されます。.bashrc か .zshenv 等に書き込んでおけばシェル起動時に設定されます。APIキーを書き込んだファイルは他人に見られないようにパーミッションを正しく設定しておきましょう。環境変数が使えないときは python-dotenv が便利そうです。
使い方の基本は次の通りです(2023-11-07に大きく変わりました):
from openai import OpenAI
client = OpenAI() # 環境変数からAPIキーを取得する場合
# もし環境変数が使えないなら次のようにする:
# client = OpenAI(api_key="sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
res = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "system", "content": "あなたは賢いAIです。"}, # 役割設定(省略可)
{"role": "user", "content": "1たす1は?"} # 最初の質問
],
temperature=0 # 温度(0-2, デフォルト1)
)
print(res.choices[0].message.content) # 答えが返る
Chat Completions APIは、質問・応答の履歴を記憶しません。以前の質問・応答を前提としたい場合は、次のように履歴を与えた上で質問をします:
res = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "system", "content": "あなたは賢いAIです。"}, # 役割設定(省略可)
{"role": "user", "content": "1たす1は?"}, # 最初の質問
{"role": "assistant", "content": "2です。"}, # 最初の答え
{"role": "user", "content": "それを3倍して。"} # 次の質問
],
temperature=0
)
print(res.choices[0].message.content) # 答えが返る
履歴と質問、答えを合わせたトークン数には上限(モデルによって違いますが、gpt-4.1 では100万トークン)があります。トークンはほぼ単語に相当するものです(後述)。トークン数を表示するには、次のようにします:
print(res.usage)
CompletionUsage(completion_tokens=8, prompt_tokens=27, total_tokens=35)
トークン上限を超えて会話を続けるには、不要な履歴を削除する必要があります。削除も含めて、簡単な会話を続けるには、例えば次のようにすればいいでしょう(通常はそんなに長い会話をしないでしょうけれど):
from openai import OpenAI
client = OpenAI()
msg = [{"role": "system", "content": "あなたは賢いAIです。"}]
while True:
prompt = input("> ").strip()
if prompt in ["quit", "exit"]:
break
msg.append({"role": "user", "content": prompt})
res = client.chat.completions.create(model="gpt-4.1", messages=msg)
ans = res.choices[0].message.content.strip()
print(ans)
msg.append({"role": "assistant", "content": ans})
if res.usage.total_tokens > 900000:
msg.pop(1)
msg.pop(1)
LLMは次のトークンの「確率分布」(確信度)を計算し、それに従って実際の次のトークンをランダムに選びます。ただしそれは temperature=1 あたりに設定したときで、temperature=0 にすればつねに最大確率のトークンが選ばれます。「確率分布」(実際には確率の対数logprobs)は次のようにして調べることができます。
from openai import OpenAI
client = OpenAI()
res = client.chat.completions.create(
model="gpt-4.1", # non-reasoning model
messages=[
{
"role": "user",
"content": "Write a one-sentence bedtime story about a unicorn."
}
],
logprobs=True,
top_logprobs=3,
temperature=0
)
print(res.choices[0].message.content)
Under a silver moon, a gentle unicorn tiptoed through a field of glowing flowers, sprinkling sweet dreams wherever her shimmering horn touched the petals.
import numpy as np
for x in res.choices[0].logprobs.content[:5]:
print(repr(x.token))
for t in x.top_logprobs:
print(f" {np.exp(t.logprob):7.5f} {repr(t.token)}")
'Under' 0.83356 'Under' 0.14485 'As' 0.00926 'A' ' a' 0.74823 ' a' 0.24292 ' the' 0.00831 'neath' ' silver' 0.25162 ' silver' 0.19596 ' blanket' 0.15261 ' shimmering' ' moon' 0.99621 ' moon' 0.00150 ' cres' 0.00049 '-m' ',' 0.99924 ',' 0.00063 'lit' 0.00008 'light'
詳しくはOpenAI Cookbookの Using logprobs 参照。分類などで各選択肢の確信度を知りたいときに便利です。
なお、このモデルの学習データの中の一角獣についての文章の83%が Under で始まっていたというような解釈をすべきではなく、このモデルではこのプロンプトで temperature = 1 で Under で始まる文章を83%生成するということに過ぎません。同じ例をこちらの最後でもやってみましたが、同じプロンプトでもモデルが違えばまったく違う分布になります。
Web 版 ChatGPT のように文字単位(単語単位)で出力するようにしてみましょう。モデル名として Gemini のモデル("gemini-1.5-pro" など)や xAI のモデル("grok-2-latest" など)や PLaMo のモデルを与えてもいいようにしました(それぞれ環境変数 GOOGLE_API_KEY、XAI_API_KEY、PLAMO_API_KEY にAPIキーを入れておきます)。さらにモデル名が lmstudio を含めばローカルの LM Studio を呼び出すようにしました(この場合のモデルは LM Studio でロードされているものが使われます)。
import os
from openai import OpenAI
class Chatbot:
def __init__(self, model="chatgpt-4o-latest", messages=None, temperature=0, stream=True):
if "gemini" in model:
self.client = OpenAI(
api_key=os.getenv("GOOGLE_API_KEY"),
base_url="https://generativelanguage.googleapis.com/v1beta/openai/"
)
elif "grok" in model:
self.client = OpenAI(
api_key=os.getenv("XAI_API_KEY"),
base_url="https://api.x.ai/v1"
)
elif "plamo" in model:
self.client = OpenAI(
api_key=os.getenv("PLAMO_API_KEY"),
base_url="https://platform.preferredai.jp/api/completion/v1",
)
elif "lmstudio" in model:
self.client = OpenAI(
base_url="http://localhost:1234/v1"
)
else:
self.client = OpenAI()
self.model = model
self.messages = messages or []
self.temperature = temperature
self.stream = stream
def chat(self, prompt, temperature=None, stream=None, verbose=0, **kwargs):
prompt = prompt.strip()
if len(self.messages) > 0 and self.messages[-1]["role"] == "user":
self.messages.pop()
self.messages.append({"role": "user", "content": prompt})
stream = stream if stream is not None else self.stream
temperature = temperature if temperature is not None else self.temperature
try:
res = self.client.chat.completions.create(model=self.model,
messages=self.messages,
temperature=temperature,
stream=stream, **kwargs)
except Exception as e:
print("Error", e)
return
if stream:
ans = ""
for chunk in res:
if chunk.choices and hasattr(chunk.choices[0].delta, "content"):
content = chunk.choices[0].delta.content or ""
else:
content = ""
print(content, end="")
ans += content
else:
ans = res.choices[0].message.content.strip()
print(ans)
if verbose:
u = res.usage
print(u.prompt_tokens, u.completion_tokens, u.total_tokens)
if verbose > 1:
print(res)
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:])
のようにして新しいチャットのインスタンスを作ってください。
上で作った 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)
LLMのトークンに移しました。
上にもちょっと書いたように、client = OpenAI() を client = OpenAI(base_url="http://localhost:1234/v1") にすればローカルのLM Studioが使える。また、client = OpenAI(base_url="http://localhost:8080/v1") にすればローカルの llama-server が使える。
ローカルモデルが gpt-oss-20b の場合、llama-server は例えば
llama-server -m /path/to/gpt-oss-20b-MXFP4.gguf --alias openai/gpt-oss-20b -c 8192 --jinja
のようにして起動しておく。詳しくは guide : running gpt-oss with llama.cpp #15396 参照。PythonでのAPIの呼び出しは上と同じである。llama-serverの場合は上の「トークンごとの確信度の出力」で示したように各トークンの確信度も出力できるが、LM Studioでは logprobs=True は無視されるようだ。