コードからモデルを呼び出す

前の章ではメッセージのリストを構築しました。この章では、Pythonから実際のモデルにそのリストを送信して、答えを取得します。そのやり取りは、これらのモデルで構築する最小限の完全なループであり、あなたが書くほぼすべての機能はその上に乗っています。
プロンプトを入力すると、コードはそれをどこかに送信して、テキストが返ってきます。モデルがプログラムの中に住んでいるように感じます。そうではなく、実際にどこで実行されるかを理解することで、コスト、速度、そして出会うことになる癖が説明されます。
では、物理的に何が起きているかを想像してください。モデルは膨大なパラメータの集合で、プロバイダーのサーバー上に、あなたが決して見ることのないハードウェア上に置かれています。呼び出すと、コードはインターネット経由で普通のウェブリクエストを送信し、メッセージを運びます。
プロバイダーはマシン上で予測ループを実行し、生成されたトークンをあなたに返します。モデルの呼び出しは他人のコンピュータを数瞬間レンタルすることです。このフレーミングは多くのことを説明します:レイテンシーはモデルが彼らの端でトークンを1つずつ生成することであり、コストはそれらがそのコンピュートのために請求することであり、全体がネットワーク呼び出しであり、それが含むすべてのことです。
例では公式なOpenAIライブラリを使用しています。他のプロバイダーは詳細が異なりますが、形状はほぼすべての場所で同じです:メッセージのリストを送信し、メッセージを返します。その同じことはロックイン対する保険です。同じコードは、ホストするか他の場所でレンタルするオープンモデルをポイントできるため、ベースURLの変更だけで多くの場合動作します(オープンおよびクローズドモデルはその選択をカバーしています)。
リクエスト
pip install openaiでライブラリをインストールし、クライアントを作成して呼び出します。クライアントはAPIキーを環境変数から読み込むため、キーはコードに表示されることはありません。
from openai import OpenAI
client = OpenAI() # 環境からOPENAI_API_KEYを読み込む
MODEL = "gpt-4o-mini" # モデルを切り替えるための1行
response = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": "You are a concise assistant. Answer in one sentence."},
{"role": "user", "content": "Why is the sky blue?"},
],
)2つの部分が必要です:model、実行したいモデルの名前、およびmessages、前の章のロールタグ付きメッセージのリスト。その他すべてはデフォルトを持っています。
モデル名は単一の定数に入っていることに注意してください。より良い、より安いモデルが数ヶ月ごとに着陸する分野では、それは意図的です。**モデル名**を1つの場所に保つため、モデルのスワップは1行の変更です。これは実践中のチャーンルールです:ボラティル固有のものを見つけることができる場所に保ち、コード全体に散らばさないでください。
modelとmessages。モデル名が1つの定数に属します。なぜなら、より安いか良いモデルが数ヶ月ごとに現れ、9つのファイルに貼り付けられた後に検索に行きたくないからです。 レスポンスの読み取り
返信は何らかの構造でラップされて返ってきます。必要なテキストは中に1つのパスですが、構造の残りは価値があります。何が起きたかを言うためです。
choice = response.choices[0]
print(choice.message.content) # 返信テキスト
print(choice.finish_reason) # "stop" -> モデルが自分で終了した
print(response.usage) # Usage(prompt_tokens=24, completion_tokens=18, total_tokens=42)3つのことがそこで重要です。message.contentはテキストで、APIは複数のオプションを返すことができるため、choices[0]の内側に座っています。ただし、ほぼ常に1つを要求して最初を読みます。
finish_reasonはモデルがなぜ停止したかを言います。"stop"はそれが思考を終了したことを意味し、"length"はそれがトークンキャップに当たり、半分の答えで切り取られたことを意味します。そのフィールドを確認することは、目の代わりにコードで切り詰められた返信を検出する方法です。
そしてusageは、呼び出しが費やしたトークンを報告し、送信したプロンプトと書き込んだ完了に分割されます。その**使用法**オブジェクトはあなたの請求書です、品目分け。すべての呼び出しはそれがトークンでコストするもの正確に言うため、構築するときに見る数字です。
response.choices[0].message.contentにありますが、2つの隣人も重要です。finish_reasonはなぜそれが停止したか言う:"stop"はできたことを意味し、"length"はそれがトークンキャップに当たり、切られたことを意味します。そしてresponse.usageはあなたの請求書であり、プロンプトと完了に分割されたトークンで、あなたが呼び出しコストを推測する必要はありません。 知る価値のあるパラメータ
modelとmessagesを超えて、2つのオプション設定は常に出ます。
temperatureはLLMの仕組みからのランダムネスダイヤルです、具体化されました。低い値(0近く)はモデルをトップピックの近くに保つ:焦点が絞られ、繰り返し可能、抽出と分類に欲しいもの。より高い値(0.7から1周り)はそれに到達させます。可能性の低いトークン:より多様と創造的、そして放浪する傾向があります。あなたはモデルがどの程度太胆にサンプリングするかをチューニングしているだけで、他の何もありません。
max_tokensは返信が実行するトークンの数をキャップします。それはランウェイ答えと走りアウェイ請求書からあなたを保護します。**max_tokens**を低すぎると設定すると、モデルは切り取られます。それは最後の部分からの"length"終了理由とまさに同じです。そのため、本物のヘッドルームを残してください。
response = client.chat.completions.create(
model=MODEL,
messages=messages,
temperature=0.2, # 焦点が絞られ、一貫性のある
max_tokens=300, # 返信の長さを上限
)temperatureはモデルがどの程度太胆にサンプリングするかをチューニングしている:焦点を絞って繰り返し可能な答えのための低い、多様で創造的なもののための高い。max_tokensは請求書を保護するために返信の長さを上限にしますが、あまりに低く設定するとそれは"length"カットオフをトリガーします。そのため、ヘッドルームを残してください。抽出と分類については、毎回低い気温に到達してください。 ストリーミング
デフォルトでは、全体の答えが完成するまで待機し、その後一度にそれが到着します。モデルはトークンを1つずつ生成するため、長い返信は何も見つめながらの長い待機を意味します。
**ストリーミング**は生成された瞬間、各トークンをあなたに送り、テキストが正しく表示され、チャットアプリが返信を入力しているままを見せる方法で、ライブに満たされます。
stream = client.chat.completions.create(
model=MODEL,
messages=messages,
stream=True,
)
for chunk in stream:
piece = chunk.choices[0].delta.content
if piece:
print(piece, end="", flush=True)総生成時間はどちらの方法でも同じです;モデルはより速くありません。ストリーミングはトークンのみが表示される場合を変更し、最後まで保持する代わりに生成されるときにそれらをサーフィンします。
チャンクを反復処理し、テキストの各deltaを印刷することでそれを処理します。トレードオフは少し多くのコード、はるかに良い待機のためのコード。ストリーミングはいつトークンを見るか変更し、どの程度速くそれらが到着するかではない。
すべての呼び出しは独立している
これはチャプターの最も重要な行動であり、コンテキストウィンドウレッスンから直接それに従います。APIは**ステートレス**:各リクエストは完全にそれ自身に立っています。プロバイダーのサーバーは、前の呼び出しを覚えていません。それはあなたが送信した正確にメッセージで予測を実行し、結果を返し、交換について何も保持しません。
したがって、会話はモデルまたはAPIによってどこにも保存されていません。それはあなたのコードの中に住んでいます。ターン2が何が起こったかに知りたいターン1で、あなたが走行中の運転者を保つ場合は、それ自身を複製し、毎回全体をすべて送信します。バックアンドフォー会話は、毎回ことを大きくなる履歴を再送信することで、あなたが作成し、幻想です。
これはまた、なぜ長い会話で、メッセージあたりのコストがより多くの時間にわたってコストなぜかを説明します:各呼び出しは前のターンをすべて入力トークンとして再送信し、ユーザーの新しい質問が短い場合さえ、プロンプトが成長し続けます。
history = [
{"role": "system", "content": "You are a friendly travel assistant. Keep answers short."},
]
def chat(user_text):
history.append({"role": "user", "content": user_text})
# 全体の履歴が毎回上がる;サーバーは何も覚えていない
response = client.chat.completions.create(model=MODEL, messages=history)
reply = response.choices[0].message.content
history.append({"role": "assistant", "content": reply}) # 次のターンのための返信を保つ
return reply
chat("I have a weekend in Tokyo. What should I see?")
chat("Which of those is best for kids?") # ターン1が履歴を運ぶため、いけい機能します2番目の質問は、最初の質問とそれの答えが依然としてhistoryにあり、一緒に送信されるため意味があります。あなたはメモリです。この1つのアイデア。あなたがフルコンテキストを組み立て、毎回それを再送信すること。下層、会話を下層。そして、後にそれは、ツールユーザーの使用、検索、およびエージェント、あまりにの下層。それらはすべて、各独立した呼び出しの前に、そのメッセージリストに何が行くかを決定することの変動。
何か誤った場合
モデル呼び出しは。他人のサーバーへのネットワーク呼び出し。そのため、ネットワーク呼び出しが失敗するすべての方法で失敗します。最初からそれのための設計。
- **エラーとレート制限。プロバイダーは。ウィンドウで送信できるリクエストの数をキャップします。上限に当たり。呼び出しはレート制限エラーで失敗します。標準応答は待ち。再試行。各失敗の後に待機を延長します(これはバックオフ**と呼ばれます)。そのため、あなたはハンマーをハンマーしません。。の忙しい。
- **タイムアウト。**呼び出しはハング。タイムアウトを設定してください。そのため。1つ遅い要求。アプリを凍結しません。
- **キーはサーバーに留まる。**APIキーはあなたのお金を費やすパスワード。APIキーをブラウザコードに入れないでください。誰もがdevツールを開いて読むことができます。ブラウザはバックエンドを話し。バックエンド。キーを保有。モデルを話します。
- **あらゆる答えが誤った可能性があると仮定してください。**均等に成功した呼び出し。返すことができます。幻想。奇形出力。そのため、次のチャプターはあなたのコードを信頼できる何かの結果を作ることについてです。
def ask_model(messages):
try:
response = client.chat.completions.create(
model=MODEL,
messages=messages,
timeout=20, # 20秒の後にあきらめ
)
return response.choices[0].message.content
except Exception as err:
print("モデル呼び出しが失敗しました:", err)
return "申し訳ありません。何かがうまくいきませんでした。もう一度試してください。"
