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

قالب المحادثة هو قالب [Jinja](https://jinja.palletsprojects.com/en/stable/templates/) مخزَّن في خاصية `~PreTrainedTokenizer.chat_template` الخاصة بمجزّئ الرموز. لغة Jinja هي لغة قوالب تتيح لك كتابة أكواد وبنية تشبه لغة بايثون.

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

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

حمّل القالب المكتوب كسلسلة نصية وقم بتعيينه إلى خاصية `chat_template` في مُجزّئ الرموز. بمجرد تعيينه، يُستخدم القالب في كل مرة تستدعي فيها [apply_chat_template()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template). كما يُحفظ مع مُجزّئ الرموز في كل مرة يتم فيها استدعاء [save_pretrained()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) أو [push_to_hub()](/docs/transformers/main/ar/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub). يُحفظ القالب في ملف `chat_template.jinja` داخل مجلد مُجزّئ الرموز. يمكنك تعديل هذا الملف مباشرةً لتغيير القالب، وهو أمر أسهل في كثير من الأحيان من التعامل مع سلسلة نصية للقالب. راجع [تخزين قوالب المحادثة وتحميلها](#storing-and-loading-chat-templates) أدناه لمعرفة الأشكال الأخرى على القرص التي يدعمها Transformers.

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

أسهل طريقة لبدء كتابة قوالب Jinja هي الرجوع إلى القوالب الموجودة مسبقاً. استخدم `print(tokenizer.chat_template)` على أي نموذج محادثة لعرض القالب الذي يستخدمه. جرّب البدء بنماذج بسيطة لا تستدعي أي أدوات ولا تدعم RAG، لأن نماذج استخدام الأدوات قد تحتوي على قوالب معقدة جداً. أخيراً، اطّلع على [توثيق Jinja](https://jinja.palletsprojects.com/en/stable/templates/#synopsis) لمزيد من التفاصيل حول التنسيق والنحو.

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

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

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

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

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

```jinja
{%- 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. وهذا يتيح لك كتابة قالبك مع مسافات بادئة وتبديلات أسطر بأسلوب بايثون، دون أن تطبع مسافة بادئة عن طريق الخطأ في المخرجات المعروضة.

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

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

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

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

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

المتغيرات الثابتة الوحيدة في القالب هي متغير `messages` والقيمة المنطقية `add_generation_prompt`. ومع ذلك، يمكنك الوصول إلى **أي معاملات أخرى تُمرَّر** إلى طريقة [apply_chat_template()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.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)](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) في بايثون.

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

يُنفَّذ Jinja بعدة لغات، وعادةً ما تتشارك هذه اللغات نفس الصياغة. يتيح لك كتابة قالب بلغة Python استخدام دوال Python مثل [lower](https://docs.python.org/3/library/stdtypes.html#str.lower) على السلاسل النصية أو [items](https://docs.python.org/3/library/stdtypes.html#dict.items) على القواميس. لكن هذا لن يعمل إذا تم استخدام القالب في تنفيذ غير مبني على Python، على سبيل المثال، عند النشر باستخدام Javascript أو Rust.

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

- استبدل دوال Python بمرشحات Jinja. على سبيل المثال، استبدل `string.lower()` بـ `string|lower` أو `dict.items()` بـ `dict|dictitems`. تتبع معظم التغييرات نفس النمط باستثناء `string.strip()`، الذي يُستبدل بـ `string|trim`. راجع قائمة [المرشحات المدمجة](https://jinja.palletsprojects.com/en/3.1.x/templates/#builtin-filters) للحصول على قائمة كاملة بالمرشحات.
- استبدل `True` و`False` و`None` (وهي خاصة بلغة بايثون) بـ`true` و`false` و`none` على التوالي.
- قد يؤدي العرض المباشر لقاموس (dict) أو قائمة (list) إلى نتائج مختلفة في التطبيقات الأخرى. على سبيل المثال، قد تتغير إدخالات النص من علامات اقتباس مفردة إلى علامات اقتباس مزدوجة. لتجنب ذلك، أضف فلتر [tojson](https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.tojson) للحفاظ على الاتساق.

### قوالب كبيرة

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

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

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

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

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

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

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

### صيغ التخزين

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

- `chat_template.jinja` (موصى به). ملف Jinja مستقل في جذر المستودع، يحتوي على قالب محادثة واحد. هذا ما يكتبه [save_pretrained()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) افتراضيًا. تخزين القالب في ملفه الخاص يسهل فحصه وتعديله ومقارنته. يقوم كل من المرمّزات والمعالجات بتحميل `chat_template.jinja` بالطريقة نفسها.
- `additional_chat_templates/<name>.jinja`. وهو مجلد يحتوي على ملفات Jinja مستقلة تُستخدم عندما يأتي النموذج مع عدة قوالب مسماة (على سبيل المثال، قالب `default` وقالب منفصل `tool_use`). لا يزال قالب `default` يُوضع في `chat_template.jinja` في جذر المستودع، لكن كل قالب مسمى آخر يُوضع في `additional_chat_templates/<name>.jinja`، حيث يكون جذر اسم الملف هو اسم القالب.

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

- حقل `chat_template` في `tokenizer_config.json`. وهو تنسيق قديم مخصص للتحميل فقط كان يُستخدم قبل ملفات `.jinja` المستقلة. يتم تضمين القالب كسلسلة JSON داخل `tokenizer_config.json`. عندما يحتوي النموذج على عدة قوالب مسمّاة، يكون الحقل قائمة من قواميس `{"name": ..., "template": ...}` بدلاً من سلسلة واحدة. تستمر المستودعات الموجودة التي تستخدم هذا التنسيق في التحميل، لكن [save_pretrained()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) يكتب تنسيق `.jinja` الحديث بدلاً من ذلك.

- `chat_template.json`. صيغة قديمة تُستخدم فقط للتحميل، كانت مستخدمة في نقاط الفحص القديمة لمعالجات الوسائط المتعددة. ملف JSON على شكل `{"chat_template": "<template string>"}`. تستمر المستودعات الموجودة التي تستخدم هذه الصيغة في التحميل، لكن [save_pretrained()](/docs/transformers/main/ar/model_doc/wav2vec2#transformers.Wav2Vec2Processor.save_pretrained) يكتب صيغة `.jinja` الحديثة بدلاً من ذلك. يثير مستودع المعالج الذي يمزج بين `chat_template.json` القديم وملفات `.jinja` الحديثة خطأً عند التحميل.

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

عند استدعاء [from_pretrained()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.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()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template) العنصر `tool_use` عند تمرير الأدوات، و`default` في حال عدم تمريرها.

### الحفظ

تكتب [save_pretrained()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) و [push_to_hub()](/docs/transformers/main/ar/main_classes/model#transformers.utils.PushToHubMixin.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`.

```py
from transformers import AutoTokenizer

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

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

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

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

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

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

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

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

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

```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"]
    }
  }
}
```

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

```jinja
{%- 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”`. وهذا دائمًا قائمة، على الرغم من أن معظم نماذج استدعاء الأدوات تدعم استدعاء أداة واحدة فقط، مما يعني أن القائمة تحتوي عادةً على عنصر واحد فقط.

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

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

```jinja
{%- 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`.

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

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

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

## المساهمة

بمجرد أن يصبح القالب جاهزًا، اضبطه على خاصية `chat_template` في مُجزّئ الرموز واختبره باستخدام [apply_chat_template()](/docs/transformers/main/ar/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template). إذا كان يعمل كما هو متوقع، فقم برفعه إلى Hub باستخدام [push_to_hub()](/docs/transformers/main/ar/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub).

حتى لو لم تكن مالك النموذج، فإن إضافة قالب لنموذج يحتوي على قالب محادثة فارغ أو غير صحيح لا تزال مفيدة. افتح [طلب سحب](https://hf.co/docs/hub/repositories-pull-requests-discussions) على مستودع النموذج لإضافة القالب!

```py
tokenizer.chat_template = template
tokenizer.push_to_hub("amazing_company/cool_model", commit_message="Add chat template", create_pr=True)
```

