APIが提供するものと、あなたが依然として所有するもの
このサービスはホストされたOpenAI互換のテキストAPIです。OpenAI SDKをhttps://api.chatbotnsfw.com/v1に指し、モデルID uncensoredを使用して通常のチャットメッセージを送信します。このモデルはオープンウェイトモデルで、法的な成人用途に対してコンテンツ拒否なしで応答するように調整されています。テキストのみです:画像、音声、ビデオはありません。
これが契約の全体です。APIはあなたのユーザーが誰であるか、年齢がいくつであるか、またはあなたのプラットフォームが何を許可しているかを認識しません。また、呼び出し間で何も記憶しません。モデルに見せたい会話全体を各リクエストに含める必要があります。
| 懸念事項 | 誰が対応するか |
|---|---|
| コンテンツ拒否なしでの応答生成 | API |
| ペルソナ、トーン、ストーリーのルール | システムプロンプトでのあなた |
| 会話のメモリ | データベースでのあなた |
| 年齢確認と同意 | あなた |
| 未成年者が関わるコンテンツのブロック | あなた、およびAPIも同じ厳格な制限があります |
| レポート、BAN、利用規約 | あなた |
チャットAPIを以前に構築したことがない場合は、APIドキュメントにリクエスト形式が記載されています。既存のNSFW LLM統合ガイドでは統合側をより詳しく説明しています。この記事は、その周辺の製品についてのものであります。
アーキテクチャ:キーをサーバーに保持
フロントエンドはAPIキーを保持すべきではありません。ブラウザまたはアプリとAPIの間に小さなバックエンドを置きます。バックエンドはユーザーを認証し、年齢ゲートを確認し、履歴を読み込み、APIを呼び出し、応答を保存します。
- ブラウザはセッション付きでユーザーメッセージをバックエンドに送信します。
- バックエンドはユーザーが成人として確認され、レート制限内であることを確認します。
- バックエンドはメッセージリストを構築します:システムプロンプト、メモリ、最近のターン、新しいメッセージ。
- バックエンドは
POST /v1/chat/completionsを呼び出し、トークンをストリーミングで返します。 - バックエンドはストリーミング終了後に新しいターンを保存します。
フルキーでは8つの同時リクエストと1分あたり300リクエストが可能です。これはホビーボットには十分であり、バックエンドで1人のユーザーが同時に複数のストリームを開かない限り、小規模な製品にとって公平なスタートとなります。ストリーミングリクエストはストリームが終了するまで同時実行数のスロットを保持するため、ストリームを開いたまま離脱したユーザーでもスロットを消費し続けます。
ペルソナを契約として記述する
システムプロンプトは製品の大部分を担います。優れたプロンプトは形容詞の段落ではなく、モデルとの短い契約のように読まれます。4つのことをカバーします:キャラクターは誰か、どのように話すか、ストーリーは何か、そして何がアウトオブバウンズかです。
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.chatbotnsfw.com/v1",
api_key=os.environ["NSFW_API_KEY"],
)
PERSONA = """You play Mara, a 34-year-old ship's navigator in a slow-burn
space romance. All characters are adults.
Voice: dry, warm, short paragraphs, first person.
Story rules: keep continuity with the notes below; never speak for the user;
stay in character unless the user writes OOC: in brackets.
Hard limits: no sexual content involving anyone under 18, ever. If the user
steers there, refuse in character and change the scene."""
def reply(history, user_text):
messages = [{"role": "system", "content": PERSONA}]
messages += history[-24:]
messages.append({"role": "user", "content": user_text})
r = client.chat.completions.create(
model="uncensored",
messages=messages,
temperature=0.9,
top_p=0.95,
)
return r.choices[0].message.content外見以上に重要なのは2つの詳細です。プロンプトで明確な言葉で「すべてのキャラクターは成人である」と明記します。また、モデルにエッジでどうすべきかを指示します(例:シーンを変更する)。これにより、エラーメッセージで終わるのではなくストーリーを進行させます。より生き生きした文章のためにtemperatureを上げ、キャラクターが逸脱する場合は下げます。
出荷前にペルソナをテストする
編集のたびに同じ20のユーザーメッセージをプロンプトに通します。キャラクターを降ろすよう求めるユーザー、シーン途中の設定を変更するユーザー、未成年ルールに迫るユーザーなど、不自然なケースを含めます。出力を保存して読みます。1つのケースを修正するプロンプトの修正は、しばしば別のケースを壊します。
ウィンドウに収まるメモリ
コンテキストウィンドウは100,000トークン(プロンプトと completion を含む)で、長いストーリーの余地があります。長いことが常に良いわけではありません。各ターンで入力トークンに対して課金され、膨大な履歴を持つモデルは早期の詳細を失うことがあります。
実用的なレイアウトは3つのレイヤーを持ちます。システムプロンプトはペルソナを保持します。短いメモブロックは名前、関係状態、ストーリー内で交わされた約束など、存続させるべき事実を保持します。最後の20〜30ターンはそのまま入力されます。古いターンは、約20ターンごとに安価な呼び出しでメモブロックに要約されます。
上記のコードはすでにhistory[-24:]をスライスしており、最後の24メッセージを保持します。メモブロックがある場合は、2番目のシステムメッセージとして追加します。この方法でコストは低く抑えられます:典型的なターンでは数千の入力トークンが送信され、100万入力トークンあたり$0.15で1セント未満です。価格ページに数字があり、統合に関する記事にリクエストの形状が示されています。
年齢確認と同意
成人向け製品には、最初のチャット呼び出しの前に年齢ゲートが必要です。APIは年齢を確認しません。その責任はあなたにあり、適切な方法はあなたが運営する場所やユーザーの居住国によって異なります。
- 私は18歳以上ですというチェックボックスは通知であり、確認ではありません。いくつかの管轄区域ではより多くの要件が必要です。
- アカウントに対してサーバー側で結果を保存し、サインアップ時だけでなく、すべてのチャットリクエストで確認します。
- サインアップの前に、ランディングページと利用規約で成人専用の性質を明確にします。
- すべてのペルソナ、プロンプト、およびユーザーに表示されるカードで、架空のキャラクターを成人に保ちます。
あなたの市場に精通した弁護士に最低要件を問い合わせてください。この記事では判断できず、APIプロバイダもあなたに代わって決定することはできません。
あなたに留保されるモデレーションの責任
無検閲モデルはモデルの拒否内容を変えます。あなたが責任を負う内容を変えるわけではありません。これら(モデレーション責任など)を初日から計画してください:
- 未成年者に対する厳格なブロック。未成年者が関わる性的コンテンツは完全に禁止されています。明らかな試行に対する独自の入力チェックを追加し、トリガーされた場合はセッションを終了します。
- レポート機能。ユーザーが会話を報告できる仕組みを提供し、その報告をどう処理するかを決定します。
- アカウント操作 ユーザーをすばやく一時停止できるようにする。
- データ。トランスクリプトをどの期間保持するかを決定し、プライバシーポリシーで明記してください。APIプロバイダはプロンプトをトレーニングに使用しませんが、データベースの管理はお客様の責任です。
- スコープ。製品を法的な成人向け利用とフィクションに限定します。利用規約でそれを明記してください。
失敗または拒否されたリクエストは課金されません。つまり、独自のチェックでブロックされたリクエストも、APIに到達しないため課金されません。
ユーザーごとの上限とコスト管理
制限のないオープンなチャットボックスは、1人のユーザーがクレジットを使い果たすきっかけになります。すべてのアカウントに1日のトークン上限を設定し、API呼び出し前にチェックしてください。レスポンスには各ターンで消費されたトークン数が含まれるため、上限にはカウンターが1つ必要です。
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.chatbotnsfw.com/v1",
api_key=os.environ["NSFW_API_KEY"],
)
DAILY_CAP = 400_000 # tokens per user per day
used_today = {} # user_id -> tokens, keep this in your database
def guarded_reply(user_id, messages):
if used_today.get(user_id, 0) >= DAILY_CAP:
return "You have reached today's limit. Come back tomorrow."
r = client.chat.completions.create(model="uncensored", messages=messages)
used = r.usage.prompt_tokens + r.usage.completion_tokens
used_today[user_id] = used_today.get(user_id, 0) + used
return r.choices[0].message.content400,000トークンの上限は調整すべき数値であり、推奨値ではありません。1ターンあたり6,000トークンのプロンプトを使用する場合、1日あたり約65ターン可能です。ユーザーの価値を算出し、それに基づいて上限価格を設定し、バグが一夜でクレジットを使い果たさないよう、自身のモニタリングでX-Balance-USDヘッダーを確認してください。
ステージング用の個別で小さなクレジット残高を保持してください。テスト用トラフィックと本番用トラフィックは、決して同じAPIキーを共有すべきではありません。エラー発生時に再試行するテストループは、1時間あたりの消費量が1週間分の実ユーザーを凌駕する可能性があります。
ストリーミングと429エラーの処理
ストリーミングなしではチャットは遅く感じられます。stream: trueを設定し、チャンクをブラウザに転送してください。ストリーミングは使用量ブロックで終了するため、コスト追跡のためにログに記録できます。
APIは制限超過時にHTTP 429を返します。エラーコードは、1分あたりのリクエスト数に対してはrate_limited、同時リクエストが多すぎる場合はconcurrencyです。一時的な過負荷の場合も、Retry-After: 5を付けて429を返します。このヘッダーを尊重してください。
import os, time
from openai import OpenAI, RateLimitError
client = OpenAI(
base_url="https://api.chatbotnsfw.com/v1",
api_key=os.environ["NSFW_API_KEY"],
max_retries=0,
)
def stream_reply(messages):
for attempt in range(4):
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:
yield chunk.choices[0].delta.content
return
except RateLimitError:
time.sleep(5)
yield "The service is busy. Please try again in a moment."ユーザーごとのロックを追加して、1つのアカウントが同時に1つのストリームのみ保持するようにしてください。これにより、8つの同時リクエストは約8人の同時チャットユーザーをサポートし、残りのリクエストはキューで吸収されます。
まず、APIキーを取得してください。新規ユーザーは、キーページでワンクリックで$0.50の試用クレジットを取得できます。試用キーは同時リクエストが2つ、1分あたりのリクエストが60に制限されており、フローのテストには十分です。
