
A Intel anunciou o lançamento do OpenVINO™ 2025.2.0, destacando avanços importantes para desenvolvedores de IA generativa. Uma das grandes novidades desta versão é a compatibilidade com o formato GGUF, amplamente utilizado por modelos baseados no llama.cpp. Agora, é possível carregar modelos GGUF diretamente em pipelines Python e C++ usando o OpenVINO GenAI, que converte esses modelos para grafos otimizados e realiza inferência acelerada por GPU de forma dinâmica.
Essa integração já foi validada com modelos populares como DeepSeek-R1-Distill-Qwen, Qwen2.5 Instruct e LLaMA 3.2 Instruct, abrangendo variantes de 1B a 8B parâmetros. Com isso, o OpenVINO se posiciona como uma solução poderosa para executar modelos LLM localmente com alta performance, baixo consumo de memória e suporte direto ao ecossistema GGUF.
O problema: incompatibilidade direta entre tokenizadores
Os modelos treinados e distribuídos no formato GGUF, mesmo que otimizados para execução leve, ainda dependem de um tokenizador — o componente responsável por transformar texto em IDs numéricos que o modelo pode entender. Esses tokenizadores geralmente vêm da biblioteca Hugging Face Transformers, mas a API da OpenVINO exige um formato próprio para integração nativa.
Assim, é necessário um processo de conversão entre esses dois mundos — Hugging Face e OpenVINO — para que a inferência funcione corretamente, especialmente quando envolvem modelos com templates de chat personalizados, como os da família Qwen.
Como converter um tokenizador Hugging Face para uso com modelos GGUF no OpenVINO
Com o avanço da inteligência artificial generativa, cresce a demanda por rodar modelos de linguagem de forma eficiente em hardware local, como CPUs e GPUs Intel. A versão 2025.2 do OpenVINO trouxe uma grande novidade nesse sentido: suporte direto ao formato GGUF, utilizado por modelos otimizados com llama.cpp. No entanto, para que um modelo GGUF funcione corretamente dentro de um pipeline OpenVINO, é necessário converter o tokenizador original da Hugging Face para um formato compatível com a API da OpenVINO.
Este artigo apresenta e explica um programa em Python que realiza exatamente essa conversão, permitindo utilizar modelos como Qwen2.5 Instruct ou LLaMA 3.2 com inferência local acelerada via OpenVINO.
Baixando o modelo GGUF
Antes de executar qualquer conversão, é necessário baixar o arquivo .gguf do modelo desejado. Neste exemplo, usamos o modelo Qwen2.5-0.5B-Instruct, que é compatível com o novo suporte a GGUF no OpenVINO 2025.2.
Utilize o comando abaixo para fazer o download diretamente do Hugging Face:
wget "https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/main/qwen2.5-0.5b-instruct-q4_0.gguf?download=true" -O qwen2.5-0.5b-instruct-q4_0.gguf
Salve o arquivo .gguf em uma pasta, por exemplo, gguf_models/, que será usada no processo de conversão.
A solução: conversão do tokenizador com template customizado
O programa apresentado é responsável por:
- Carregar o tokenizador Hugging Face de um modelo GGUF.
- Converter esse tokenizador para o formato OpenVINO.
- Injetar um template de chat compatível com a estrutura de mensagens esperada pelo modelo.
- Salvar os arquivos resultantes (
tokenizer.xmledetokenizer.xml) para uso direto no pipeline.
Análise do código
A função principal do programa é convert_ov_tokenizers(). Ela recebe dois argumentos: o diretório onde está o modelo e o nome do arquivo .gguf.
hf_tokenizer = AutoTokenizer.from_pretrained(output_dir, gguf_file=gguf_file, trust_remote_code=True)
Essa linha carrega o tokenizador padrão da Hugging Face, mesmo com base em arquivos GGUF, graças à opção trust_remote_code.
Em seguida, ocorre a conversão para o formato OpenVINO:
ov_tokenizer, ov_detokenizer = convert_tokenizer(hf_tokenizer, with_detokenizer=True)
Aqui, são criados dois objetos: um tokenizador OpenVINO e um detokenizador, necessário para reconstruir o texto após a inferência.
Antes de salvar, o script injeta um chat template customizado, que define como o histórico de mensagens será apresentado ao modelo:
ov_tokenizer.set_rt_info(chat_template, "chat_template")
Esse passo é especialmente importante para modelos como Qwen, onde o comportamento do modelo depende do formato da conversa.
Por fim, os dois componentes são salvos em disco no formato XML:
ov.save_model(ov_tokenizer, output_dir / "openvino_tokenizer.xml")ov.save_model(ov_detokenizer, output_dir / "openvino_detokenizer.xml")
Esses arquivos serão utilizados posteriormente no pipeline de inferência da OpenVINO para processar texto de entrada e reconstruir a saída do modelo em linguagem natural.
Execução prática
A função main() define o caminho onde está o modelo .gguf e dispara o processo de conversão, medindo também o tempo necessário para a tarefa.
path_model = Path("gguf_models")convert_ov_tokenizers(path_model, "qwen2.5-0.5b-instruct-q4_0.gguf")
Abaixo o código completo.
from pathlib import Path
import time
from transformers import AutoTokenizer
from openvino_tokenizers import convert_tokenizer
import openvino as ov
chat_template = {
"{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are Qwen, created by Alibaba Cloud. You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n"}
def convert_ov_tokenizers(output_dir, gguf_file):
hf_tokenizer = AutoTokenizer.from_pretrained(
output_dir, gguf_file=gguf_file, trust_remote_code=True)
# hf_tokenizer_chat_template = hf_tokenizer.get_chat_template()
# Qwen2.5 GGUF model stored chat_template is different from HF tokenizers_config.json, replace as chat_tempalte with HF tokenizers_config
ov_tokenizer, ov_detokenizer = convert_tokenizer(
hf_tokenizer, with_detokenizer=True)
ov_tokenizer.set_rt_info(chat_template, "chat_template")
ov.save_model(ov_tokenizer, output_dir / "openvino_tokenizer.xml")
ov.save_model(ov_detokenizer, output_dir / "openvino_detokenizer.xml")
def main():
path_model = Path("gguf_models")
start = time.perf_counter()
convert_ov_tokenizers(path_model,"qwen2.5-0.5b-instruct-q4_0.gguf")
end = time.perf_counter()
print(f"Convert OpenVINO tokenizer finished, elapsed: {(end - start) *1e3:.3f} ms")
if '__main__' == __name__:
main()
Executando inferência com modelos GGUF no OpenVINO: guia prático com Qwen 2.5
Com o suporte nativo ao formato GGUF introduzido no OpenVINO™ 2025.2, ficou mais fácil executar modelos de linguagem grandes (LLMs) de forma otimizada em CPUs ou GPUs Intel. Além da conversão de tokenizadores (já explicada em outro artigo), o próximo passo é realizar a inferência em tempo real, com suporte a streaming de texto, integração com o tokenizador OpenVINO e controle de parâmetros como o número máximo de tokens gerados.
Neste artigo, vamos analisar passo a passo um código funcional que realiza inferência com streaming de tokens, utilizando o modelo Qwen2.5-0.5B-Instruct no formato GGUF, com backend CPU.
🛠️ Requisitos
Antes de rodar este código, certifique-se de que:
- Você já converteu o tokenizador com o
convert_tokenizere salvou os arquivosopenvino_tokenizer.xmleopenvino_detokenizer.xml. - O arquivo
.ggufdo modelo está presente (neste exemplo,qwen2.5-0.5b-instruct-q4_0.ggufna pastagguf_models/). - As bibliotecas
openvino,openvino_genaieopenvino_tokenizersestão instaladas.
Descrição do código de inferência
from pathlib import Pathimport timeimport openvino as ovimport openvino_genaifrom openvino_tokenizers import convert_tokenizer
Essas bibliotecas fornecem a base para rodar a inferência e utilizar o tokenizador OpenVINO. openvino_genai é o novo módulo que lida com pipelines LLM em OpenVINO.
📡 Função streamer() – exibindo os tokens em tempo real
def streamer(subword):print(subword, end='', flush=True)return openvino_genai.StreamingStatus.RUNNING
Esta função é chamada a cada novo token gerado pelo modelo, permitindo exibir o texto na tela conforme a resposta é construída, simulando o comportamento de um chatbot.
⚙️ Função principal main()
max_new_tokens = 40prompt = "What is OpenVINO?"
Define o número máximo de tokens que o modelo pode gerar e o texto de entrada (prompt).
🔄 Inicialização do tokenizador e modelo
tokenizer = openvino_genai.Tokenizer("gguf_models/")pipe = openvino_genai.LLMPipeline("gguf_models/qwen2.5-0.5b-instruct-q4_0.gguf", tokenizer, "CPU")
- O tokenizador é carregado do diretório onde os arquivos
.xmlforam salvos. - O LLMPipeline é a estrutura que junta o modelo
.gguf, o tokenizador e o dispositivo de execução ("CPU"neste caso).
🧠 Configuração de geração
config = openvino_genai.GenerationConfig()config.max_new_tokens = max_new_tokens
Você pode personalizar a geração ajustando parâmetros como:
max_new_tokens- temperatura, top_k, top_p (não usados aqui, mas disponíveis via
GenerationConfig)
💬 Ciclo de chat
pipe.start_chat()print("\nPrompt: ", prompt)print("Start generation ...")print("\nResponse: \n")pipe.generate(prompt, config, streamer)pipe.finish_chat()
Essas chamadas controlam o ciclo de conversação com o modelo:
start_chat(): inicia um novo histórico (importante para manter contexto).generate(...): realiza a inferência com streaming em tempo real.finish_chat(): encerra o contexto da conversa atual.
🧪 Exemplo de saída
Prompt: What is OpenVINO?Start generation ...Response:OpenVINO™ is an open-source toolkit developed by Intel for optimizing and deploying AI inference ...
Abaixo o código completo:
from pathlib import Path
import time
import openvino as ov
import openvino_genai
from openvino_tokenizers import convert_tokenizer
def streamer(subword):
print(subword, end='', flush=True)
return openvino_genai.StreamingStatus.RUNNING
def main():
max_new_tokens = 40
prompt = "What is OpenVINO?"
tokenizer = openvino_genai.Tokenizer("gguf_models/")
pipe = openvino_genai.LLMPipeline("gguf_models/qwen2.5-0.5b-instruct-q4_0.gguf", tokenizer, "CPU")
config = openvino_genai.GenerationConfig()
config.max_new_tokens = max_new_tokens
pipe.start_chat()
print("\nPrompt: ", prompt)
print("Start generation ...")
print("\nResponse: \n")
pipe.generate(prompt, config, streamer)
pipe.finish_chat()
if '__main__' == __name__:
main()
Conclusão
O suporte ao formato GGUF no OpenVINO representa um marco importante para a execução de LLMs de forma local e eficiente. No entanto, para que essa integração funcione, é crucial garantir que o tokenizador esteja no formato esperado. Este programa oferece uma solução prática e robusta para converter tokenizadores da Hugging Face para uso direto com pipelines de inferência da OpenVINO, incluindo o suporte a templates de chat específicos.
Com isso, desenvolvedores podem aproveitar o melhor dos dois mundos: a comunidade e o ecossistema da Hugging Face com a performance e eficiência do OpenVINO.
Este exemplo deste documento demonstra como realizar inferência local e eficiente com um modelo LLM em formato GGUF usando OpenVINO 2025.2. A combinação do LLMPipeline, Tokenizer e função streamer() permite simular o comportamento de um assistente de IA de forma fluida e otimizada para CPUs.
Esse pipeline é ideal para aplicações corporativas, embarcadas ou offline, onde o custo computacional e a latência precisam ser reduzidos sem depender de serviços em nuvem.