Een system prompt heeft twee taken
De meeste zwakke outputs komen van een system prompt die probeert een roman te zijn. Geef het maar twee taken: zeg wie er praat en zeg hoe het antwoord eruit moet zien. Alles wat daar niet toe behoort, hoort in het gesprek, waar het van beurt tot beurt kan veranderen.
Hier is een persona-prompt die werkt omdat elke regel controleerbaar is. De stem is benoemd, de lengte is beperkt en de eindregel geeft de speler een haakje.
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.Stel dat tegen "Je bent een geweldig, creatief, meeslepend verteller die de immersie nooit breekt en altijd de best mogelijke reactie schrijft." Niets hierin is verifieerbaar, dus het model heeft niets om aan vast te houden. Concrete feiten winnen elke keer van superlatieven.
Een onbeperkt model volgt een donker of expliciet uitgangspunt zonder disclaimer toe te voegen, dus de sturing ligt bij jou. Wat je niet specificeert, vult het model met de meest typische keuze. Als je in één scène rust wilt en in de volgende passie, zeg het dan in de instructies, niet in de hoop.
Aansluiten met de OpenAI SDK
De endpoint is OpenAI-compatible, dus de officiële Python SDK werkt zodra je de base URL aanpast. De model id is altijd uncensored. Sampling velden gaan er gewoon doorheen, dus je stelt ze in zoals je dat al kent.
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)Houd het system message eerst en de user turn laatste. De persona herhalen in elke user message verspillt tokens; bij $0,25 per miljoen input tokens is het goedkoop, maar het vult ook het 100.000-token contextvenster in lange sessies. Zie de quickstart als je liever de SDK overslaat.
Persona's en toon: tonen, niet bijvoeglijk naamwoord
Bijvoeglijke naamwoorden zijn zwakke hendels. "Sarcastisch" geeft je een generieke oogopslag. Een voorbeeld van twee regels van de stem geeft je de echte stem. Zet een kort voorbeeldgesprek in de system prompt, en vertel het model dan om het ritme te volgen, niet de woorden te kopiëren.
- Geef een spraaktic. "Eindigt dreigementen met een vraag" is nuttiger dan "bedreigend".
- Geef een wens. Een personage dat wil dat de speler vertrekt, blijft scherper dan degene die gewoon "onbeleefd" is.
- Geef een verbodlijst. "Zeg nooit 'plotseling', gebruik nooit het woord 'beven'" verwijdert de standaardzinnen waar het model eerst naar grijpt.
- Geef een lengte. Woorden of zinnen, niet "kort". Modellen lezen "kort" zeer ruim op.
Voor toonverschuivingen tijdens de sessie voeg je een korte nieuwe system-achtige instructie toe als laatste bericht in plaats van het origineel te herschrijven. Een regel zoals "Vanaf hier is Mara bang en spreekt in fragmenten" werkt beter als het recent is.
JSON op instructie krijgen
Er is geen speciale JSON-modus om aan te zetten, dus je vraagt het op en verifieert het. Dat is minder fragiel dan het klinkt als je vier gewoontes volgt: geef het exacte schema op, verbied proza en code fences, houd de temperatuur laag en parse defensief met een retry die de fout terug citeert.
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."))Let op wat de lus doet bij een fout. Hij voert het slechte antwoord terug en geeft aan wat er mis was, wat de meeste gevallen op de tweede poging oplost. Som de toegestane waarden op ("calm"|"angry"|"afraid") omdat open strings afdrijven. Als je het model wilt laten iets in je app activeren in plaats van het te beschrijven, dan worden tool calls in het OpenAI-formaat ondersteund en zijn ze beter geschikt dan het parseren van tekst.
Few-shot samples en stop-sequenties
Als een regel moeilijk in woorden te vatten is, toon hem dan. Twee of drie voorbeelduitwisselingen als eerdere user- en assistant-berichten leren de opmaak, lengte en register betrouwbaarder aan dan een alinea met beschrijving. Houd de samples kort en verschillend van elkaar, anders klont het model de structuur van het eerste voorbeeld in elk antwoord.
Samples lossen ook het tegenovergestelde probleem op: antwoorden die te lang worden. Als je sample-antwoorden 60 woorden bevatten, zullen live-antwoorden naar 60 woorden afdrijven. Combineer dat met een verstandige max_tokens als veiligheidsnet, niet als primaire regeling. Een harde limiet halveert een zin; een goede sample stopt hem beleefd.
Voor scènes met meerdere sprekers voorkomt een stop-waarde die gelijk is aan je spreeklabel dat het model de speler nabootst. Kies een label dat nooit in normale tekst voorkomt en verwijder het uit de opgeslagen geschiedenis zodat het niet in latere beurten lekt.
Temperatuur en top_p: kies één hendel
Beide velden herschikken dezelfde waarschijnlijkheidsverdeling, dus het tegelijk veranderen van beide maakt de resultaten moeilijk te doorgronden. Verander eerst temperature en laat top_p dicht bij 1, tenzij je een reden hebt.
| Taak | temperatuur | top_p | Waarom |
|---|---|---|---|
| Gestructureerde JSON, classificatie | 0,0 tot 0,3 | 1,0 | Je wilt elke keer hetzelfde antwoord |
| Dialoog, roleplay | 0,7 tot 0,9 | 0,95 | Verscheidenheid zonder onzin |
| Brainstormen, wilde fictie | 1,0 tot 1,2 | 0,9 | Bredere woordenschat; verwacht wat mislukkingen |
Deze bereiken zijn startpunten uit algemene praktijk, geen garanties. Test met tien samples van je eigen prompt en lees ze. Voeg stop-sequenties toe als je het model wilt laten stoppen bij een beurtmarker zoals \nPlayer: in plaats van de lijnen van de speler voor hem te schrijven.
Prompt-antipatronen die tokens verspillen
- De hoofdletterschreeuw. "NEVER EVER FORGET" verhoogt de naleving niet. Herhaal een regel één keer, duidelijk, en verplaats hem naar het einde van de system prompt.
- Alleen-negatieve regels. "Wees niet repetitief" geeft geen doel. Zeg "varieer zinsaanvang; geen twee opeenvolgende zinnen beginnen met hetzelfde woord".
- Tegenstrijdigheden. "Wees uiterst gedetailleerd" plus "houd het beknopt" dwingt een muntworp. Kies er één en geef een getal.
- Verontschuldigende inleiding. Een verzoek voorafgaan met "Ik weet dat dit grensverleggend is, maar" nodigt uit tot aarzelen. Beschrijf de scène als een feitelijke waarheid van de fictie.
- Geschiedenis vullen. Elke beurt de volledige logboek plakken bereikt uiteindelijk de limiet van 100.000 tokens en een 400-fout. Knip oude beurt.
- Onbeperkte uitvoer.
max_tokensvergeten betekent dat de standaard van 2048 de lengte van je antwoord bepaalt, niet jij.
Eén grens blijft vast, ongeacht wat de prompt zegt: seksuele inhoud met minderjarigen wordt altijd geblokkeerd met een 403, ook in fictie en rollenspel, en de dienst is alleen voor volwassenen. Besteed geen tokens aan het proberen om er omheen te formuleren. Weer ook de drang om na elk slecht antwoord een nieuwe regel toe te voegen; een prompt die per dag een regel groeit, komt binnen een week in contradictie, dus snoei zo vaak als je toevoegt.
Een lus voor het verbeteren van een prompt
Behandel de systeem prompt als code. Houd het in een bestand, verander één ding per run, en vergelijk outputs naast elkaar. Een werkzame routine: schrijf vijf vaste testinputs, waaronder één hostiel en één ambigu; voer elk bij je gekozen temperatuur drie keer uit; noteer welke regel het meest faalde; fix alleen die regel; run opnieuw.
Budget de experimenten. Als aanname is een 300-token system prompt plus een 100-token input en een 250-token reply 400 input en 250 output tokens. Vijftien runs kosten ongeveer 15 x (400 x $0,25 + 250 x $1,00) / 1.000.000 = ongeveer $0,005. Testen is bijna gratis, dus er is geen excuus voor raden.
Zodra de prompt standhoudt, verplaats hem dan naar een echte app. De bot-tutorial laat zien hoe je een aparte geschiedenis per kanaal bijhoudt, en de contentgids behandelt adult-fiction-gebruik. Prijsdetails staan op de prijslijst.