Scenari di roleplay testuale con API LLM
Gli scenari di roleplay testuale funzionano meglio quando il modello interpreta un narratore con regole, e il tuo codice gestisce lo stato. Questo articolo illustra un piccolo motore in Python: prompt, streaming, memoria e una stima dei costi per una sessione lunga.

Punti chiave
- Dividi il lavoro: il modello scrive la storia, il tuo codice memorizza fatti, inventario e regole.
- Usa lo streaming per le risposte e mantieni ogni stream abbastanza breve da liberare rapidamente il suo slot di concorrenza.
- Una finestra di 100.000 token contiene una sessione molto lunga, ma i riassunti tengono bassi i costi e la deriva.
- Una sessione di 200 turni con 6.000 token di input e 400 token di output per turno costa circa $0,22.
Inizia con il narratore, non con la trama
Le persone che creano scenari di roleplay testuale spesso iniziano scrivendo una trama. È sbagliato. Uno scenario è un ciclo tra un giocatore e un narratore, e il narratore ha bisogno di una descrizione del lavoro prima di avere bisogno di una storia. Il modello deve sapere chi parla, cosa può decidere per il giocatore, quanto dovrebbe essere lunga una risposta e cosa non deve mai fare. Tutto il resto può essere improvvisato.
Un buon prompt di sistema per un motore di scenari ha quattro parti. La prima è il ruolo: master di gioco, intervistatore, computer di bordo, un personaggio di una pièce. La seconda sono le regole del mondo, espresse in poche frasi semplici: tono, ambientazione, cosa esiste e cosa non esiste. La terza sono le regole di formattazione, perché il tuo codice potrebbe doverle analizzare: ad esempio, "termina ogni risposta con una riga che inizia con OPTIONS:". La quarta sono i limiti. L'API testuale è ottimizzata per rispondere senza rifiuti di generazione per l'uso adulto lecito, il che la rende adatta a narrativa dark, villain con vera minaccia e trame moralmente grigie. C'è ancora una linea dura: nessun contenuto sessuale che coinvolga minorenni. Inserisci anche tu questa regola nei tuoi termini di servizio e applica un controllo dell'età nella tua app se i tuoi scenari sono rivolti agli adulti.
Mantieni il prompt sotto le 600 parole circa. Le liste lunghe di regole vengono seguite solo parzialmente. Se hai bisogno di molte regole, ordinale per priorità e scarta il terzo inferiore.
Un ciclo di lavoro con streaming
Lo streaming è più importante nel roleplay che nella maggior parte degli altri usi. Un giocatore che legge una storia vuole che le parole appaiano mentre il modello le scrive, e una risposta di 300 token sembra più lenta del previsto se arriva tutta in una volta. L'API effettua lo streaming tramite server-sent events con stream: true, e gli SDK OpenAI gestiscono l'analisi. L'endpoint è compatibile con OpenAI, l'ID del modello è uncensored e l'URL di base per questo sito è https://api.sdxlapi.com/v1.
from openai import OpenAI
client = OpenAI(base_url="https://api.sdxlapi.com/v1", api_key="YOUR_KEY")
SYSTEM = """You are the narrator of a noir mystery set in a rainy harbour city.
Write in second person, present tense, 120 to 200 words per reply.
Never decide what the player says or does. Invent details freely, but stay
consistent with the STATE block you are given.
End every reply with one line: OPTIONS: a | b | c"""
state = {"place": "Pier 9 office", "time": "23:40", "inventory": ["brass key"], "facts": []}
history = []
def turn(player_text: str) -> str:
history.append({"role": "user", "content": player_text})
msgs = [{"role": "system", "content": SYSTEM + "\nSTATE: " + str(state)}] + history[-20:]
stream = client.chat.completions.create(
model="uncensored", messages=msgs, stream=True,
temperature=0.9, top_p=0.95, max_tokens=500,
presence_penalty=0.3,
)
reply = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
piece = chunk.choices[0].delta.content
reply += piece
print(piece, end="", flush=True)
history.append({"role": "assistant", "content": reply})
return reply
turn("I check the desk drawer.")Le impostazioni sono ottimizzate per la narrativa. Una temperatura intorno a 0,8-1,0 offre una prosa varia, e top_p vicino a 0,95 elimina le scelte lessicali più strane. Una piccola penalità di presenza scoraggia il narratore dal tornare sulle stesse immagini. Imposta max_tokens per limitare la lunghezza della risposta; è opzionale, ma senza di essa un narratore chiacchierone potrebbe scrivere ben più di quanto desideri. Per una scena che vuoi riprodurre esattamente durante i test, passa seed e mantieni fissi gli altri parametri.
Lascia che il codice gestisca i fatti
Il fallimento più comune nel roleplay lungo è la deriva. La chiave è nella tasca del giocatore al turno 5 e sul tavolo al turno 40. L'oste si chiamava Mara, poi Marta. Un modello che legge 30.000 token di cronologia spesso risolve questo problema, ma "spesso" non è una progettazione. Tieni i fatti in una piccola struttura che il tuo programma aggiorna e inserisci quella struttura in ogni richiesta, come fa la riga STATE sopra.
Come aggiorni lo stato? Chiedi al modello di segnalare le modifiche dopo ogni turno, in una seconda chiamata economica che usa la modalità JSON. La prima chiamata scrive la storia per il giocatore. La seconda legge il nuovo passaggio e restituisce solo le modifiche.
import json
def extract_changes(passage: str) -> dict:
r = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
temperature=0,
max_tokens=250,
messages=[
{"role": "system", "content":
'Read the passage. Return JSON: {"place": str|null, "inventory_add": [str], '
'"inventory_remove": [str], "new_facts": [str]}. Only report what the passage states.'},
{"role": "user", "content": passage},
],
)
return json.loads(r.choices[0].message.content)
def apply(ch: dict):
if ch.get("place"):
state["place"] = ch["place"]
for item in ch.get("inventory_add", []):
if item not in state["inventory"]:
state["inventory"].append(item)
state["inventory"] = [i for i in state["inventory"] if i not in ch.get("inventory_remove", [])]
state["facts"] = (state["facts"] + ch.get("new_facts", []))[-30:]Limita l'elenco dei fatti, come fa la slice, o il blocco di stato crescerà fino a diventare pesante quanto la cronologia che doveva sostituire. Se una risposta JSON non viene analizzata, salta l'aggiornamento per quel turno e vai avanti; un fatto perso costa meno di un crash.
Dadi, salute e timer sono gestiti meglio nel codice normale. Quando il giocatore prova qualcosa di rischioso, tira in Python e comunica al narratore il risultato: "Il tentativo del giocatore fallisce". Il modello scrive poi le conseguenze. Questo impedisce al modello di lasciare che il giocatore vinca ogni volta in silenzio, cosa che i modelli linguistici tendono a fare.
Memoria per sessioni che durano ore
La finestra di contesto è di 100.000 token, contando prompt e completamento insieme. Una risposta tipica di 250 token più un messaggio del giocatore di 30 token fa circa 280 token per turno, quindi la cronologia grezza da sola potrebbe contenere centinaia di turni. Puoi inviarli tutti. Molti sviluppatori non lo fanno per due motivi: il costo cresce con ogni turno, perché l'intera cronologia viene fatturata come input ogni volta, e i dettagli vecchi possono distrarre il modello.
Un layout ragionevole mantiene tre strati:
- Fissato: il system prompt e il blocco di stato. Sempre inviato.
- Riassunto: un paragrafo o due in continuo aggiornamento su quanto è successo, riscritti ogni 10-15 turni con una breve chiamata.
- Turni recenti: gli ultimi 10-20 messaggi verbatim, così la prosa mantiene il suo ritmo.
Quando la finestra degli ultimi turni si riempie, comprimi i dieci più vecchi nel riassunto e scartali. La chiamata di riassunto può usare una temperatura bassa e un limite di 300 token. I giocatori raramente notano la compressione, purché nomi, promesse e minacce irrisolte finiscano nel riassunto. Specifica esplicitamente nel prompt di riassunto: "mantieni nomi, debiti, ferite, domande aperte".
Se vuoi che un personaggio mantenga una voce stabile, fornisci una scheda di stile breve nello strato fisso: tre righe di esempio del suo discorso e due cose che non dice mai. Esempi brevi modellano il tono meglio degli aggettivi.
Costo di una sessione
I prezzi sono $0,15 per 1M di token di input e $0,50 per 1M di token di output, con credito prepagato e senza abbonamento. Gestisci una sessione lunga. Diciamo 200 turni, con lo strato fisso, il riassunto e gli ultimi turni che si aggiungono a una media di 6.000 token di input per richiesta e 400 token di output per risposta (la risposta alla storia più una piccola quota per le chiamate di stato e riassunto).
Input: 200 x 6.000 = 1.200.000 token, che è 1,2 x $0,15 = $0,18. Output: 200 x 400 = 80.000 token, che è 0,08 x $0,50 = $0,04. La sessione costa $0,22.
Confronta la versione semplice che reinvia tutta la cronologia grezza. Al turno 200 la cronologia è di circa 56.000 token, e nell'intera sessione l'input medio è di circa 28.000 token per richiesta. Questo fa 200 x 28.000 = 5.600.000 token di input, o 5,6 x $0,15 = $0,84, più gli stessi $0,04 per l'output, che fanno $0,88. Ancora accessibile, ma quattro volte il conto per nessun guadagno in qualità. Per un'app con molti giocatori, questo divario decide il tuo margine.
Le richieste fallite o rifiutate non vengono addebitate e il credito non scade mai, quindi il saldo inutilizzato resta in attesa. Le ricariche vanno da $20 a $500 in dollari interi, con un bonus del 5% da $50, 10% da $100 e 15% da $200. Vedi la pagina dei prezzi per l'elenco completo delle opzioni.
Gestire molti giocatori contemporaneamente
Una chiave completa permette 8 richieste parallele e 300 richieste al minuto. Una richiesta streaming mantiene il suo slot fino alla fine dello streaming, quindi con 8 slot e risposte che durano circa dieci secondi, puoi servire circa 48 flussi di risposta al minuto, assumendo che non corra nient'altro. Questa è una regola approssimativa, non un dato misurato, poiché la velocità varia con il carico e la lunghezza della risposta. L'obiettivo è pianificarlo: se il tuo gioco ha più giocatori simultanei degli slot, accoda le richieste sul tuo server e mostra un breve stato "il narratore sta pensando" invece di scatenare tutto insieme.
Quando superi un limite, l'API restituisce HTTP 429. Il codice è rate_limited per le richieste al minuto e concurrency per troppi contemporaneamente. Il sovraccarico temporaneo dà anche 429, con un'intestazione Retry-After: 5. Un semplice retry che aspetta quei cinque secondi è sufficiente per un gioco occasionale. Leggi anche le intestazioni di risposta: X-RateLimit-Limit-Requests e X-RateLimit-Limit-Concurrency mostrano i tuoi limiti, e X-Balance-USD mostra quanto rimane, che puoi esporre in una schermata di amministrazione.
Non eseguire la chiamata di estrazione dello stato in parallelo con il turno successivo del giocatore. Attendi, altrimenti il narratore scriverà da fatti non aggiornati. Un buon compromesso è avviare l'estrazione non appena termina lo stream e lasciare che il giocatore legga mentre viene eseguita.
Collegare le scene alla tua pipeline di immagini
Questo sito è pensato per chi usa modelli di testo nei flussi di lavoro di Stable Diffusion, e gli scenari si adattano bene. L'API è solo testuale: non genera immagini, audio o video. Ma il narratore può scrivere una descrizione di scena che il tuo pipeline trasforma in un prompt per immagini. Aggiungi un'altra chiamata JSON dopo un cambiamento di scena importante, chiedendo una breve descrizione visiva dell'ambientazione con illuminazione, atmosfera e angolazione della telecamera, poi passa quella stringa allo strumento di immagini che usi. La pagina documentazione mostra la forma della richiesta per le chiamate di scrittura del prompt, e l'articolo sul generatore di prompt SD approfondisce quell'aspetto.
Per provare il motore, crea un account e attiva la prova. I nuovi utenti possono ottenere $0,50 di credito di prova gratuito con un clic sulla pagina della chiave API, valido per 7 giorni senza bisogno di carta. Dai numeri sopra, questo è sufficiente per un paio di sessioni complete. Le chiavi di prova sono limitate a 2 richieste parallele e 60 al minuto, il che si adatta bene ai test in solitaria.
Mantieni la finzione nella finzione. Gli scenari a tema adulto sono uso lecito quando tutti i partecipanti sono adulti; l'unica regola che non si piega è nessun contenuto sessuale che coinvolga minorenni. I prompt inviati all'API non vengono utilizzati per l'addestramento.
Altre guide
Domande e risposte
Il modello può interpretare qualsiasi personaggio o genere?
È un modello di testo generale sintonizzato per non rifiutare richieste adulte legali, quindi horror, crime, dramma e commedia vanno bene. L'unico limite duro è il contenuto sessuale che coinvolge minorenni.
Per quanto può durare una sessione prima che il contesto si riempia?
La finestra di contesto è di 100.000 token per prompt più completamento. Con uno strato di riassunto puoi andare ben oltre nella pratica, poiché i turni precedenti vengono compressi.
Devo usare lo streaming per le risposte?
Sì, per qualsiasi cosa una persona legge in diretta. Lo streaming ti dà l'utilizzo alla fine, e uno stream occupa uno dei tuoi slot di concorrenza fino a che finisce.
Esiste un modo per far ripetere esattamente una scena?
Passa lo stesso seed con messaggi e impostazioni identici. È un aiuto per i test e non garantisce un output byte-per-byte identico.
La tua chiave è a un modulo di distanza
Crea un account, copia la chiave, modifica l'URL di base. È tutta la configurazione.