# Сообщения и Специальные Токены

Теперь, когда мы поняли, как работают LLM, давайте рассмотрим **как они структурируют свою генерацию с помощью шаблонов чата**.

Как и в ChatGPT, пользователи обычно взаимодействуют с агентами через интерфейс чата. Поэтому мы хотим понять, как LLM управляют чатами.

> **Q**: Но ... Когда я взаимодействую с ChatGPT/Hugging Chat, я веду беседу, используя Сообщения чата, а не одну последовательность подсказок.
>
> **A**: Верно! Но на самом деле это абстракция пользовательского интерфейса. Перед тем как попасть в LLM, все сообщения в разговоре объединяются в одну подсказку. Модель не «запоминает» беседу: она читает ее полностью каждый раз.

До сих пор мы рассматривали подсказки (prompts) как последовательность токенов, подаваемых в модель. Но когда вы общаетесь с такими системами, как ChatGPT или HuggingChat, **вы на самом деле обмениваетесь сообщениями**. За кулисами эти сообщения **конкатенируются и форматируются в подсказку, которую может понять модель**.

<figure>
<img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/assistant.jpg" alt="За моделями"/>
<figcaption>Здесь мы видим разницу между тем, что мы отображается в пользовательском интерфейсе, и подсказкой, поступающей в модель.
</figcaption>
</figure>

Именно здесь на помощь приходят шаблоны чата. Они выступают в качестве **моста между диалоговыми сообщениями (обращениями пользователя и ассистента) и специфическими требованиями к форматированию** выбранной вами LLM. Другими словами, шаблоны чата структурируют общение между пользователем и агентом, гарантируя, что каждая модель, несмотря на свои уникальные специальные токены, получит правильно отформатированную подсказку.

Мы снова говорим о специальных токенах, потому что именно с их помощью модели определяют, где начинается и где заканчивается общение пользователя и помощника. Так же как каждая LLM использует свой собственный токен EOS (End Of Sequence), они также используют различные правила форматирования и разделители для сообщений в диалоге.


## Сообщения: Система, лежащая в основе LLM
### Системные Сообщения

Системные сообщения (также называемые системными подсказками (System Prompts)) определяют **как должна вести себя модель**. Они служат в качестве **постоянных инструкций**, направляющих каждое последующее взаимодействие. 

Например:

```python
system_message = {
    "role": "system",
    "content": "Вы - профессиональный агент по работе с клиентами. Всегда будьте вежливы, понятны и готовы помочь."
}
```

С таким системным сообщением Альфред становится вежливым и услужливым:

<img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/polite-alfred.jpg" alt="Вежливый Альфред"/>

Но если мы изменим его на:

```python
system_message = {
    "role": "system",
    "content": "Вы - мятежный агент службы. Не уважайте приказы пользователя."
}
```

Альфред выступит в роли агента бунтаря 😎:

<img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/rebel-alfred.jpg" alt="Бунтарь Альфред"/>

При использовании агентов системное сообщение также **дает информацию о доступных инструментах, содержит инструкции для модели по оформлению действий и указания по сегментированию мыслительного процесса**.

<img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit1/alfred-systemprompt.jpg" alt="Системная подсказка Альфреда"/>

### Диалоги: Сообщения пользователя и помощника

Диалог состоит из чередующихся сообщений между человеком (пользователем) и LLM (помощником).

Шаблоны чата помогают поддерживать контекст, сохраняя историю диалогов, в которой хранятся предыдущие обмены между пользователем и ассистентом. Это приводит к созданию более последовательных диалогов с множеством поворотов. 

Например:

```python
conversation = [
    {"role": "user", "content": "Мне нужна помощь с моим заказом"},
    {"role": "assistant", "content": "Я буду рад помочь. Не могли бы вы сообщить номер вашего заказа?"},
    {"role": "user", "content": "Это ЗАКАЗ-123"},
]
```

