Pythonスクリプトに無料LLMを組み込む2つの方法
Python用の無料LLMを検索する際、通常は2つの意味のいずれかを指します。1つ目はOllama、llama.cpp、またはHugging Face transformersライブラリを使用してダウンロードして自分で実行するオープンウェイトモデルです。2つ目はプロトタイプでは無料のように感じられるほど低コスト、または十分な試用クレジットが付随するホストエンドポイントです。両者は正当な選択肢であり、異なる用途に適しています。
ローカル実行はトークンごとの請求がなく、データがマシンから流出しないことを意味します。その代わり、選択したモデルに十分なRAMまたはGPUメモリが必要で、ダウンロード待ちが発生し、ノートPCに収まる小規模モデルは大型モデルより回答品質が劣ります。ホストAPIはハードウェアの問題を解消しますが、試用終了後はトークンごとに課金されます。このページでは両方を公平に解説し、OpenAI互換で標準のopenaiパッケージと互換性のある当サイトのホストオプションのPythonコードを示します。
ローカルモデルとホストAPIの比較
| 質問 | 自分で実行する | ホスト前払いAPI |
|---|---|---|
| トークンあたりのコスト | いいえ(ただしハードウェアと電力のコストが発生) | 入力 1M トークンあたり $0.15、出力 1M トークンあたり $0.50 |
| セットアップ時間 | ランタイムのインストール、重みのダウンロード、設定の調整 | パッケージのインストールとキーの貼り付けのみ |
| コンテキストの長さ | あなたのメモリによって制限されます | 100,000 トークン(プロンプトと補完を含む) |
| 同時実行 | マシンが処理できる範囲 | フルキーでは同時に8リクエスト、試用キーでは同時に2リクエスト |
| 拒否 | 選択する重みによる | 法的な成人利用に対して、コンテンツ拒否いいえで回答するようにチューニング済み |
| オフライン使用 | はい | いいえ |
高性能なGPUがあり、作業がプライベートまたはオフラインであれば、ローカル環境が適切な選択肢です。ここで読むのをやめても構いません。マシンを所有せずに大きなモデルを使いたい場合や、スクリプトがノートPC、サーバー、CIランナーのいずれでも同じように動作することを望む場合は、ホストされたルートのほうが簡単です。多くのプロジェクトでは両方を使用しています:開発中は小さなローカルモデルを、本番環境ではホストされたモデルを使用し、ベースURLを1つ変更するだけで切り替えます。
ホストエンドポイントを5分で設定
まずAPI キーが必要です。Googleまたはメールアドレスとパスワードでサインインし、API key pageでワンクリックで$0.50の無料トライラークレジットを受け取ってください。トライアルは7日間有効で、1人1回限り、支払い情報は不要です。次にSDKをインストールし、API キーをソースコードに含めないようにしてください。
pip install openai
export NOTRACK_API_KEY="sk-your-key-here"
クライアントは標準的なOpenAI設定から2つの変更が必要です:Base URLとキーです。モデルIDはuncensoredで、提供されるモデルは1つだけです。
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwvideogenerator.cc/v1",
api_key=os.environ["NOTRACK_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise writing assistant."},
{"role": "user", "content": "Give me three title ideas for a noir short story."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)
認証は標準的なAuthorization: Bearerヘッダーで行われ、SDKが自動的に付与します。使用可能なメソッドはPOST /v1/chat/completionsとGET /v1/modelsです。client.models.list()で接続を確認でき、利用可能なモデルIDが返されます。パラメータ一覧はドキュメントページをご覧ください。
ストリーミングレスポンス
人が確認する場合はストリーミングを使用してください。stream=Trueを設定し、サーバー送信イベントとして到着するチャンクをイテレートします。使用状況はストリームの末尾で報告されるため、トークン数をログに記録できます。
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a four-line poem about rain on a tin roof."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
usage = getattr(chunk, "usage", None)
if usage:
print("\n", usage)
人々を混乱させる1つの詳細:ストリーミングリクエストは、ストリームが完了するまで同時実行数のスロットを占有します。スロット2の試用キーでは、2つのオープンストリームがある場合、3番目の呼び出しはコードconcurrencyの429エラーを受けます。必ずストリームを消費するか、閉じてください。
ツール呼び出しとJSONモード
エンドポイントはtools、tool_choice、ストリーミングされたtool_calls、およびrole: "tool"として返される結果をサポートします。以下は1つの関数を含む完全な往復の例です。
import json
tools = [{
"type": "function",
"function": {
"name": "word_count",
"description": "Count the words in a piece of text.",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
},
}]
messages = [{"role": "user", "content": "How many words are in: the quick brown fox jumps"}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = len(args["text"].split())
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
})
final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
print(final.choices[0].message.content)
argumentsは常に防御的に解析してください。モデルはこれをJSON文字列として返し、任意のモデルがまれに不正なものを生成する可能性があります。ツールいいえで構造化された出力が必要な場合はJSONモードを使用してください。
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Reply with a JSON object with keys title and tags (list of strings)."},
{"role": "user", "content": "Describe a lighthouse keeper's diary."},
],
response_format={"type": "json_object"},
temperature=0.2,
)
data = json.loads(resp.choices[0].message.content)
print(data["title"], data["tags"])
プロンプトでJSONとキーの名前を指定し、温度を低く設定してください。本番コードではjson.loadsをtryブロックで囲んでください。
レート制限、エラー、リトライ
フルキーは最大8つの同時リクエストと1分あたり300件のリクエストを許可します。トライアルキーはそれぞれ2と60です。上限を超えると、HTTP 429エラーが返され、エラーコードはrate_limited(1分あたりのリクエスト数超過)またはconcurrency(同時接続数超過)になります。一時的な過負荷時には、APIは429を返し、Retry-After: 5ヘッダーを付与します。SDKは一部のエラーを自動的にリトライしますが、並列実行するスクリプトでは、明示的なラッパーを小さく実装するほうが明確です。
import time
from concurrent.futures import ThreadPoolExecutor
from openai import RateLimitError
def ask(prompt, tries=4):
for attempt in range(tries):
try:
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
)
return r.choices[0].message.content
except RateLimitError:
time.sleep(5 * (attempt + 1))
raise RuntimeError("still rate limited")
prompts = [f"Summarize idea number {i} in one sentence." for i in range(20)]
with ThreadPoolExecutor(max_workers=2) as pool: # 8 on a full key
results = list(pool.map(ask, prompts))
max_workersをキーの同時実行数制限に合わせることで、429エラーの大部分を未然に防げます。各応答にはX-Request-Id、X-Balance-USD、および2つのレート制限ヘッダーが含まれており、デバッグやサポートが必要な場合はclient.chat.completions.with_raw_response.create(...)で取得できます。
ローカルとホストの選択方法
イデオロギーの話は飛ばして、プロジェクトに関する4つの質問に答えてください。
- データはあなたのマシン上に留め置く必要がありますか?契約や習慣で「はい」と答えるなら、ローカルで実行してください。ホストされたプロンプトはここでは学習に使用されませんが、ネットワーク上を移動します。
- 利用可能なメモリはどのくらいですか? モデルのサイズが品質を決定します。専用GPUのないノートPCは小規模モデルに制限され、回答品質に現れます。ホストモデルは100,000トークンのコンテキストウィンドウを持つ大規模モデルを提供し、自宅ではこれを再現するのは困難です。
- どのくらいのリクエストを送信しますか? 週数百回ならどちらでも問題ありません。月数十万回になるとホストの請求額が現実的な数字になり、アイドル状態のローカルマシンが魅力的に見えます。計算してください:100,000リクエスト(入力1,000トークン、出力500トークン)は100,000 x $0.0004 = $40かかります。
- 拒否の処理を特定の方式で行う必要がありますか? 多くのオープンウェイトモデルは組み込みの拒否動作を備えており、他のモデルはそれを削除するように調整されています。ここで提供されているホストモデルは、法的な成人向けコンテンツに対してコンテンツ拒否いいえで応答するように調整されているため、適切なウェイトを探す必要はありません。
回答が両方の場合、最初の1週間はホストから始めてください。試用は無料であり、ハードウェアを購入したり、何ギガバイトものファイルをダウンロードしたりする前に、プロンプトが何を必要とするかを学べます。
Pythonスクリプトでの一般的なミス
人が遭遇する問題のほとんどは小さく、修正可能です。
- ベースURLが間違っている。
/v1で終わる必要があります。サフィックスがないと404エラーになり、親切なメッセージは返されません。不明な場合はclient.base_urlを出力してください。 - キーがコードに含まれている。 上記のように環境変数から読み取り、
.envファイルを.gitignoreに追加してください。 - モデル名が間違っている。 唯一のモデルIDは
uncensoredです。他のプロバイダーの名前は機能しないため、コードを移植する際は変更してください。 - 制限のないループ 終わらないツール呼び出しループは高コストなバグです。5ラウンドなどに制限してください。
- コンテキスト予算を無視している。 プロンプトと補完は100,000トークンを共有します。すべての会話を永遠に追加すると、最終的に制限に達します。古いメッセージをトリムまたは要約してください。
- JSONが常に有効であると仮定する。 JSONモードでも、使用する前に必要なキーを確認してください。
サーバー側で問題がある場合は、サポートに連絡する際に、応答からX-Request-Idの値を引用してください。これにより、オペレーターが正確な呼び出しを特定できます。
Python作業におけるホストオプションのコスト
1,000件の入力トークンと500件の出力トークンを含む典型的なスクリプト呼び出しのコストは$0.00015に$0.00025を加えた$0.0004です。$0.50のトライアルクレジットはこれらを約1,250回カバーできます。最小チャージ額である$20のチャージで50,000回カバーできます。クレジットは期限切れにならず、失敗または拒否されたリクエストは課金されず、プロンプトはトレーニングに使用されません。チャージは最大$500まで可能で、$50からボーナスが付きます。支払いには暗号資産、TRC20上のUSDTまたはBase上のUSDCを使用します。詳細はpricing pageをご覧ください。
テキストのみです:画像、音声、動画、埋め込み、ファインチューニングはありません。そして、絶対的なルールは未成年者を含む性的コンテンツがないことです。拒否率が低くテキスト生成が必要なPythonプロジェクトの場合、それが全ての範囲です。
