devsomosahub's picture
Upload README.md with huggingface_hub
9196f6f verified
# 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](https://huggingface.co/devsomosahub/agent-os-adapter-1.5b) |
| 7B (preciso) | Qwen/Qwen2.5-7B-Instruct | [devsomosahub/agent-os-adapter-7b](https://huggingface.co/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](https://huggingface.co/devsomosahub/agent-os-1b5-merged) | Sim (T4) |
| 7B merged | [devsomosahub/agent-os-7b-merged](https://huggingface.co/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](https://huggingface.co/datasets/devsomosahub/agent-os-dataset)
- **Formato:** JSONL com campos `input` e `output`
- **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
```python
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)
```python
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)
```python
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
```python
BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
)
```
## Como Treinar na Cloud (HuggingFace AutoTrain)
### Pre-requisitos
```bash
pip install autotrain-advanced
```
### 1. Upload do dataset para o HuggingFace
```python
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"]`.
```python
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
```bash
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
```python
# 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)
```python
# 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:
```python
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
```python
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).
```python
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_completo` em vez de `display_name` (coluna real)
- Gerou `user_profiles` em vez de `users` (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:
```python
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:
```python
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:
```python
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)
```bash
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)