JA ▾
今すぐ始める

ChatGPT 無検閲 API:ストリーミング、ツール、JSON

実アプリに必要なストリーミングテキスト、ツール呼び出し、構造化出力をサポートしていなければ、ChatGPT無検閲APIは有用ではありません。このエンドポイントはOpenAI形式でこれら3つをすべてサポートします。以下に各機能の動作コードと、サポートされない機能の明確なリストを記載します。

最終更新日:

ストリーミングツールとJSONのための、コンベアベルト上の光の粒子と構造化ブロックをロボットアームが選択するイメージ

まとめ

  1. サポート:SSEを介したストリーミング、ストリーミングされたtool_callsによるツール呼び出し、JSONモード、および通常のサンプリング設定。
  2. 非サポート:画像、音声、動画の生成、埋め込み、ファインチューニング。
  3. ストリームは完了するまで同時実行的なスロットを保持します:フルキーでは8スロット、トライアルキーでは2スロットです。
  4. モデルは不正な出力を返す可能性があるため、ツール引数とJSONレスポンスは防御的に解析してください。

サポートされる機能とされない機能

項目ステータス注記
チャット補完はいPOST /v1/chat/completions
レスポンスはいPOST /v1/responses
ストリーミング(SSE)はいstream: true、末尾にusage
関数呼び出しはいtools、tool_choice、ストリーミングされた tool_calls、結果は role: "tool" として
JSON モードはいresponse_format: {"type":"json_object"}
サンプリング制御はいtemperature、top_p、stop、seed、ペナルティ
画像、音声、動画なしテキストのみ
埋め込み、ファインチューニングなし提供不可

このモデルは、独立したプロバイダが運用する ID uncensored のオープンウェイトテキストモデルです。合法的な成人向け利用に対してコンテンツ拒否なしで応答し、OpenAI やその他のベンダーの公式 API ではありません。以下のすべての例では、Base URL https://api.chatgptunfiltered.com/v1 を使用しています。

このリストにないものは、ドキュメントで異なる旨が明記されるまで利用不可として扱ってください。これには、以下に示す関数呼び出しとは異なるベンダー固有のホスト型ツールも含まれます。

ストリーミング

stream: trueを設定し、チャンクをイテレートします。到着したテキスト片を出力し、最終チャンクからusageを読み取ります。

from openai import OpenAI

client = OpenAI(
    base_url="https://api.chatgptunfiltered.com/v1",
    api_key="YOUR_KEY",
)

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Write a four-line poem about a night train."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\nusage:", chunk.usage)

2つの注意点があります。まず、ストリーミングリクエストはストリームが終了するまで同時接続スロットを占有するため、放棄されたストリームはスロットを占有し続けます。ユーザーが離脱した場合は閉じてください。第二に、最終チャンクにはテキストなしでusageのみが含まれる可能性があるため、上記のようにchoicesへのアクセスをガードしてください。

Webアプリでは、到着したチャンクをブラウザに転送します(例えば独自のSSEルート経由)。ページ側でテキストの各部分を受信メッセージに追加します。ストリームが終了した時点でサーバー側で最終チャンクからリクエストのコストを把握し、使用量の管理を行います。

ツール呼び出し、エンドツーエンド

関数を宣言し、モデルに呼び出しを依頼し、自分で実行し、role: "tool"で結果を送り返します。

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Look up the status of an order by its ID.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]

def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Where is order A-1042?"}]
r = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = r.choices[0].message

if msg.tool_calls:
    messages.append(msg.model_dump(exclude_none=True))
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
    print(final.choices[0].message.content)
else:
    print(msg.content)

モデルが関数を呼び出すかどうかを判断します。tool_choiceでこれを誘導できます。関数自体はサーバー上ではなくあなたのコードで実行されるため、アクセス可能なリソースを制御できます。モデルは間違った型や欠落したフィールドを送信する可能性があるため、実行前に引数を検証してください。

ツール呼び出しのストリーミング

stream: true と tools を組み合わせると、呼び出しは複数のチャンクとして到着します。関数名が最初に来ます。引数文字列は断片化された形式で到着するため、index を使用して結合する必要があります。

calls = {}

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Where is order A-1042?"}],
    tools=tools,
    stream=True,
)

for chunk in stream:
    if not chunk.choices:
        continue
    for tc in chunk.choices[0].delta.tool_calls or []:
        slot = calls.setdefault(tc.index, {"id": "", "name": "", "args": ""})
        if tc.id:
            slot["id"] = tc.id
        if tc.function and tc.function.name:
            slot["name"] = tc.function.name
        if tc.function and tc.function.arguments:
            slot["args"] += tc.function.arguments

for slot in calls.values():
    print(slot["name"], json.loads(slot["args"]))

ストリームが終了するまでargsを解析しないでください。半分だけ受信された文字列は有効なJSONではありません。その後、関数を実行し、ストリーミングなしの例と同じ方法で結果を送り返します。

JSON モード

構造化されたレスポンスにはJSONモードを有効にし、プロンプトで希望する形状を記述します。

