Skip to content

الوضع الفردي

شغّل خط تعليق توضيحي كاملًا بمفردك في Potato، حيث يعمل خبير واحد ونموذج لغوي كبير عبر آلة حالات من المراحل تغطي صياغة الموجّه والتعليق التوضيحي المتوازي وحل الخلافات والتحقق النهائي.

جديد في الإصدار v2.3.0

تتطلب مشاريع التعليق التوضيحي التقليدية معلّقين متعددين وحساب الاتفاق بين المعلّقين وجولات تحكيم وعبئًا كبيرًا من التنسيق. بالنسبة لكثير من فرق البحث، يكون هذا هو العائق الرئيسي. ليست المشكلة في واجهة التعليق التوضيحي، بل في الجوانب اللوجستية لتوظيف فريق وتدريبه وإدارته.

يستبدل الوضع الفردي نموذج المعلّقين المتعددين بخبير بشري واحد يتعاون مع نموذج لغوي كبير (LLM). يقدم الإنسان تصنيفات عالية الجودة على مجموعة فرعية صغيرة مختارة بعناية. يتعلم النموذج من تلك التصنيفات ويقترح تصنيفات للباقي، ولا يراجع الإنسان إلا الحالات التي يكون فيها النموذج غير متأكد أو يُرجَّح أنه مخطئ. تتولى آلة حالات من المراحل تنظيم ذلك، فتتقدم وفق العتبات التي تحددها وتعود إلى الموجّه كلما تبيّن أن الموجّه نفسه هو مصدر الخطأ.

في الاختبارات المعيارية الداخلية، حقق الوضع الفردي اتفاقًا يزيد على 95% مع خطوط المعالجة الكاملة متعددة المعلّقين، مع الحاجة إلى 10-15% فقط من إجمالي التصنيفات البشرية.

سير العمل

الوضع الفردي آلة حالات. يتتبع SoloPhaseController المرحلة التي يوجد فيها المشروع والمراحل التي يُسمح له بالانتقال إليها بعدها، لذا فالمسار ليس خطًا مستقيمًا. يمكن لعدة مراحل أن تعود إلى الوراء عندما يظل الموجّه بحاجة إلى عمل.

الإعداد

وصف المهمة والبيانات. يطلب Generate prompt من نموذج مراجعة (revision_models) أن يكتب الموجّه الأول انطلاقًا من الوصف ومخططات التعليق التوضيحي. وإن لم يوجد نموذج مراجعة، أو لم يُعِد شيئًا، يصير الوصف نفسه الموجّه الأول ويذكر سجل الموجّهات ذلك. والمرحلة الوحيدة التي يمكن الانتقال إليها من الإعداد هي مراجعة الموجّه.

مراجعة الموجّه

يصوغ Potato موجّهًا انطلاقًا من وصف المهمة وتقوم أنت بتحريره. تعود عدة مراحل لاحقة إلى هنا عندما يتبيّن أن الموجّه هو المشكلة، مما يجعل هذه المرحلة محور سير العمل كله وليست خطوة تُنفَّذ مرة واحدة.

من هنا إما أن يمر المشروع بحلقة الحالات الحدية أو ينتقل مباشرة إلى التعليق التوضيحي المتوازي.

توليد الحالات الحدية، ثم تصنيف الحالات الحدية

يولّد النموذج أمثلة حدية وتقوم أنت بتصنيفها. يمكن أن يعود التصنيف إلى مراجعة الموجّه إذا أظهرت الأمثلة أن الموجّه خاطئ.

التحقق من الموجّه

تُقارَن تصنيفاتك للحالات الحدية بما ينتجه الموجّه. إما أن يتقدم المشروع إلى التعليق التوضيحي المتوازي أو يعود إلى مراجعة الموجّه.

التعليق التوضيحي المتوازي

تعلّق أنت والنموذج على البيانات نفسها في الوقت نفسه. هذه هي المرحلة التي تنتج رقم الاتفاق الذي يعتمد عليه كل ما بعده.

حل الخلافات

