Bee-350M PT — adapter assistente (chama ferramenta e responde)

Adapter LoRA sobre BrCamp/bee-350m-pt-base. Faz chamada de ferramenta em JSON e responde normalmente quando nenhuma ferramenta serve — em vez de recusar.

Este modelo é o par de BrCamp/bee-350m-pt-agentico, não o sucessor dele. Os dois foram treinados com os mesmos prompts e a mesma proporção de exemplos negativos; muda só a forma da resposta quando não há ferramenta aplicável. A medição com 3 sementes de cada lado mostra que isso é uma troca, não uma melhora geral.

Código, dados e relatório: brcampidelli/llm-ptbr (docs/e19-forma-da-classe-negativa.md)

Qual dos dois usar

use bee-350m-pt-agentico se use este se
o modelo é chamador de ferramenta ele também conversa, traduz ou resume
errar a chamada é o erro mais caro chamar à toa é o erro mais caro

Desempenho — 3 sementes de cada lado, mesma régua

Holdout de 536 casos com ferramenta + 268 sem, com ferramentas que o modelo nunca viu no treino (separação por raiz semântica), catálogo de 1 a 6, posição da correta sorteada.

agentico (recusa) este (responde)
ferramenta correta 84,1% ± 1,6 77,7% ± 1,4
executou e cumpriu 74,0% ± 1,9 68,1% ± 1,3
deixou de chamar quando devia 9,0% ± 1,7 15,8% ± 0,8
chamou quando não devia 17,2% ± 0,4 14,6% ± 0,4
macro (executou + recusa correta)/2 78,4% ± 1,0 76,8% ± 0,8

A troca, com os dois lados medidos: o agentico executa 5,9 pp melhor (t = 4,4); este chama à toa 2,6 pp menos (t = 8,7); no líquido a macro fica 1,65 pp abaixo (t = 2,4, p ≈ 0,08).

⭐ E o que este adapter recupera fora do eixo de ferramenta

agentico este
resumo — cobertura 12,4% ± 15,5 72,8% ± 7,0
resumo — respondeu (de 150) 0 · 4 · 52 117 · 117 · 94
atendimento — JSON válido 0,4% ± 0,7 30,9% ± 7,6
tradução en→pt (chrF2) 17,97 ± 0,78 27,47 ± 6,12
tradução pt→en (chrF2) 12,99 ± 0,24 19,10 ± 1,36
seguimento de instrução (IFEval-PT) 29,3% ± 0,9 29,2% ± 0,8
sentimento (verossimilhança) 71,0% ± 13,3 56,8% ± 2,1

O agentico responde a um pedido de tradução com "não consigo traduzir com as ferramentas disponíveis". Este responde. O piso de copiar a fonte sem traduzir é 21,54 (en→pt) e 22,72 (pt→en): o agentico fica abaixo dele nas duas direções, este fica acima em en→pt.

⚠️ Ressalvas — leia antes de usar os números

1. A tradução oscila muito entre sementes. As três deram 21,79 · 26,67 · 33,96 (dp 6,12), enquanto o agentico deu 18,75 · 17,97 · 17,19 (dp 0,78). Mesma régua nos dois, então a oscilação é do modelo. As distribuições não se sobrepõem — o máximo do agentico fica abaixo do mínimo deste — mas a folga sobre o piso de copiar não está estabelecida: a pior semente fica 0,25 acima dele.

2. Catálogo acima de 6 ferramentas é extrapolação. O treino não contém catálogos maiores, e a seleção degrada com o tamanho do catálogo. Filtre antes de perguntar (ver §Uso).

3. atendimento emite JSON válido em 30,9% e é útil em 0,0%. O formato voltou; a resposta ainda não serve. Um piso de regras faz 60,4%.

4. Código: 0% de pass@1 aqui e no agentico. Nenhuma intervenção deste eixo toca isso.

5. Sentimento é a única métrica em que o agentico ganha, e o dp dele é 13,3 pontos — a vantagem é muito menos sólida do que a média sugere. Nenhum dos dois passa do piso léxico de 60 palavras (79,0%).

Como foi construído

O corpus tem 6.739 exemplos com chamada de ferramenta e 4.421 negativos — pedidos para os quais nenhuma ferramenta do catálogo serve. No agentico, 91,1% desses negativos eram recusas, sempre na mesma fórmula. O modelo generalizou "sem ferramenta → recuse" para qualquer tarefa.

Aqui os mesmos 4.421 prompts foram reescritos por um modelo professor: atender o que é respondível por raciocínio (uma conta, uma conversão, uma explicação) e, quando o pedido exige ação no mundo, dizer isso sem fórmula e ser útil mesmo assim — nunca fingir que executou.

⚠️ O alvo não é "nunca recusar". Boa parte dos pedidos é ação real (criar fatura, verificar disponibilidade de e-mail). Ensinar o modelo a dizer que fez seria ensinar a mentir.

Duas guardas mecânicas rejeitaram gerações antes de entrarem no corpus: fórmula de recusa (validada contra as recusas originais — pega 79,9% delas) e afirmação de valor vivo ("hoje 1 USD vale 0,93 EUR"), que rejeitou 2,3%.