r = client.chat.completions.create(
    model="uncensored",
    temperature=0.2,
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": "Reply with JSON only: {\"mood\": string, \"intensity\": integer from 1 to 5}."},
        {"role": "user", "content": "Rate this line: The door slammed and nobody spoke for an hour."},
    ],
)

try:
    data = json.loads(r.choices[0].message.content)
except json.JSONDecodeError:
    data = None  # retry once, or fall back
print(data)

JSON モードは有効な JSON の生成を大幅に容易にしますが、スキーマを強制するわけではありません。キーと型を自身で確認し、リトライ経路を確保してください。抽出には低い temperature が役立ちます。temperature、top_p、stop、seed、ペナルティなどのサンプリング設定はそのまま渡されます。

有用なパターンは、JSONモードを明確なエラーメッセージを返す小さな検証関数と組み合わせ、そのメッセージを1回のリトライでモデルにフィードバックすることです。「最後のレスポンスではキー intensity が欠けていました。有効なJSONで再回答してください。」モデルは通常2回目の試行で自己修正し、追加のリクエストは1セントの小数点以下のコストで済みます。

ストリーム中のエラー処理

ストリームは通常のリクエストでは発生しない方法で失敗します。テキストの半分が送られた後に接続が切断される可能性があります。その可能性に備えてください。

import time
from openai import RateLimitError, APIConnectionError

def stream_reply(messages):
    for attempt in range(3):
        parts = []
        try:
            stream = client.chat.completions.create(
                model="uncensored", messages=messages, stream=True
            )
            for chunk in stream:
                if chunk.choices and chunk.choices[0].delta.content:
                    parts.append(chunk.choices[0].delta.content)
            return "".join(parts)
        except RateLimitError:
            time.sleep(5)
        except APIConnectionError:
            time.sleep(1)
    raise RuntimeError("stream failed after retries")

ここで429は制限または一時的な過負荷を意味し、APIが送信するRetry-After: 5に一致する5秒待機するのが正しい対応です。ストリームが途中で切れた場合、部分的な回答が得られます。それを破棄してリトライするか、維持してモデルに継続を依頼するかを判断してください。チャットUIでは、破棄してリトライする方がユーザーにとって混乱が少ない場合がほとんどです。

計画すべき制限とコスト

  • レート: フルキーでは1分あたり300リクエスト、トライアルキーでは60リクエスト。
  • 同時実行数: フルキーでは8、トライアルキーでは2。オープンなストリームはすべてカウントされることを忘れないでください。
  • サイズ: リクエストボディ最大8 MB、コンテキストウィンドウ100,000トークン(プロンプトと補完を含む)。
  • 429処理: コードrate_limitedとconcurrencyは制限超過を意味します。一時的な過負荷はRetry-After: 5付きの429を返します。

コスト例:2つのリクエストを含む関数呼び出しのやり取り。1回目は入力1,200トークン、出力80トークン、2回目は入力1,400トークン、出力150トークン。入力は 2,600 x $0.15 / 1M = $0.00039。出力は 230 x $0.50 / 1M = $0.000115。合計 $0.000505。失敗または拒否されたリクエストには課金されません。料率は 料金に記載されており、完全なメソッドリストは ドキュメントにあります。APIキーページから $0.50 のトライアルクレジットで開始します。モデルの背景については 無検閲ChatGPTモデルの理解をご覧ください。すべてのモードで硬い制限が適用されます:未成年者が関わる性的コンテンツは禁止されています。

プレリリースチェックリスト

  • ツール引数はtryブロック内でjson.loadsで解析され、実行前に期待されるキーと型に対してチェックされます。
  • 公開する関数は、匿名ユーザーに許可するもののみを実行できます。呼び出すタイミングはあなたではなくモデルが判断します。
  • JSONレスポンスは検証され、失敗時に1回リトライします。
  • ユーザーがページを離れるとストリームは終了するため、同時実行数のスロットを占有しません。
  • コードでの同時実行数の制限は、キーの制限以下です。フルキーの場合の制限は 8 です。
  • X-Request-IdからのリクエストIDはログに記録されます。
  • キーはサーバー上にあり、再生成可能です。古いキーはすぐに無効になります。

サービスはテキストのみなので、画像や埋め込みが必要な機能は別のプロバイダから取得する必要があります。

よくある質問

エンドポイントはストリーミングに対応していますか?

はい。stream: true を送信すると、Server-Sent Eventsを受信できます。使用状況はストリームの末尾に報告されます。

関数呼び出しをサポートしていますか?

はい。tools と tool_choice を使用し、返信から tool_calls を読み取り、role tool のメッセージとして結果を返信します。ストリーミングされた tool_calls もサポートされています。

JSON モードは常に有効な JSON を返しますか?

有効な JSON が返される可能性を大幅に高めますが、保証するものではなく、スキーマも強制しません。try ブロック内で解析し、失敗した場合は再試行してください。

1つのリクエストでストリーミングと関数呼び出しを同時に使用できますか?

はい。ツール呼び出しの名前と引数は断片化して送信されるため、インデックスで結合し、ストリーミング終了後に引数を解析してください。

APIキーを取得

サインアップしてトークンをコピーし、Base URL を更新してください。それだけです。

今すぐ始める