سيناريوهات لعب الأدوار النصية باستخدام واجهة برمجة تطبيقات LLM
تعمل سيناريوهات لعب الأدوار النصية بشكل أفضل عندما يلعب النموذج دور الراوي مع القواعد، ويملك الكود الخاص بك حالة المشهد. تتناول هذه المقالة محركًا صغيرًا في Python: الموجّه، البث المتدفق، الذاكرة وتقدير التكلفة لجلسة طويلة.

نقاط رئيسية
- قسّم العمل: يكتب النموذج القصة، ويخزن الكود الحقائق والمخزون والقواعد.
- قم ببث الردود واحتفظ بكل دفق قصيرًا بما يكفي لتحرير فتحة التزامن بسرعة.
- تحتوي نافذة 100,000 رمز على جلسة طويلة جدًا، لكن التلخيصات تبقي التكلفة والانحراف منخفضين.
- جلسة مكونة من 200 دور بتكلفة 6,000 رمز إدخال و400 رمز إخراج لكل دور تكلف حوالي $0.22.
ابدأ بالسارد، لا بالقصة
غالباً ما يبدأ من يبنون سيناريوهات لعب الأدوار النصي بكتابة القصة. وهذا عكس الصحيح. السيناريو هو حلقة تكرار بين لاعب وسارد، ويحتاج السارد إلى وصف لوظيفته قبل أن يحتاج إلى قصة. يجب أن يعرف النموذج من الذي يتكلم، وما الذي قد يقرره نيابة عن اللاعب، وكم يجب أن تكون مدة الرد، وما الذي يجب ألا يفعله. يمكن تحسين الباقي بشكل عفوي.
يحتوي الموجّه الجيد لنظام سيناريو على أربعة أجزاء. أولاً يأتي الدور: مدير اللعبة، أو المقابِل، أو حاسوب السفينة، أو شخصية في مسرحية. ثانياً، قواعد العالم في بضع جمل واضحة: النبرة، والإعداد، وما يوجد وما لا يوجد. ثالثاً، قواعد التنسيق، لأن الكود الخاص بك قد يقوم بتحليلها: على سبيل المثال، "أنهِ كل رد بسطر واحد يبدأ بـ OPTIONS:". رابعاً، الحدود. واجهة برمجة التطبيقات النصية هنا مضبوطة للإجابة بدون رفض المحتوى للاستخدام القانوني للبالغين، مما يجعلها مناسبة للخيال الداكن، والأشرار ذوي الخطر الحقيقي، والقصص ذات الزوايا الأخلاقية الرمادية. لا يزال لديها خط أحمر واحد: لا محتوى جنسي يتضمن القاصرين. ضع هذه القاعدة في شروط الاستخدام الخاصة بك أيضاً، ونفّذ حد عمر في تطبيقك إذا كانت سيناريوهاتك موجهة للبالغين.
حافظ على طول الموجّه أقل من 600 كلمة تقريباً. قوائم القواعد الطويلة تُتبع نصفها فقط. إذا كنت بحاجة إلى العديد من القواعد، رتبها وأزل الثلث الأخير.
حلقة عمل مع البث المتدفق
يعد البث المتدفق أكثر أهمية في لعب الأدوار منه في معظم الاستخدامات. يريد اللاعب الذي يقرأ قصة ظهور الكلمات بينما يكتب النموذج، ويبدو أن ردًا بطول 300 رمز أبطأ مما هو عليه إذا وصل دفعة واحدة. تقوم واجهة برمجة التطبيقات بالبث المتدفق عبر أحداث المرسلة من الخادم مع stream: true، وتتعامل مكتبات OpenAI SDK مع التحليل. نقطة النهاية متوافقة مع OpenAI، ومعرف النموذج هو uncensored، والرابط الأساسي (Base 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.")الإعدادات مضبوطة للخيال. تعطي درجة حرارة (temperature) تتراوح بين 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 لكل مليون رمز مدخل و$0.50 لكل مليون رمز مخرج، مع رصيد مسبق الدفع ولا اشتراك. اعمل خلال جلسة طويلة. لنقل 200 دور، مع الطبقة المثبتة، والملخص والأدوار الأخيرة التي تضيف ما مجموعه متوسط 6,000 رمز مدخل لكل طلب، و400 رمز مخرج لكل رد (رد القصة بالإضافة إلى حصة صغيرة لمكالمات الحالة والملخص).
المدخلات: 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 رمز (token)، وعلى مدار الجلسة يكون متوسط المدخلات حوالي 28,000 رمز (token) لكل طلب. هذا يعني 200 × 28,000 = 5,600,000 رمز (token) مدخل، أو 5.6 × $0.15 = $0.84، بالإضافة إلى $0.04 نفسها للمخرجات، مما يجعل المجموع $0.88. لا يزال ذلك معقولاً، لكنه أربعة أضعاف الفاتورة دون أي تحسن في الجودة. للتطبيق الذي يحتوي على العديد من اللاعبين، يحدد هذا الفارق هامش الربح.
الطلبات الفاشلة أو المرفوضة لا تُحسب، والرصيد المسبق الدفع لا ينتهي، لذا فإن الرصيد غير المستخدم ينتظر ببساطة. شحن الرصيد يتراوح من $20 إلى $500 بمبالغ كاملة، مع مكافأة بنسبة 5% من $50، و10% من $100، و15% من $200. انظر إلى صفحة الأسعار للحصول على القائمة الكاملة للخيارات.
تشغيل العديد من اللاعبين في وقت واحد
المفتاح الكامل يسمح بـ 8 طلبات متزامنة و300 طلب في الدقيقة. يحتفظ الطلب المتدفق بمكانه حتى انتهاء البث، لذا مع 8 أماكن واستجابات تستغرق حوالي عشر ثوانٍ، يمكنك خدمة حوالي 48 دفق استجابة في الدقيقة، بافتراض عدم تشغيل أي شيء آخر. هذه قاعدة تقريبية وليست رقماً مقاساً، لأن السرعة تختلف حسب الحمل وطول الاستجابة. الهدف هو التخطيط لذلك: إذا كانت لعبتك تحتوي على لاعبين متزامنين أكثر من الأماكن المتاحة، قم بترتيب الطلبات في خادمك وأظهر حالة "السارد يفكر" بدلاً من إطلاق كل شيء في آن واحد.
عند تجاوز حدّ، تُعيد الواجهة البرمجية استجابة HTTP 429. يكون الرمز rate_limited للطلبات في الدقيقة وconcurrency للطلبات المتزامنة الزائدة. يؤدي الحمل المؤقت أيضاً إلى 429، مع رأس Retry-After: 5. إعادة المحاولة البسيطة التي تنتظر هذه الخمس ثوانٍ كافية للعبة عادية. اقرأ رؤوس الاستجابة أيضاً: يُظهر X-RateLimit-Limit-Requests وX-RateLimit-Limit-Concurrency حدودك، بينما يُظهر X-Balance-USD الرصيد المتبقي، والذي يمكنك عرضه في شاشة الإدارة.
لا تشغّل استدعاء استخراج الحالة بالتوازي مع دور اللاعب التالي. انتظره، وإلا سيكتب السارد من حقائق قديمة. الحل الجيد هو بدء الاستخراج بمجرد انتهاء البث والسماح للاعب بالقراءة أثناء تشغيله.
ربط المشاهد بخط أنابيب الصور الخاص بك
هذا الموقع مخصص للأشخاص الذين يستخدمون نماذج النصوص حول سير عمل Stable Diffusion، والسيناريوهات تناسب ذلك جيداً. الواجهة البرمجية (API) نفسها للنصوص فقط: فهي لا تولّد صوراً أو صوتاً أو فيديو. لكن السارد يمكنه كتابة وصف مشهد يحوله خطك البرمجي إلى موجّه للصورة. أضف استدعاء JSON آخر بعد تغيير مشهد كبير، اطلب فيه وصفاً مرئياً قصيراً للإعداد مع الإضاءة والمزاج وزاوية الكاميرا، ثم مرّر هذا النص إلى أي أداة صور تستخدمها. يُظهر صفحة التوثيق شكل الطلب لاستدعاءات كتابة الموجّه، والمقال حول مولّد موجّهات SD يذهب إلى أعمق في هذا الجانب.
لتجربة المحرك، أنشئ حساباً واحصل على التجربة. يمكن للمستخدمين الجدد الحصول على $0.50 من رصيد تجريبي بنقرة واحدة على صفحة مفتاح API، ساري لمدة 7 أيام دون الحاجة إلى بطاقة. وفقاً للأرقام أعلاه، هذا يكفي لجلسة كاملة أو اثنتين. مفاتيح التجربة محدودة بـ 2 طلب متزامن و60 في الدقيقة، وهو ما يناسب الاختبار الفردي.
حافظ على الخيال في الخيال. السيناريوهات ذات الطابع البالغين استخدام قانوني عندما يكون الجميع بالغين؛ القاعدة الوحيدة التي لا تتغير هي عدم وجود محتوى جنسي يتضمن القاصرين. الموجّهات المرسلة إلى الـ API لا تُستخدم للتدريب.
المزيد من الأدلة
أسئلة وأجوبة
هل يمكن للنموذج أن يلعب أي شخصية أو نوع؟
إنه نموذج نصوص عام مضبوط لعدم رفض الطلبات القانونية للبالغين، لذا فإن الرعب والجريمة والدراما والكوميديا كلها مقبولة. الحد الصارم الوحيد هو المحتوى الجنسي الذي يتضمن القاصرين.
كم يمكن أن تكون مدة الجلسة قبل أن تمتلئ نافذة السياق؟
النافذة هي 100,000 رمز للموجّه بالإضافة إلى الإكمال. مع طبقة تلخيص، يمكنك تشغيلها لفترة أطول بكثير في الواقع، حيث يتم ضغط الأدوار القديمة.
هل يجب أن أقوم بالبث المتدفق للردود؟
نعم، لأي شيء يقرأه شخص مباشرة. يمنحك البث المتدفق معلومات الاستخدام في النهاية، ويشغل البث المتدفق أحد أماكن الطلبات المتزامنة حتى ينتهي.
هل هناك طريقة لجعل المشهد يتكرر بالضبط؟
مرر نفس البذرة مع رسائل وإعدادات متطابقة. إنها أداة اختبار ولا تضمن إخراجاً مطابقاً تماماً.
مفتاحك على بُعد نموذج واحد
أنشئ حساباً، وانسخ المفتاح، ثم غيّر URL الأساسي. هذه هي الإعدادات الكاملة.