حيث تختلف أنت والنموذج، تحسم أنت الأمر. يعود المشروع إلى التعليق التوضيحي المتوازي، أو إلى مراجعة الموجّه عندما تشير الخلافات إلى الموجّه لا إلى عناصر بعينها.

التعليق التوضيحي النشط

مرحلة التعليق التوضيحي الرئيسية، وتُختار فيها العناصر من خلال المجمّعات الموزونة في instance_selection. من هنا يمكن أن ينتقل Potato إلى المراجعة الدورية أو مراجعة القواعد أو إلى التصنيف الذاتي.

المراجعة الدورية

تصنيفات النموذج منخفضة الثقة، تُعرض كل thresholds.periodic_review_interval عنصرًا. يعود المشروع إلى التعليق التوضيحي النشط، أو ينتقل إلى مراجعة القواعد، أو يرجع إلى مراجعة الموجّه.

مراجعة القواعد

قواعد الحالات الحدية التي استخرجها Potato من تصنيفك، معروضة للموافقة عليها. يعود المشروع إلى التعليق التوضيحي النشط أو إلى مراجعة الموجّه.

التصنيف الذاتي

يصنّف النموذج ما تبقى. مخرجه الوحيد هو التحقق النهائي.

التحقق النهائي

تفحص عينة من التصنيفات التي وضعها النموذج وحده. تجاوز thresholds.end_human_annotation_agreement يُكمل المشروع، والإخفاق فيه يعيده إلى التعليق التوضيحي النشط.

مكتمل

حالة نهائية.


الإعداد

البدء السريع

إعداد أدنى للوضع الفردي:

yaml
annotation_task_name: "Sentiment Classification"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
solo_mode:
  enabled: true
 
  # The models that label. A list: the first is used, the rest are fallbacks.
  labeling_models:
    - endpoint_type: openai
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
 
  # When the project may stop asking the human
  thresholds:
    end_human_annotation_agreement: 0.92
    minimum_validation_sample: 50
    confidence_low: 0.5
 
  state_dir: "solo_state_sentiment"
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment of this text?"
    labels:
      - Positive
      - Neutral
      - Negative
 
output_annotation_dir: "output/"
export_annotation_format: "jsonl"

مرجع الإعداد الكامل

yaml
solo_mode:
  enabled: true
 
  # Models that label, and models that revise. Both are lists; revision_models
  # defaults to labeling_models when omitted.
  labeling_models:
    - endpoint_type: openai       # openai, anthropic, ollama, vllm, ...
      model: "gpt-4o"
      api_key: ${OPENAI_API_KEY}
      temperature: 0.1
      max_tokens: 1024
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
      max_tokens: 1024
 
  # Sentence embeddings, used for diversity and neighbour lookups
  embedding:
    model_name: "all-MiniLM-L6-v2"
 
  # How the LLM's uncertainty is measured
  uncertainty:
    strategy: direct_confidence
    sampling_diversity:
      num_samples: 5
      temperature: 1.0
 
  # When the human can stop, and what counts as agreement per scheme type
  thresholds:
    end_human_annotation_agreement: 0.90
    minimum_validation_sample: 50
    confidence_low: 0.5
    confidence_high: 0.8
    periodic_review_interval: 100
    likert_tolerance: 1
    multiselect_jaccard_threshold: 0.5
    textbox_embedding_threshold: 0.7
    span_overlap_threshold: 0.5
 
  # Weights for the pools that choose what the human sees next
  instance_selection:
    low_confidence_weight: 0.3
    disagreement_weight: 0.1
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200
 
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3               # instances, not a fraction
    vote_threshold: 0.5
 
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2
 
  confusion_analysis:
    enabled: true
    min_instances_for_pattern: 3
    max_patterns: 20
    auto_suggest_guidelines: false
 
  prompt_optimization:
    enabled: true
    find_smallest_model: true
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7
    length_weight: 0.2
 
  # Send cheap cases to a small model and hard ones to a larger one
  confidence_routing:
    enabled: false
    tiers:
      - name: cheap
        confidence_threshold: 0.8
        model:
          endpoint_type: ollama
          model: "llama3.2"
 
  refinement_loop:
    enabled: true
    trigger_interval: 50
    min_improvement: 0.02
    max_cycles: 5
    patience: 2
 
  state_dir: "solo_state_sentiment"

