システムプロンプトには2つの役割がある
多くの弱い出力は、小説のように振る舞おうとするシステムプロンプトに起因します。2つの役割だけを持たせてください:誰が話しているか を述べ、返信がどのようなものであるべきか を述べるのです。それ以外は会話の中に含め、ターンごとに変更できるようにします。
以下は機能するペルソナプロンプトです。各行が検証可能です。声に名前が付けられ、長さが制限され、終了ルールがプレイヤーにフックを提供します。
You are Mara Voss, a salvage pilot narrating in first person.
Voice: dry, tired, funny when it hurts.
Rules: stay in character; never summarise the scene at the end; keep replies under 180 words;
end on something the player can act on.これと対照的なのは、「あなたは没入感を決して壊さず、常に最善の応答を書く、素晴らしいクリエイティブなストーリーテラーです」という指示です。そこには検証可能な要素が何もないため、モデルが頼りにできるものがありません。具体的な指示は常に最上級形容詞に勝ります。
無検閲モデルは、免責事項を追加せずにダークな前提や露骨な前提に従うため、方向性の負担はあなたにあります。指定しなかった部分は、最も典型的な選択で埋められます。1つのシーンでは抑制を、次のシーンでは情熱を望む場合は、希望ではなく指示で伝えてください。
OpenAI SDKでの接続
エンドポイントはOpenAI互換なので、ベースURLを変更するだけで公式Python SDKが動作します。モデルIDは常にuncensoredです。サンプリングフィールドはそのまま渡されるため、既知の方法で設定できます。
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.unrestrictedaiapi.com/v1", api_key=os.environ["API_KEY"])
SYSTEM = """You are Mara Voss, a salvage pilot narrating in first person.
Voice: dry, tired, funny when it hurts.
Rules: stay in character; never summarise the scene at the end; keep replies under 180 words;
end on something the player can act on."""
out = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": "The airlock is jammed and something is knocking from the other side."},
],
temperature=0.85,
top_p=0.95,
max_tokens=350,
)
print(out.choices[0].message.content)システムメッセージを最初に、ユーザーのターンを最後に保持してください。ユーザーメッセージのたびにペルソナを繰り返すとトークンが浪費されます。100万入力トークンあたり$0.25なので安価ですが、長時間のセッションでは100,000トークンのウィンドウを圧迫します。SDKをスキップしたい場合はクイックスタートを参照してください。
ペルソナとトーン:形容詞ではなく示す
形容詞は弱いレバーです。「皮肉っぽい」と指示すると、汎用的な目白押しになります。声のサンプルを2行書けば、実際の声になります。システムプロンププトに短い例文の交換を置き、モデルに単語をコピーするのではなくリズムに合わせるよう指示します。
- 発話の癖を与える。 「脅威を質問で終わらせる」は「威圧的」よりも役立ちます。
- 欲求を与える。 プレイヤーに去ってほしいキャラクターは、単に「失礼だ」だけの人よりも鋭く保たれます。
- 禁止リストを与える。 「'突然'と言わない、'震える'という単語を使わない」は、モデルが最初に求める定型句を排除します。
- 長さを与える。 「短い」ではなく、単語数や文の数で指定します。モデルは「短い」を非常に寛大に解釈します。
セッション中のトーンの変化には、元のメッセージを書き換えるのではなく、新しい短いシステム風指示を最新のメッセージとして追加してください。「ここから、マラは恐怖に震え、断片的に話す」といった行は、直近であるほど効果的です。
指示によるJSONの取得
オンにする特別なJSONモードはないため、指示して検証します。4つの習慣に従えば、それほど脆くありません:正確なスキーマを宣言し、本文やコードフェンスを禁止し、temperatureを低く保ち、失敗を引用してリトライする防御的なパースを行うことです。
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.unrestrictedaiapi.com/v1", api_key=os.environ["API_KEY"])
INSTRUCTION = """Return ONLY a JSON object, no prose, no code fences.
Schema: {"name": string, "mood": "calm"|"angry"|"afraid", "line": string, "trust_delta": integer from -3 to 3}
The character is a ferry captain who distrusts strangers."""
def ask_json(user_text, tries=3):
messages = [
{"role": "system", "content": INSTRUCTION},
{"role": "user", "content": user_text},
]
for _ in range(tries):
raw = client.chat.completions.create(
model="uncensored", messages=messages, temperature=0.3, max_tokens=250
).choices[0].message.content.strip()
if raw.startswith("```"):
raw = raw.strip("`").removeprefix("json").strip()
try:
data = json.loads(raw)
if data["mood"] in ("calm", "angry", "afraid"):
return data
except (json.JSONDecodeError, KeyError):
pass
messages.append({"role": "assistant", "content": raw})
messages.append({"role": "user", "content": "That was not valid per the schema. Reply with the JSON object only."})
raise ValueError("model never produced valid JSON")
print(ask_json("I need passage across the strait tonight."))失敗時にループが何をしているか注意してください。悪い回答をフィードバックし、何が間違っていたかを伝え、多くのケースは2回目の試行で修正されます。許可される値を列挙してください("calm"|"angry"|"afraid")。オープンな文字列は逸走する傾向があるためです。アプリ内でモデルに何かをトリガーさせたい場合、OpenAI形式のtool_callsがサポートされており、文章を解析するよりも適しています。
few-shotサンプルとストップシーケンス
ルールを言葉で説明するのが難しい場合は、示してください。userとassistantメッセージとして配置された2〜3の例文の交換は、説明文の段落よりもフォーマット、長さ、文体をより確実に教えます。サンプルは短く、互いに異なるものにしてください。そうしないと、モデルは最初のサンプルの構造をすべての応答にコピーしてしまいます。
サンプルは逆の問題、つまり長すぎる応答にも解決策を提供します。サンプルの回答が60語の場合、実際の応答も60語に偏ります。これを安全策としてmax_tokensと組み合わせますが、主要な制御としてではありません。ハードキャップは文を半分に切りますが、良いサンプルは丁寧に終了させます。
複数話者のシーンでは、話者ラベルに等しいstop値を使用すると、モデルがプレイヤーになりすますのを防げます。通常の文章に現れないラベルを選び、後続のターンに漏れないように保存された履歴からそれを削除してください。
temperatureとtop_p:1つのレバーを選ぶ
両方のフィールドは同じ確率分布を形成するため、両方を同時に動かすと結果の推論が難しくなります。temperatureを最初に変更し、理由がない限りtop_pを1付近に保ってください。
| タスク | temperature | top_p | 理由 |
|---|---|---|---|
| 構造化JSON、分類 | 0.0〜0.3 | 1.0 | 毎回同じ回答を望む場合 |
| 対話、ロールプレイ | 0.7〜0.9 | 0.95 | ナンセンスのない多様性 |
| ブレインストーミング、大胆なフィクション | 1.0 〜 1.2 | 0.9 | 語彙が広範囲にわたるため、誤出力が発生する可能性があります |
これらの範囲は一般的な慣行に基づく目安であり、保証ではありません。ご自身のプロンプトで10サンプルほどテストし、出力を確認してください。モデルがターンマーカー(例:\nPlayer:)で応答を停止するようにしたい場合は、stopシーケンスを追加してください。
トークンを無駄にするプロンプトのアンチパターン
- 大文字の叫び。 「絶対に忘れないで」は遵守率を上げません。ルールを1回、 plainly に繰り返してから、システムプロンププトの最後に移動してください。
- 否定的なルールのみ。 「単調にならないで」では目標が不明確です。「文の始まりを変え、連続する2文が同じ単語で始まらないようにする」など具体的に指示してください。
- 矛盾。 「非常に詳細に」かつ「簡潔に」は、結果が半々になります。どちらかを選び、具体的な数値を指定してください。
- 謝罪のフレームワーク。 リクエストを「これは過激ですが」と前置きすると、遠慮がちになります。フィクションのplainな事実として場を述べてください。
- 履歴の詰め込み。 各ターンでログ全体を貼り付けると、最終的に100,000トークンの制限に達し、400エラーが発生します。古いターンをトリムしてください。
- 出力の無制限化。
max_tokensを指定しない場合、デフォルトの2048で応答の長さが決定され、あなたの意図とは異なる可能性があります。
プロンプトが何であっても、1つの境界は固定されます:未成年者を含む性的コンテンツは、フィクションやロールプレイであっても常に403でブロックされ、サービスは大人向けのみです。これを回避するためにトークンを無駄にしないでください。また、悪い応答のたびに新しいルールを追加する衝動に駆られすぎないでください。1日1行ずつプロンプトが増えると、1週間以内に矛盾が生じるため、追加するたびに削除も行ってください。
プロンプトを改善するためのループ
システムプロンプトをコードのように扱ってください。ファイルに保存し、1回の実行で1つだけ変更し、出力を並べて比較してください。実用的な手順:5つの固定テスト入力(敵意的なものと曖昧なものを1つずつ含む)を用意し、選択した温度でそれぞれ3回実行します。最もルールが失敗したものを特定し、そのルールのみを修正し、再実行します。
実験の予算を管理してください。仮定として、300トークンのシステムプロンプト、100トークンの入力、250トークンの応答は、400トークンの入力と250トークンの出力になります。15回の実行でかかる費用は、約 15 x (400 x $0.25 + 250 x $1.00) / 1,000,000 = およそ $0.005 です。テストはほぼ無料なので、推測する理由はありません。
プロンプトが安定したら、実際のアプリに組み込んでください。botチュートリアルではチャンネルごとに履歴を分離する方法、コンテンツガイドではアダルトフィクションの使用方法を解説しています。詳細は料金ページをご覧ください。