Transformers documentation
Menulis template chat
Menulis template chat
Template chat adalah template Jinja yang disimpan dalam atribut ~PreTrainedTokenizer.chat_template tokenizer. Jinja adalah bahasa templating yang memungkinkan Anda menulis kode dan sintaks yang mirip Python.
{%- for message in messages %}
{{- '<|' + message['role'] + '|>\n' }}
{{- message['content'] + eos_token }}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|assistant|>\n' }}
{%- endif %}Jika Anda memperhatikan ini cukup lama, Anda seharusnya menyadari bahwa ini sebenarnya sangat mirip dengan Python, meskipun dengan sintaks {%- yang agak aneh. Template ini mengiterasi daftar pesan, dan untuk setiap pesan, ia mencetak peran dan isi pesan tersebut, diikuti oleh token akhir-sekuens. Jika add_generation_prompt=True, ia menambahkan header awal untuk pesan asisten ke akhir percakapan.
Muat template yang telah ditulis sebagai string dan tetapkan ke atribut chat_template tokenizer. Setelah diatur, template ini akan digunakan setiap kali Anda memanggil apply_chat_template(). Template juga disimpan bersama tokenizer setiap kali save_pretrained() atau push_to_hub() dipanggil. Template disimpan dalam file chat_template.jinja di direktori tokenizer. Anda dapat mengedit file ini secara langsung untuk mengubah template, yang sering kali lebih mudah daripada memanipulasi string template. Lihat Menyimpan dan memuat chat template di bawah untuk bentuk lain di disk yang didukung oleh Transformers.
Tips menulis template
Cara termudah untuk mulai menulis template Jinja adalah dengan merujuk pada template yang sudah ada. Gunakan print(tokenizer.chat_template) pada model chat mana pun untuk melihat template yang digunakan. Cobalah mulai dengan model sederhana yang tidak memanggil tool apa pun atau tidak mendukung RAG, karena model tool-use dapat memiliki template yang sangat kompleks. Terakhir, lihat dokumentasi Jinja untuk detail lebih lanjut tentang format dan sintaks.
Namun, ada beberapa tips dan jebakan tertentu yang mungkin Anda temui saat menulis chat template secara khusus, dan bagian ini akan membahas beberapa di antaranya secara lebih rinci.
Menulis template chat multimodal
Untuk template multimodal, atribut chat_template diset pada processor, bukan tokenizer. Kunci content dari sebuah pesan sering berupa daftar dict konten,
bukan sekadar satu string. Anda mungkin perlu memeriksa tipe dari setiap item konten dalam daftar tersebut, dan menanganinya sesuai.
Secara umum, template tidak boleh mengakses data gambar atau video secara langsung. Hal ini biasanya ditangani oleh processor setelah proses rendering template selesai. Sebagai gantinya, template Anda harus mengeluarkan satu token khusus seperti <|image|> atau <|video|> ketika menemui konten gambar atau video. Processor kemudian akan memperluas token khusus tunggal tersebut menjadi rangkaian token gambar atau video. Token spesifik yang harus dikeluarkan bergantung pada model yang Anda gunakan. Kami sangat menyarankan untuk memuat processor multimodal yang sudah ada untuk melihat bagaimana ia menangani data.
Template contoh di bawah ini menangani konten campuran gambar dan teks.
{%- 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 %}Template multimodal ini sangat mirip dengan template yang lebih sederhana di atas, tetapi memeriksa daftar content,
dan mengiterasinya untuk merender token <|image|> di mana diperlukan. Hal ini memungkinkan gambar disisipkan “ke dalam alur”
teks pengguna.
Tidak semua model bekerja dengan cara ini - beberapa, misalnya, memindahkan semua gambar ke akhir pesan pengguna. Template chat harus selalu sesuai dengan format yang digunakan saat model dilatih.
Memangkas spasi
Jinja mencetak spasi apa pun sebelum atau sesudah blok teks. Hal ini dapat menjadi masalah untuk template chat karena penambahan spasi ekstra yang tidak ada selama pelatihan model dapat merusak kinerja. Untuk menghapus spasi tersebut, tambahkan - ke sintaks baris Jinja. Hal ini memungkinkan Anda menulis template dengan indentasi dan baris baru yang Pythonic, tanpa secara tidak sengaja mencetak indentasi dalam output yang dirender.
Template contoh di bawah ini tidak menggunakan -, sehingga menghasilkan spasi tambahan yang dicetak dalam output.
{% for message in messages %}
{{ message['role'] + message['content'] }}
{% endfor %}Kami sangat merekomendasikan penggunaan - untuk memastikan hanya konten yang dimaksud yang dicetak.
{%- for message in messages %}
{{- message['role'] + message['content'] }}
{%- endfor %}Variabel dan callable khusus
Satu-satunya konstanta dalam sebuah template adalah variabel messages dan boolean add_generation_prompt. Namun, Anda memiliki akses ke argumen kata kunci lainnya yang diteruskan ke metode apply_chat_template().
Hal ini memberikan fleksibilitas dan memungkinkan dukungan untuk kasus penggunaan yang mungkin tidak kami pikirkan saat merancang spesifikasi. Variabel tambahan yang paling umum adalah tools, yang berisi daftar alat dalam format skema JSON. Meskipun Anda dapat menggunakan nama variabel apa pun yang Anda inginkan, kami sangat merekomendasikan untuk tetap mengikuti konvensi dan menggunakan tools untuk tujuan ini. Hal ini membuat template lebih kompatibel dengan API standar.
Anda juga memiliki akses ke token apa pun yang terkandung dalam tokenizer.special_tokens_map, yang sering kali mencakup token khusus seperti bos_token dan eos_token. Akses token ini secara langsung berdasarkan nama, seperti {{- bos_token }}.
Tersedia dua fungsi callable untuk Anda. Untuk memanggilnya, gunakan {{- function_name(argument) }}.
raise_exception(msg)memicuTemplateException. Hal ini berguna untuk debugging atau memberi tahu pengguna tentang penggunaan template yang salah.strftime_now(format_str)mengambil tanggal dan waktu saat ini dalam format tertentu, yang sering diperlukan dalam pesan sistem. Ini setara dengan datetime.now().strftime(format_str) di Python.
Kompatibilitas dengan Jinja non-Python
Jinja diimplementasikan dalam beberapa bahasa dan umumnya memiliki sintaksis yang sama. Menulis template dalam Python memungkinkan Anda menggunakan metode Python seperti lower pada string atau items pada dict. Namun, ini tidak akan berfungsi jika template digunakan dalam implementasi non-Python, misalnya, saat melakukan deployment dengan Javascript atau Rust.
Lakukan perubahan di bawah ini untuk memastikan kompatibilitas di semua implementasi Jinja.
- Ganti metode Python dengan filter Jinja. Sebagai contoh, ganti
string.lower()denganstring|lowerataudict.items()dengandict|dictitems. Sebagian besar perubahan mengikuti pola yang sama, kecualistring.strip(), yang diganti denganstring|trim. Lihat daftar filter bawaan untuk daftar lengkap filter. - Ganti
True,False, danNone(ini spesifik Python) dengantrue,false, dannonemasing-masing. - Merender dict atau list secara langsung dapat menghasilkan hasil yang berbeda pada implementasi lain. Misalnya, entri string dapat berubah dari tanda kutip tunggal menjadi tanda kutip ganda. Untuk menghindari hal ini, tambahkan filter tojson untuk menjaga konsistensi.
Template besar
Model yang lebih baru atau model dengan fitur seperti tool-calling dan RAG memerlukan template yang lebih besar, yang bisa lebih dari 100 baris. Mungkin lebih mudah untuk menulis template yang lebih besar di file terpisah. Nomor baris di file terpisah tersebut sesuai persis dengan nomor baris dalam error parsing atau eksekusi template, sehingga memudahkan debugging masalah potensial.
Tulis template di file terpisah dan ekstrak ke template chat.
open("template.jinja", "w").write(tokenizer.chat_template)Anda juga dapat memuat kembali template yang telah diedit ke dalam tokenizer.
tokenizer.chat_template = open("template.jinja").read()Menyimpan dan memuat templat obrolan
Template chat disimpan di disk dalam beberapa format yang berbeda. Checkpoint modern menyimpan template sebagai file .jinja mandiri, sedangkan checkpoint lama menyematkannya di konfigurasi tokenizer atau processor.
Format penyimpanan
Template dapat disimpan dalam salah satu format berikut.
chat_template.jinja(disarankan). File Jinja mandiri di root repositori, berisi satu template chat. Ini adalah yang ditulis secara default oleh save_pretrained(). Menyimpan template di file tersendiri memudahkan untuk memeriksa, mengedit, dan membandingkan (diff). Baik tokenizer maupun processor memuatchat_template.jinjadengan cara yang sama.additional_chat_templates/<name>.jinja. Direktori berisi file Jinja mandiri yang digunakan ketika sebuah model menyediakan beberapa template bernama (misalnya, templatedefaultdan templatetool_useyang terpisah). Templatedefaulttetap berada dichat_template.jinjapada root repo, tetapi setiap template bernama lainnya berada diadditional_chat_templates/<name>.jinja, di mana nama file (tanpa ekstensi) adalah nama template-nya.
Format lama di bawah ini dipertahankan hanya untuk pemuatan yang kompatibel mundur. Jangan menulis templat obrolan ke salah satu dari format tersebut.
chat_templateditokenizer_config.json. Format legacy yang hanya untuk dimuat, digunakan sebelum file.jinjamandiri. Template disematkan sebagai string JSON ditokenizer_config.json. Jika sebuah model memiliki beberapa template bernama, field ini berupa daftar dict{"name": ..., "template": ...}alih-alih string tunggal. Repositori yang ada dan menggunakan format ini tetap dapat dimuat, tetapi save_pretrained() menulis format.jinjamodern.chat_template.json. Format legacy yang hanya dapat dimuat, digunakan oleh checkpoint pemroses multimodal yang lebih lama. File JSON dengan bentuk{"chat_template": "<template string>"}. Repositori yang ada dan menggunakan format ini tetap dapat dimuat, tetapi save_pretrained() menulis format.jinjamodern sebagai gantinya. Repositori pemroses yang mencampurkanchat_template.jsonlegacy dengan file.jinjamodern akan memicu error saat dimuat.
Prioritas pemuatan
Saat memanggil from_pretrained(), Transformers menyelesaikan format penyimpanan dengan prioritas tetap. File .jinja mandiri memiliki prioritas lebih tinggi daripada template yang tertanam di config. Loader:
- Membaca bidang
chat_templateapa pun yang ada ditokenizer_config.json(atau, untuk prosesor,chat_template.jsonversi lama). - Membaca
chat_template.jinjadi akar repo jika ada, dan menggunakannya sebagai templatedefault, menggantikan langkah 1. - Membaca setiap file
.jinjadiadditional_chat_templates/, dengan kunci berupa nama file tanpa ekstensi, lalu menggabungkannya.
Jika hasil memiliki satu template default, ~PreTrainedTokenizer.chat_template diset ke string tersebut. Jika beberapa template bernama ada, chat_template menjadi dict dari {name: template_string}. Dalam kasus itu, apply_chat_template() memilih entri tool_use ketika tools diteruskan, dan default selain itu.
Menyimpan
save_pretrained() dan push_to_hub() menulis format .jinja secara default. Satu string template menjadi chat_template.jinja. Dict berisi template bernama menulis entri default ke chat_template.jinja dan satu file untuk setiap entri lainnya di bawah additional_chat_templates/. Field chat_template dihapus dari tokenizer_config.json untuk menghindari duplikasi.
Tidak ada cara yang didukung untuk menyimpan template dalam salah satu format lama. Format tersebut hanya dipertahankan untuk memuat repositori yang lebih lama.
Memperbarui repositori yang lebih lama
Migrasikan template tertanam di tokenizer_config.json atau chat_template.json ke format .jinja yang direkomendasikan dengan memuat dan menyimpannya kembali.
Langkah load menormalisasi format legacy apa pun yang digunakan repositori di chat_template, dan ~PushToHubMixin.push_to_hub mengembalikan file chat_template.jinja.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("your-org/your-model")
tokenizer.push_to_hub("your-org/your-model")Template untuk alat
Tidak ada format khusus untuk menulis template untuk alat, tetapi sebaiknya mengikuti API standar. Hal ini memastikan template dapat diakses secara luas di berbagai model tanpa mengharuskan pengguna menulis kode khusus untuk menggunakan alat dengan model Anda.
Format seperti spasi dan token khusus bersifat spesifik model. Pastikan semuanya persis sesuai dengan format yang digunakan saat model dilatih.
Bagian berikut ini merangkum elemen-elemen API standar untuk menulis template untuk tools.
Definisi alat
Tools diteruskan sebagai fungsi Python atau skema JSON. Ketika fungsi diteruskan, skema JSON dibuat secara otomatis dan diteruskan ke template. Ketika template mengakses variabel tools, variabel tersebut selalu berupa daftar skema JSON.
Meskipun template selalu menerima tools sebagai JSON schema, Anda mungkin perlu mengubah format ini secara drastis saat merendernya agar sesuai dengan format yang digunakan untuk melatih model. Sebagai contoh, Command-R dilatih dengan tools yang didefinisikan menggunakan header fungsi Python. Template secara internal mengonversi tipe JSON schema dan merender input tools sebagai header Python.
Contoh di bawah ini menunjukkan bagaimana sebuah tool didefinisikan dalam format JSON schema.
{
"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"]
}
}
}Contoh penanganan definisi alat dalam templat chat ditunjukkan di bawah ini. Token dan tata letak spesifik harus diubah agar sesuai dengan yang digunakan saat model dilatih.
{%- 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 %}Pemanggilan alat
Selain merender definisi tool, Anda juga perlu merender tool calls dan tool responses di dalam template.
Panggilan tool umumnya diteruskan pada kunci tool_calls dari pesan "assistant”. Ini selalu berupa daftar meskipun sebagian besar model pemanggilan tool hanya mendukung satu panggilan tool, yang berarti daftar biasanya hanya berisi satu elemen.
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "multiply",
"arguments": {
"a": 5,
"b": 6
}
}
}
]
}Pola umum untuk menangani panggilan alat ditunjukkan di bawah ini. Anda dapat menggunakannya sebagai titik awal, tetapi pastikan template Anda benar-benar sesuai dengan format yang digunakan saat model dilatih!
{%- 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 %}Respons alat
Respons tool adalah dict pesan dengan peran tool. Respons ini jauh lebih sederhana daripada panggilan tool, dan biasanya hanya berisi kunci role, name, dan content.
{
"role": "tool",
"name": "multiply",
"content": "30"
}Beberapa template bahkan mungkin tidak memerlukan kunci name, dalam hal ini, Anda dapat menulis template Anda agar hanya membaca kunci content.
{%- if message['role'] == 'tool' %}
{{- "<tool_result>" + message['content'] + "</tool_result>" }}
{%- endif %}Kontribusi
Setelah template siap, atur ke atribut chat_template di tokenizer dan uji dengan apply_chat_template(). Jika berfungsi sesuai harapan, unggah ke Hub dengan push_to_hub().
Meskipun Anda bukan pemilik model, tetap berguna untuk menambahkan template untuk model yang memiliki template chat kosong atau salah. Buka pull request di repositori model untuk menambahkan template!
tokenizer.chat_template = template
tokenizer.push_to_hub("amazing_company/cool_model", commit_message="Add chat template", create_pr=True)