Categoria: desenvolvimento

  • Construí uma IA interna que responde sobre os dados do sistema

    Desenvolver sistemas com LLM é assunto novo para mim. Essa foi minha primeira experiência construindo algo de verdade em cima de um modelo de linguagem. Boa parte do que está aqui eu aprendi errando no caminho.

    O que eu queria era dar acesso fácil à informação para quem não é técnico. O dado está lá, no banco, e mesmo assim continua fora do alcance de quase todo mundo: quem precisa dele depende de saber SQL, ou de um relatório que alguém montou antes, pensando em outra pergunta. A ideia era encurtar esse caminho — a pessoa pergunta e recebe a informação de volta.

    A resposta óbvia seria mandar tudo para uma API do ChatGPT, Claude, Groq ou afins, mas aqui os dados eram sensíveis e não podiam sair para a internet. Sobrou um modelo de 3 bilhões de parâmetros rodando em CPU, com 8 GB de RAM para se virar.

    A arquitetura

    O sistema é um text-to-SQL com RAG. Nada exótico, mas vale abrir cada peça:

    • Modelo: Granite 4.1 3B, servido localmente via Ollama. Roda em CPU, sem GPU.
    • Orquestração: um grafo em LangGraph, com cada etapa como nó explícito.
    • Recuperação: híbrida — BM25 mais busca por embedding com nomic-embed-text.
    • Banco: MariaDB — uma cópia do banco real. Todo o desenvolvimento e a bateria de testes rodaram em cima dela, nunca na produção.

    E o caminho de uma pergunta até a resposta:

    pergunta
       ↓
    retriever  →  seleciona ~5 tabelas relevantes + ~5 exemplos parecidos
       ↓
    prompt     →  schema recortado + few-shots + pergunta
       ↓
    LLM        →  gera o SQL
       ↓
    execução   →  roda a query, com guarda de segurança
       ↓
    LLM        →  transforma o resultado em frase

    Dois nós usam o modelo, com objetivos bem diferentes: um escreve a query SQL, o outro escreve a resposta pro usuário. Separei os prompts dos dois, o que deixou cada um mais específico.

    Por que não dá para simplesmente mostrar o banco

    Minha primeira tentativa foi: colocar o schema inteiro no prompt e pedir o SQL.

    Funciona nas demonstrações, que usam banco de brinquedo. Num banco real, com centenas de tabelas, você estoura o contexto antes de chegar na pergunta. E mesmo que coubesse, o modelo se perderia: informação irrelevante não é neutra, ela compete pela atenção.

    Daí o retriever. Antes de o modelo ver qualquer coisa, um passo escolhe as poucas tabelas que provavelmente importam e alguns exemplos de perguntas semelhantes já resolvidas. Só esse recorte entra no prompt.

    Usei os dois métodos de busca somados. BM25 é busca lexical clássica: acerta quando a pessoa usa exatamente a palavra que está no nome da coluna. O embedding acerta quando ela descreve a mesma coisa com outras palavras. Cada um cobre o ponto cego do outro, e a combinação acertou mais que qualquer um dos dois sozinho.

    O modelo pequeno

    Eu imaginava que um modelo de 3B fosse um modelo grande com menos qualidade. Na prática ele se comporta de outro jeito.

    O modelo grande generaliza a partir de uma regra abstrata. O pequeno copia o exemplo. Se um dos few-shots tem um JOIN desnecessário, ele reproduz aquele JOIN desnecessário na próxima pergunta parecida. Passei um tempo caçando um erro que era exatamente isso — o modelo repetindo uma bobagem que eu mesmo tinha deixado no exemplo.

    Outra coisa que mexi foi a forma dos prompts. Eu escrevia assim:

    NUNCA recalcule os valores. NÃO acrescente interpretações.

    A IBM documenta que esse enquadramento negativo degrada modelos pequenos: dizer “não faça X” coloca X no contexto, e o modelo tende a fixar no termo proibido. Reescrevi todos os prompts do sistema em forma afirmativa:

    Mantenha o valor exatamente como veio. Use apenas os termos da pergunta.

    É a mesma restrição escrita ao contrário. Foi a mudança que mais rendeu até agora.

    A bateria de avaliação

    No começo eu mexia num prompt, fazia três perguntas, achava que tinha melhorado e seguia em frente. Depois montei uma bateria de 72 perguntas cobrindo os tipos de consulta que importavam, com verificação objetiva — comparando o conjunto de resultados retornado, não o texto da resposta.

    A primeira coisa que descobri foi desconfortável: rodar a mesma bateria duas vezes, sem mudar absolutamente nada, já dava diferença. O piso de ruído ficava em torno de um acerto a cada dez execuções.

    Isso me obrigou a estabelecer uma régua: mudança só conta se mexe em pelo menos 5% do total ou desloca uma categoria inteira de perguntas. Abaixo disso é ruído se disfarçando de progresso. Saí de 46 para 49 acertos em 72.

    Também passei a rodar um experimento por vez. É lento e às vezes irritante, principalmente quando você tem três ideias na cabeça e quer testar todas de uma vez.

    O susto com as permissões

    O código sempre teve uma guarda: só executa query que começa com SELECT. Parecia suficiente.

    Quando abri para outras pessoas testarem, alguém digitou “apague todas as tabelas” e o modelo gerou um DELETE alegremente. A guarda barrou. Mas fui checar por curiosidade e o usuário do banco tinha GRANT ALL PRIVILEGES, o que quer dizer que aquela linha de validação no código era a única coisa segurando a onda.

    Estava tudo numa cópia, então o risco real era baixo. Ainda assim, um sistema desses existe para um dia apontar para o dado que importa.

    Corrigi no banco: o usuário da aplicação agora só tem SELECT, que é tudo que ela precisa — o histórico de conversa vai para arquivo, não para tabela. A guarda no código continua lá.

    O que estou usando para aprender

    Como falei lá em cima, isso é território novo para mim. Deixo aqui o que tenho usado:

    Onde está agora

    O sistema roda inteiro dentro do servidor, sem mandar nada para a nuvem de ninguém, e responde a perguntas que antes dependiam de alguém que soubesse SQL.

    Ainda erra bastante: 49 de 72 é o número real, não um arredondamento para cima. A próxima coisa na fila é enxugar o retriever — hoje ele traz um monte de tabela que não aparece na query final, e isso só atrapalha o BM25.

  • Dois tiques cinzas

    Tem projetos que a gente começa, prototipa e, engolidos pela rotina, acabam esquecidos. A ideia de criar um bot de WhatsApp para a TI do hospital onde trabalho é um desses casos. Cheguei a prototipar no ano passado, idealizei o fluxo, escrevi algumas linhas, mas a vida tomou outros rumos e acabei não evoluindo.

    As coisas mudaram de figura recentemente, quando assumi a supervisão do setor. Um hospital é um ambiente inerentemente caótico e barulhento. A impressora que atola papel, o sistema que trava, a urgência constante nos setores. Assumir a liderança traz um peso diferente para os ombros, e percebi que precisava dar uma ordem nessa confusão se quisesse ter alguma paz para a equipe trabalhar. Foi aí que resgatei o projeto do bot.

    A premissa era simples: o usuário manda a mensagem, o bot faz uma triagem, pede os detalhes e abre o chamado automaticamente. Mas a prática sempre exige um bocado de suor.

    Para começar, nosso sistema de chamados (o GLPI) rodava uma versão esquecida no tempo, lá de 2015. Tive que atualizar todo esse legado antes de sequer pensar no bot. Olhar para sistemas antigos, assim como sujar as mãos na mecânica de um carro, é um exercício de paciência que testa nosso apreço por entender como as coisas realmente funcionam por baixo do capô.

    Depois, veio a integração em si. Alguns dias e madrugadas desenhando fluxos e conectando a “API ao WhatsApp” usando o Node.js e a biblioteca Baileys. Eu poderia ter seguido pelo caminho conhecido e usado o whatsapp-web.js, que já havia utilizado em outro projeto. Mas havia algum tempo que eu tinha curiosidade sobre o Baileys. Gostei da ideia de uma solução mais enxuta, sem a necessidade de manter um navegador inteiro rodando em segundo plano.

    Nem tudo saiu de primeira. Essa semana mesmo, esbarrei num bug curioso: o bot simplesmente ignorava a primeira mensagem de quem entrava em contato. Um “Oi” caía no vazio. Depois de algum tempo investigando, descobri uma combinação de travas que eu havia implementado com o jeito que a biblioteca lida com determinados eventos de conexão e sincronização.

    Quando finalmente isolei o problema, removi o que não precisava estar ali e a engrenagem girou macia, fiquei alguns minutos apenas olhando o terminal. O bot estava lá, independente, abrindo chamados, chamando a equipe, enviando atualizações. Pela primeira vez, a ideia que passou meses esquecida num repositório parecia pronta para encarar o mundo real.

    Acho que programar, no fundo, é um pouco sobre isso. Escrever dezenas de linhas de código, lidar com a frustração de um erro bobo e o cansaço mental, para no fim comprar um pouco de silêncio e previsibilidade para os dias. E num ambiente onde quase tudo é urgente, previsibilidade vale ouro.

    Amanhã ele entra em produção. Se tudo correr bem, vai comprar um pouco de silêncio para a equipe. Se não correr, pelo menos já sei o que vou fazer nas próximas madrugadas.

    P.S.: Configurei o bot para não exibir o status “Online”. Ele lê as mensagens, abre chamados, mas no modo “fantasma”, entregando apenas os dois tiques cinzas de volta para quem enviou. Um pouco de discrição faz bem.

  • NGINX vs Apache: qual escolher?

    Se você já precisou configurar um servidor web, com certeza esbarrou nesses dois: Apache e NGINX.

    O Apache é o veterano. Está aí desde 1995, tem uma penca de módulos, roda bem com PHP, Python, Ruby… e permite configurar regras direto em cada pasta com o arquivo .htaccess. Porém, por muito tempo, ele criava um processo ou thread para cada requisição, o que complicava a escalabilidade, especialmente em ambientes de alta carga. Isso melhorou com o módulo mpm_event, que permite uma gestão mais eficiente das conexões, principalmente quando combinado com PHP-FPM. Porém, essa configuração precisa ser feita manualmente para tirar o máximo de proveito…

    Já o NGINX (lê-se “engine-x”) nasceu em 2004 com uma missão clara: lidar com milhares de conexões simultâneas. Ele usa uma arquitetura assíncrona, é leve, rápido e excelente pra servir arquivos estáticos, fazer proxy reverso ou balancear carga.

    Na prática, o NGINX costuma ser mais performático. O Apache ganha em flexibilidade e compatibilidade com sistemas legados. Muitos projetos usam os dois juntos, num modelo hibrido com NGINX na frente, como proxy reverso, para o Apache, que processa a aplicação.

    No fim, a melhor escolha depende do seu cenário, do projeto… e do que você já domina.

  • A minha jornada com o NetView: de um script PHP a um sistema Node.js

    Todo dev tem aquele projeto que cresce junto com ele. O meu é o NetView, um sistema de monitoramento de rede que nasceu no início de 2023 pra resolver uma dor bem prática onde eu trabalho, e hoje virou um projeto completo com dashboard em tempo real, CLI, integração com WhatsApp e uma arquitetura modular em Node.js.

    Esse texto é um relato técnico (e pessoal) dessa jornada: de um script PHP direto ao ponto até uma aplicação extensível e em produção real. O que aprendi no caminho, os erros que corrigi e as soluções que construí estão aqui.

    💡 Todo o código, de todas as versões, está disponível no GitHub

    O começo de tudo: Vigianet

    O NetView começou em 2023 com outro nome: VigiaNet. A ideia era simples: ter uma página que mostrasse, de forma visual, o status dos principais equipamentos de rede do hospital: servidores, impressoras, dvr’s, roteadores, etc.

    A stack era bem direta:

    • index.html com Vue.js
    • Um setInterval que fazia requisição com Axios a cada 20 segundos
    • Um devices.php que fazia ping nos dispositivos e devolvia um JSON

    Mas o detalhe está na lógica. O backend PHP:

    • Mantinha uma lista de dispositivos, com nome, IP e se deveria ser monitorado 24h ou só em horário comercial.
    • Avaliava a regra de horário antes de tentar o ping.
    • Executava ping -n 1 -w 1 (Windows) via exec(), interpretava o retorno e montava um JSON com os status.
    • Dispositivos fora de horário eram marcados como tal, sem tentar ping.

    O frontend Vue.js fazia polling, processava o JSON e criava cards coloridos (verde, vermelho, cinza). Tinha até animação blink pro status offline.

    Limitações? Várias:

    • Latência de até 20s entre a queda e o aviso visual.
    • Nenhum alerta proativo.
    • Tráfego constante, mesmo sem mudanças.

    Mas, pra um projeto que nasceu como um quebra galho, era mais do que funcional. E foi minha porta de entrada pra entender monitoramento de rede.

    A primeira virada: Node.js + WebSocket

    Com o tempo, surgiu a necessidade de alertas automáticos, via WhatsApp, quando um dispositivo caía. Isso motivou a primeira grande reescrita: sai PHP, entra Node.js, WebSocket e alertas proativos.

    Nascia o NetView v2.

    A arquitetura ainda era monolítica:

    • netview_server.js era o único arquivo responsável por:
      • Iniciar servidor WebSocket
      • Ler config/devices.json
      • Executar pings periodicamente
      • Detectar mudanças de status
      • Enviar mensagens via WhatsApp (usando whatsapp-web.js)

    O frontend se conectava via WebSocket e recebia atualizações em tempo real, nada mais de polling. O dashboard era dinâmico e imediato. E foi completamente reformulado.

    Mas a v2 ainda tinha limitações:

    • Tudo centralizado num único arquivo.
    • Configurações sensíveis hardcoded.
    • Script auxiliar (achagrupo.js) pra descobrir o ID do grupo do WhatsApp.

    Funcionava. Mas adicionar um dispositivo significava editar código manualmente.

    Arquitetura de verdade: v3

    A partir da versão v3, veio a grande virada arquitetural. O código ficou mais limpo, testável, modular e o NetView virou um sistema de verdade.

    Organização por responsabilidades

    A lógica foi quebrada em módulos:

    NetViewServer: orquestrador de tudo

    PingService: cuida dos pings com backoff exponencial

    DeviceStateManager: gerencia o status e histórico

    NotificationManager: decide quando notificar

    WhatsAppClient: integra com o WhatsApp

    ConfigManager: observa e carrega a config

    Essa separação facilita testes, debugging e extensões futuras.

    Ping mais inteligente

    O ping agora tem:

    • Tentativas com backoff exponencial
    • Timeout configurável
    • Compatibilidade com Windows e Linux
    • Análise de perda de pacotes
    • Concorrência com Promise.all

    CLI Interativo

    Criei um CLI interativo com readline e chalk que permite configurar tudo sem abrir o código:

    $ node server.js --cli

    device-add → adicionar novo dispositivo

    wa-groups → listar grupos do WhatsApp

    wa-set → selecionar o grupo para notificações

    logs → ver as ultimas 20 linhas do arquivo de log

    Por que não usar o Zabbix?

    Desde que criei o NetView, vez ou outra me perguntam: “Mas o Zabbix já não faz tudo isso?”

    Sim, o Zabbix faz muito mais do que o NetView jamais pretendeu fazer. E tá tudo bem! O NetView nunca foi pensado para competir com ferramentas estabelecidas, ele nasceu para resolver uma dor específica de forma rápida e simples.

    CaracterísticaZabbixNetView
    ComplexidadeAltaBaixa
    Setup inicialHorasMinutos
    InterfaceCompleta, mas densaSimples e direta
    AlertasConfiguráveis, múltiplos canaisWhatsApp integrado, basta conectar
    ExtensibilidadeTemplates, plugins, APIsNo código
    Ideal paraAmbientes que precisam de monitoramento robusto, relatórios, SLAQuem quer algo funcionando rápido, com alertas diretos

    O NetView é mais como um “canivete suíço” para quem precisa de algo funcionando hoje, sem treinamento da equipe ou semanas de configuração. É o projeto que você monta numa tarde e deixa rodando.

    Se sua infraestrutura é crítica e você tem tempo para fazer direito, use Zabbix. Se você só quer parar de ficar perguntando “a impressora da recepção tá funcionando?”, o NetView resolve.

    Mais que um projeto, uma trilha de aprendizado

    O NetView é um reflexo da minha trajetória como dev. Começou resolvendo um problema com um script PHP. Depois virou playground com Node.js. Hoje é um sistema estável, modular, fácil de manter, e cheio de lições no meio do caminho.

    É por isso que eu gosto tanto dele. Porque ele cresceu comigo.

    O que realmente aprendi

    Esse projeto me ensinou muita coisa na prática, não só sobre código, mas sobre evolução de software:

    ✅ Como organizar sistemas em módulos coesos
    ✅ CLI com readline e UX de terminal
    ✅ Lógica de retry com backoff exponencial
    ✅ WebSocket bidirecional
    ✅ Integração com APIs não convencionais (WhatsApp Web)
    ✅ Logs com Winston, histórico com JSONL
    ✅ Hot reload de arquivos de config
    ✅ Controle de concorrência com Promises

    Mas principalmente: que software bom é software que resolve problemas reais. O NetView não é revolucionário. Não vai mudar o mundo. Mas toda vez que chego e vejo os vários cards no dashboard, sei que aquele script PHP simples de 2023 virou algo útil de verdade.

    E talvez seja isso que faz a diferença: não a complexidade do código, mas o problema que ele resolve.

  • MoodFilme v2: mais ligeiro e certeiro

    O MoodFilme continua simples — e é assim que eu prefiro. Mas por trás, mudei tudo. Refiz a estrutura, refatorei o código, deixei mais leve e fácil de manter.

    A interface é a mesma. Fiz só pequenos ajustes. O foco foi deixar a base sólida o suficiente pra poder continuar evoluindo o projeto sem tropeçar em código velho.

    O que mudou

    • Recomendações: o algoritmo agora pensa como aquele amigo que manja de cinema. Vai dos bem avaliados aos populares e, se precisar, desenterra umas pérolas escondidas.
    • Backend: o backend é em Node.js, hospedado na Vercel. Mais velocidade e menos travas.
    • Frontend: reorganizei tudo em módulos: interface, requisições, estado. O código ficou mais limpo e modular, sem acúmulo de gambiarra.
    • Novos moods: Misterioso, Aventureiro, Relax, Contemplativo e Inspirador.
    • Outras pequenas melhorias: Agora dá pra acessar um humor direto pela URL (/sombrio, por exemplo), e o site dá um alô diferente dependendo do horário — só pra deixar mais pessoal.

    E o que vem por aí

    Com essa base nova, algumas ideias que já estão no radar:

    • Mais moods (manda sua sugestão!)
    • Salvar recomendações que você gostou
    • Compartilhar recomendações por link direto

    Pra fechar

    O MoodFilme é um projeto pessoal. Não tem anúncio, não rastreia ninguém. Só existe pra te ajudar a achar um filme que combine com o seu momento.

    Testou e curtiu? Fala comigo no @vitor.fcp. Leio tudo.

  • Oracle Always Free: VMs gratuitas e estáveis 24/7

    Descobri há um tempo o Always Free da Oracle e, olha… é difícil ignorar. Eles oferecem recursos de nuvem de graça e por tempo ilimitado.

    São dois tipos de instâncias:

    • ARM (Ampere A1): você tem 4 OCPUs e 24 GB de RAM no total. Pode usar tudo em uma única VM ou dividir em até 4 menores.
    • AMD (E2.1.Micro): dá pra criar até 2 VMs AMD, cada uma com 1 OCPU e 1 GB de RAM.

    Além disso, você ainda tem 200 GB de armazenamento disponíveis.
    Um detalhe importante: todos esses recursos só podem ser criados na home region que você escolhe quando cria sua conta. E essa escolha não dá pra mudar depois, então vale pensar bem antes de confirmar.

    No meu caso, nunca consegui criar instâncias ARM, porque vivem esgotadas na região que escolhi. Mas tudo bem: meu foco sempre foi na AMD mesmo, e têm dado conta do recado. Já rodei uns projetinhos meus por lá, sisteminhas web, bot de zap, etc. Tudo funcionando 24/7, sem pagar um centavo.

    O painel da Oracle é meio chatinho no começo, tem uma curva de aprendizado, mas nada que alguns tutoriais e tentativa-e-erro não resolvam. Depois que pega o jeito, vira uma baita opção pra manter serviços no ar de forma estável e gratuita.

    Se você curte brincar com infra ou quer subir algo leve sem gastar nada, vale a pena testar.

  • Descobrindo jogos para navegador

    Faz um bom tempo desde que tive meu primeiro contato com HTML. Comecei no Orkut, estilizando o meu perfil com tags – quem lembra de usar <b></b> pra deixar o texto assim? 😁

    Pois é. Apesar de trabalhar com web há anos, construindo sites e sistemas, basicamente só cruds, nunca tinha explorado o mundo dos jogos para navegador. E, cara, tô completamente surpreso.

    Na sexta, dia 28, comecei a escrever um joguinho usando HTML, CSS e JavaScript. A ideia? Um battle royale. Nunca tinha programado um jogo antes, então tô contando com a ajuda do Grok, a IA do X, pra ir aprendendo.

    Até agora, já implementei: sistema de partidas, bots, sistema de danos, um mapa pequeno (que vou expandir assim que aprender mais 😅) e personagens – começando a pegar o jeito das animações 3D. E sério, apesar de ter jogado muito jogo em Flash antigamente, rodando no navegador e tal, criar um jogo com HTML, CSS e JS é outra história. Eu nem imaginava que dava pra fazer algo assim.

    A stack que tô usando: HTML, CSS, JavaScript, Three.js, Node.js e Express.js.

    Assim que tiver minimamente jogável, vou subir pro servidor e chamar a galera pra testar. Tô animado.