Transformers documentation

كتابة قالب محادثة

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

كتابة قالب محادثة

قالب المحادثة هو قالب Jinja مخزَّن في خاصية ~PreTrainedTokenizer.chat_template لمُقسِّم الرموز. لغة Jinja هي لغة قوالب تتيح لك كتابة أكواد وبنية تشبه لغة بايثون.

{%- for message in messages %}
    {{- '<|' + message['role'] + '|>\n' }}
    {{- message['content'] + eos_token }}
{%- endfor %}
{%- if add_generation_prompt %}
    {{- '<|assistant|>\n' }}
{%- endif %}

إذا تأملت هذا لفترة من الوقت، ستدرك أنه يشبه بايثون إلى حد كبير، على الرغم من وجود بعض الصياغات الغريبة مثل {%-. يقوم القالب بالتكرار على قائمة من الرسائل، ولكل رسالة، يطبع الدور والمحتوى الخاص بها، متبوعًا برمز نهاية التسلسل. إذا كان add_generation_prompt=True، فإنه يضيف الترويسة الافتتاحية لرسالة المساعد إلى نهاية المحادثة.

حمّل القالب المكتوب كسلسلة نصية وقم بتعيينه إلى خاصية chat_template في المُرمِّز. بمجرد تعيينه، يُستخدم هذا القالب في كل مرة تستدعي فيها apply_chat_template(). كما يتم حفظه مع المُرمِّز في كل مرة يُستدعى فيها save_pretrained() أو push_to_hub(). يُحفظ القالب في ملف chat_template.jinja داخل مجلد المُرمِّز. يمكنك تحرير هذا الملف مباشرةً لتغيير القالب، وهو أمر أسهل غالبًا من التعامل مع سلسلة نصية للقالب. راجع تخزين قوالب المحادثة وتحميلها أدناه لمعرفة الأشكال الأخرى على القرص التي يدعمها Transformers.

نصائح لكتابة القوالب

أسهل طريقة لبدء كتابة قوالب Jinja هي الرجوع إلى القوالب الموجودة مسبقًا. استخدم print(tokenizer.chat_template) على أي نموذج محادثة لعرض القالب الذي يستخدمه. جرّب البدء بنماذج بسيطة لا تستدعي أي أدوات ولا تدعم RAG، لأن نماذج استخدام الأدوات قد تحتوي على قوالب معقدة للغاية. أخيرًا، اطّلع على توثيق Jinja لمزيد من التفاصيل حول التنسيق والنحو.

هناك بعض النصائح المحددة والمصاعق التي قد تواجهها عند كتابة قوالب المحادثة على وجه التحديد، وسيتناول هذا القسم بعضاً منها بتفصيل أكبر.

كتابة قوالب محادثة متعددة الوسائط

في قوالب متعددة الوسائط، يُضبط السمة chat_template على processor وليس على مُجزّئ الرموز. وغالبًا ما يكون المفتاح content لرسالة قائمة من قواميس المحتوى، بدلاً من مجرد سلسلة نصية واحدة. قد ترغب في التحقق من نوع كل عنصر محتوى في القائمة، والتعامل معه وفقًا لذلك.

بشكل عام، يجب ألا يصل القالب مباشرةً إلى بيانات الصور أو مقاطع الفيديو. عادةً ما يتولى المعالج (processor) هذه المهمة بعد اكتمال عرض القالب. بدلاً من ذلك، يجب أن يُصدر قالبك رمزًا خاصًا واحدًا مثل <|image|> أو <|video|> عند مواجهته محتوى صورة أو فيديو. سيقوم المعالج لاحقًا بتوسيع هذا الرمز الخاص الواحد إلى تسلسل من رموز الصور أو مقاطع الفيديو. تعتمد الرموز الدقيقة التي يجب إصدارها على النموذج الذي تعمل عليه. ننصح بشدة بتحميل معالج متعدد الوسائط (multimodal processor) موجود مسبقًا لرؤية كيفية تعامله مع البيانات.

يتعامل قالب المثال أدناه مع محتوى مختلط من الصور والنص.

{%- for message in messages %}
    {%- if loop.index0 == 0 %}
        {{- bos_token }}
    {%- endif %}
    {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' }}
    {%- if message['content'] is string %}
        {{- message['content'] }}
    {%- else %}
        {%- for content in message['content'] %}
            {%- if content['type'] == 'image' %}
                {{- '<|image|>' }}
            {%- elif content['type'] == 'text' %}
                {{- content['text'] }}
            {%- endif %}
        {%- endfor %}
    {%- endif %}
    {{- '<|eot_id|>' }}
{%- endfor %}
{%- if add_generation_prompt %}
    {{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
{%- endif %}

هذا القالب متعدد الوسائط مشابه جدًا للقالب الأبسط أعلاه، لكنه يتحقق من قوائم content، ويتكرر عليها لعرض رموز <|image|> حيثما كان ذلك ضروريًا. وهذا يتيح إدراج الصور “ضمن تدفق” نص المستخدم.

لا تعمل جميع النماذج بهذه الطريقة - فقد تنقل بعض النماذج جميع الصور إلى نهاية رسالة المستخدم، على سبيل المثال. يجب أن يتطابق قالب المحادثة دائمًا مع الصيغة التي تم تدريب النموذج عليها.

حذف المسافات الزائدة

يطبع Jinja أي مسافة بيضاء قبل كتلة نص أو بعدها. وقد يكون ذلك مشكلة في قوالب المحادثة، لأن إضافة مسافات بيضاء إضافية لم تكن موجودة أثناء تدريب النموذج قد تضر بالأداء. لإزالة المسافات البيضاء، أضف - إلى صياغة سطر Jinja. وهذا يتيح لك كتابة قالبك مع مسافات بادئة وتبديلات أسطر بأسلوب بايثون، دون أن تطبع مسافة بادئة عن طريق الخطأ في المخرجات المعروضة.

لا يستخدم قالب المثال أدناه -، مما يؤدي إلى طباعة مسافات زائدة في المخرجات.

{% for message in messages %}
    {{ message['role'] + message['content'] }}
{% endfor %}

نوصي بشدة باستخدام - للتأكد من طباعة المحتوى المقصود فقط.

{%- for message in messages %}
    {{- message['role'] + message['content'] }}
{%- endfor %}

متغيرات ودوال خاصة

المتغيرات الثابتة الوحيدة في القالب هي متغير messages والقيمة المنطقية add_generation_prompt. ومع ذلك، يمكنك الوصول إلى أي معاملات أخرى تُمرَّر إلى طريقة apply_chat_template().

يوفر هذا المرونة ويتيح دعم حالات الاستخدام التي قد لا تكون قد خطرت ببالنا أثناء تصميم المواصفة. المتغير الإضافي الأكثر شيوعًا هو tools، والذي يحتوي على قائمة بالأدوات بصيغة مخطط JSON. على الرغم من أنه يمكنك استخدام أي اسم متغير تريده، فإننا نوصي بشدة بالالتزام بالمواصفة واستخدام tools لهذا الغرض. وهذا يجعل القوالب أكثر توافقًا مع واجهة برمجة التطبيقات القياسية.

لديك أيضًا إمكانية الوصول إلى أي رموز (tokens) موجودة في tokenizer.special_tokens_map، والتي غالبًا ما تتضمن رموزًا خاصة مثل bos_token وeos_token. يمكنك الوصول إليها مباشرةً بالاسم، كما في {{- bos_token }}.

تتوفر لديك دالتان قابلتان للاستدعاء. لاستدعائهما، استخدم {{- function_name(argument) }}.

  • raise_exception(msg) يرمي TemplateException. وهذا مفيد لتصحيح الأخطاء أو تحذير المستخدمين من الاستخدام غير الصحيح للقالب.
  • strftime_now(format_str) يسترجع التاريخ والوقت الحاليين بتنسيق محدد، وهو ما يُطلب غالبًا في رسائل النظام. وهو مكافئ لـ datetime.now().strftime(format_str) في بايثون.

التوافق مع Jinja غير المكتوب بلغة بايثون

يُنفَّذ Jinja بعدة لغات، وعادةً ما تتشارك هذه اللغات نفس الصياغة. يتيح لك كتابة قالب بلغة Python استخدام طرق Python مثل lower على السلاسل النصية أو items على القواميس. لكن هذا لن يعمل إذا استُخدم القالب في تنفيذ غير مبني على Python، على سبيل المثال، عند النشر باستخدام Javascript أو Rust.

أجرِ التغييرات أدناه لضمان التوافق عبر جميع تطبيقات Jinja.

  • استبدل دوال Python بمرشحات Jinja. على سبيل المثال، استبدل string.lower() بـ string|lower أو dict.items() بـ dict|dictitems. تتبع معظم التغييرات نفس النمط باستثناء string.strip()، الذي يُستبدل بـ string|trim. راجع قائمة المرشحات المدمجة للحصول على قائمة كاملة بالمرشحات.
  • استبدل True وFalse وNone (وهي خاصة بلغة بايثون) بـtrue وfalse وnone على التوالي.
  • قد يؤدي العرض المباشر لقاموس (dict) أو قائمة (list) إلى نتائج مختلفة في التطبيقات الأخرى. على سبيل المثال، قد تتغير إدخالات النص من علامات اقتباس مفردة إلى علامات اقتباس مزدوجة. لتجنب ذلك، أضف فلتر tojson للحفاظ على الاتساق.

قوالب كبيرة

تتطلب النماذج الأحدث أو النماذج التي تتضمن ميزات مثل استدعاء الأدوات وRAG قوالب أكبر قد تتجاوز 100 سطر. قد يكون من الأسهل كتابة القوالب الكبيرة في ملف منفصل. تتطابق أرقام الأسطر في الملف المنفصل تمامًا مع أرقام الأسطر في أخطاء تحليل القالب أو التنفيذ، مما يسهّل تصحيح أي مشاكل محتملة.

اكتب القالب في ملف منفصل واستخرجه إلى قالب المحادثة.

open("template.jinja", "w").write(tokenizer.chat_template)

يمكنك أيضًا تحميل قالب معدّل مرة أخرى إلى مُرمّز الرموز.

tokenizer.chat_template = open("template.jinja").read()

تخزين قوالب المحادثة وتحميلها

تُخزَّن قوالب المحادثة على القرص بعدة صيغ مختلفة. تحفظ نقاط التفتيش الحديثة القوالب كملفات .jinja مستقلة، بينما تضمّنها نقاط التفتيش الأقدم في إعدادات المُجزّئ أو المُعالِج.

صيغ التخزين

يمكن تخزين القوالب بأي من الصيغ التالية.

  • chat_template.jinja (موصى به). ملف Jinja مستقل في جذر المستودع، يحتوي على قالب محادثة واحد. هذا ما يكتبه save_pretrained() افتراضيًا. تخزين القالب في ملفه الخاص يسهل فحصه وتعديله ومقارنته. يقوم كل من المرمّزات والمعالجات بتحميل chat_template.jinja بالطريقة نفسها.
  • additional_chat_templates/<name>.jinja. وهو مجلد يحتوي على ملفات Jinja مستقلة تُستخدم عندما يأتي النموذج مع عدة قوالب مسماة (على سبيل المثال، قالب default وقالب منفصل tool_use). لا يزال قالب default يُوضع في chat_template.jinja في جذر المستودع، لكن كل قالب مسمى آخر يُوضع في additional_chat_templates/<name>.jinja، حيث يكون جذر اسم الملف هو اسم القالب.

تُحافظ الصيغ القديمة أدناه على التوافق مع الإصدارات السابقة فقط عند التحميل. لا تكتب قوالب المحادثة إلى أيٍّ منها.

  • حقل chat_template في tokenizer_config.json. وهو تنسيق قديم مخصص للتحميل فقط كان يُستخدم قبل ملفات .jinja المستقلة. يتم تضمين القالب كسلسلة JSON داخل tokenizer_config.json. عندما يحتوي النموذج على عدة قوالب مسمّاة، يكون الحقل قائمة من قواميس {"name": ..., "template": ...} بدلاً من سلسلة واحدة. تستمر المستودعات الموجودة التي تستخدم هذا التنسيق في التحميل، لكن save_pretrained() يكتب تنسيق .jinja الحديث بدلاً من ذلك.

  • chat_template.json. صيغة قديمة تُستخدم فقط للتحميل، كانت مستخدمة في نقاط الفحص القديمة لمعالجات الوسائط المتعددة. ملف JSON على شكل {"chat_template": "<template string>"}. تستمر المستودعات الموجودة التي تستخدم هذه الصيغة في التحميل، لكن save_pretrained() يكتب صيغة .jinja الحديثة بدلاً من ذلك. يثير مستودع المعالج الذي يمزج بين chat_template.json القديم وملفات .jinja الحديثة خطأً عند التحميل.

أولوية التحميل

عند استدعاء from_pretrained()، تحدد Transformers صيغ التخزين بأولوية ثابتة. ملفات .jinja المستقلة لها الأولوية على القوالب المضمنة في الإعداد. يقوم المحمّل بما يلي:

  1. يقوم بقراءة أي حقل chat_template موجود في tokenizer_config.json (أو، في حالة المعالجات، chat_template.json القديم).
  2. يقوم بقراءة chat_template.jinja في جذر المستودع إذا كان موجودًا، ويستخدمه كقالب default، مما يلغي الخطوة 1.
  3. يقوم بقراءة كل ملف .jinja داخل additional_chat_templates/، مع استخدام جذر اسم الملف كمفتاح، ثم يدمجها.

إذا كان الناتج يحتوي على قالب default واحد، يتم تعيين ~PreTrainedTokenizer.chat_template إلى ذلك النص. إذا وُجدت عدة قوالب مُسماة، يصبح chat_template قاموسًا من {name: template_string}. في هذه الحالة، يختار apply_chat_template() العنصر tool_use عند تمرير الأدوات، وdefault في حال عدم تمريرها.

الحفظ

تكتب save_pretrained() و push_to_hub() صيغة .jinja افتراضيًا. يتحول قالب نصي واحد إلى chat_template.jinja. أما قاموس من القوالب المسماة فيكتب الإدخال default إلى chat_template.jinja وملفًا واحدًا لكل إدخال متبقٍ تحت additional_chat_templates/. يُحذف الحقل chat_template من tokenizer_config.json لتجنب التكرار.

لا توجد طريقة مدعومة لحفظ قالب في أحد الصيغ القديمة. يتم الاحتفاظ بها فقط لتحميل المستودعات الأقدم.

تحديث مستودع أقدم

انقل قالبًا مدمجًا في tokenizer_config.json أو chat_template.json إلى صيغة .jinja الموصى بها عن طريق تحميله ثم حفظه مرة أخرى.

يعمل خطو التحميل على تطبيع أي صيغة قديمة يستخدمها المستودع في chat_template، ويعيد ~PushToHubMixin.push_to_hub ملف chat_template.jinja.

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("your-org/your-model")
tokenizer.push_to_hub("your-org/your-model")

قوالب للأدوات

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

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

تُدرج العناصر التالية في القسم التالي مكونات الواجهة البرمجية القياسية (API) لكتابة القوالب للأدوات.

تعريفات الأدوات

تُمرَّر الأدوات كدوال Python أو كبنية JSON. عند تمرير الدوال، يتم توليد بنية JSON تلقائيًا وتمريرها إلى القالب. عند وصول القالب إلى متغير tools، يكون دائمًا قائمة من بنيات JSON.

على الرغم من أن القالب يستقبل دائمًا الأدوات كمخطط JSON، فقد تحتاج إلى تغيير هذا التغيير جذريًا عند عرضها لتتوافق مع الصيغة التي تم تدريب النموذج عليها. على سبيل المثال، تم تدريب Command-R على أدوات معرّفة بترويسات دوال Python. يقوم القالب داخليًا بتحويل أنواع مخطط JSON وعرض الأدوات المدخلة كترويسات Python.

يُظهر المثال أدناه كيفية تعريف أداة بتنسيق مخطط JSON.

{
  "type": "function",
  "function": {
    "name": "multiply",
    "description": "A function that multiplies two numbers",
    "parameters": {
      "type": "object",
      "properties": {
        "a": {
          "type": "number",
          "description": "The first number to multiply"
        },
        "b": {
          "type": "number",
          "description": "The second number to multiply"
        }
      },
      "required": ["a", "b"]
    }
  }
}

يُظهر ما يلي مثالاً على معالجة تعريفات الأدوات في قالب المحادثة. يجب تغيير الرموز والتخطيطات المحددة لتتطابق مع تلك التي تم تدريب النموذج عليها.

{%- if tools %}
    {%- for tool in tools %}
        {{- '<tool>' + tool['function']['name'] + '\n' }}
        {%- for argument in tool['function']['parameters']['properties'] %}
            {{- argument + ': ' + tool['function']['parameters']['properties'][argument]['description'] + '\n' }}
        {%- endfor %}
        {{- '\n</tool>' }}
    {%- endfor %}
{%- endif %}

استدعاءات الأدوات

بالإضافة إلى عرض تعريفات الأدوات، تحتاج أيضًا إلى عرض استدعاءات الأدوات واستجابات الأدوات في القالب.

تُمرَّر استدعاءات الأدوات بشكل عام في المفتاح tool_calls من رسالة "assistant”. وهذا دائمًا قائمة، على الرغم من أن معظم نماذج استدعاء الأدوات تدعم استدعاء أداة واحدة فقط، مما يعني أن القائمة تحتوي عادةً على عنصر واحد فقط.

{
  "role": "assistant",
  "tool_calls": [
    {
      "type": "function",
      "function": {
        "name": "multiply",
        "arguments": {
          "a": 5,
          "b": 6
        }
      }
    }
  ]
}

يُظهر ما يلي نمطًا شائعًا للتعامل مع استدعاءات الأدوات. يمكنك استخدام هذا كنقطة انطلاق، لكن تأكد من أن القالب الخاص بك يطابق فعليًا التنسيق الذي تم تدريب النموذج عليه!

{%- if message['role'] == 'assistant' and 'tool_calls' in message %}
    {%- for tool_call in message['tool_calls'] %}
            {{- '<tool_call>' + tool_call['function']['name'] + '\n' + tool_call['function']['arguments']|tojson + '\n</tool_call>' }}
    {%- endfor %}
{%- endif %}

استجابات الأدوات

استجابات الأدوات هي قواميس رسائل بدور tool. وهي أبسط بكثير من استدعاءات الأدوات، وعادةً ما تحتوي فقط على مفاتيح role وname وcontent.

{
  "role": "tool",
  "name": "multiply",
  "content": "30"
}

قد لا تحتاج بعض القوالب إلى المفتاح name على الإطلاق، وفي هذه الحالة، يمكنك كتابة قالبك بحيث يقرأ المفتاح content فقط.

{%- if message['role'] == 'tool' %}
    {{- "<tool_result>" + message['content'] + "</tool_result>" }}
{%- endif %}

المساهمة

بمجرد أن يصبح القالب جاهزًا، اضبطه على خاصية chat_template في مُجزّئ الرموز واختبره باستخدام apply_chat_template(). إذا كان يعمل كما هو متوقع، فقم برفعه إلى Hub باستخدام push_to_hub().

حتى لو لم تكن مالك النموذج، فإن إضافة قالب لنموذج يحتوي على قالب محادثة فارغ أو غير صحيح لا تزال مفيدة. افتح طلب سحب على مستودع النموذج لإضافة القالب!

tokenizer.chat_template = template
tokenizer.push_to_hub("amazing_company/cool_model", commit_message="Add chat template", create_pr=True)
Update on GitHub