# 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)