Autor: Vitor

  • 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.

  • Linux

    Estou usando Linux no meu notebook.

    A frase parece mais importante do que deveria. Afinal, é só um sistema operacional.

    Mas faz algumas semanas que estou usando 100% Linux, depois de alguns anos usando só Windows.

    A última vez que tive um Mac foi em 2021. Estava no MacOS Catalina, acho. Depois disso fui parar no Windows e fiquei por lá. Não porque gostasse particularmente do Windows, mas porque funcionava e eu não tinha nenhum motivo especialmente forte para sair.

    Tenho e-mail no Outlook, tenho Xbox e, de alguma maneira, construí uma vida bastante confortável dentro do ecossistema Microsoft (sim, eu uso outlook que nem os maias faziam).

    Mesmo assim, resolvi me colocar em uma situação um pouco menos confortável.

    Queria ver se conseguia ficar só no Linux.

    Minha experiência com Linux até então era praticamente toda ligada a servidores. Uso Linux há bastante tempo nesse contexto e nunca tive grandes problemas com ele. Desktop, porém, era outra história. Eu imaginava que em algum momento apareceria alguma coisa que me faria pensar “é, por isso que eu uso Windows”.

    Não apareceu.

    Instalei o Linux Mint 22.3 com Cinnamon e tenho gostado bastante da experiência.

    O sistema é bonito e organizado. Gostei das atualizações automáticas, gostei dos Flatpaks e, no geral, tenho a impressão de que o Mint entendeu uma coisa importante sobre sistemas operacionais: eu só quero usar o computador.

    A adaptação também foi mais tranquila do que esperava.

    Às vezes esqueço que estou em outro sistema. No trabalho, as estações usam Windows, então vez ou outra tento usar um atalho que não existe aqui ou ali. É uma espécie de bilinguismo ruim: sei falar os dois idiomas, mas às vezes misturo as palavras.

    Fora essas pequenas confusões, o negócio simplesmente funciona.

    A instalação está aqui desde 25 de junho. Descobri isso porque o sistema deixou seus fósseis no /var/log/installer, como todo bom sistema que sabe que um dia alguém vai querer descobrir quando tudo começou.

    Desde então, Linux 100%. Coloquei até um adesivo do Tux.

    Não sei se isso vai durar para sempre.

    Mas está durando.

  • 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.

  • Seus amigos e vizinhos. (Jonathan Tropper, 2025-2026)

  • Domingo de tarde. Eu no Fortnite, Jaja com o caderno de desenho.


    Em algum momento olhei para o lado e vi o que ela estava desenhando, nós três: eu, ela e Helisa.


    Quis que não tivesse fim.

  • O tempo, as curvas e o silêncio de janeiro

    Meu último post por aqui foi publicado no dia 2 de janeiro. Depois disso, um silêncio longo se instalou neste espaço. Nem sempre o silêncio significa falta de assunto; quase sempre, é o exato oposto. Tempos de calmaria geram ótimos textos em tempo real. Já os tempos em que a vida resolve capotar os dias exigem que a gente primeiro viva para, só depois, tentar traduzir tudo em palavras.

    E quanta coisa cabe em pouco mais de quatro meses.

    Se eu olhasse para o meu cotidiano de um ano atrás, mal reconheceria a rotina que tenho hoje. A começar pela faculdade. Iniciei mais um semestre e, quem está nessa jornada sabe, conciliar os estudos, vida pessoal e profissional é sempre um exercício de equilibrismo.

    No trabalho, a engrenagem mudou de ritmo. Fui promovido a supervisor de TI. Assumir a liderança de um setor em um ambiente complexo e dinâmico como o de um hospital traz um peso diferente para os ombros. De repente, o seu escopo deixa de ser apenas resolver o problema técnico que está na sua frente e passa a ser a gestão das pessoas, dos processos e do tempo. É desafiador, por vezes exaustivo, mas é recompensador notar o próprio amadurecimento profissional se materializando.

    Para acompanhar essas transições e tantas outras nuances que se acumularam na rotina, até as ferramentas do dia a dia mudaram. Em janeiro, troquei de carro. Curiosamente, fiz o caminho inverso da maioria: saí de um modelo mais recente e comprei um mais antigo. Um veículo que exige mais atenção, claro, mas que de alguma forma conversa melhor com o meu apreço por entender como as coisas funcionam.

    Olhando de fora, parece apenas a descrição de um começo de ano corrido. Mais trabalho, mais estudos, deslocamentos, burocracias. Mas a verdade é que toda essa aceleração, a busca por estabilidade, o foco na carreira, a mudança na dinâmica dos dias, não aconteceu por acaso, nem por mera ambição ordinária.

    Havia um norte invisível puxando tudo isso.

    Em outubro do ano passado, o mundo mudou de eixo. Lembro de olhar para aquele resultado e ficar alguns segundos sem saber muito bem o que fazer com o tamanho do que estava vendo. Não era susto, era a sensação de que a vida havia acabado de reorganizar todas as suas prioridades sem me pedir licença. A Jaja, minha companheira, estava grávida. A Helisa, minha primeira filha, estava a caminho.

    Tudo o que fiz, decidi ou organizei desde então foi uma preparação para a chegada dela. E agora, enquanto escrevo estas linhas, estamos batendo na porta das 36 semanas. A expectativa já não cabe mais nos planos. Ela se materializa na nossa sala com várias encomendas da Amazon daquela lista enorme de enxoval chegando quase todo santo dia. Está no berço, que já está pronto e posicionado logo ali, ao lado da nossa cama. E está, principalmente, no corpo da Jaja. O desconforto dessa reta final tem se intensificado bastante nos últimos dias, transformando o sono e o descanso dela em tarefas difíceis. Há um cansaço real aqui, misturado com uma ansiedade boa de quem sabe que a espera está acabando.

  • […] deveríamos ter contato com a experiência de estar vivos para além dos aparatos tecnológicos que podemos inventar. A ideia da economia, por exemplo, essa coisa invisível, a não ser por aquele emblema de cifrão. Pode ser uma ficção afirmar que se a economia não estiver funcionando plenamente nós morremos. Nós poderíamos colocar todos os dirigentes do Banco Central em um cofre gigante e deixá-los vivendo lá, com a economia deles. Ninguém come dinheiro.

    — Ailton Krenak, A vida não é útil.

  • Mercado aquecido?

    Todo mundo diz que o mercado de ti tá aquecido (ondee!?)

    Hoje chegou mais um daqueles e-mails padrão de processo seletivo:

    “Agradecemos sua participação… blá blá blá…”

    Engraçado porque eu nem lembrava de ter me candidatado nessa vaga específica (das tantas que já mandei, normal esquecer uma ou outra, né?)

    Coincidência que ontem mesmo resolvi criar uma planilha pra organizar tudo: vaga, empresa, data, link… só pra não me perder. E o timing foi perfeito: a negativa chegou e já ganhou seu espacinho na planilha 🤣

    No fim, até um não virou dado organizado.

    Melhor que nada.

  • A mulher da casa abandonada

    Assisti ao documentário “A mulher da casa abandonada“, que estreou na última quarta-feira, 13. Como não havia acompanhado o podcast de 2022 que deu origem ao documentário (de mesmo nome), assisti sem conhecer nada do caso. O caso envolve Margarida Bonetti, uma brasileira acusada de manter uma mulher em situação análoga à escravidão nos Estados Unidos.

    Fiquei simplesmente incrédulo com tudo, enojado com a forma como a própria Margarida Bonetti fala sobre o caso, negando tudo e acusando sua vítima de mentirosa. Ela claramente subestima tanto quem entende minimamente do caso quanto a própria vítima. Mas não é de se esperar muito… esse tipo de comportamento, essa fala altiva, é típico da elite branca.

    Estou aqui agora ouvindo o podcast, no episódio 2. Ele conta muito mais do que o documentário inteiro. Ainda assim, o documentário tem seu valor e vale assistir.

  • Sem limites (Neil Burger, 2011)