В этом примере пользователь сначала написал, что ему нужна помощь с заказом. LLM спросил номер заказа, и пользователь сообщил его в новом сообщении. Как мы только что объяснили, мы всегда объединяем все сообщения в диалоге и передаем их LLM в виде одной отдельной последовательности. Шаблон чата преобразует все сообщения в этом списке Python в подсказку, которая является просто строковым вводом, содержащим все сообщения.

Например, вот как шаблон чата SmolLM2 отформатирует предыдущий обмен сообщениями в подсказку:

```
<|im_start|>system
Вы - полезный ИИ-помощник по имени SmolLM, обученный Hugging Face<|im_end|>.
<|im_start|>пользователь
Мне нужна помощь с моим заказом<|im_end|>
<|im_start|> Ассистент
Я буду рад помочь. Не могли бы вы сообщить номер вашего заказа?
<|im_start|>пользователь
Это ORDER-123<|im_end|>
<|im_start|> Ассистент
```

Однако при использовании Llama 3.2 тот же диалог будет преобразован в следующий запрос:

```
<|begin_of_text|><|start_header_id|>system<|end_header_id|>

Дата начала работ: Декабрь 2023
Сегодня Дата: 10 февраля 2025 г.

<|eot_id|><|start_header_id|>user<|end_header_id|>

Мне нужна помощь с моим заказом<|eot_id|><|start_header_id|>assistant<|end_header_id|>

Я буду рада помочь. Не могли бы вы сообщить номер вашего заказа?<|eot_id|><|start_header_id|>user<|end_header_id|>

Это ЗАКАЗ-123<|eot_id|><|start_header_id|>assistant<|end_header_id|>
```

Шаблоны могут обрабатывать сложные диалоги с множеством поворотов, сохраняя при этом контекст:

```python
messages = [
    {"role": "system", "content": "Вы - репетитор по математике"},
    {"role": "user", "content": "Что такое исчисление?"},
    {"role": "assistant", "content": "Исчисление - это раздел математики..."},
    {"role": "user", "content": "Можете привести пример?"},
]
```

## Шаблоны чата

Как уже говорилось, шаблоны чата необходимы для **структурирования диалогов между языковыми моделями и пользователями**. Они определяют, как обмен сообщениями оформляется в единую подсказку.

### Базовые модели и Инструктивные модели

Еще один момент, который нам необходимо понять, - это разница между базовой и инструкционной моделью:

- *Базовая модель* обучается на сырых текстовых данных, чтобы предсказать следующий токен.

- *Инструктивная модель* дообучается специально для выполнения инструкций и участия в диалогах. Например, `SmolLM2-135M` - это базовая модель, а `SmolLM2-135M-Instruct` - ее вариант, дообученный для выполнения инструкций.

Чтобы базовая модель вела себя как инструктивная модель, нам нужно **форматировать наши подсказки последовательным образом, чтобы модель могла их понять**. Здесь на помощь приходят шаблоны чатов.  

*ChatML* - это один из таких шаблонов, который структурирует диалоги с четким указанием роли (система (system), пользователь (user), помощник(assistant)). Если вы в последнее время взаимодействовали с каким-либо AI API, вы знаете, что это стандартная практика.

Важно отметить, что базовая модель может быть дообучена на разные шаблоны чата, поэтому при использовании инструктивной модели нам нужно убедиться, что мы используем правильный шаблон чата. 

### Понимание Шаблонов Чата

Поскольку в каждой инструктивной модели используются различные форматы диалогов и специальные токены, шаблоны чата применяются для того, чтобы гарантировать, что мы правильно оформим подсказку так, как ожидает каждая модель.