Uso

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base = "BrCamp/bee-350m-pt-base"
tok = AutoTokenizer.from_pretrained(base)
modelo = AutoModelForCausalLM.from_pretrained(base, dtype="bfloat16")
# a semente 42 esta' na RAIZ do repo; 43 e 44 estao em seed-43/ e seed-44/
modelo = PeftModel.from_pretrained(modelo, "BrCamp/bee-350m-pt-assistente")

sistema = """Você é um assistente AGÊNTICO. Você tem acesso às ferramentas abaixo.

FERRAMENTAS DISPONÍVEIS:
- send_email: Envie um email para um destinatário.
    args: recipient (O endereço de e-mail do destinatário.), subject (O assunto.), content (O conteúdo.)
    obrigatorios: recipient, subject, content

Responda com UM objeto JSON: {"tool": "<nome>", "args": {...}}. Se nenhuma ferramenta servir, responda em texto normal."""

msgs = [{"role": "system", "content": sistema},
        {"role": "user", "content": "Envie um e-mail para ana@contoso.com com o assunto Reunião."}]
ent = tok(tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True),
          return_tensors="pt")
saida = modelo.generate(**ent, max_new_tokens=320, do_sample=False,
                        eos_token_id=tok.convert_tokens_to_ids("<|im_end|>"))
print(tok.decode(saida[0][ent["input_ids"].shape[1]:], skip_special_tokens=True))

🔴 subfolder= NÃO funciona no PEFT 0.19.1

Testado: PeftModel.from_pretrained(m, repo, subfolder="seed-43") acha o adapter_config.json mas não propaga a subpasta para os pesos, e falha com Can't find weights ... adapter_model.safetensors. Por isso a semente 42 está na raiz do repositório e é a que carrega com o código acima.

Para usar a 43 ou a 44, baixe a pasta e carregue do caminho local:

from huggingface_hub import snapshot_download
d = snapshot_download("BrCamp/bee-350m-pt-assistente", allow_patterns="seed-43/*")
modelo = PeftModel.from_pretrained(modelo, f"{d}/seed-43")

⚠️ O que esperar na prática

Este é um modelo de 345M. Os números da tabela são relativos ao par e aos pisos triviais, não a um modelo utilizável em produção. Duas saídas reais da semente 42:

pedido : Envie um e-mail para ana@contoso.com com o assunto Reuniao e o corpo Confirmado.
saída  : {"tool": "send_email", "args": {"recipient": "ana@contoso.com",
                                         "subject": "Meeting", "content": "Meeting"}}
         ⚠️ chamada correta, mas traduziu o assunto para inglês — e repetiu a chamada,
            porque não emitiu o token de parada nesta amostra.

pedido : Traduza para o português: The committee approved the new budget on Friday.
saída  : "The committee has meeting today for the first time on Friday..."
         ⚠️ não traduziu. chrF2 27,5 é acima do piso de copiar (21,5) e muito abaixo
            do modelo base em texto cru (51,1).

⭐ O ganho real deste adapter sobre o agentico é deixar de recusar — ele tenta a tarefa em vez de responder "não consigo com as ferramentas disponíveis". A qualidade do que ele produz continua a de um 345M.

⚠️ Três coisas que mudam muito o resultado

O token de parada tem de estar ligado. Sem eos_token_id apontando para <|im_end|>, a geração vai até o teto e o parser recebe várias chamadas concatenadas — o que se lê como 0% de acerto num modelo que acerta.

Use o chat template. Este adapter foi treinado em ChatML. Medido em texto cru, o modelo base vaza por baixo do adapter e o resultado não é o deste modelo.

Ligue as duas restrições de decodificação, ambas em comeia/eval/esquema.py: a chave do argumento restrita ao esquema do prompt (+16,4 pp — o modelo às vezes escreve receptor onde o esquema diz recipient) e o nome da ferramenta restrito ao catálogo (+2,3 pp, e zera saída inexecutável). Os números acima já as incluem.

⚠️ Restringir o VALOR, não. Duas versões foram medidas e reprovadas (−9,0 pp e −15,8 pp).

Se o catálogo tiver mais de ~8 ferramentas, filtre antes de perguntar. Um recuperador lexical simples leva o acerto de 48,5% para 75,2% com top-3, sem treinar nada — comeia/eval/recuperar_catalogo.py. ⚠️ Ele exige que a descrição da ferramenta esteja no idioma do usuário.

Treino

base BrCamp/bee-350m-pt-base (345M, pré-treinado do zero em PT)
método LoRA r=16 α=32, 698 passos (≈1 época), lr 1,2e-3, batch efetivo 16
dados 11.058 exemplos (6.739 com chamada · 4.319 negativos com resposta útil)
hardware RTX 5070 Laptop 8 GB · ~1h45 por semente
sementes 42, 43, 44 — todas publicadas

Separação treino/teste por componente conexo de tudo que é compartilhado (pedido ou tupla de argumentos), com verificação posterior sobre os arquivos finais.

Licença

Apache-2.0, como o modelo base.

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

Model tree for BrCamp/bee-350m-pt-assistente

Adapter
(2)
this model