Manacá-1B-Instruct — GGUF (F16)

Conversão para GGUF do menezesbruno/manaca-1b-instruct, para uso com llama.cpp.

Os pesos são do autor original; aqui não há re-treino, fine-tuning nem quantização com perda — o arquivo é F16, conversão direta do safetensors bf16.

⚠️ Leia a seção "A adaptação obrigatória" antes de converter você mesmo. A conversão padrão do convert_hf_to_gguf.py produz um GGUF que parece funcionar mas alucina em qualquer pergunta. Este arquivo já vem corrigido.

Conversão feita no contexto do Carcará (LNCC / Santos Dumont), onde o modelo é servido em produção.

A adaptação obrigatória: vocabulário UGM em vez de SPM

O config.json do modelo declara LlamaForCausalLM. Por causa disso, o convert_hf_to_gguf.py roteia a conversão para LlamaModel.set_vocab()_set_vocab_sentencepiece(), que grava tokenizer.ggml.model = "llama". Em tempo de execução isso seleciona o tokenizador SPM do llama.cpp, cujo algoritmo é merge guloso por score — o algoritmo correto para SentencePiece BPE.

Mas o tokenizador do Manacá é SentencePiece Unigram, cujo algoritmo correto é Viterbi. O resultado da conversão padrão:

referência (sp.encode) : ['▁explique', '▁o', '▁que', '▁é', '▁fotossíntese', '▁em', '▁uma', '▁frase', '.']    →  9 tokens
GGUF padrão (SPM)      : [' ex','pli','que',' o',' que',' é',' fotos','sí','nte','se',' em',' uma',...]       → 14 tokens

O modelo passa a receber sequências de tokens que nunca viu no treino. O efeito é silencioso e fácil de confundir com limitação do modelo:

pergunta GGUF padrão (SPM) este GGUF (UGM)
"Qual é a capital do Brasil?" "rio de janeiro." "brasília."
"Explique o que é fotossíntese" "as células se reproduzem, convertendo energia química em energia luminosa" "as plantas usam a energia da luz solar para converter dióxido de carbono e água em oxigênio e glicose"

A correção é usar o caminho de vocabulário UGM (tokenizer.ggml.model = "t5"), que o conversor já implementa para modelos T5 e que embute o precompiled_charsmap do tokenizer.model — a normalização nmt_nfkc_cf (NMT + NFKC + case folding) do autor.

Metadados deste arquivo:

tokenizer.ggml.model                 't5'          # implementação UGM = Unigram/Viterbi
tokenizer.ggml.precompiled_charsmap  244410 bytes  # nmt_nfkc_cf do tokenizer.model oficial
tokenizer.ggml.add_bos_token         1
llama.context_length                 4096
general.architecture                 'llama'

Paridade verificada com a referência

IDs comparados token a token contra sp.encode() (a SentencePieceProcessor carregada com o tokenizer.model oficial), no servidor de produção:

entrada sp.encode() este GGUF IDs idênticos
Qual é a capital do Brasil? 7 7
ATENÇÃO: Coordenação de Aperfeiçoamento (CAPES) — 2026! 15 15
O LNCC fica em Petrópolis, no Rio de Janeiro. 13 13
prompt Alpaca completo (single-turn) 40 40
prompt Alpaca completo (multi-turn) 54 54

⚠️ Leia com atenção o que esta paridade significa — e o que ela não significa.

A comparação acima é contra sp.encode(), ou seja, contra o tokenizer.model. Contra ele a paridade é exata. Mas o repositório traz dois tokenizadores que não são equivalentes, e a implementação de referência (AutoTokenizer, usada por transformers e vLLM) usa o outro:

\n vira usado por
tokenizer.json <0x0A> (byte-fallback) transformers, vLLM — e, ao que tudo indica, o treino
tokenizer.model espaço (normalizador nmt_nfkc_cf) este GGUF

Como o template Alpaca é feito de quebras de linha, o prompt que este GGUF entrega ao modelo não tem \n nenhum — vira …ao pedido. ### Instrução: … ### Resposta: em linha única. Na maioria das perguntas o resultado é o mesmo; em algumas, não. Medido (greedy, mesma configuração):

pergunta referência (transformers) este GGUF
"Qual a capital da Argentina?" buenos aires. — para sozinho buenos aires. #argentina #buenosaires #buenosaires… até o teto

Por que não dá para simplesmente usar o tokenizer.json aqui: o tokenizador UGM do llama.cpp (o único que segmenta Unigram corretamente) não implementa byte-fallback\n viraria <unk>. Em llama.cpp é um ou outro: segmentação Unigram correta sem quebras de linha (este GGUF), ou quebras de linha com segmentação errada (o caminho SPM, que é muito pior — ver acima).

Quem precisa de fidelidade máxima ao comportamento de referência deve usar os safetensors originais com transformers ou vLLM, que carregam o tokenizer.json nativamente. Este GGUF existe para quem precisa de llama.cpp — com a ressalva acima.

Outras divergências menores: , · e emoji diferem em 1–3 tokens (compatibilidade Unicode e plano suplementar).

Como usar

O modelo não tem chat_template no tokenizer_config.json — foi treinado no formato Alpaca-PT. Salve o template abaixo como manaca_alpaca.jinja:

