لموجّه النظام وظيفتان
تأتي معظم المخرجات الضعيفة من توجيه نظام يحاول أن يكون رواية. أعطه وظيفتين فقط: قل من يتحدث وقل كيف يجب أن تبدو الإجابة. كل شيء آخر ينتمي إلى المحادثة، حيث يمكن أن يتغير دورًا بدور.
إليك موجّه شخصية يعمل لأن كل سطر قابل للتحقق. تم تسمية الصوت، وتم تحديد الطول، وقاعدة النهاية تمنح اللاعب نقطة ارتباط.
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.قارن ذلك بـ "أنت راوٍ مدهش، إبداعي، غامر لا يكسر الغمر أبدًا ويكتب دائمًا أفضل استجابة ممكنة." لا شيء فيه يمكن التحقق منه، لذا لا يملك النموذج شيئًا يعتمد عليه. الملموس يتفوق على المبالغة في كل مرة.
سيتبع النموذج غير المقيد premise مظلمًا أو صريحًا دون إضافة إخلاء مسؤولية، لذا فإن عبء التوجيه يقع عليك. أي شيء تتركه غير محدد، سيملاؤه النموذج بأكثر خيار نموذجي. إذا كنت تريد ضبط النفس في مشهد واحد والحرارة في المشهد التالي، قل ذلك في التعليمات، لا بالأمل.
ربطه باستخدام SDK الخاص بـ OpenAI
نقطة النهاية متوافقة مع OpenAI، لذا يعمل SDK الرسمي لـ Python بمجرد تغيير Base URL. معرف النموذج هو دائمًا 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)احتفظ برسالة النظام في البداية ودور المستخدم في النهاية. تكرار الشخصية في كل رسالة مستهلك للرموز؛ بسعر $0.25 لكل مليون رمز مدخل، الأمر رخيص، لكنه يزدحم نافذة 100,000 رمز في الجلسات الطويلة. انظر إلى البدء السريع إذا كنت تفضل تخطي SDK.
الشخصيات والنبرة: أظهر ولا تستخدم الصفات
الصفات روافع ضعيفة. "سخرية" ستعطيك ضيعة عين عامة. عينة من سطر واحد للصوت ستعطيك الصوت الفعلي. ضع تبادلًا قصيرًا في موجّه النظام، ثم أخبر النموذج بمطابقة إيقاعه، وليس نسخ كلماته.
- أعطه لسان حال. "ينهي التهديدات بسؤال" أكثر فائدة من "مخيف".
- أعطه رغبة. الشخصية التي تحتاج من اللاعب أن يغادر تبقى أكثر حدة من تلك التي "تكون وقحة فقط".
- أعطه قائمة محظورات. "لا تقل 'فجأة'، لا تستخدم كلمة 'ارتجاف'" يزيل العبارات الجاهزة التي يلجأ إليها النموذج أولاً.
- أعط طولًا. كلمات أو جمل، وليس "قصير". النماذج تقرأ "قصير" بكرامة كبيرة.
لتغييرات النبرة أثناء الجلسة، أضف تعليمات قصيرة جديدة على نمط النظام كآخر رسالة بدلاً من إعادة كتابة الأصلية. سطر مثل "من هنا، مارا خائفة وتتحدث بجمل متقطعة" يكون أكثر تأثيرًا عندما يكون حديثًا.
الحصول على JSON بالأمر
لا يوجد وضع JSON خاص لتشغيله، لذا تطلبه وتحقق منه. هذا أقل هشاشة مما يبدو إذا اتبعت عادات أربع: حدد المخطط الدقيق، منع النصوص وأقواس الكود، حافظ على درجة حرارة منخفضة، وحلل بشكل دفاعي مع إعادة محاولة تذكر الفشل.
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."))لاحظ ما تفعله الحلقة عند الفشل. إنها تغذي الإجابة الخاطئة وتقول ما كان خاطئًا، مما يصلح معظم الحالات في المحاولة الثانية.Enumerate القيم المسموح بها ("calm"|"angry"|"afraid") لأن السلاسل المفتاحة تنحرف. إذا كنت بحاجة إلى أن يُطلق النموذج شيئًا في تطبيقك بدلاً من وصفه، فإن استدعاءات الأدوات بتنسيق OpenAI مدعومة وهي أفضل تناسبًا من تحليل النثر.
عينات قليلة النماذج وتسلسلات التوقف
عندما يكون من الصعب وضع القاعدة في كلمات، أظهرها. تبادلان أو ثلاثة أمثلة موضوعة كرسائل user وassistant سابقة تعلم التنسيق والطول والنبرة بشكل أكثر موثوقية من فقرة وصفية. حافظ على العينات قصيرة ومختلفة عن بعضها البعض، وإلا فإن النموذج سيستنسخ بنية الأولى في كل إجابة.
تحل العينات أيضًا المشكلة المعاكسة: الإجابات الطويلة. إذا كانت إجابات العينة 60 كلمة، فإن الإجابات الحية تنحرف نحو 60 كلمة. اربط ذلك بـ max_tokens معقول كشبكة أمان، وليس كتحكم أساسي. الحد القاطع يقطع الجملة نصفًا؛ والعينة الجيدة تنهيها بأدب.
لمشاهد متعددة المتحدثين، قيمة stop تساوي تسمية المتحدث تمنع النموذج من التمثيل للاعب. اختر تسمية لا تظهر أبدًا في النثر العادي، واحذفها من السجل المخزن حتى لا تتسرب إلى الأدوار اللاحقة.
درجة الحرارة وtop_p: اختر رافعة واحدة
كلا الحقلين يعيدان تشكيل نفس التوزيع الاحتمالي، لذا فإن تحريكهما معًا يجعل من الصعب التمعن في النتائج. غيّر temperature أولاً واترك top_p قريبًا من 1 ما لم يكن لديك سبب.
| المهمة | درجة الحرارة | top_p | السبب |
|---|---|---|---|
| JSON المهيكل، التصنيف | 0.0 إلى 0.3 | 1.0 | تريد نفس الإجابة في كل تشغيل |
| الحوار، لعب الأدوار | 0.7 إلى 0.9 | 0.95 | التنوع دون سخافة |
| العصف الذهني، الخيال الواسع | 1.0 إلى 1.2 | 0.9 | مفردات أوسع؛ توقع بعض الإخفاقات |
هذه النطاقات هي نقاط بداية من الممارسة العامة، وليست ضمانات. اختبرها بعشر عينات من توجيهك الخاص واقرأها. أضف تسلسلات stop عندما تريد أن يتوقف النموذج عند علامة دور مثل \nPlayer: بدلاً من كتابة أسطر اللاعب نيابة عنه.
أنماط خاطئة للموجّه تهدر الرموز
- صرخة الحروف الكبيرة. "NEVER EVER FORGET" لا يرفع الامتثال. كرر القاعدة مرة واحدة، بوضوح، وانقلها إلى نهاية توجيه النظام.
- القواعد السلبية فقط. "لا تكن مكررًا" لا يعطي هدفًا. قل "اختلف افتتاحيات الجمل؛ لا تبدأ جملتين متتاليتين بنفس الكلمة".
- التناقضات. "كن مفصلاً للغاية" بالإضافة إلى "اجعله موجزاً" يجبرك على رمي العملة. اختر واحداً وأعط رقماً.
- الإطار الاعتذاري. تمهيد الطلب بـ "أعلم أن هذا جريء، لكن" يدعو إلى التردد. صِف المشهد كحقيقة عادية في الخيال.
- حشو التاريخ. لصق السجل الكامل كل دور سيصل في النهاية إلى حد 100,000 رمز وخطأ 400. اقتص الأدوار القديمة.
- الإخراج غير المحدود. نسيان
max_tokensيعني أن الافتراضي 2048 يقرر طول ردك، لا أنت.
يبقى حد واحد ثابتًا بغض النظر عن ما يقوله الموجّه: محتوى الجنس الذي ينطوي على قاصرين يتم حظره دائمًا بخطأ 403، بما في ذلك في الخيال وألعاب الأدوار، والخدمة مخصصة للبالغين فقط. لا تنفق الرموز في محاولة لصياغة حولها. أيضًا قاوم الرغبة في إضافة قاعدة جديدة بعد كل إجابة سيئة؛ فالموجه الذي ينمو سطرًا واحدًا في اليوم ينتهي به الأمر بالتناقض مع نفسه خلال أسبوع، لذا اقتلِ كلما أضفت.
حلقة لتحسين الموجّه
عامل موجّه النظام مثل الكود. احتفظ به في ملف، غيّر شيئاً واحداً في كل تشغيل، وقارن المخرجات جنباً إلى جنب. روتين قابل للتطبيق: اكتب خمسة مدخلات اختبار ثابتة، بما في ذلك عدواني واحد وغامض واحد؛ شغّل كل منها بدرجة الحرارة المختارة ثلاث مرات؛ لاحظ أي قاعدة فشلت أكثر؛ أصلح تلك القاعدة فقط؛ أعد التشغيل.
وازن التجارب. بافتراض أن توجيه نظام بحجم 300 رمز بالإضافة إلى إدخال بحجم 100 رمز وإجابة بحجم 250 رمز هو 400 رمز إدخال و250 رمز إخراج. تكلف 15 تجربة حوالي 15 x (400 x $0.25 + 250 x $1.00) / 1,000,000 = حوالي $0.005. الاختبار مجاني تقريبًا، لذا لا عذر للتخمين.
بمجرد أن يتحمل الموجّه، انقله إلى تطبيق حقيقي. يوضح bot tutorial كيفية الحفاظ على سجل منفصل لكل قناة، ويغطي content guide استخدام الخيال البالغ. تفاصيل التسعير موجودة في the pricing page.