系統提示詞有兩個任務
大多數品質差的輸出來自試圖寫成小說的系統提示詞。只給它兩項任務:說明誰在說話,以及回覆必須長什麼樣子。其他內容都放在對話中,讓它們可以逐輪變化。
這裡有一個有效的角色提示詞,因為每一行都可以檢查。語氣已命名,長度已限制,結尾規則為玩家提供了切入點。
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.與「你是一位驚人的、有創意的、沉浸式的說書人,從不打破沉浸感,且總是寫出最好的回覆」相比。裡面沒有任何內容可以驗證,所以模型沒有憑藉。具體細節總是勝過極端形容詞。
無審查模型會遵循黑暗前提或露骨內容而不加備註,因此引導的責任在你。任何未指定的部分,它都會以最常見的選擇填補。如果你想在某個場景中保持克制,在下一個場景中增加熱情,請在指示中說明,而不是寄望。
使用 OpenAI SDK 連接
端點與 OpenAI 相容,因此只要更改 base 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)將系統訊息放在最前面,使用者輪次放在最後面。在每則使用者訊息中重複角色設定會浪費 token;雖然每百萬個輸入 token 僅需 $0.25,但在長對話中會擠壓 100,000 token 的上下文視窗。如果你希望跳過 SDK,請參見快速入門。
角色與語氣:展示而非形容
形容詞是薄弱的控制手段。「諷刺」只會讓你得到一個通用的翻白眼反應。兩行語氣範例才能讓你得到真正的語氣。在系統提示詞中加入一段短對話範例,然後指示模型匹配其節奏,而非複製其用詞。
- 給予口頭禪。「以提問結束威脅」比「具威脅性」更有用。
- 給予動機。一個需要玩家離開的角色,比一個只是「粗魯」的角色更鮮明。
- 給予禁止清單。「從不說『突然』,從不使用『顫抖』一詞」可移除模型首先使用的陳詞濫調。
- 給予長度。字數或句數,而非「短」。模型對「短」的解讀非常寬鬆。
對於會話中途的語氣變化,請附加新的短系統風格指示作為最新訊息,而不是重寫原始訊息。像「從這裡開始,Mara 感到害怕並以片段方式說話」這樣的行,當它較新時效果更強。
透過指令取得 JSON
沒有需要開啟的特殊 JSON 模式,因此你只需要求並驗證即可。如果你遵循四個習慣,這聽起來比實際情況更穩定:明確指定 schema、禁止散文和程式碼圍欄、保持 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."))注意迴圈在失敗時做什麼。它將錯誤答案餵回並說明哪裡出錯,這在第二次嘗試時修復大多數情況。列舉允許的值("calm"|"angry"|"afraid"),因為開放字串會漂移。如果你需要模型在你的應用程式中觸發某事而非描述它,支援 OpenAI 格式的 tool_calls 是更好的選擇。
少樣本範例與停止序列
當規則難以用文字表達時,展示它。兩三個範例對話作為較早的user與assistant訊息,比一段描述更能可靠地教導格式、長度與語域。保持範例短且彼此不同,否則模型會將第一個範例的結構複製到每個回覆中。
範例也解決相反的問題:回覆過長。如果你的範例答案是 60 個字,實際回覆會偏向 60 個字。將其與合理的max_tokens配對作為安全網,而非主要控制。硬性上限會切斷句子的一半;好的範例會禮貌地結束它。
對於多說話者場景,stop值等於你的說話者標籤可防止模型模仿玩家。選擇一個從不出現在正常散文中的標籤,並將其從儲存歷史中移除,以免洩漏到後續輪次。
temperature 與 top_p:選擇一個控制桿
兩個欄位都會重塑相同的機率分佈,因此同時移動兩者會使結果難以推論。先更改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 | 詞彙更廣;預期會有部分失誤 |
這些範圍是一般實踐的起點,並非保證。使用你自己的十個提示詞樣本進行測試並閱讀它們。當你想要模型在如\nPlayer:這樣的輪次標記處停止,而不是替玩家寫出對話時,加入stop序列。
浪費 token 的提示詞反模式
- 全大寫喊叫。「永遠不要忘記」並不會提高遵循率。清楚地重複一次規則,然後將其移至系統提示詞的末尾。
- 僅限否定規則。 "不要重複" 沒有提供目標。請說 "變化句子開頭;連續兩個句子不得以相同字詞開頭"。
- 矛盾。 "極度詳細" 加上 "保持簡短" 會迫使模型擲硬幣決定。選擇其一並提供數字。
- 道歉式框架。 在請求前加上 "我知道這有點大膽,但是" 會誘發保留態度。將場景作為小說的既定事實直接陳述。
- 塞滿歷史記錄。 每輪貼上完整的日誌最終會遇到 100,000 token 限制並產生 400 錯誤。修剪舊的輪次。
- 無限制的輸出。 若遺忘
max_tokens,則預設的 2048 將決定你的回覆長度,而非由你決定。
無論提示詞為何,有一個界限保持固定:涉及未成年人的性內容一律會以 403 封鎖,包括小說和角色扮演,且該服務僅供成人使用。不要浪費 token 試圖繞過它。此外,請克制在每次不良回應後新增規則的衝動;如果提示詞每天增加一行,一週內就會自相矛盾,因此新增多少就修剪多少。
改善提示詞的循環
將系統提示詞視為程式碼。將其保存在文件中,每次運行只更改一項,並並排比較輸出結果。可行的常規流程:寫入五個固定的測試輸入,包括一個敵對輸入和一個模糊輸入;以你選擇的溫度值運行每個輸入三次;記錄哪條規則失敗最多;僅修復該規則;重新運行。
為實驗預算。假設一個 300 token 的系統提示詞加上 100 token 的輸入和 250 token 的回覆,總計為 400 個輸入 token 和 250 個輸出 token。15 次執行的成本約為 15 x (400 x $0.25 + 250 x $1.00) / 1,000,000 = 約 $0.005。測試幾乎免費,所以沒有藉口去猜測。
一旦提示詞通過測試,請將其移入實際應用程式。機器人教程 展示了如何為每個頻道保持獨立歷史記錄,內容指南 涵蓋成人小說用途。定價詳情請見 定價頁面。