एक LLM API के साथ टेक्स्ट-आधारित रोलप्ले परिदृश्य
टेक्स्ट रोलप्ले परिदृश्य तब सबसे अच्छे काम करते हैं जब मॉडल नियमों के साथ एक नैरेटर का अभिनय करता है, और आपका कोड स्थिति को संभालता है। यह लेख Python में एक छोटे इंजन को समझाता है: प्रॉम्प्ट, स्ट्रीमिंग, मेमोरी और एक लंबे सत्र के लिए लागत अनुमान।

मुख्य बिंदु
- काम को बांटें: मॉडल कहानी लिखता है, आपका कोड तथ्यों, इन्वेंटरी और नियमों को संग्रहीत करता है।
- प्रतिक्रियाओं को स्ट्रीम करें और प्रत्येक स्ट्रीम को इतना छोटा रखें कि उसका कन्करेंसी स्लॉट जल्दी खाली हो जाए।
- एक 100,000-टोकन विंडो एक बहुत लंबे सत्र को धारण करती है, लेकिन सारांश लागत और विचलन को कम रखते हैं।
- प्रति टर्न 6,000 इनपुट और 400 आउटपुट टोकन के साथ एक 200-टर्न सत्र की लागत लगभग $0.22 आती है।
प्लॉट से नहीं, नैरेटर से शुरुआत करें
रोलप्ले टेक्स्ट-आधारित परिदृश्य बनाने वाले अक्सर पहले एक प्लॉट लिखना शुरू करते हैं। यह उल्टा है। एक परिदृश्य एक प्लेयर और एक नैरेटर के बीच की लूप है, और नैरेटर को कहानी से पहले एक जॉब डिस्क्रिप्शन की जरूरत होती है। मॉडल को यह जानना होगा कि कौन बोल रहा है, वह प्लेयर के लिए क्या निर्णय ले सकता है, एक जवाब कितना लंबा होना चाहिए, और उसे कभी क्या नहीं करना चाहिए। बाकी सब इम्प्रोवाइज किया जा सकता है।
एक परिदृश्य इंजन के लिए एक अच्छा सिस्टम प्रॉम्प्ट चार भागों का होता है। पहले आता है भूमिका: गेम मास्टर, इंटरव्यूअर, जहाज का कंप्यूटर, या एक नाटक का पात्र। दूसरा, कुछ सरल वाक्यों में दुनिया के नियम: टोन, सेटिंग, क्या मौजूद है और क्या नहीं। तीसरा, फॉर्मेटिंग के नियम, क्योंकि आपका कोड उन्हें पार्स कर सकता है: उदाहरण के लिए, "हर जवाब के अंत में OPTIONS: से शुरू होने वाली एक पंक्ति लिखें।" चौथा, सीमाएँ। यहाँ टेक्स्ट API कानूनी वयस्क उपयोग के लिए बिना सामग्री अस्वीकृति के जवाब देने के लिए ट्यून किया गया है, जो इसे डार्क फिक्शन, असली खतरनाक खलनायकों और नैतिक रूप से भूरे प्लॉट्स के लिए उपयुक्त बनाता है। इसमें अभी भी एक कड़ा नियम है: नाबालिगों से जुड़ी यौन सामग्री नहीं। उस नियम को अपने उपयोग की शर्तों में भी डालें, और यदि आपके परिदृश्य वयस्कों के लिए हैं तो अपने ऐप में आयु जाँच लागू करें।
प्रॉम्प्ट को लगभग 600 शब्दों के अंदर रखें। लंबे नियमों की सूची आंशिक रूप से पालन की जाती है। यदि आपको कई नियमों की आवश्यकता है, तो उन्हें क्रमबद्ध करें और नीचे के एक तिहाई को हटा दें।
स्ट्रीमिंग के साथ एक कार्यरत लूप
रोलप्ले में स्ट्रीमिंग अधिकांश उपयोगों की तुलना में अधिक महत्वपूर्ण है। कहानी पढ़ने वाला खिलाड़ी चाहता है कि मॉडल के लिखते समय शब्द प्रकट होते जाएँ, और 300 टोकन का जवाब यदि एक साथ आता है तो वह अपने वास्तविक गति से धीमा लगता है। API सर्वर-सेंट इवेंट्स के माध्यम से stream: true के साथ स्ट्रीम करता है, और OpenAI SDKs पार्सिंग संभालते हैं। एंडपॉइंट OpenAI-कम्पैटिबल है, मॉडल ID uncensored है, और इस साइट के लिए बेस URL 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.")सेटिंग्स फिक्शन के लिए ट्यून्ड हैं। 0.8 से 1.0 के आसपास टेम्परेचर विविध प्रोज़ देता है, और top_p करीब 0.95 सबसे अजीब शब्दों के चयन को कम करता है। एक छोटा प्रेजेंस पेनाल्टी नैरेटर को एक ही इमेज के चक्कर लगाने से रोकता है। जवाब की लंबाई को कैप करने के लिए max_tokens सेट करें; यह ऑप्शनल है, लेकिन इसके बिना एक चैटिंग नैरेटर आपसे ज्यादा लिख सकता है। एक दृश्य के लिए जिसे आप टेस्टिंग के दौरान बिल्कुल दोहराना चाहते हैं, seed पास करें और अन्य पैरामीटर को फिक्स रखें।
तथ्यों को कोड को दें
लंबे रोलप्ले में सबसे आम विफलता ड्रिफ्ट है। 5वें टर्न पर कुंजी खिलाड़ी की जेब में होती है और 40वें टर्न पर टेबल पर। सराय के मालिक का नाम मारा था, फिर मार्टा। 30,000 टोकन का इतिहास पढ़ने वाला मॉडल अक्सर इसे सही करेगा, लेकिन "अक्सर" डिज़ाइन नहीं है। तथ्यों को एक छोटी संरचना में रखें जिसे आपका प्रोग्राम अपडेट करता है, और उस संरचना को हर अनुरोध में डालें, जैसा कि ऊपर STATE लाइन करती है।
स्थिति को कैसे अपडेट करें? मॉडल से हर टर्न के बाद बदलाव रिपोर्ट करने के लिए कहें, एक दूसरे, सस्ते कॉल में जो JSON मोड का उपयोग करता है। पहला कॉल खिलाड़ी के लिए कहानी लिखता है। दूसरा नए पृष्ठ को पढ़ता है और केवल वही लौटाता है जो बदला है।
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:]तथ्यों की सूची को कैप करें, जैसे कि स्लाइस करता है, या स्टेट ब्लॉक तब तक बढ़ता रहेगा जब तक कि वह उस इतिहास के बराबर न हो जाए जिसे वह बदलने वाला था। यदि एक JSON जवाब पार्स नहीं होता है, तो उस टर्न के लिए अपडेट को छोड़ दें और आगे बढ़ें; एक मिस की गई फैक्ट क्रैश से सस्ती है।
डाइस, स्वास्थ्य और टाइमर को सादे कोड में बेहतर तरीके से संभाला जाता है। जब खिलाड़ी कुछ जोखिम भरा करने की कोशिश करता है, तो Python में रोल करें और नैरेटर को परिणाम बताएं: "खिलाड़ी की कोशिश असफल हो गई।" मॉडल फिर परिणाम लिखता है। यह मॉडल को चुपचाप हर बार खिलाड़ी को जीतने से रोकता है, जो भाषा मॉडल करते हैं।
सत्रों के लिए मेमोरी जो घंटों चलती हैं
कॉन्टेक्स्ट विंडो 100,000 टोकन की है, प्रॉम्प्ट और कंप्लीशन को मिलाकर। 250 टोकन का एक सामान्य जवाब और 30-टोकन का खिलाड़ी संदेश मिलकर प्रति टर्न लगभग 280 टोकन होता है, इसलिए केवल इतिहास सैकड़ों टर्न रख सकता है। आप इसे सब भेज सकते हैं। कई बिल्डर अभी भी ऐसा नहीं करते, दो कारणों से: लागत हर टर्न के साथ बढ़ती है, क्योंकि पूरा इतिहास हर बार इनपुट के रूप में बिल किया जाता है, और पुरानी जानकारी मॉडल को भ्रमित कर सकती है।
एक उचित लेआउट तीन परतें रखता है:
- पिन किया गया: सिस्टम प्रॉम्प्ट और स्टेट ब्लॉक। हमेशा भेजा जाता है।
- सारांश: चल रहे पैराग्राफ या दो, जो हर 10 से 15 टर्न्स पर एक छोटे कॉल द्वारा फिर से लिखा जाता है।
- हालिया टर्न्स: पिछले 10 से 20 संदेशों का शब्दशः, ताकि लेखन की लय बनी रहे।
जब हालिया टर्न की विंडो भर जाती है, तो सबसे पुराने दस को सारांश में मिला दें और उन्हें हटा दें। सारांश कॉल में कम तापमान और 300 टोकन की सीमा का उपयोग किया जा सकता है। खिलाड़ी संपीड़न को तब तक नहीं नोटिस करते, जब तक कि नाम, वादे और अनसुलझे खतरे सारांश में शामिल न हों। सारांशीकरण प्रॉम्प्ट में स्पष्ट रूप से कहें: "नाम, कर्ज, चोट, खुले सवालों को रखें।"
यदि आप चाहते हैं कि एक किरदार एक स्थिर आवाज रखे, तो उसे पिन की गई परत में एक छोटी स्टाइल कार्ड दें: उसकी बोलने की तीन उदाहरण लाइनें और दो चीजें जो वह कभी नहीं कहता। छोटे उदाहरण एडजेक्टिव से बेहतर टोन बनाते हैं।
एक सत्र की लागत
मूल्य निर्धारण $0.15 प्रति 1M इनपुट टोकन और $0.50 प्रति 1M आउटपुट टोकन है, प्रीपेड क्रेडिट के साथ और कोई सब्सक्रिप्शन नहीं। एक लंबे सत्र से गुजरें। मान लें 200 टर्न, पिन्ड लेयर, सारांश और हालिया टर्न मिलाकर प्रति अनुरोध औसतन 6,000 इनपुट टोकन और प्रति जवाब 400 आउटपुट टोकन (कहानी जवाब plus राज्य और सारांश कॉल के लिए एक छोटा हिस्सा)।
इनपुट: 200 x 6,000 = 1,200,000 टोकन, जो 1.2 x $0.15 = $0.18 है। आउटपुट: 200 x 400 = 80,000 टोकन, जो 0.08 x $0.50 = $0.04 है। सत्र की लागत $0.22 है।
संपूर्ण कच्चे इतिहास को फिर से भेजने वाले लैज़ी वर्जन की तुलना करें। 200वीं बारी तक इतिहास लगभग 56,000 टोकन का होता है, और सत्र भर प्रति अनुरोध इनपुट का औसत लगभग 28,000 टोकन होता है। यह 200 x 28,000 = 5,600,000 इनपुट टोकन के बराबर है, या 5.6 x $0.15 = $0.84, आउटपुट के लिए समान $0.04 जोड़ने पर यह $0.88 बनता है। यह अभी भी किफ़ायती है, लेकिन गुणवत्ता में कोई वृद्धि के बिना बिल चार गुना है। कई खिलाड़ियों वाले ऐप के लिए, यह अंतर आपकी मार्जिन तय करता है।
असफल या अस्वीकृत अनुरोधों के लिए चार्ज नहीं किया जाता है, और क्रेडिट कभी समाप्त नहीं होता, इसलिए इस्तेमाल न किया गया बैलेंस बस इंतज़ार करता है। टॉप-अप $20 से $500 तक पूर्ण डॉलर में होते हैं, $50 से 5% बोनस, $100 से 10% और $200 से 15% बोनस मिलता है। विकल्पों की पूर्ण सूची के लिए मूल्य निर्धारण पेज देखें।
एक साथ कई खिलाड़ियों को चलाना
एक पूर्ण कुंजी 8 समानांतर अनुरोध और प्रति मिनट 300 अनुरोध की अनुमति देती है। एक स्ट्रीमिंग अनुरोध स्ट्रीम समाप्त होने तक अपनी स्लॉट को बनाए रखता है, इसलिए 8 स्लॉट और लगभग दस सेकंड का जवाब देने वाली प्रतिक्रियाओं के साथ, आप प्रति मिनट लगभग 48 प्रतिक्रिया स्ट्रीम सेवा दे सकते हैं, यह मानते हुए कि कुछ और नहीं चल रहा है। यह एक अनुमानित नियम है, मापा गया आंकड़ा नहीं, क्योंकि गति लोड और प्रतिक्रिया की लंबाई के साथ बदलती है। बिंदु यह है कि इसकी योजना बनाएं: यदि आपके गेम में स्लॉट्स से अधिक एक साथ खिलाड़ी हैं, तो अपने सर्वर पर अनुरोध क्यू करें और एक साथ सब कुछ फायर करने के बजाय एक छोटा "नैरेटर सोच रहा है" स्थिति दिखाएं।
जब आप एक सीमा से अधिक हो जाते हैं तो API HTTP 429 लौटाता है। प्रति मिनट अनुरोधों के लिए कोड rate_limited है और एक साथ बहुत अधिक के लिए concurrency है। अस्थायी ओवरलोड भी 429 देता है, Retry-After: 5 हेडर के साथ। एक सरल पुनर्प्रयास जो उन पांच सेकंड का इंतज़ार करता है, एक साधारण गेम के लिए पर्याप्त है। प्रतिक्रिया हेडर भी पढ़ें: X-RateLimit-Limit-Requests और X-RateLimit-Limit-Concurrency आपकी कैप्स दिखाते हैं, और X-Balance-USD दिखाता है कि क्या बचा है, जिसे आप एक एडमिन स्क्रीन पर दिखा सकते हैं।
स्टेट-एक्सट्रैक्शन कॉल को अगले खिलाड़ी की बारी के साथ समानांतर में न चलाएं। इसका इंतज़ार करें, या नैरेटर पुराने तथ्यों से लिखेगा। एक अच्छा समझौता यह है कि स्ट्रीम समाप्त होते ही एक्सट्रैक्शन शुरू करें और खिलाड़ी को पढ़ने दें जबकि यह चल रहा है।
दृश्यों को अपनी इमेज पाइपलाइन से जोड़ना
यह साइट उन लोगों के लिए बनाई गई है जो टेक्स्ट मॉडल्स का उपयोग स्टेबल डिफ्यूजन वर्कफ्लो के आसपास करते हैं, और परिदृश्य इसमें अच्छे से फिट बैठते हैं। API स्वयं केवल टेक्स्ट है: यह कोई इमेज, ऑडियो या वीडियो जनरेट नहीं करता है। लेकिन नैरेटर एक दृश्य विवरण लिख सकता है जिसे आपका पाइपलाइन इमेज प्रॉम्प्ट में बदल देता है। एक बड़े दृश्य परिवर्तन के बाद एक और JSON कॉल जोड़ें, लाइटिंग, मूड और कैमरा एंगल के साथ सेटिंग का एक छोटा दृश्य विवरण मांगें, और फिर उस स्ट्रिंग को उस इमेज टूल को पास करें जिसे आप चलाते हैं। docs page प्रॉम्प्ट-लिखने वाली कॉल्स के लिए अनुरोध का ढांचा दिखाता है, और SD prompt generator पर आर्टिकल उस पक्ष पर और गहराई से जाता है।
इंजन की कोशिश करने के लिए, एक खाता बनाएं और ट्रायल क्लेम करें। नए उपयोगकर्ता API कुंजी पेज पर एक क्लिक से $0.50 का ट्रायल क्रेडिट प्राप्त कर सकते हैं, जो 7 दिनों के लिए मान्य है और कार्ड की आवश्यकता नहीं है। ऊपर दिए गए आंकड़ों के अनुसार, यह एक जोड़ी पूर्ण सत्रों के लिए पर्याप्त है। ट्रायल कुंजियां 2 समानांतर अनुरोधों और प्रति मिनट 60 तक सीमित हैं, जो अकेले परीक्षण के लिए उपयुक्त है।
कथा को कथा में रखें। वयस्क-विषयक परिदृश्य तब तक कानूनी उपयोग हैं जब सभी शामिल वयस्क हों; एकमात्र नियम जो कभी नहीं झुकता नाबालिगों से जुड़ी कोई यौन सामग्री नहीं। API को भेजे गए प्रॉम्प्ट प्रशिक्षण के लिए उपयोग नहीं किए जाते हैं।
अधिक गाइड
प्रश्न और उत्तर
क्या मॉडल किसी भी पात्र या शैली को खेल सकता है?
यह एक सामान्य टेक्स्ट मॉडल है जिसे वयस्कों के कानूनी अनुरोधों को अस्वीकार न करने के लिए ट्यून्ड किया गया है, इसलिए हॉरर, अपराध, नाटक और कॉमेडी सभी ठीक हैं। एकमात्र कठोर सीमा नाबालिगों से जुड़ी यौन सामग्री है।
संदर्भ भर जाने से पहले एक सत्र कितना लंबा हो सकता है?
विंडो प्रॉम्प्ट और पूर्णता के लिए 100,000 टोकन की है। सारांश परत के साथ, आप व्यावहारिक रूप में उससे कहीं अधिक दूर तक चला सकते हैं, क्योंकि पुरानी बारी संपीड़ित हो जाती हैं।
क्या मुझे प्रतिक्रियाओं को स्ट्रीम करना चाहिए?
हाँ, किसी भी चीज़ के लिए जिसे कोई व्यक्ति लाइव पढ़ता है। स्ट्रीमिंग आपको अंत में उपयोग का डेटा देती है, और एक स्ट्रीम इसे समाप्त होने तक आपके कॉन्करेंसी स्लॉट में से एक घेरती है।
क्या कोई तरीका है जिससे एक दृश्य बिल्कुल दोहराया जा सके?
समान संदेशों और सेटिंग्स के साथ समान सीड पास करें। यह एक परीक्षण सहायक है और बाइट-समान आउटपुट की गारंटी नहीं देता है।
आपकी कुंजी बस एक फ़ॉर्म दूर है
एक खाता बनाएँ, कुंजी कॉपी करें, बेस URL बदलें। सेटअप यही है।