القدرات الرئيسية

تحليل الالتباس

بعد كل جولة تصنيف، يبني Potato مصفوفة التباس بين تصنيفات الإنسان وتصنيفات النموذج. تعرض لوحة تحكم المسؤول:

  • الدقة والاستدعاء ومقياس F1 لكل فئة من منظور النموذج
  • أكثر أزواج الالتباس شيوعًا (مثل: "neutral مصنف خطأً على أنه positive: 23 عنصرًا")
  • أمثلة على العناصر لكل زوج التباس
  • مخططات اتجاه تُظهر التحسن عبر جولات التحسين

لا يملك الوضع الفردي واجهة سطر أوامر. فهو يعمل داخل الخادم ويُدار من صفحاته الخاصة ضمن النسخة قيد التشغيل، لذا يُقرأ التحليل من لوحة التحكم لا من CLI. هذا مثال على ما يعرضه:

text
Confusion Analysis (Round 2)
============================
Overall Accuracy: 0.87 (target: 0.92)

Top Confusion Pairs:
  neutral -> positive:  23 instances (15.3%)
  negative -> neutral:  11 instances (7.3%)
  positive -> neutral:   5 instances (3.3%)

Per-Class Performance:
  Positive:  P=0.91  R=0.94  F1=0.92
  Neutral:   P=0.78  R=0.71  F1=0.74
  Negative:  P=0.93  R=0.88  F1=0.90

حلقة التحسين الآلية

تتنقل حلقة التحسين بين تصنيف النموذج وتحليل الالتباس وتحديث الإرشادات. في كل تكرار:

  1. يصنّف النموذج مجموعة البيانات كاملة وفق الإرشادات الحالية
  2. يقارن Potato النتائج بجميع التصنيفات البشرية المتاحة
  3. إذا كانت الدقة دون العتبة، يُجرى تحليل الالتباس
  4. يقترح النموذج تعديلات على الإرشادات بناءً على أنماط الأخطاء
  5. يراجع الإنسان التعديلات ويوافق عليها
  6. تتكرر الدورة (حتى max_cycles)
yaml
solo_mode:
  revision_models:
    - endpoint_type: anthropic
      model: "claude-sonnet-4-20250514"
      api_key: ${ANTHROPIC_API_KEY}
 
  refinement_loop:
    enabled: true
    trigger_interval: 50           # run a cycle every 50 new human labels
    min_improvement: 0.02          # stop if a cycle gains less than 2%
    max_cycles: 5
    patience: 2                    # cycles below min_improvement before stopping

دوال التصنيف (مستوحاة من ALCHEmist)

يولّد Potato دوال تصنيف خفيفة من الأنماط الملحوظة في التعليقات التوضيحية البشرية. هذه ليست استدعاءات للنموذج اللغوي، بل قواعد سريعة وحتمية.

أمثلة على دوال تصنيف مولَّدة:

python
# Auto-generated labeling function 1
# Precision: 0.96, Coverage: 0.08
def lf_strong_positive_words(text):
    positive = {"excellent", "amazing", "fantastic", "outstanding", "perfect"}
    if any(w in text.lower() for w in positive):
        if not any(neg in text.lower() for neg in {"not", "never", "no"}):
            return "Positive"
    return None  # abstain
 
# Auto-generated labeling function 2
# Precision: 0.93, Coverage: 0.05
def lf_explicit_negative(text):
    negative = {"terrible", "awful", "horrible", "worst", "disgusting"}
    if any(w in text.lower() for w in negative):
        return "Negative"
    return None

اضبط سلوك دوال التصنيف:

yaml
solo_mode:
  labeling_functions:
    enabled: true
    auto_extract: true
    max_functions: 50
    min_confidence: 0.85
    min_coverage: 3          # minimum instances a function must cover
    vote_threshold: 0.5      # agreement needed when functions disagree

مستكشف الخلافات

