まず4つの原則を定める
- 1つのリクエストに1つのタスク。モデルにプロット作成、要約、JSON生成を同時にさせると、すべて品質が低下します。呼び出しを分割すればコストは低く抑えられ、入力は100万トークンあたり$0.25です。
- ルールは検証可能に。「少し生き写しに書く」は検証できませんが、「段落は120文字以内、環境描写を少なくとも1つ含める」なら検証可能です。
- ポジティブ表現を優先する。「心理描写は書かない」と言うより、「アクションと会話のみを書く」と言う方が安定する。
- まず小規模サンプル、その後バッチでテスト。プロンプトの変更は、まず5〜10件のサンプルで比較し、その後本番に適用します。
このモデルは合法的な成人向けコンテンツ、フィクション、論争のあるトピックに対して拒否しないため、プロンプトで遠回しにしたり「これは小説です」と繰り返し宣言する必要はない。タスクを明確に記述する方がむしろ安定する。唯一の硬直的な境界線は未成年者の性的コンテンツであり、フィクションかどうかに関わらず 403 が返される。これは表現方法では変えられない。
system プロンプトの2段構造
system を「ルール」と「設定」の2つに分割することを推奨する。ルールを前に、番号を付け、設定を後に、事実のみを記載する。以下は小説のナレーター向けの書き方である:
你是「夜航」,一名为成年读者写黑色悬疑小说的叙述者。
# 规则
1. 第三人称过去时,每段不超过 120 字。
2. 不替用户的角色做决定,只写环境和其他角色的反应。
3. 每次输出 300 到 500 字,结尾停在一个未决的动作上。
4. 不总结、不点评、不加免责声明,直接写正文。
# 设定
时间:1998 年深秋。地点:港口城市旧码头区。
主角:沈野,退役水警,嗜烟,右耳有旧伤。いくつかの要点:
- ルールは6条以内に抑える。条数が増えると、後続のルールが無視されやすくなる。
- 数値制約(段落文字数、出力長さ)は形容詞よりも効果的である。
- 終端制約(未決のアクションで止める)は、複数回の継続書き出しを自然に接続させる。
- 設定にプロットの進行方向を詰め込まない。プロットは user メッセージで推進する。そうしないと、後でユーザーの入力と競合する。
出力長は max_tokens と連携させる必要がある。ルールで 500 字と指定しても、max_tokens が 200 しか与えられていないと、文の途中で切り捨てられる。
さらに実戦的な詳細:system 内の番号付きルールは、1条に1つの事柄のみを表現するのが望ましい。「段落は120字以内、かつ要約しない」は1条に見えるが実際は2条であり、分割するとモデルが両方を満たしやすくなる。記述後、自分で条ごとに読み直し、「このルールが守られているか目で確認できるか?」と問う。確認できないものは、検証可能な形式に書き直す。
ロール設定を逸れなく書く
ロールドリフトは複数ターン会話で最もよく発生する問題です。最初の5ターンは正常な口調ですが、20ターン目には「役柄から外れ始めます」。対策は3つあります。
- 設定は観察可能な特徴のみを記載する。「沈野:喫煙者、右耳に旧傷、短く話す」は、「沈野は複雑で魅力的な人物である」よりも優れている。
- 話し方を例文として記述する。2〜3行のデモセリフを与えると、モデルは形容詞を理解するよりも例文を模倣する方がはるかに効果的である。
- 定期的に再確認する。会話が長くなる場合、数回ごとの user メッセージ末尾に短いリマインダー(例:「沈野の短く話すスタイルを維持」)を追加する。コストは数十トークンだけである。
また、ロール設定と出力ルールを混同しないでください。ルールは「どう書くか」、設定は「誰を書くか」です。分けておけば、ルールを変えずにロールを変更でき、A/Bテストも容易です。長時間の会話ではコンテキストウィンドウの合計量に注意してください。詳細は100k 長文脈の実践をご覧ください。
複数ロールのシナリオにはさらに1つ追加する:各ロールを別々の行で設定し、会話スタイルに例文を1つずつ与え、全員が同じように話さないようにする。ユーザーが演じるロールは、設定に「ユーザーが制御する」とのみ記載し、モデルが代筆しないようにする。
指示で JSON 出力を制御する
モデルが「必ず」有効な JSON を返すと想定しないでください。確実な手法は3層構造です。フォーマットを指示で固定し、パラメータでランダム性を抑え、コード側で解析のフォールバックを行います。
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.wushenchaapi.com/v1", api_key=os.environ["API_KEY"])
SYSTEM = (
"你是信息抽取器。只输出一个 JSON 对象,不要 Markdown 代码块,不要任何解释。"
'格式:{"name": 字符串, "mood": "calm|tense|angry", "items": [字符串]}。'
"缺失的字段用 null,items 没有则给空数组。"
)
def extract(text, retries=2):
for _ in range(retries + 1):
resp = client.chat.completions.create(
model="uncensored",
temperature=0.2,
max_tokens=300,
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": text},
],
)
raw = resp.choices[0].message.content.strip()
raw = raw.removeprefix("```json").removesuffix("```").strip()
try:
return json.loads(raw)
except json.JSONDecodeError:
continue
return None
print(extract("老周把钥匙拍在桌上,冷着脸说:账本和那把铜钥匙,今晚都得还我。"))このコードは以下の習慣を示している:
- フォーマット説明は system に書き、フィールドの値の範囲(
calm|tense|angry)も示す。 - コードブロックと説明テキストを明確に禁止し、コード側でも出現する可能性のあるフェンスを剥ぎ取ることで、二重の保険をかけます。
- 欠落フィールドの規約(null、空配列)を指示に組み込み、モデルが独自に捏造しないようにする。
- 解析失敗のリトライには上限を設け、最終的に None を返し、呼び出し元に判断を委ねる。
フィールドが多い場合は、まずプロンプトに完全な例オブジェクトを貼り付け、それに従って出力させる方が、多くのルールを記述するよりも通常、精度が高い。
temperatureとtop_pの推奨値
以下は起点であり、結論ではない。最終的にはサンプルの比較を基準とする。原則:一度に1つのパラメータのみを変更し、もう1つはデフォルトのままにする。
| シナリオ | temperature | top_p | 備考 |
|---|---|---|---|
| JSON 抽出、分類 | 0〜0.3 | デフォルト | 安定性が必要で、繰り返し呼び出しの結果は一致するべきである |
| 書き直し、推敲 | 0.5 から 0.7 | デフォルト | 意味を保持し、表現の柔軟性を許可 |
| 小説の続き、キャラクターの会話 | 0.8 から 1.0 | 0.9 から 0.95 | 多様性を重視。ただし、偶発的な逸脱に注意 |
| ブレインストーミング、ネーミング | 1.0 前後 | デフォルト | 複数サンプリングを行い、最良の結果を採用 |
方向性を判断する2つのシグナルがあります。出力が冗長で同じ構文を繰り返す場合、temperature が低すぎます。無関係な内容や、キャラクター名の前後不一致が発生する場合、temperature または top_p が高すぎます。stop パラメータも有用です。特定のマークに達したらモデルを停止させることで、セグメントごとの生成が容易になります。
よくあるプロンプトの書き方
| 書き方 | 問題点 | 修正案 |
|---|---|---|
| 「長すぎないこと」 | 数値がないため、実行不可能 | 「400字以内」 |
| 「Aを書かず、かつAを書かないこと」 | ルールが矛盾している | 明確なルールを1つだけ残す |
| フォーマット要件が会話の途中にある | 長い会話の後に埋もれる | system に設定するか、各ターンの末尾で再掲する |
| モデルに「制限のないAIを演じる」と指示する | 中身のない設定で、出力に制約がない | 具体的な役割とライティングルールを記載する |
| 一度に数十のルールを読み込ませる | 後半のルールが機能しない | 6件以内に削減し、残りは別リクエストに分割する |
| JSON 形式の指示が「JSONを返す」だけ | フィールド名が毎回異なる | 完全なフォーマットと例を示す |
この表の共通する傾向は、具体的であればあるほど効果があり、抽象的であればあるほど無意味だということです。また、「繰り返し強調する」ことを解決策にしないでください。同じ文を3回書き、太字や感嘆符を追加するよりも、数字を含むルールに書き換える方が効果的です。重要なのは、数が少なく正確で、サンプルで検証することです。
デバッグ手順チェックリスト
- temperature を 0.2 に固定し、問題を再現する。
- プロンプトの1箇所だけを変更し、同じサンプルセットで再実行する。
finish_reasonを確認する。length の場合、問題はプロンプトではなく max_tokens に起因します。- usage の prompt_tokens を確認し、system が長すぎないか、履歴の会話を圧迫していないかを確認します。
- 修正後、temperature を業務値に戻し、再サンプリングで検証する。
接続の詳細は接続チュートリアルをご覧ください。パラメータの完全リストはドキュメントにあります。ミドルウェアサービスの利用を検討している場合、コストとトレードオフの分析はAPI 中継サービス分析の記事にまとめられています。ここでは省略します。