الأدلة الإرشادية
المستخدمون المحاكَون
تختبر المحادثة المكتوبة سلفًا مساعدك مقابل مستخدم لا يتفاعل أبدًا. أما المستخدم المحاكى فيتفاعل: نموذج يؤدي دور شخص تصفه كل حالة، ويكتب كل رسالة تالية بناءً على ما قاله مساعدك للتو، ويتوقف حين يتحقق هدفه أو يستسلم. ويسجّل Oloproof ما دار بينهما، ويقيسه كأي محادثة، ولا يدع البوابة تحسم عليه إلا بعد أن يجد الأشخاص المستخدم المحاكى واقعيًا. ويأخذك الدرس التعليمي عبر مكتب دعم يتحدث مع عملاء محاكَين.
الشخصيات
يحمل مُدخَل كل حالة شخصية: من هو المستخدم، وماذا يريد، وماذا يعرف.
{"id": "c25", "input": {"persona": {
"description": "A retired teacher who ordered a cookbook last week. Writes short, polite messages.",
"goal": "Have the order of a cookbook delivered to my new address.",
"facts": {"order": "B-35", "postcode": "10275"}
}}}- الحقلان description وgoal مطلوبان. اكتب هدفًا يستطيع القارئ أن يتحقق منه من المحادثة وحدها: "أن يبدأ المتجر استردادًا للمبلغ" يمكن الحسم فيه مما يقوله المساعد، أما "الحصول على استرداد للمبلغ" فلا، لأن المال يصل لاحقًا.
- facts هي ما يعرفه المستخدم ولا يشاركه إلا حين تحتاج إليه المحادثة، مثل رقم طلب. ولا يرى مساعدك الشخصية أبدًا.
- opening هي أول رسالة للمستخدم، حرفًا بحرف، حين يجب أن يُستقبل كل نظام بالطريقة نفسها.
- max_turns يحدّ رسائل هذا المستخدم، ويتقدم على قيمة المشروع.
الشخصية من محتوى مجموعة البيانات: تغطيها بصمة مجموعة الاختبار، وهي نفسها لكل نظام يُقاس مقابلها.
إعلان المستخدم المحاكى
يُعلَن النموذج الذي يؤدي كل الشخصيات على النظام، مثل الحَكَم:
system:
name: support-desk
callable: desk:talk
simulated_user:
provider: openai_compatible
base_url: http://localhost:11434/v1
model: qwen2.5vl
max_turns: 6كل حقل موجود في مرجع التهيئة. والنموذج المحلي لا يكلّف شيئًا؛ أما المستضاف فيُحاسَب على كل رسالة يكتبها، وتُسجَّل رموزه (tokens) بجانب المحادثة ولا تُحسب أبدًا من تكلفة نظامك.
نظامك يحتفظ بحلقته
لا يقود Oloproof المحادثة. فنظامك يطلب أول رسالة من المستخدم، ويجيب عنها بالطريقة التي يجيب بها المستخدمين عادةً، ويعيد كل إجابة ليحصل على الرسالة التالية:
from oloproof import current_case
def talk(case):
user = current_case().simulated_user()
message = user.opening()
while message is not None:
message = user.reply(my_assistant.answer(message))
return {}يعيد reply القيمة None حين يتوقف المستخدم. ويمكن لنظامك أن ينهي المحادثة بنفسه بـ user.end(answer)، كما يفعل التحويل إلى شخص؛ والنظام المكتوب بـ async def ينتظر aopening() وareply(answer) وaend(answer). ويسجّل Oloproof ما مرّ عبر المقبض بوصفه conversation/v1 وكيف انتهت المحادثة بوصفه simulated_user/v1، ويرفض أيًّا منهما إن جاء من نظامك (مرجع SDK).
كيف تنتهي المحادثة
بعد كل إجابة من نظامك، يُسأل نموذج المستخدم المحاكى، سؤالًا منفصلًا عند درجة الحرارة 0 ودون تمثيل الدور المحيط به، هل حققت تلك الإجابة الهدف. فإن حققته، انتهت المحادثة بـ goal_reached. وإلا كتب المستخدم رسالته التالية، أو غادر (user_left)، إلى أن يرسل max_turns رسالة (turn_limit). والنظام الذي يتوقف دون أن يجيب عن الرسالة الأخيرة يترك المحادثة unanswered ومبتورة.
والسؤال المنفصل هو ما يجعل نموذجًا محليًا صغيرًا صالحًا للاستعمال: فأداء دور شخصية والحسم في تحقق الهدف مهمتان مختلفتان، والنموذج الذي يُطلب منه فعل الأمرين في رد واحد يميل إلى مواصلة الدردشة بعد أن يتحقق هدفه، أو إلى القول إنه مغادر وهو راضٍ.
قياس المحادثات
المحادثة هي الوحدة: كل مُقيِّم يعطي حكمًا واحدًا لكل محادثة، لذا تنطبق كل فترة ومقارنة وبوابة دون تغيير.
| النوع | ينجح حين |
|---|---|
| conversation_completed | يجيب نظامك عن كل رسالة |
| goal_reached | يجد فحص الهدف أن الهدف تحقق |
| turns_to_goal | يحدث ذلك في غضون max_turns من رسائل المستخدم |
| every_turn | تستوفي كل إجابة الشروط المعلنة: كلمات يجب أن تحتويها أو ألا تحتويها، ونمط، وطول |
| conversation_judge | يُنجحها حَكَم نموذجي للنص الكامل للمحادثة، بعد التحقق منه مقابل وسوم الأشخاص |
تلتقي التكرارات (replicates:) مستخدمين ببذور مختلفة، لذا تصبح الشخصية الواحدة عدة محادثات. ونسخة المستخدم المحاكى (نموذجه وموجِّهه ودرجة حرارته وميزانيته) جزء من مفتاح التخزين المؤقت لكل تنفيذ، لذا فإن مستخدمًا محاكى مختلفًا يقيس نظامك من جديد.
التحقق من المستخدم المحاكى
لا تمتد نتيجة مبنية على مستخدمين محاكَين إلى المستخدمين الحقيقيين إلا بقدر ما يشبههم المستخدم المحاكى. لذا فإن كل قاعدة على تشغيل كان مستخدموه محاكَين تكون INSUFFICIENT_EVIDENCE مع simulated_user_not_validated إلى أن يجد الأشخاص محادثاته واقعية:
oloproof labels export RUN_ID --criterion simulated_user_realistic --sample 30 --local --out realism.csv
oloproof labels import realism.csv
oloproof simulated-users RUN_IDتعرض الورقة كل محادثة مسحوبة مع الشخصية وكيف انتهت. ويعلّم الشخص passed بنعم حين يكون بوسع عميل حقيقي أن يكتب جانب المستخدم وأن ينهي المحادثة هناك، بتلك الطريقة، وهذا يغطي فحص الهدف أيضًا: فالمحادثة التي انتهت بـ goal_reached والهدف لم يتحقق ليست واقعية. ويُتحقَّق من المستخدم المحاكى على مجموعة الاختبار حين يبلغ الحد الأدنى الدقيق لنسبة المحادثات الواقعية قيمة minimum_simulated_user_realism في release.yaml. وتُعدّ المحادثة المسحوبة التي تُركت بلا وسم غير واقعية، ولا تكون المحادثة واقعية إلا حين يقول ذلك كل من وضع لها وسمًا.
ويغطي التحقق الواحد نسخة واحدة لمستخدم محاكى على مجموعة اختبار واحدة، أيًّا كان النظام الذي تحدث معه، لذا يُحسم المرشَّح الذي يُقاس مقابل الشخصيات نفسها بوسوم خط الأساس. والنموذج الجديد، أو إعداد الموجِّه الجديد، أو مجموعة الشخصيات الجديدة، يحتاج إلى وسوم جديدة. وضبط require_validated_evaluators: false يحسم على المستخدمين المحاكَين دونها، عن علم.
المحاكى بجانب الحقيقي
يضع oloproof simulated-users RUN_ID --real REAL_RUN_ID شكل المحادثات المحاكاة بجانب تشغيل لمحادثات حقيقية (محادثات مسجَّلة يُعاد تشغيلها بوصفها conversation/v1): كم رسالة أرسل المستخدم، وكم كان طولها، وكم كان طول الإجابات، وكم مرة قُطعت محادثة قبل أوانها، وكيف انتهت المحادثات المحاكاة. وهو وصفي ولا يحسم أبدًا: فمقارنة عيّنتين غير مقترنتين تحتاج إلى طريقة لم يعتمدها Oloproof. اقرأه لما تفوّته وسوم الواقعية، مثل مستخدمين محاكَين يكتبون في جملة واحدة مرتبة ما يوزّعه الحقيقيون على ثلاث.
غير مدعوم
- الحسم على مستخدمين محاكَين في مساحة عمل مستضافة: تبقى قراراتها محجوبة إلى أن يستطيع طابور المراجعة فيها أن يسحب وسوم الواقعية ويختمها بنفسه.
- بوابة على كيفية مقارنة المحادثات المحاكاة بالحقيقية.
- المعدلات على مستوى الدور داخل محادثة، وإعادة تشغيل محادثة ابتداءً من دور.
- المستخدمون المحاكَون لنظام HTTP: فالكود الخاص بنظامك هو الذي يشغّل الحلقة، لذا يجب أن يكون نظامًا قابلًا للاستدعاء.