يعرض مستكشف الخلافات العناصر التي تتعارض فيها إشارات مختلفة. لكل عنصر، يرى المعلّق:

  • التصنيف الذي تنبأ به النموذج ودرجة ثقته
  • أصوات دوال التصنيف (إن وُجدت)
  • أقرب الجيران المصنَّفين في فضاء التضمينات
  • النص أو المحتوى الخام

هذا هو نشاط التعليق التوضيحي الأعلى قيمة، إذ يحسم كل تصنيف غموضًا حقيقيًا.

yaml
solo_mode:
  # Disagreement is a phase, not a configurable block. What you control is how
  # heavily disagreement weighs when Potato picks the next instance.
  instance_selection:
    disagreement_weight: 0.3
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.1
    random_weight: 0.1

التصعيد المتتالي حسب الثقة

بعد أن يصنّف النموذج الجزء الأكبر من مجموعة البيانات، يرتّب Potato جميع العناصر التي صنّفها النموذج حسب الثقة ويصعّد الأقل ثقة منها إلى الإنسان. يستمر ذلك على دفعات حتى تستقر الجودة.

yaml
solo_mode:
  thresholds:
    confidence_low: 0.5           # below this, the human sees it
    confidence_high: 0.8          # above this, the LLM's label stands
    periodic_review_interval: 100 # surface low-confidence labels this often
 
  batches:
    llm_labeling_batch: 50
    max_parallel_labels: 200

نماذج القرار

بدءًا من Potato 2.11، يمكن لنموذج قرار أن يصنّف في الوضع الفردي. يجيب نموذج القرار عن سؤال ذي اختيارات ثابتة في تمريرة أمامية واحدة ويعيد احتمالًا لكل خيار، مما يجعله أسرع وأرخص من النموذج اللغوي الكبير في التصنيف، لكنه لا يستطيع كتابة الموجّهات أو القواعد أو التفسيرات. ويصل إليه نوعان من نقاط النهاية. يشغّل laya نموذج Laya داخل عملية Potato على المعالج المركزي (CPU) ويحتاج إلى pip install 'potato-annotation[decision]'، ويتصل systemone بأي خادم يوفّر واجهة /v1/systemone، مثل Jev المستضاف أو OpenJev.

المكان المعتاد لنموذج القرار هو المستوى الأول من التوجيه حسب الثقة، حيث يصنّف كل شيء، ولا ينتقل إلى النموذج اللغوي الكبير إلا العناصر التي لا يكون متأكدًا منها:

yaml
solo_mode:
  labeling_models:
    - endpoint_type: laya
      model: convaiinnovations/laya
  revision_models:                     # required: decision models cannot write text
    - endpoint_type: vllm
      model: google/gemma-4-12B-it-qat-w4a16-ct
      base_url: http://localhost:8001/v1
  confidence_routing:
    enabled: true
    tiers:
      - name: laya
        confidence_threshold: 0.9
        model:
          endpoint_type: laya
          model: convaiinnovations/laya
          device: cpu
      - name: llm
        confidence_threshold: 0.5
        model:
          endpoint_type: vllm
          model: google/gemma-4-12B-it-qat-w4a16-ct
          base_url: http://localhost:8001/v1

يجب أن يكون revision_models نموذجًا توليديًا. وقيمته الافتراضية هي labeling_models، لذا يجب على الدراسة التي يكون نموذجها الوحيد نموذج قرار أن تضبطه. تصنّف نماذج القرار مخططات radio وselect وlikert وslider، ويرفض الوضع الفردي البدء إذا ضُبط أحدها لتصنيف مخططات multiselect أو الامتدادات النصية (span) أو النص الحر. وعلى مقاييس التقدير، بالكاد تتبع درجات Laya المقياس، فافحص بضعة عناصر باستخدام Try on 10 items قبل توجيه مخطط likert أو slider عبره. والثقة هي الاحتمال الذي أعطاه النموذج للخيار الذي اختاره، وتختلف معايرتها باختلاف المجال، لذا اضبط عتبة المستوى من بياناتك أنت. ويقدّم config-decision-model.yaml نسخة قابلة للتشغيل.

الاقتراحات العمياء

