Шаблоны чатов как часть контракта модели: роли, специальные токены и двойная токенизация
Шаблон чата определяет точную последовательность токенов, которую ожидает модель, включая управляющие токены ролей и специальные разделители. Рассмотрение шаблона как части контракта данных предотвращает скрытую потерю качества из-за неверного форматирования и дублирования специальных токенов.
В этом материале
Короткий ответ
Рассматривайте шаблон чата как задекларированную часть контракта модели: фиксируйте поддерживаемые роли (system, user, assistant), управляющие токены, к которым привязана каждая роль, специальные токены токенизатора и точные флаги, используемые при форматировании. Создавайте промпты с помощью tokenizer.apply_chat_template, отдавая предпочтение параметру tokenize=True, чтобы только специальные токены самого шаблона были переданы в модель. Если вы сначала форматируете данные в строку, передавайте add_special_tokens=False при последующей токенизации, так как шаблон уже включает необходимые граничные токены. Используйте add_generation_prompt=True только при начале нового ответа ассистента, используйте continue_final_message только при предварительном заполнении финального сообщения, и никогда не передавайте оба флага одновременно. Проверяйте соблюдение контракта с помощью короткого теста генерации для каждого варианта модели, так как шаблоны могут различаться даже между моделями, дообученными на одной и той же базе.
Почему шаблон чата должен быть частью контракта модели
Причинно-следственная языковая модель никогда не получает разговор как таковой. Она получает одну плоскую последовательность токенов и предсказывает, что будет следующим. Шаблон чата - это компонент, который преобразует список словарей с ролями и содержимым в точную последовательность, которую модель видела во время дообучения (chat fine-tuning), включая управляющие токены, такие как <|user|>, <|assistant|>, и маркеры конца сообщения, которые позволяют модели видеть структуру обмена данными.
Поскольку две модели, дообученные на одной базе, могут использовать совершенно разные форматы, шаблон является данными поведенческого контракта, а не просто элементом представления. Несоответствие обычно не вызывает исключений; оно молча снижает качество ответов, поэтому в контракте должны быть явно указаны шаблон и его токены.
Практический совет
Сохраняйте строку отрендеренного промпта вместе с ревизией модели в вашем тестовом окружении. Если качество ответов упадет после обновления модели или библиотеки, декодируйте сохраненный промпт и сравните его с новым рендером; это позволит изолировать дрейф шаблона от изменения поведения модели до того, как вы начнете исследовать настройки генерации.
Стандартные роли и их семантика
Три роли покрывают большинство общих случаев. system содержит директивы о том, как должна вести себя модель, и обычно появляется первой. user содержит запрос человека. assistant содержит ответ модели. Шаблон сопоставляет каждую роль с управляющими токенами, и это сопоставление специфично для каждой модели: Mistral-7B-Instruct оборачивает ходы пользователя в [INST] и [/INST], в то время как Zephyr-7B использует маркеры стиля <|user|> и <|assistant|> с разделителями конца последовательности.
Следовательно, контракт должен содержать роли и их конкретные текстовые представления токенов. Простого именования ролей недостаточно, так как одна и та же роль может рендериться по-разному в разных чекпоинтах, а неверный набор токенов - это именно тот сценарий сбоя, который шаблон призван предотвратить.
Определение специальных токенов в токенизаторе
Конфигурация токенизатора предоставляет элементы, которые потребляет шаблон. К соответствующим атрибутам относятся chat_template (строка шаблона Jinja, которая форматирует списки сообщений), а также специальные токены, такие как bos_token, eos_token, unk_token, sep_token, pad_token, cls_token и mask_token. Шаблон считывает эти атрибуты, а не использует жестко закодированные значения, поэтому токенизатор и шаблон должны быть загружены из одной и той же ревизии модели.
Предварительное условие: токенизатор должен фактически иметь атрибут chat_template. Если его нет, apply_chat_template не сможет ничего отрендерить, и вам придется предоставить шаблон вручную или выбрать другой чекпоинт. Это вопрос окружения и версии, а не гарантия того, что любой данный шаблон соответствует формату обучения конкретной модели.
Применение шаблона с помощью apply_chat_template
Рабочая последовательность действий такова: создайте список словарей с ключами role и content, вызовите apply_chat_template и выберите tokenize=True, если вам нужны ID токенов для метода generate(), или tokenize=False, если вам нужна отформатированная строка для проверки или логирования. Устанавливайте add_special_tokens=False только в том случае, если вы намерены добавить специальные токены самостоятельно позже.
Пример ниже загружает токенизатор с шаблоном чата, форматирует одно системное и одно пользовательское сообщение и запрашивает промпт для генерации. Выведенная строка является иллюстративной: точные отступы зависят от версии токенизатора и ревизии шаблона.
Иллюстративный вывод (структура, а не реальный запуск):
<|system|> You are a helpful assistant </s><|user|> What is 2+2? </s><|assistant|>
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('HuggingFaceH4/zephyr-7b-beta')
messages = [
{"role": "system", "content": "You are a helpful assistant"},
{"role": "user", "content": "What is 2+2?"}
]
ids = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors='pt'
)
print(tokenizer.decode(ids['input_ids'][0]))Избегание двойной токенизации
Шаблоны чатов уже генерируют специальные токены, необходимые модели. Если вы выполняете рендеринг с tokenize=False, а затем пропускаете полученную строку через обычный вызов токенизатора, путь по умолчанию add_special_tokens=True может вставить токены bos или eos второй раз. Дублирование не вызывает явной ошибки; оно меняет последовательность, которую модель ожидала на основе обучения.
Безопасный паттерн - apply_chat_template(tokenize=True), который возвращает ID, включая управляющие токены. Если промежуточная строка неизбежна, токенизируйте ее с add_special_tokens=False. Это различие наиболее критично в конвейерах, где форматирование и кодирование происходят в разных сервисах.
Промпты генерации и обработка финального сообщения
Параметр add_generation_prompt=True добавляет токены, которые объявляют начало ответа ассистента, чтобы модель отвечала, а не продолжала текст пользователя. Он не оказывает влияния на такие модели, как Llama, у которых нет специального токена начала ответа ассистента, поэтому в контракте должно быть зафиксировано, использует ли целевая модель такой токен.
Параметр continue_final_message делает противоположное: он удаляет токены конца последовательности, чтобы генерация продолжалась внутри финального сообщения. Это полезно для предварительного заполнения известного префикса ответа или поля рассуждений, такого как reasoning_content. Эти два флага взаимно исключаются, и их совместное использование вызывает ошибку. Во время обучения используйте add_generation_prompt=False, так как токены начала ответа ассистента не полезны в обучающей последовательности.
Тестирование контракта на вариантах моделей
Один лишь синтаксис не доказывает корректность. Для каждого варианта модели отрендерите фиксированный двухходовой разговор, декодируйте его и убедитесь, что управляющие и граничные токены соответствуют формату, на котором обучалась эта модель. Затем запустите короткую генерацию и проверьте, что модель отвечает как ассистент, а не продолжает сообщение пользователя.
Проводите эту проверку всякий раз, когда меняется ревизия модели, версия transformers или источник токенизатора. Одинаковые названия ролей не означают одинаковых последовательностей токенов, и шаблон, который работает для одного дообучения, может молча дать сбой на другом, производном от той же базы.
Документирование шаблона в контракте
Воспроизводимый контракт фиксирует роли, требуемые управляющие токены, поведение промпта генерации и точную строку шаблона или его неизменяемую ревизию источника. Фрагмент ниже представляет собой схему для вашей системы документации, а не исполняемую конфигурацию: в нее нужно вписать идентификатор вашей модели и текст шаблона.
Фиксируйте источник шаблона (атрибут токенизатора или явную строку), а не только его вывод, так как вывод может измениться при смене версии библиотеки токенизатора.
model_contract:
model_id: <hugging-face-model-id>
tokenizer_revision: <revision-or-commit>
roles:
system: <role-token-or-pattern>
user: <role-token-or-pattern>
assistant: <role-token-or-pattern>
special_tokens:
bos: <token>
eos: <token>
add_generation_prompt_on_inference: true
add_generation_prompt_on_training: false
continue_final_message: prefill-only
chat_template_source: tokenizer.chat_template
chat_template_text: <exact-jinja-template>Что проверить
- Подтвердите, что токенизатор имеет ненулевой атрибут chat_template для используемой ревизии модели.
- Декодируйте отрендеренный промпт и убедитесь, что управляющие токены каждой роли соответствуют формату обучения модели.
- Убедитесь, что после токенизации на каждой границе сообщения появляется только один разделитель bos/eos.
- Если форматирование в строку происходит первым шагом, подтвердите, что последующий вызов токенизатора использует add_special_tokens=False.
- Подтвердите, что add_generation_prompt и continue_final_message никогда не передаются вместе.
- Зафиксируйте, влияет ли add_generation_prompt на целевую модель, так как в некоторых моделях отсутствует токен начала ответа ассистента.
- Проведите короткий тест генерации для каждого варианта модели и проверьте, что модель отвечает, а не продолжает ход пользователя.
- Привяжите версию transformers и ревизию токенизатора вместе с сохраненным текстом шаблона.
Границы применения
Шаблоны специфичны для конкретных моделей: контракт, написанный для одного чекпоинта, может не подойти для другого, даже если оба происходят от одной базовой модели. Параметр add_generation_prompt неэффективен для моделей вроде Llama, не имеющих явного токена начала ответа ассистента, а его совместное использование с continue_final_message вызывает ошибку. Примеры предполагают использование токенизатора Hugging Face с атрибутом chat_template и установленной библиотекой transformers; точные отступы и ID токенов зависят от версии токенизатора и библиотеки, поэтому декодированная строка является иллюстративной. Эта статья охватывает только форматирование промптов и не делает заявлений о качестве генерации, задержке или точности выполнения задач.