Agent OS - Fine-Tuning Guide
Guia completo de como treinar adaptadores LoRA para o Agent OS, incluindo erros encontrados, soluções e boas praticas.
O que e o Agent OS Adapter?
Um modelo fine-tunado que converte linguagem natural em portugues para comandos JSON (SQL queries, shell commands, GitHub actions) para o sistema Agent OS da Hub Formaturas.
Exemplo:
- Input:
"quais contratos ativos" - Output:
{"action": "sql", "sql": "SELECT * FROM contratos WHERE status='ativo'"}
Modelos Treinados
Adapters LoRA (precisam do modelo base pra rodar)
| Modelo | Base | Repo HuggingFace |
|---|---|---|
| 1.5B (rapido) | Qwen/Qwen2.5-1.5B-Instruct | devsomosahub/agent-os-adapter-1.5b |
| 7B (preciso) | Qwen/Qwen2.5-7B-Instruct | devsomosahub/agent-os-adapter-7b |
Modelos Merged (prontos pra usar, incluem base + adapter)
| Modelo | Repo HuggingFace | Inference Endpoint |
|---|---|---|
| 1.5B merged | devsomosahub/agent-os-1b5-merged | Sim (T4) |
| 7B merged | devsomosahub/agent-os-7b-merged | Sim (A10G) |
IMPORTANTE: Use os modelos merged pra Inference Endpoints. Os adapters LoRA nao funcionam direto na Inference API.
Dataset
- Repo: devsomosahub/agent-os-dataset
- Formato: JSONL com campos
inputeoutput - Tamanho: 415 exemplos (duplicados 4x para 1660 amostras no treino)
- Dominio: Queries SQL para banco Supabase do sistema Hub Formaturas (contratos, parcelas, turmas, user_profiles)
Configuracao do Treino
LoRA Config
LoraConfig(
r=32,
lora_alpha=64,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
Training Args (1.5B - FP16)
TrainingArguments(
num_train_epochs=7,
per_device_train_batch_size=8,
gradient_accumulation_steps=1,
learning_rate=2e-4,
fp16=True,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
save_strategy="epoch",
push_to_hub=True,
hub_model_id="devsomosahub/agent-os-adapter-1.5b",
)
Training Args (7B - Q4 quantizado)
TrainingArguments(
num_train_epochs=7,
per_device_train_batch_size=4,
gradient_accumulation_steps=2,
learning_rate=2e-4,
fp16=True,
warmup_ratio=0.1,
lr_scheduler_type="cosine",
save_strategy="steps",
save_steps=500,
push_to_hub=True,
hub_model_id="devsomosahub/agent-os-adapter-7b",
)
Quantizacao para 7B
BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
)
Como Treinar na Cloud (HuggingFace AutoTrain)
Pre-requisitos
pip install autotrain-advanced
1. Upload do dataset para o HuggingFace
from huggingface_hub import HfApi
api = HfApi(token="SEU_HF_TOKEN")
api.create_repo("SEU_USER/agent-os-dataset", repo_type="dataset")
api.upload_file(
path_or_fileobj="dataset_v3.jsonl",
path_in_repo="train.jsonl",
repo_id="SEU_USER/agent-os-dataset",
repo_type="dataset",
)
2. Criar script de treino (cloud-train/script.py)
IMPORTANTE: NAO coloque tokens HF hardcoded no script - o HuggingFace bloqueia o upload. Use os.environ["HF_TOKEN"].
import os, torch
from datasets import load_dataset, concatenate_datasets
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments
from peft import LoraConfig, get_peft_model
from trl import SFTTrainer
from huggingface_hub import login
HF_TOKEN = os.environ["HF_TOKEN"] # NUNCA hardcode o token!
login(token=HF_TOKEN)
ds = load_dataset("SEU_USER/agent-os-dataset", data_files="train.jsonl", split="train")
def fmt(ex):
return {"text": f"<|im_start|>system\nYou are a command adapter. Output ONLY valid JSON.<|im_end|>\n<|im_start|>user\n{ex['input']}<|im_end|>\n<|im_start|>assistant\n{ex['output']}<|im_end|>"}
ds = ds.map(fmt)
ds = concatenate_datasets([ds, ds, ds, ds])
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-1.5B-Instruct", trust_remote_code=True)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-1.5B-Instruct", torch_dtype=torch.float16, device_map="auto", trust_remote_code=True)
model = get_peft_model(model, LoraConfig(
r=32, lora_alpha=64,
target_modules=["q_proj","v_proj","k_proj","o_proj","gate_proj","up_proj","down_proj"],
lora_dropout=0.05, bias="none", task_type="CAUSAL_LM",
))
trainer = SFTTrainer(
model=model,
train_dataset=ds,
args=TrainingArguments(
output_dir="./output",
num_train_epochs=7,
per_device_train_batch_size=8,
learning_rate=2e-4,
fp16=True,
save_strategy="epoch",
push_to_hub=True,
hub_model_id="SEU_USER/agent-os-adapter-1.5b",
hub_token=HF_TOKEN,
),
processing_class=tokenizer,
)
trainer.train()
trainer.push_to_hub()
model.push_to_hub("SEU_USER/agent-os-adapter-1.5b", token=HF_TOKEN)
tokenizer.push_to_hub("SEU_USER/agent-os-adapter-1.5b", token=HF_TOKEN)
3. Lancar o treino na cloud
autotrain spacerunner \
--project-name "meu-treino" \
--script-path ./cloud-train \
--username SEU_USER \
--token SEU_HF_TOKEN \
--backend spaces-t4-small \
--env "HF_TOKEN=SEU_HF_TOKEN"
Backends disponiveis:
| Backend | GPU | VRAM | Custo/hr |
|---|---|---|---|
| spaces-t4-small | T4 | 16GB | ~$0.60 |
| spaces-a10g-small | A10G | 24GB | ~$1.05 |
| spaces-a100-large | A100 | 80GB | ~$4.13 |
4. Acompanhar o treino
Acesse: https://huggingface.co/spaces/SEU_USER/autotrain-meu-treino
Merge: Adapter LoRA → Modelo Completo
Para usar na Inference API ou Inference Endpoints do HuggingFace, o adapter LoRA precisa ser mergeado com o modelo base.
REGRA CRITICA: Merge SEMPRE em FP16, NUNCA em Q4
# ERRADO - merge a partir de modelo quantizado Q4 (pesos corrompidos!)
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", quantization_config=bnb_config)
model = PeftModel.from_pretrained(base, "adapter")
merged = model.merge_and_unload() # SHAPES ERRADAS! Nao funciona.
# CERTO - merge a partir de modelo FP16 na CPU
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", torch_dtype=torch.float16, device_map="cpu")
model = PeftModel.from_pretrained(base, "adapter")
merged = model.merge_and_unload() # OK! Pesos corretos.
O merge Q4 gera erro size mismatch for weight: copying a param with shape torch.Size([33947648, 1]) no Inference Endpoint. O modelo precisa estar em FP16 completo pra merge funcionar.
RAM necessaria: 7B em FP16 = ~14GB RAM. Use A100 na cloud ou CPU local com RAM suficiente.
Script de merge na cloud (HuggingFace AutoTrain)
# cloud-merge/script.py
import os, torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
from huggingface_hub import login
HF_TOKEN = os.environ["HF_TOKEN"]
login(token=HF_TOKEN)
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", torch_dtype=torch.float16, device_map="cpu", trust_remote_code=True)
model = PeftModel.from_pretrained(base, "devsomosahub/agent-os-adapter-7b")
merged = model.merge_and_unload()
tok = AutoTokenizer.from_pretrained("devsomosahub/agent-os-adapter-7b", trust_remote_code=True)
merged.push_to_hub("devsomosahub/agent-os-7b-merged", token=HF_TOKEN, max_shard_size="2GB")
tok.push_to_hub("devsomosahub/agent-os-7b-merged", token=HF_TOKEN)
Lancar: autotrain spacerunner --project-name "merge-7b" --script-path ./cloud-merge --username SEU_USER --token TOKEN --backend spaces-a100-large --env "HF_TOKEN=TOKEN"
Limpar quantization_config do config.json
Se o modelo merged ficou com quantization_config no config.json (heranca do treino Q4), o Inference Endpoint falha com erro de bitsandbytes not found. Remova manualmente:
from huggingface_hub import HfApi, hf_hub_download
import json
api = HfApi(token="TOKEN")
path = hf_hub_download("SEU_USER/modelo-merged", "config.json")
config = json.load(open(path))
if "quantization_config" in config:
del config["quantization_config"]
api.upload_file(path_or_fileobj=json.dumps(config, indent=2).encode(), path_in_repo="config.json", repo_id="SEU_USER/modelo-merged")
Inference Endpoints
Criar Endpoint via SDK
from huggingface_hub import HfApi
api = HfApi(token="TOKEN")
endpoint = api.create_inference_endpoint(
name="agent-os-1b5",
repository="devsomosahub/agent-os-1b5-merged",
framework="pytorch",
task="text-generation",
accelerator="gpu",
vendor="aws",
region="us-east-1",
type="protected",
instance_size="x1",
instance_type="nvidia-t4", # T4 pra 1.5B, A10G pra 7B
namespace="devsomosahub",
)
GPUs recomendadas por modelo
| Modelo | GPU minima | instance_type | instance_size |
|---|---|---|---|
| 1.5B merged | T4 (16GB) | nvidia-t4 | x1 |
| 7B merged | A10G (24GB) | nvidia-a10g | x1 |
IMPORTANTE: Pausar endpoints quando nao usar!
Endpoints cobram por hora enquanto rodando (~$0.60/hr T4, ~$1.05/hr A10G).
api.pause_inference_endpoint("agent-os-1b5", namespace="devsomosahub")
# Para religar:
api.resume_inference_endpoint("agent-os-1b5", namespace="devsomosahub")
O modelo merged precisa de pipeline_tag no README
Sem pipeline_tag: text-generation no README.md, a Inference API nao reconhece o modelo:
```markdown
pipeline_tag: text-generation library_name: transformers
### Chamar o Endpoint
```python
import requests
URL = "https://SEU-ENDPOINT.aws.endpoints.huggingface.cloud"
headers = {"Authorization": "Bearer HF_TOKEN"}
prompt = '<|im_start|>system\nYou are a command adapter. Output ONLY valid JSON.<|im_end|>\n<|im_start|>user\nquais tabelas existem<|im_end|>\n<|im_start|>assistant\n'
r = requests.post(URL, headers=headers, json={"inputs": prompt, "parameters": {"max_new_tokens": 200, "return_full_text": False}})
print(r.json()[0]["generated_text"])
Teste Real: Modelo vs Banco Supabase (Cloud-Hub)
Testamos o modelo 1.5B com tabelas que NUNCA viu no treino (Cloud-Hub: users, boards, vms, activity_log, board_memberships).
Resultados
| Query | SQL gerado | Executou no banco? | Observacao |
|---|---|---|---|
| quais colunas tem a tabela vms | information_schema.columns WHERE table_name='vms' |
OK - 25 colunas | Perfeito |
| lista os boards com seus donos | JOIN boards + users |
OK - 11 resultados | Acertou o JOIN |
| quais usuarios tem role admin | WHERE role='admin' |
OK - 0 resultados | Query valida |
| qual o ip das vms rodando | WHERE power_status='running' |
OK - 11 VMs | Acertou a logica |
Limitacao: modelo inventa colunas
O modelo generaliza a estrutura (JSON, action, sql) mas chuta nomes de colunas baseado no dataset de treino quando a query e direta. Exemplo:
- Gerou
nome_completoem vez dedisplay_name(coluna real) - Gerou
user_profilesem vez deusers(tabela real)
Solucao: fluxo de 2 passos
1. User pergunta: "quais admins tem?"
2. Agent OS pede ao modelo: "quais colunas tem a tabela users"
→ Modelo: information_schema query (SEMPRE acerta)
→ Executa no banco → descobre colunas reais
3. Agent OS pede ao modelo com contexto: "a tabela users tem (id, email, display_name, role). liste os admins"
→ Modelo gera SQL com colunas corretas
O modelo acerta 100% das queries de information_schema. O problema so aparece quando ele tenta gerar SQL direto sem conhecer o schema.
Erros Comuns e Solucoes
1. "Script must be base64 encoded"
O --script-path na API precisa de base64. Use o CLI autotrain spacerunner que faz isso automaticamente.
2. "project_name must be alphanumeric but can contain hyphens"
NAO use pontos no nome. agent-os-1.5b da erro, use agent-os-small.
3. "You already created this dataset repo"
O autotrain cria um dataset auxiliar autotrain-NOME. Se rodar de novo, mude o --project-name ou delete o repo antigo:
api.delete_repo("SEU_USER/autotrain-NOME-ANTIGO", repo_type="dataset")
4. "Offending files contain valid HuggingFace secrets"
NUNCA coloque tokens HF hardcoded no script. Use os.environ["HF_TOKEN"] e passe via --env.
5. Treino termina mas modelo nao aparece no Hub
Causa: O script antigo so salvava local com model.save_pretrained(), sem push.
Solucao: Use push_to_hub=True no TrainingArguments + push explicito no final:
TrainingArguments(
push_to_hub=True,
hub_model_id="SEU_USER/MEU_MODELO",
hub_token=HF_TOKEN,
save_strategy="epoch", # salva checkpoints intermediarios
)
# No final do treino:
trainer.push_to_hub()
model.push_to_hub("SEU_USER/MEU_MODELO", token=HF_TOKEN)
tokenizer.push_to_hub("SEU_USER/MEU_MODELO", token=HF_TOKEN)
6. OOM (Out of Memory) na RTX 5060 (8.5GB VRAM) com 7B
Use quantizacao Q4 + batch_size=1:
BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")
TrainingArguments(per_device_train_batch_size=1, gradient_accumulation_steps=8)
7. Merge Q4 gera pesos corrompidos no Inference Endpoint
Erro: RuntimeError: size mismatch for weight: copying a param with shape torch.Size([33947648, 1])
Causa: Merge foi feito com modelo base carregado em Q4 (BitsAndBytes 4-bit). Os pesos quantizados tem shapes diferentes.
Solucao: SEMPRE mergear com modelo base em FP16. Use device_map="cpu" se nao tiver VRAM suficiente. Precisa de ~14GB RAM pro 7B.
8. Inference Endpoint falha com "bitsandbytes not found"
Causa: O config.json do modelo merged herdou quantization_config do treino Q4.
Solucao: Remova quantization_config do config.json (ver secao Merge acima).
9. Memory limit exceeded (14Gi) no AutoTrain Space
Causa: Carregar modelo 7B FP16 na RAM do Space excede o limite.
Solucao: Use spaces-a100-large (80GB) em vez de spaces-a10g-small (14GB).
10. Inference API retorna "410 Gone" ou "model doesn't support task"
Causa: A API antiga api-inference.huggingface.co foi desativada. Modelos custom nao rodam na Inference API gratuita.
Solucao: Crie um Inference Endpoint pago. Modelos merged com pipeline_tag: text-generation no README funcionam.
11. "openai/arcee-ai/trinity-large-preview:free is not a valid model ID" (PentAGI)
O PentAGI prefixa o LLM_SERVER_PROVIDER ao modelo. Se o provider for openai e o modelo arcee-ai/trinity:free, vira openai/arcee-ai/trinity:free.
Solucao: Coloque LLM_SERVER_PROVIDER=arcee-ai e LLM_SERVER_MODEL=trinity-large-preview:free.
Entendendo os Numeros do Treino
loss (erro)
loss: 9.21 → modelo perdido, respondendo lixo
loss: 2.31 → comecou a entender o padrao
loss: 0.37 → quase perfeito
loss: 0.01 → memorizou o dataset
grad_norm (forca do ajuste)
grad_norm: 14.0 → ajustes grandes (inicio)
grad_norm: 1.0 → ajustes finos (modelo convergindo)
learning_rate (velocidade)
- Comeca baixo (warmup) → sobe ate o maximo → desce devagar (cosine)
- Padrao: 2e-4 = 0.0002
epoch (passadas pelo dataset)
- epoch 1.0 = viu todo o dataset 1 vez
- Treinamos ate epoch 7 = 7 passadas completas
Exportar para GGUF (uso local com llama.cpp)
pip install llama-cpp-python
python -c "
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
base = AutoModelForCausalLM.from_pretrained('Qwen/Qwen2.5-1.5B-Instruct', torch_dtype=torch.float16, device_map='cpu')
model = PeftModel.from_pretrained(base, 'devsomosahub/agent-os-adapter-1.5b')
merged = model.merge_and_unload()
merged.save_pretrained('./merged-model')
AutoTokenizer.from_pretrained('devsomosahub/agent-os-adapter-1.5b').save_pretrained('./merged-model')
"
# Converter para GGUF
python llama.cpp/convert_hf_to_gguf.py ./merged-model --outfile adapter-q8.gguf --outtype q8_0
Infraestrutura
PentAGI (Pentest AI Autonomo)
- Servidor: Vultr Sao Paulo, 4CPU 8GB RAM, Ubuntu 24.04
- URL: https://216.238.107.254:8443
- Login: admin@pentagi.com / admin
- LLM: OpenRouter + arcee-ai/trinity-large-preview:free (gratis)
- Ferramentas: nmap, nikto, sqlmap, metasploit em containers Docker isolados
Agent OS
- Frontend: React + TypeScript
- Backend: Python (smol-daemon.py) com smolagents
- Modelo local: llama.cpp servindo o adapter GGUF
- Banco: Supabase (PostgreSQL)