افتراضيًا تعرض صفحة التعليق التوضيحي تصنيف النموذج اللغوي الكبير وثقته وزر قبول قبل أن تجيب، وهذا سريع، لكنه يشدّ إجابتك نحو الاقتراح أيضًا. يسلّم الوضع الفردي بقية المدوّنة إلى النموذج اللغوي الكبير حين يتجاوز اتفاقك معه end_human_annotation_agreement، لذا فإن الاتفاق المقيس على تصنيفات أعطيتها بعد رؤية الاقتراح قد ينهي دورك في العمل قبل الأوان. وبدءًا من Potato 2.11، يتحكم solo_mode.suggestions في وقت ظهور الاقتراح:

yaml
solo_mode:
  suggestions:
    display: blind_sample      # before_label | after_label | blind_sample
    blind_rate: 0.2
    minimum_blind_sample: 20

يخفي blind_sample الاقتراح على نسبة blind_rate من العناصر، ولا يعرضه after_label أولًا أبدًا. وبمجرد وجود minimum_blind_sample من المقارنات العمياء، يقارن التسليم الاتفاق الأعمى بـ end_human_annotation_agreement. وتعرض بطاقة Blind Agreement في لوحة التحكم الاتفاق الأعمى والاتفاق مع العرض جنبًا إلى جنب، والفجوة الموجبة الكبيرة تعني أنك تتفق مع النموذج اللغوي الكبير أكثر حين تستطيع رؤية إجابته.

ويجعل Potato 2.11 أيضًا الوضع الفردي يفحص سقف الإنفاق الخاص بالمشروع (ai_budget.cap_usd) قبل بدء التصنيف وقبل كل دفعة، ويضيف ذاكرة تخزين مؤقت للردود (solo_mode.cache)، ومجموعة ذهبية مع تجارب للموجّهات (solo_mode.gold)، وفحصًا لحساسية الموجّه، ولجنة تحكيم للتصنيف (solo_mode.jury)، وترتيبًا وفق منهج متدرّج (solo_mode.instance_selection.curriculum). وتوثّق صفحة Solo Mode Advanced Features كلًا منها. وحين يكون محرّك الوسم مفعّلًا، يكتب الوضع الفردي أيضًا تنبؤات نماذجه في مخزن المحرّك، فيستطيع المحرّك معايرتها والإشهاد عليها.

ترتيب أولويات العناصر متعدد الإشارات

في جميع المراحل التي تتضمن تصنيفًا بشريًا، يستخدم Potato نظام مجمّعات موزونة لاختيار العناصر الأكثر إفادة. تغذي سبعة مجمّعات قائمة أولويات موحدة:

yaml
solo_mode:
  instance_selection:
    low_confidence_weight: 0.3
    diversity_weight: 0.2
    llm_predicted_weight: 0.3
    disagreement_weight: 0.1
    random_weight: 0.1
    cartography_weight: 0.0
    edge_case_rule_weight: 0.0
  • low_confidence: تنخفض ثقة النموذج عن thresholds.confidence_low
  • diversity: بعيد، في فضاء التضمينات، عما صنّفته بالفعل
  • llm_predicted: لدى النموذج تصنيف لم يُتحقق منه بعد
  • disagreement: الإشارات المتاحة للعنصر لا تتفق
  • random: سحب عشوائي صغير، لإبقاء التقدير نزيهًا واكتشاف النقاط العمياء
  • cartography: إشارة من ديناميكيات التدريب، معطّلة افتراضيًا
  • edge_case_rule: يطابق قاعدة مستخرجة من تصنيفك السابق، معطّل افتراضيًا

توليد الحالات الحدية

يستخدم Potato النموذج لتوليد أمثلة اصطناعية تستهدف نقاط ضعف معروفة:

yaml
solo_mode:
  edge_case_rules:
    enabled: true
    auto_extract_on_labeling: true   # derive rules as you label
    confidence_threshold: 0.75
    min_rules_for_clustering: 10
    target_cluster_size: 15
    reannotation_enabled: true       # revisit items a new rule contradicts
    reannotation_confidence_threshold: 0.60
    max_reannotations_per_instance: 2