{{- 'Abaixo está uma instrução que descreve uma tarefa. Escreva uma resposta que atenda adequadamente ao pedido.' -}}
{%- for m in messages -%}
{%- if m['role'] == 'user' -%}{{- '\n\n### Instrução:\n' + m['content'] -}}
{%- elif m['role'] == 'assistant' -%}{{- '\n\n### Resposta:\n' + m['content'] + '</s>' -}}
{%- endif -%}
{%- endfor -%}
{%- if add_generation_prompt -%}{{- '\n\n### Resposta:\n' -}}{%- endif -%}

Este é o template do autor (2026-09-04) — note o </s> fechando cada resposta do assistente.

E suba o servidor:

llama-server \
  --model manaca-1b-instruct-F16-UGM.gguf \
  --chat-template-file manaca_alpaca.jinja --jinja \
  --ctx-size 4096 \
  --temp 0 --top-p 1 --top-k 0 --min-p 0 --repeat-penalty 1.0 --seed 0 \
  --n-predict 512

Amostragem determinística, especificada pelo autor (2026-09-04): temperature 0, top_p 1.0, top_k 0, min_p 0.0, repeat_penalty 1.0, frequency_penalty 0.0, presence_penalty 0.0, seed 0 — ou seja, greedy puro com todos os amostradores nos seus valores neutros.

Dois ajustes que a prática exigiu

  1. Limite a geração (--n-predict 512). O modelo não emite EOS de forma confiável em modo sampling: sem teto ele divaga por ~2000 tokens, e como o contexto é de 4096 uma única resposta consome metade da janela — a segunda pergunta já sai degradada e a terceira estoura.

  2. Use stop sequences. Por ser um modelo Alpaca, depois de responder ele começa o próximo turno sozinho. O autor especifica ["\n### Instrução:", "\n### Resposta:", "</s>"] — correto para a pilha dele (transformers/vLLM). Neste GGUF é preciso acrescentar "###", pelo motivo da seção seguinte. Lista recomendada aqui:

    "stop": ["###", "\n### Instrução:", "\n### Resposta:", "</s>"]
    

    O llama-server não tem flag de CLI para stop no endpoint OpenAI — mande no corpo da requisição.

Por que este GGUF precisa de "###" no stop

Consequência direta da diferença entre os dois tokenizadores do repositório original:

\n vira quem usa
tokenizer.json <0x0A> (byte-fallback) transformers, vLLM
tokenizer.model espaço (normalizador nmt_nfkc_cf) este GGUF

Como o GGUF é convertido do .model (é ele que traz o precompiled_charsmap), o modelo não consegue emitir \n — ele emite espaço. Saída crua medida, sem stop algum:

' machado de assis.  ###'      stop_type: eos

Ele escreve o marcador do próximo turno como ### (com espaços) e só então emite EOS. As stop sequences ancoradas em \n nunca casam, o ### vaza para a resposta e ainda polui o histórico dos turnos seguintes. O "###" resolve.

Limitações (do modelo, não da conversão)

Verificamos cada uma destas com três implementações — este GGUF, transformers (a referência do autor) e vLLM com o tokenizador nativo. As três abaixo aparecem igual nas três pilhas, ou seja, não são efeito da conversão (a cauda de hashtags da seção anterior, essa sim, é):

  • É um modelo de pergunta única. O SFT é Alpaca single-turn (uma instrução → uma resposta) e ele não tem noção de diálogo. A partir do 3º/4º turno repete a resposta anterior, vaza os marcadores do template e erra fatos que acertava no 1º turno. Recomendação prática: uma pergunta por conversa.
  • Não sustenta texto longo. Pedidos de poema em modo determinístico entram em repetição; "três parágrafos sobre X" costuma sair como três frases.
  • Erra fatos. O próprio card original declara "experimental research release (v0.1)", com capacidades deliberadamente modestas e não recomendado para produção.

Reproduzindo a conversão

# 1. baixar os pesos (inclusive o tokenizer.model OFICIAL — indispensável)
huggingface-cli download menezesbruno/manaca-1b-instruct --local-dir manaca-hf

# 2. aplicar o patch que força o vocabulário UGM no conversor
python3 manaca_patch_converter_ugm.py llama.cpp/conversion/llama.py

# 3. converter
python3 llama.cpp/convert_hf_to_gguf.py manaca-hf \
    --outtype f16 --outfile manaca-1b-instruct-F16-UGM.gguf

O patch do conversor (manaca_patch_converter_ugm.py) está publicado junto deste modelo — é idempotente e aborta sem alterar nada se os anchors do upstream mudarem. O chat template (manaca_alpaca.jinja) também está aqui, pronto para usar.

Sem o passo 2 o GGUF sai quebrado — e o sintoma parece ser do modelo, não da conversão.

Verificação de integridade

sha256  538713e550299089e3029c431026834b67288dab015853800366d6963daf1074
tamanho 3.448.096.640 bytes (3,3 GiB)

Créditos e licença

  • Modelo original: menezesbruno/manaca-1b-instruct — todo o crédito pelo treino é do autor.
  • Licença: CC BY-NC 4.0, herdada do modelo original (a mistura de SFT inclui fontes não-comerciais: Alpaca-PT, XLSum). Esta conversão é obra derivada e mantém exatamente a mesma licença: atribuição obrigatória e uso não-comercial.
  • Conversão e verificação: equipe do Carcará — LNCC / Santos Dumont.

Se encontrar divergência entre este GGUF e a implementação de referência, abra uma discussion — os testes de paridade de tokenização são reprodutíveis e estão descritos acima.

Downloads last month
82
GGUF
Model size
2B params
Architecture
llama
Hardware compatibility
Log In to add your hardware

16-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for sulfierry/manaca-1b-instruct-GGUF

Quantized
(1)
this model