Transformers documentation
Escrevendo um modelo de chat
Escrevendo um modelo de chat
Um chat template é um template Jinja armazenado no atributo ~PreTrainedTokenizer.chat_template do tokenizer. O Jinja é uma linguagem de templates que permite escrever código e sintaxe semelhantes aos do Python.
{%- for message in messages %}
{{- '<|' + message['role'] + '|>\n' }}
{{- message['content'] + eos_token }}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|assistant|>\n' }}
{%- endif %}Se você observar isso por um tempo, deve perceber que é muito parecido com Python, embora tenha uma sintaxe estranha de {%-. O modelo itera sobre uma lista de mensagens e, para cada mensagem, imprime o papel e o conteúdo da mensagem, seguido por um token de fim de sequência. Se add_generation_prompt=True, ele adiciona o cabeçalho inicial de uma mensagem do assistente ao final da conversa.
Carregue o template escrito como uma string e atribua-o ao atributo chat_template do tokenizer. Uma vez definido, o template é usado sempre que você chama apply_chat_template(). Ele também é salvo
com o tokenizer sempre que save_pretrained() ou push_to_hub() é chamado. O template é salvo no arquivo chat_template.jinja no diretório do tokenizer. Você pode
editar esse arquivo diretamente para alterar o template, o que muitas vezes é mais fácil do que manipular uma string de template. Veja Armazenar e carregar templates de chat abaixo para as outras formas em disco que o Transformers suporta.
Dicas para escrever templates
A maneira mais fácil de começar a escrever templates Jinja é consultar templates existentes. Use print(tokenizer.chat_template) em qualquer modelo de chat para ver o template que ele está usando. Comece tentando com modelos simples que não chamam ferramentas nem suportam RAG, pois modelos de uso de ferramentas podem ter templates muito complexos. Por fim, consulte a documentação do Jinja para mais detalhes sobre formatação e sintaxe.
Há algumas dicas e armadilhas específicas que você pode encontrar ao escrever templates de chat, e esta seção abordará alguns deles em mais detalhes.
Escrevendo modelos de chat multimodais
Para modelos multimodais, o atributo chat_template é definido no processor, e não no tokenizer. A chave content de uma mensagem é frequentemente uma lista de dicionários de conteúdo, em vez de apenas uma única string. Você pode querer verificar o tipo de cada item de conteúdo na lista e tratá-lo de acordo.
Em geral, o template não deve acessar diretamente dados de imagem ou vídeo. Normalmente, isso é tratado pelo processador após a renderização do template. Em vez disso, seu template deve emitir um único token especial, como <|image|> ou <|video|>, quando encontrar conteúdo de imagem ou vídeo. O processador expandirá esse único token especial em uma sequência de tokens de imagem ou vídeo posteriormente. Os tokens exatos a serem emitidos dependem do modelo com o qual você está trabalhando. Recomendamos fortemente carregar um processador multimodal existente para ver como ele trata os dados.
O modelo de exemplo abaixo trata conteúdo misto de imagem e texto.
{%- 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 %}Este template multimodal é muito semelhante ao template mais simples acima, mas ele verifica listas de content
e itera sobre elas para renderizar tokens <|image|> onde necessário. Isso permite que as imagens sejam inseridas “no fluxo”
do texto do usuário.
Nem todos os modelos funcionam assim — alguns, por exemplo, podem mover todas as imagens para o final da mensagem do usuário. O modelo de chat deve sempre corresponder ao formato com o qual o modelo foi treinado.
Removendo espaços em branco
O Jinja imprime qualquer espaço em branco antes ou depois de um bloco de texto. Isso pode ser um problema para modelos de chat, pois adicionar espaços em branco extras que não estavam presentes durante o treinamento do modelo pode prejudicar o desempenho. Para remover os espaços em branco, adicione - à sintaxe de linha do Jinja. Isso permite que você escreva seu modelo com indentação e quebras de linha no estilo Python, sem imprimir acidentalmente uma indentação na saída renderizada.
O modelo de exemplo abaixo não usa -, resultando em espaços em branco extras na saída.
{% for message in messages %}
{{ message['role'] + message['content'] }}
{% endfor %}Recomendamos fortemente o uso de - para garantir que apenas o conteúdo pretendido seja exibido.
{%- for message in messages %}
{{- message['role'] + message['content'] }}
{%- endfor %}Variáveis e chamáveis especiais
As únicas constantes em um template são a variável messages e o booleano add_generation_prompt. No entanto, você tem acesso a quaisquer outros argumentos de palavra-chave que forem passados ao método apply_chat_template().
Isso oferece flexibilidade e permite o suporte a casos de uso que talvez não tenhamos considerado ao projetar a especificação. A variável adicional mais comum é tools, que contém uma lista de ferramentas em formato de esquema JSON. Embora você possa usar qualquer nome de variável que desejar, recomendamos fortemente seguir a convenção e usar tools para esse fim. Isso torna os modelos mais compatíveis com a API padrão.
Você também tem acesso a quaisquer tokens contidos em tokenizer.special_tokens_map, que frequentemente inclui tokens especiais como bos_token e eos_token. Acesse-os diretamente pelo nome, como {{- bos_token }}.
Há duas funções chamáveis disponíveis para você. Para chamá-las, use {{- function_name(argument) }}.
raise_exception(msg)levanta umaTemplateException. Isso é útil para depuração ou para alertar os usuários sobre o uso incorreto de modelos.strftime_now(format_str)recupera a data e a hora atuais em um formato específico, o que é frequentemente necessário em mensagens de sistema. É equivalente a datetime.now().strftime(format_str) em Python.
Compatibilidade com Jinja não em Python
O Jinja é implementado em várias linguagens e, em geral, possui a mesma sintaxe. Escrever um modelo em Python permite o uso de métodos do Python, como lower em strings ou items em dicts. No entanto, isso não funcionará se o modelo for usado em uma implementação que não seja em Python, por exemplo, ao fazer o deploy com Javascript ou Rust.
Faça as alterações abaixo para garantir compatibilidade em todas as implementações do Jinja.
- Substitua os métodos Python por filtros Jinja. Por exemplo, substitua
string.lower()porstring|loweroudict.items()pordict|dictitems. A maioria das alterações segue o mesmo padrão, excetostring.strip(), que é substituído porstring|trim. Consulte a lista de filtros integrados para obter a lista completa de filtros. - Substitua
True,FalseeNone(específicos do Python) portrue,falseenone, respectivamente. - Renderizar diretamente um dict ou uma list pode retornar resultados diferentes em outras implementações. Por exemplo, entradas de string podem mudar de aspas simples para aspas duplas. Para evitar isso, adicione o filtro tojson para manter a consistência.
Modelos grandes
Modelos mais recentes ou modelos com recursos como tool-calling e RAG exigem templates maiores, que podem ter mais de 100 linhas. Pode ser mais fácil escrever templates maiores em um arquivo separado. Os números de linha no arquivo separado correspondem exatamente aos números de linha nos erros de análise ou execução do template, o que facilita a depuração de possíveis problemas.
Escreva o template em um arquivo separado e extraia-o para o chat template.
open("template.jinja", "w").write(tokenizer.chat_template)Você também pode carregar um modelo editado de volta no tokenizer.
tokenizer.chat_template = open("template.jinja").read()Armazenar e carregar modelos de chat
Os modelos de chat são armazenados em disco em vários formatos diferentes. Os checkpoints modernos salvam os modelos como arquivos .jinja independentes, enquanto os checkpoints antigos os incorporam na configuração do tokenizer ou do processor.
Formatos de armazenamento
Os modelos podem ser armazenados em qualquer um dos seguintes formatos.
chat_template.jinja(recomendado). Um arquivo Jinja independente na raiz do repositório, contendo um único chat template. É isso que o save_pretrained() grava por padrão. Armazenar o template em um arquivo próprio facilita inspecioná-lo, editá-lo e comparar diferenças. Tanto tokenizers quanto processors carregam ochat_template.jinjada mesma forma.additional_chat_templates/<name>.jinja. Um diretório de arquivos Jinja independentes usado quando um modelo é distribuído com vários templates nomeados (por exemplo, um templatedefaulte um templatetool_useseparado). O templatedefaultcontinua emchat_template.jinjana raiz do repositório, mas todos os outros templates nomeados ficam emadditional_chat_templates/<name>.jinja, em que o nome do arquivo (sem a extensão) é o nome do template.
Os formatos legados abaixo são mantidos apenas para carregamento com compatibilidade reversa. Não escreva modelos de chat em nenhum deles.
campo
chat_templateemtokenizer_config.json. Um formato legado somente para carregamento, usado antes dos arquivos.jinjaindependentes. O template é incorporado como uma string JSON emtokenizer_config.json. Quando um modelo possui múltiplos templates nomeados, o campo é uma lista de dicts{"name": ..., "template": ...}em vez de uma única string. Repositórios existentes que usam esse formato continuam a ser carregados, mas save_pretrained() grava o formato moderno.jinjaem vez disso.chat_template.json. Um formato legado somente para carregamento, usado por checkpoints de processadores multimodais mais antigos. Um arquivo JSON no formato{"chat_template": "<template string>"}. Repositórios existentes que usam esse formato continuam a ser carregados, mas save_pretrained() grava o formato moderno.jinjaem vez disso. Um repositório de processador que mistura umchat_template.jsonlegado com arquivos modernos.jinjagera um erro no carregamento.
Prioridade de carregamento
Ao chamar from_pretrained(), o Transformers resolve os formatos de armazenamento com uma precedência fixa. Arquivos .jinja independentes têm prioridade sobre os modelos embutidos na configuração. O carregador:
- Lê qualquer campo
chat_templatepresente emtokenizer_config.json(ou, para processadores, o legadochat_template.json). - Lê
chat_template.jinjana raiz do repositório, se existir, e o usa como o modelodefault, substituindo a etapa 1. - Lê todos os arquivos
.jinjaemadditional_chat_templates/, indexados pelo nome do arquivo, e os mescla.
Se o resultado tiver um único modelo default, ~PreTrainedTokenizer.chat_template é definido como essa string. Se existirem vários modelos nomeados, chat_template se torna um dicionário de {name: template_string}. Nesse caso, apply_chat_template() seleciona a entrada tool_use quando ferramentas são passadas e default caso contrário.
Salvando
save_pretrained() e push_to_hub() gravam o formato .jinja por padrão. Um único modelo de string se torna chat_template.jinja. Um dicionário de modelos nomeados grava a entrada default em chat_template.jinja e um arquivo para cada entrada restante sob additional_chat_templates/. O campo chat_template é removido de tokenizer_config.json para evitar duplicação.
Não há uma maneira suportada para salvar um template em um dos formatos legados. Eles são mantidos apenas para carregar repositórios mais antigos.
Atualizando um repositório mais antigo
Migre um template embutido em tokenizer_config.json ou chat_template.json para o formato .jinja recomendado, carregando-o e salvando-o novamente.
A etapa de carregamento normaliza o formato legado que o repositório utiliza em chat_template, e ~PushToHubMixin.push_to_hub retorna um arquivo chat_template.jinja.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("your-org/your-model")
tokenizer.push_to_hub("your-org/your-model")Modelos para ferramentas
Não há um formato específico para escrever templates de ferramentas, mas é melhor seguir a API padrão. Isso garante que o template seja amplamente acessível entre os modelos, sem exigir que os usuários escrevam código personalizado para usar ferramentas com o seu modelo.
Formatações, como espaços em branco e tokens especiais, são específicas de cada modelo. Certifique-se de que tudo corresponda exatamente ao formato com o qual o modelo foi treinado.
A seção a seguir lista os elementos da API padrão para a criação de modelos para ferramentas.
Definições de ferramentas
As Ferramentas são passadas como funções Python ou um esquema JSON. Quando funções são passadas, um esquema JSON é gerado automaticamente e enviado ao template. Quando um template acessa a variável tools, ela é sempre uma lista de esquemas JSON.
Embora um template sempre receba ferramentas como um esquema JSON, pode ser necessário alterar radicalmente esse formato ao renderizá-las para corresponder ao formato com o qual um modelo foi treinado. Por exemplo, o Command-R foi treinado com ferramentas definidas por cabeçalhos de funções Python. O template converte internamente os tipos do esquema JSON e renderiza as ferramentas de entrada como cabeçalhos Python.
O exemplo abaixo mostra como uma ferramenta é definida no formato de esquema 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"]
}
}
}Um exemplo de como lidar com definições de ferramentas em um modelo de chat é mostrado abaixo. Os tokens e layouts específicos devem ser alterados para corresponder aos com os quais o modelo foi treinado.
{%- 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 %}Chamadas de ferramentas
Além de renderizar as definições das ferramentas, você também precisa renderizar chamadas de ferramentas e respostas de ferramentas no modelo.
As chamadas de ferramentas são geralmente passadas na chave tool_calls de uma mensagem "assistant”. Isso é sempre uma lista, mesmo que a maioria dos modelos de chamada de ferramentas suporte apenas chamadas únicas, o que significa que a lista geralmente contém apenas um elemento.
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "multiply",
"arguments": {
"a": 5,
"b": 6
}
}
}
]
}Um padrão comum para lidar com chamadas de ferramentas é mostrado abaixo. Você pode usá-lo como ponto de partida, mas certifique-se de que seu template realmente corresponde ao formato com o qual o modelo foi treinado!
{%- 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 %}Respostas de ferramentas
As respostas de ferramentas são dicionários de mensagens com o papel tool. Elas são muito mais simples que as chamadas de ferramentas e geralmente contêm apenas as chaves role, name e content.
{
"role": "tool",
"name": "multiply",
"content": "30"
}Alguns modelos podem nem precisar da chave name, caso em que você pode escrever seu modelo para ler apenas a chave content.
{%- if message['role'] == 'tool' %}
{{- "<tool_result>" + message['content'] + "</tool_result>" }}
{%- endif %}Contribuir
Quando um template estiver pronto, defina-o no atributo chat_template do tokenizer e teste-o com apply_chat_template(). Se funcionar como esperado, envie-o para o Hub com push_to_hub().
Mesmo que você não seja o proprietário do modelo, ainda é útil adicionar um template para um modelo com um template de chat vazio ou incorreto. Abra um pull request no repositório do modelo para adicionar o template!
tokenizer.chat_template = template
tokenizer.push_to_hub("amazing_company/cool_model", commit_message="Add chat template", create_pr=True)