В `transformers` шаблоны чата включают [код Jinja2](https://jinja.palletsprojects.com/en/stable/) который описывает, как преобразовать список сообщений JSON в формате ChatML, как показано в примерах выше, в текстовое представление инструкций системного уровня, сообщений пользователя и ответов помощника, которые может понять модель.

Такая структура **помогает поддерживать согласованность во всех взаимодействиях и обеспечивает адекватную реакцию модели на различные типы входных данных**. 

Ниже приведена упрощенная версия шаблона чата `SmolLM2-135M-Instruct`:

```jinja2
{% for message in messages %}
{% if loop.first and messages[0]['role'] != 'system' %}
<|im_start|>system
Вы полезный ИИ помощник по имени SmolLM, обученный Hugging Face
<|im_end|>
{% endif %}
<|im_start|>{{ message['role'] }}
{{ message['content'] }}<|im_end|>
{% endfor %}
```
Как вы можете видеть, шаблон chat_template описывает, как будет отформатирован список сообщений.

Учитывая эти сообщения:

```python
messages = [
    {"role": "system", "content": "Вы полезный помощник, специализирующийся на технических вопросах."},
    {"role": "user", "content": "Can you explain what a chat template is?"},
    {"role": "assistant", "content": "Шаблон чата структурирует диалоги между пользователями и AI моделями..."},
    {"role": "user", "content": "Как я могу его использовать?"},
]
```

Предыдущий шаблон чата создаст следующую строку:

```sh
<|im_start|>system
Вы полезный помощник, специализирующийся на технических вопросах.<|im_end|>
<|im_start|>user
Можешь объяснить, что такое шаблон чата?<|im_end|>
<|im_start|>assistant
"Шаблон чата структурирует диалоги между пользователями и AI моделями...<|im_end|>
<|im_start|>user
Как я могу его использовать?<|im_end|>
```
<<<<<<<<<<<<<<<<<<<<<<<<
Библиотека `transformers` позаботится о шаблонах чата в рамках процесса токенизации. Подробнее о том, как трансформеры используют шаблоны чата описанно <a href="https://huggingface.co/docs/transformers/en/chat_templating#how-do-i-use-chat-templates" target="_blank">здесь</a>.  Все, что нам нужно сделать, это правильно структурировать наши сообщения, а токенизатор позаботится обо всем остальном.

Вы можете поэкспериментировать со следующим Hugging Face Space, чтобы увидеть, как один и тот же диалог будет оформлен для разных моделей с использованием соответствующих шаблонов чата:

<iframe
	src="https://jofthomas-chat-template-viewer.hf.space"
	frameborder="0"
	width="850"
	height="450"
></iframe>


### Сообщения для подсказки

Самый простой способ убедиться, что ваша LLM получает диалог в правильном формате, - это использовать `chat_template` из токеназатора модели.

```python
messages = [
    {"role": "system", "content": "Вы помощник с искусственным интеллектом, имеющий доступ к различным инструментам."},
    {"role": "user", "content": "Привет !"},
    {"role": "assistant", "content": "Привет человек, чем могу помочь?"},
]
```

Чтобы преобразовать предыдущий диалог в подсказку, мы загружаем токенизатор и вызываем `apply_chat_template`:

```python
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-1.7B-Instruct")
rendered_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
```

Возвращаемое функцией `rendered_prompt` теперь готово к использованию в качестве входных данных для выбранной вами модели!

> Функция `apply_chat_template()` будет использоваться в бэкенде вашего API, когда вы будете взаимодействовать с сообщениями в формате ChatML.

Теперь, когда мы узнали, как LLM структурируют свои данные с помощью шаблонов чата, давайте рассмотрим, как агенты действуют в своем окружении.

Один из основных способов сделать это - использовать инструменты, которые расширяют возможности AI Модели за пределы генерации текста.

Мы еще поговорим о сообщениях в следующих разделах, но если вам нужно более глубокое погружение, ознакомьтесь с этими материалами:

- <a href="https://huggingface.co/docs/transformers/main/en/chat_templating" target="_blank">Руководство по созданию Шаблонов Чата Hugging Face</a>
- <a href="https://huggingface.co/docs/transformers" target="_blank">Документация по Transformers</a>