يولّد النموذج أمثلة ملتبسة بين أزواج التصنيفات المحددة. يصنّفها الإنسان، وتُضاف هذه التصنيفات إلى سياق الأمثلة القليلة (few-shot) في جولات تصنيف النموذج اللاحقة.

تحسين الموجّهات (مستوحى من DSPy)

يُجري Potato تحسينًا آليًا للموجّهات للعثور على أفضل صيغة تعليمات للنموذج، ويبحث عن أصغر نموذج يظل محافظًا على الدقة المستهدفة:

yaml
solo_mode:
  prompt_optimization:
    enabled: true
    find_smallest_model: true          # prefer the cheapest model that holds up
    target_accuracy: 0.85
    optimization_interval_seconds: 300
    accuracy_weight: 0.7               # how the candidates are scored
    length_weight: 0.2

متابعة التقدم

تعرض لوحة تحكم المسؤول تقدم الوضع الفردي في الوقت الفعلي:

  • المرحلة الحالية والتقدم داخل كل مرحلة
  • التصنيفات البشرية المكتملة مقابل الميزانية الإجمالية
  • دقة النموذج بمرور الوقت (لكل جولة)
  • تغطية دوال التصنيف ودقتها
  • مدرج تكراري لتوزيع الثقة
  • الوقت المقدّر حتى الاكتمال

لا توجد وحدة potato.solo ولا واجهة سطر أوامر للوضع الفردي. سير العمل كله يقع خلف مسارات الخادم نفسه، لذا يُقرأ التقدم من لوحة التحكم. وهي تعرض:

text
Solo Mode Status
================
Current Phase: 6 (Active Labeling) - Batch 3/10
Human Labels: 142 / ~300 estimated total
LLM Accuracy: 0.89 (target: 0.92)
LF Coverage: 0.23 (labeling functions cover 23% of data)
Dataset Size: 10,000 instances
  - Human labeled: 142
  - LF labeled: 2,300
  - LLM labeled: 7,558
  - Unlabeled: 0

متى تستخدم الوضع الفردي مقابل التعليق التوضيحي التقليدي متعدد المعلّقين

استخدم الوضع الفردي عندما:

  • لديك خبير في المجال يمكنه تقديم تصنيفات عالية الجودة
  • الميزانية أو الجوانب اللوجستية تمنع توظيف معلّقين متعددين
  • المهمة لها فئات واضحة ومحددة جيدًا
  • تحتاج إلى تصنيف مجموعة بيانات كبيرة (1,000+ عنصر)
  • السرعة أهم من قياس الاتفاق بين المعلّقين

استخدم التعليق التوضيحي التقليدي متعدد المعلّقين عندما:

  • تحتاج إلى إحصائيات الاتفاق بين المعلّقين للنشر
  • المهمة ذاتية للغاية (مثل الإساءة أو الفكاهة)
  • تحتاج إلى دراسة أنماط الخلاف بين المعلّقين
  • المتطلبات التنظيمية تفرض معلّقين مستقلين متعددين
  • فضاء التصنيفات معقد أو متغير (إرشادات التعليق التوضيحي لا تزال قيد التطوير)

النهج الهجين: استخدم الوضع الفردي للتصنيف المبدئي بالجملة، ثم كلّف معلّقًا ثانيًا بعينة عشوائية نسبتها 10-20% لحساب إحصائيات الاتفاق. يجمع هذا بين كفاءة الوضع الفردي وضمان الجودة الذي يوفره التحقق متعدد المعلّقين.

ليس للوضع الفردي كتلة تحقق خاصة به. شغّل المعلّق الثاني كجولة عادية في Potato على التصنيفات المصدَّرة، واحسب الاتفاق بين تصنيفاته وتصنيفات الوضع الفردي كما تفعل مع أي معلّقَين اثنين. (يملك icl_labeling كتلة verification، لكنها تخص التصنيف داخل السياق، لا الوضع الفردي.)


قراءة إضافية

لمزيد من تفاصيل التنفيذ، راجع الوثائق المصدرية.