Você tem um Pentium N3700 com 2 GB de RAM e quer rodar inteligência artificial
local — texto, imagem, análise visual — tudo offline, tudo no seu hardware, sem pagar nada?
Testei isso na prática, errei, corrigi, e aqui está o que realmente funciona.
Este guia cobre desde a instalação do antiX Linux até rodar modelos de
linguagem com Ollama, gerar imagens com Stable Diffusion em CPU,
analisar imagens com Moondream2, e ainda criar uma
Web UI própria com chat, memória e agentes — tudo rodando no mesmo PC modesto.
Por que isso vale a pena mesmo sendo hardware antigo
Privacidade total
100% offline, zero nuvem
Nenhuma prompt sua sai do computador. Sem conta, sem API key, sem limite de uso.
Para quem usa IA para escrever código, rascunhar textos ou analisar documentos,
isso é um diferencial enorme — especialmente com dados sensíveis.
Custo zero
Sem mensalidade, sem limites
Depois de instalar, você usa à vontade. O modelo fica no seu disco e funciona sem
internet. Comparado aos R$ 100-200/mês de APIs cloud, o investimento aqui é zero —
só o hardware que você já tem.
Aprendizado real
Entenda LLMs de verdade
Quando você vê a RAM enchendo token por token durante o carregamento, você entende
na prática o que "parâmetros", "quantização" e "contexto" significam. Isso vale mais
do que qualquer curso teórico.
Reaproveitamento
PC antigo com função nova
O N3700 foi lançado em 2015. Em vez de acumular poeira, ele vira um servidor de IA
doméstico funcional. Lento comparado a uma RTX 4090, mas completamente funcional
para uso pessoal e aprendizado.
Versatilidade
Texto, imagem e visão no mesmo PC
Com o setup completo deste guia você roda três tipos de IA: TinyLlama
para texto, Stable Diffusion 1.5 para imagens e Moondream2
para análise visual. Tudo no mesmo hardware com 2 GB.
Web UI própria
Seu ChatGPT local com Flask
Além dos modelos, este guia ensina a criar uma interface web com chat, histórico,
personas e agentes com acesso à internet — tudo rodando no antiX, acessível por
qualquer dispositivo da sua rede local.
Como o setup completo funciona
1
Instalar o antiX Linux como base mínima do sistema
O antiX consome apenas ~200 MB de RAM em repouso — esse é o segredo de tudo.
Com Windows você perde 2 GB só pra abrir o desktop. Com Ubuntu, ~1,1 GB.
Com antiX, você tem quase 1,8 GB livres para os modelos de IA antes de qualquer coisa.
A escolha do Linux não é detalhe — é o que decide se o setup funciona ou trava.
2
Criar SWAP generoso e configurar o ambiente de ferramentas
Com 2 GB físicos, o SWAP é o que impede os processos de morrer quando a RAM encher.
Um arquivo de 8 GB no disco funciona como "RAM lenta de emergência". Além disso,
você instala corretamente o Python3, pip3 e os compiladores C++ necessários —
e sim, tem truques específicos para o antiX que este guia cobre com comandos exatos.
3
Instalar cada ferramenta de IA separadamente e expandir com sua Web UI
Ollama para texto, stable-diffusion.cpp para imagens e Moondream2 para visão — cada
um tem sua instalação independente. Depois, você constrói uma Web UI com Flask para
ter chat com memória, templates personalizados e agentes autônomos rodando localmente.
Você não precisa de tudo ao mesmo tempo — instale conforme a necessidade.
Para quem é este guia
🖥️ Donos de netbooks e mini-PCs antigos com processadores Intel Atom,
Celeron N3xxx, Pentium N4xxx ou similares — que querem usar IA sem comprar hardware novo
e sem pagar por serviços cloud.
🎓 Estudantes e entusiastas de Linux que querem aprender como LLMs,
geração de imagem e RAG funcionam na prática, com hardware real e sem gastar nada
com servidor, API ou assinatura paga.
🔧 Técnicos em informática e recicladores de hardware que querem
mostrar para clientes que um PC velho ainda tem utilidade real em 2025 — e sair com
um argumento técnico concreto nas mãos.
🚀 Curiosos que já tentaram instalar IA e travaram o PC — este guia
explica exatamente por que travou, mostra os erros reais encontrados durante os testes
e como evitá-los da próxima vez com os comandos corretos.
Parte 1 — Escolhendo a distro certa para rodar IA
Antes de instalar qualquer modelo de IA, preciso ser direto com você: a escolha
do Linux é mais importante do que a escolha do modelo. Aprendi isso da forma mais
dolorosa possível — instalei o Ubuntu 24.04 no N3700, abriu o sistema usando 1,1 GB em
repouso, mal consegui abrir o terminal e já estava no limite. Com o antiX, são 200 MB.
Essa diferença de 900 MB é literalmente o que decide se o TinyLlama vai carregar ou travar.
Testei ou analisei todas as distros abaixo antes de fechar esta recomendação:
Distro
Base
Ambiente
RAM em repouso
RAM livre para IA
Recomendação
antiX 23.1 ✅
Debian Stable
IceWM / Fluxbox
~200 MB
~1,8 GB
Melhor escolha
Puppy Linux
Variada
JWM
~100 MB
~1,9 GB
Funciona, mas setup difícil
Lubuntu 24.04
Ubuntu
LXQt
~450 MB
~1,5 GB
Funciona com limitações
Linux Lite 7
Ubuntu
Xfce
~550 MB
~1,4 GB
Apertado
Zorin OS Lite
Ubuntu
Xfce
~600 MB
~1,3 GB
Não recomendado
Ubuntu 24.04
Ubuntu
GNOME
~1,1 GB
~0,9 GB
❌ Impraticável
Windows 10
—
Explorer
~2,0 GB
~0 GB
❌ Impossível
A recomendação deste guia é o antiX 23.1 de 64 bits. Os motivos práticos:
é baseado no Debian Stable com suporte de longo prazo, tem drivers excelentes para chipsets
Intel Atom/Pentium N, acesso completo ao repositório Debian, e o IceWM consome memória
ridiculamente pouca. O Puppy seria ainda mais leve, mas instalar Ollama, compiladores C++
e Flask nele é trabalho manual demais para quem está começando.
ℹ️ Por que não o MX Linux? O MX Linux é ótimo, mas usa Xfce por padrão
(~500 MB). Para este caso específico de hardware com 2 GB, o antiX é o mesmo projeto
(mesma equipe, mesmo base Debian) mas com ambiente gráfico muito mais leve.
Parte 2 — Instalando o antiX Linux passo a passo
2.1 — Baixando a ISO correta
Acesse o site oficial e baixe a versão full 64-bit. Não baixe a versão
"base" — ela não inclui gerenciador de pacotes gráfico nem bibliotecas básicas que vamos
precisar. A versão "full" inclui tudo que você vai usar neste guia.
https://antixlinux.com/download/
Arquivo esperado: antiX-23.1_x64-full.iso
— aproximadamente 1,5 GB. Verifique o SHA256 informado na página de download antes de gravar,
especialmente se a ISO vai vir de um espelho (mirror).
2.2 — Criando o pendrive bootável
Use o Balena Etcher (mais simples) ou o Ventoy (mais
versátil — permite ter várias ISOs no mesmo pendrive). Aqui vou com o Etcher por ser
mais direto para quem está começando.
Escolha seu pendrive em "Select target" — mínimo 4 GB
Clique em "Flash!" e aguarde cerca de 5 minutos
⚠️ Cuidado: O Etcher apaga tudo no pendrive selecionado. Confira que
escolheu o dispositivo certo antes de clicar em Flash — ele lista nome e tamanho do drive
para facilitar a identificação.
2.3 — Configurando a BIOS para bootar pelo pendrive
Ligue o computador e pressione repetidamente a tecla de boot — geralmente
F2, F12, Del ou Esc dependendo do fabricante. Procure a aba
"Boot" ou "Boot Priority" e coloque o USB primeiro na lista.
Salve com F10 e reinicie.
⚠️ Secure Boot: Em máquinas com UEFI fabricadas após 2012, pode ser
necessário desativar o Secure Boot. Procure a opção na aba
"Security" da BIOS e mude para "Disabled". O antiX não
tem assinatura UEFI por padrão, então sem isso ele simplesmente não inicia.
2.4 — Processo de instalação
O antiX inicia direto em modo Live — você pode testar tudo antes de instalar.
O desktop vai parecer diferente do Windows, mas clique com o botão direito para ver
o menu de aplicativos. Quando decidir instalar:
Clique duas vezes no ícone "Install antiX" no desktop
Selecione o idioma Português do Brasil
Na etapa de partição:
"Use entire disk" — se for o único sistema na máquina
"Manual" — se quiser dual-boot com outro SO
Defina nome de usuário e senha. Anote — sem senha root o acesso administrativo complica
Aguarde a cópia dos arquivos (10 a 20 minutos dependendo do disco)
Quando solicitado, reinicie e retire o pendrive
ℹ️ Experiência real: Na primeira inicialização o desktop do antiX parece
muito "cru" — sem dock, sem área de notificação colorida, sem efeitos visuais. É isso mesmo.
Toda essa leveza é intencional e é exatamente o que libera RAM para os modelos de IA.
Com o tempo você vai achar o IceWM incrivelmente prático.
2.5 — Primeira atualização do sistema
Abra o terminal — atalho Ctrl+Alt+T ou clique direito no desktop e
escolha Terminal. Execute:
sudo apt update && sudo apt upgrade -y
Isso atualiza todos os pacotes do sistema. Na primeira vez pode demorar de 5 a 15 minutos.
Aguarde finalizar completamente antes de prosseguir para as próximas etapas.
ℹ️ Confirmado no terminal real: Este comando está no histórico do N3700
(linha 3 do log) e funcionou sem nenhum problema no antiX 23.1 logo após a instalação.
Parte 3 — Criando o SWAP de 8 GB (obrigatório para IA)
O SWAP é um espaço no disco que o Linux usa como memória de emergência. Quando a RAM física
enche, o sistema começa a mover dados menos usados para o disco. É mais lento — muito mais
lento — mas impede que o processo seja encerrado pelo kernel. Para rodar
modelos de IA com 2 GB de RAM, sem SWAP o processo simplesmente é morto pelo OOM killer
(Out Of Memory) antes de terminar de responder.
Antes de criar, verifique se já existe SWAP ativo no sistema:
free -h
Se a linha Swap:
mostrar 0B,
você não tem SWAP. Se mostrar algum valor, ainda assim recomendo criar um arquivo maior.
Execute os comandos abaixo um de cada vez:
Passo 1 — Criar o arquivo de 8 GB:
sudo fallocate -l 8G /swapfile
Passo 2 — Definir permissões corretas (obrigatório por segurança):
sudo chmod 600 /swapfile
Passo 3 — Formatar como área de SWAP:
sudo mkswap /swapfile
Passo 4 — Ativar imediatamente:
sudo swapon /swapfile
Passo 5 — Tornar permanente após reinicialização:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Passo 6 — Confirmar que funcionou:
free -h
A saída deve mostrar Swap: 8,0G na linha correspondente. Se aparecer,
o SWAP está ativo e será reativado automaticamente toda vez que o sistema ligar.
Passo extra — Ajustar o swappiness (muito recomendado):
Por padrão, o Linux começa a usar SWAP quando a RAM chega a 40% de uso. Para um setup
dedicado a IA com 2 GB, é muito melhor deixar ele usar a RAM ao máximo antes de recorrer
ao disco. O valor 10 significa "use SWAP só quando realmente precisar":
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
⚠️ SSD x HD: Se o seu armazenamento é um SSD, o SWAP vai ser mais rápido
que num HD, mas SSDs têm vida útil limitada por número de escritas. Para uso intenso e
contínuo com modelos de IA (que usam muito SWAP), prefira colocar o arquivo de swap em um
HD mecânico se tiver um disponível no sistema.
Parte 4 — Instalando o Ollama e rodando modelos de texto
O Ollama é a ferramenta mais prática para rodar LLMs localmente no Linux.
Ele funciona como um gerenciador completo: você instala uma vez e depois baixa, troca e
remove modelos com um único comando. Não precisa compilar nada, não precisa configurar
CUDA, não precisa resolver dependências manualmente. Para hardware modesto como o N3700,
é a escolha certa — leve, direto e funcional.
4.1 — Instalando dependências básicas
Antes de instalar o Ollama, garanta que o sistema tem as ferramentas essenciais.
No antiX 23.1 a maioria já vem instalada, mas este comando garante que nada está faltando:
sudo apt install -y curl wget git build-essential
4.2 — Instalando o Ollama
O Ollama tem um script de instalação oficial que baixa o binário, instala no sistema e
configura o serviço automaticamente. Execute no terminal:
curl -fsSL https://ollama.com/install.sh | sh
O script faz tudo: baixa o binário (~50 MB), cria o serviço systemd e já deixa o
Ollama rodando em background. Aguarde finalizar completamente.
4.3 — Verificando a instalação
Confirme que o Ollama foi instalado corretamente:
ollama --version
Deve retornar algo como ollama version 0.x.x.
Se retornou, está pronto. Se der "command not found", feche e reabra o terminal
ou execute source ~/.bashrc
para recarregar o PATH.
ℹ️ Confirmado no terminal real: A linha 12 do histórico mostra
ollama --version executado com sucesso logo após a instalação no antiX 23.1.
A linha 15 mostra ollama list funcionando normalmente em seguida.
4.4 — Baixando e rodando o primeiro modelo
Para o N3700 com 2 GB de RAM, o modelo mais equilibrado para começar é o
SmolLM 135M — ocupa apenas ~90 MB e responde em alguns segundos.
O TinyLlama é a segunda opção, com respostas mais completas mas carregamento mais lento.
# Mais rápido — bom para testar se tudo funciona
ollama run smollm:135m
# Melhor equilíbrio velocidade/qualidade neste hardware
ollama run smollm2:1.7b
# O clássico — respostas mais completas, mais lento no N3700
ollama run tinyllama
Na primeira execução de cada modelo, o Ollama faz o download automático. Depois ele abre
o chat direto no terminal. Digite sua pergunta e pressione Enter.
Para sair: Ctrl+D ou digite /bye.
🔧 Erro encontrado no terminal real (linha 20 do histórico):
O comando ollama run sollm "fale em portugues..." foi digitado com erro
de digitação — "sollm" em vez de "smollm".
O Ollama retorna erro de modelo não encontrado nesse caso.
Correto:ollama run smollm "fale em portugues, o que voce faz"
— confirmado nas linhas 22 e 23 do histórico após a correção.
⚠️ Expectativa de velocidade real: No N3700 sem GPU dedicada, o TinyLlama
gera em torno de 1 a 3 tokens por segundo. Uma resposta de 100 palavras leva de 1 a 3
minutos. O SmolLM 135M é consideravelmente mais rápido. Isso é completamente normal —
você está usando um processador de 6W TDP para fazer o que uma placa RTX faz em segundos.
A paciência faz parte do setup.
4.5 — Tabela completa de modelos compatíveis com 2 GB
Nem todo modelo do Ollama roda neste hardware. A tabela abaixo mostra o que foi testado
ou verificado como compatível com a limitação de RAM:
Modelo
Tamanho
RAM usada
Velocidade
Qualidade
Comando
SmolLM 135M
~90 MB
~300 MB
⚡ Muito rápido
Básica
smollm:135m
SmolLM 360M
~230 MB
~500 MB
⚡ Rápido
Boa
smollm:360m
SmolLM2 1.7B ✅
~1,1 GB
~1,3 GB
Moderado
Muito boa
smollm2:1.7b
TinyLlama 1.1B
~640 MB
~900 MB
Lento
Boa
tinyllama
Gemma3 1B
~820 MB
~1,1 GB
Lento
Muito boa
gemma3:1b
Gemma3 4B
~2,5 GB
~3,0 GB
Muito lento
Excelente
⚠️ Usa SWAP intenso
Llama3 8B
~4,7 GB
>5 GB
—
—
❌ Não roda
ℹ️ Confirmado no histórico: As linhas 17, 21–23 e 24 do terminal mostram
exatamente a sequência de testes com smollm:135m e gemma3:1b
no N3700. O Gemma3 1B rodou, porém com velocidade notavelmente menor que o SmolLM.
O SmolLM2 1.7B é a melhor relação custo-benefício para este hardware.
4.6 — Comandos essenciais do Ollama
Estes são os comandos que você vai usar com mais frequência no dia a dia:
# Ver modelos instalados
ollama list
# Baixar modelo sem abrir o chat
ollama pull smollm2:1.7b
# Rodar modelo com prompt direto (sem abrir chat interativo)
ollama run smollm2:1.7b "explique o que é quantização em LLMs"
# Remover modelo para liberar espaço em disco
ollama rm tinyllama
# Ver informações do serviço
ollama ps
# Parar o serviço (libera RAM quando não estiver usando)
sudo systemctl stop ollama
# Iniciar o serviço novamente
sudo systemctl start ollama
✅ Dica de produtividade: Se você não vai usar o Ollama por um período,
pare o serviço com sudo systemctl stop ollama. Isso libera a RAM que o
daemon ocupa em background (~50–100 MB) e pode fazer diferença quando você vai usar
o stable-diffusion.cpp logo em seguida.
4.7 — Monitorando o uso de RAM em tempo real
Enquanto o modelo carrega e processa, é útil ver o que está acontecendo com a memória.
Abra um segundo terminal e execute:
# Atualiza a cada 2 segundos
watch -n 2 free -h
Você vai ver a RAM enchendo durante o carregamento do modelo, estabilizar durante a
geração e cair quando você encerrar o chat. Quando o SWAP começar a ser usado, a coluna
used
do Swap vai aumentar — isso significa que a geração vai ficar mais lenta, mas não vai parar.
Parte 5 — Stable Diffusion em CPU: geração de imagens local
O stable-diffusion.cpp é uma implementação em C++ do Stable Diffusion
otimizada para rodar inteiramente em CPU, sem precisar de GPU. É a mesma filosofia do
llama.cpp — pega um modelo pesado, aplica quantização agressiva e consegue rodar em
hardware modesto através de otimizações de baixo nível. Para o N3700, você vai gerar
imagens de 256×256 pixels com o modelo SD 1.5 quantizado em 4 bits.
Vou ser honesto sobre o que esperar: uma imagem de 256×256 com 20 steps leva de
15 a 40 minutos no N3700. Sim, é lento. Mas funciona — testei,
a imagem sai no final. A paciência é o único requisito extra que este hardware exige.
⚠️ Leia antes de começar: Esta seção contém correções críticas
baseadas nos erros reais encontrados durante os testes no antiX 23.1. Se você seguir
tutoriais genéricos da internet sem essas correções, vai travar em pelo menos 3 pontos
diferentes. Cada correção está marcada claramente.
5.1 — Instalando as dependências de compilação
O stable-diffusion.cpp precisa ser compilado do zero. Isso exige um conjunto específico
de ferramentas e bibliotecas. Execute este comando completo de uma vez:
🔧 Erros encontrados no terminal real (linhas 25–42):
Erro 1 — Typo no comando apt: As primeiras tentativas usaram
sudo pt install (sem o "a") — o shell retornou "comando não encontrado".
Sempre use sudo apt install.
Erro 2 — cmake não estava instalado: O cmake não vem por padrão no
antiX 23.1 minimal. A linha 40 mostra sudo apt install cmake sendo executado
separadamente após o primeiro cmake falhar. O comando acima já inclui o cmake junto com
todas as outras dependências — instale tudo de uma vez para evitar esse problema.
5.2 — Clonando o repositório
Volte para a pasta home e clone o repositório oficial com todas as submodules:
cd ~
git clone --recursive https://github.com/leejet/stable-diffusion.cpp
cd stable-diffusion.cpp
🔧 Erro encontrado no terminal real (linha 30):
O comando digitado foi cd staable-diffusion.cpp (com duplo "a").
O terminal retornou "No such file or directory". O nome correto do diretório é
stable-diffusion.cpp — use Tab para autocomplete e evite erros de digitação
em nomes longos.
5.3 — Compilando com suporte a OpenBLAS
O OpenBLAS é uma biblioteca de álgebra linear otimizada especificamente para CPUs Intel.
Compilar o stable-diffusion.cpp com esse suporte melhora a velocidade de geração em
até 30% em hardware como o N3700. Execute a sequência completa:
mkdir -p build && cd build
cmake .. -DGGML_OPENBLAS=ON -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
O -j$(nproc)
usa todos os núcleos disponíveis do processador durante a compilação. No N3700 são 4 núcleos —
a compilação completa leva de 10 a 25 minutos. Você verá centenas de linhas de compilação
passando — é completamente normal.
🔧 Erros encontrados no terminal real (linhas 32–44):
Erro 1 — cmake executado fora da pasta build: As primeiras tentativas
(linhas 33 e 37) rodaram o cmake sem entrar na pasta build primeiro. O cmake reclamou
que não encontrava os arquivos de configuração. A sequência correta é sempre:
criar a pasta build → entrar nela → rodar cmake.
Erro 2 — cmake --build vs make: O post original usava
cmake --build . --config Release -j$(nproc), mas o que realmente funcionou
no antiX 23.1 (linha 87 do histórico) foi simplesmente make -j$(nproc)
após o cmake configurar o projeto. Use make — é mais direto e compatível.
5.4 — Identificando o binário correto após a compilação
Quando a compilação terminar, liste os executáveis que foram gerados:
ls bin
A saída que você vai ver é:
sd-cli sd-server
🔧 Correção crítica (linha 95 do terminal):
Quase todos os tutoriais sobre o stable-diffusion.cpp usam o comando
./bin/sd — mas esse binário não existe nas versões atuais
do projeto. O binário correto é ./bin/sd-cli para geração
de imagens e ./bin/sd-server para rodar como servidor HTTP.
A linha 95 do histórico confirma isso com clareza: sd-cli sd-server.
Se você rodar ./bin/sd, vai receber "No such file or directory".
5.5 — Baixando o modelo SD 1.5 quantizado
Você precisa do modelo no formato GGUF, que é o formato comprimido
compatível com o stable-diffusion.cpp. O download direto via wget sem autenticação
vai falhar no HuggingFace para repositórios que exigem conta. Siga o método correto:
Método 1 — Via huggingface-cli (recomendado, mais confiável):
# Passo 1 — Instalar o pip3 (se não estiver instalado)
sudo apt install python3-pip -y
# Passo 2 — Instalar o huggingface_hub com a flag correta para Debian/antiX
pip3 install --user huggingface_hub --break-system-packages
# Passo 3 — Recarregar o PATH (necessário para encontrar o huggingface-cli)
source ~/.bashrc
# Passo 4 — Criar a pasta dos modelos
cd ~
mkdir -p sd-models && cd sd-models
# Passo 5 — Baixar o modelo (~850 MB)
huggingface-cli download leejet/ggml-stable-diffusion-v1-5 ggml-model-q4_0.gguf
Método 2 — Via wget com URL alternativa (funciona sem token):
cd ~/sd-models
# URL alternativa que funciona sem autenticação HuggingFace
wget -c https://huggingface.co/leejet/ggml-stable-diffusion-v1-5/resolve/main/ggml-model-q4_0.gguf
🔧 Erros encontrados no terminal real (linhas 47–76):
Erro 1 — wget sem token falhou: A linha 47 tentou baixar o modelo
com wget direto e falhou. A linha 49 tentou com token Bearer mas também falhou
(token expirado ou URL errada).
Erro 2 — pip não existe no antiX: As linhas 48, 53, 57 e 63 tentaram
usar pip install e receberam "bash: pip: comando não encontrado". No antiX
(e no Debian em geral) o comando é pip3, nunca apenas
pip.
Erro 3 — pip3 sem a flag --break-system-packages: Em sistemas Debian
modernos com PEP 668, instalar pacotes Python sem essa flag retorna erro de "externally
managed environment". O comando correto é sempre
pip3 install --user PACOTE --break-system-packages.
Solução que funcionou (linha 76):pip3 install --user huggingface_hub --break-system-packages
seguido de huggingface-cli download leejet/ggml-stable-diffusion-v1-5 ggml-model-q4_0.gguf
(linha 59 do histórico, após instalar corretamente).
5.6 — Gerando sua primeira imagem
Com a compilação pronta e o modelo baixado, execute o comando de geração. Atenção ao
nome do binário — use sd-cli, não sd:
cd ~/stable-diffusion.cpp/build
./bin/sd-cli \
--model ~/sd-models/ggml-model-q4_0.gguf \
--prompt "a simple landscape with mountains and a river, digital art" \
--negative-prompt "blurry, low quality, ugly, distorted" \
--height 256 \
--width 256 \
--steps 20 \
--seed 42 \
--output ~/imagem_gerada.png
O que cada parâmetro faz:
--model — caminho completo para o arquivo .gguf baixado
--prompt — descrição do que gerar. Inglês funciona melhor com SD 1.5
--negative-prompt — o que você NÃO quer na imagem
--height / --width — 256×256 é muito mais rápido que 512×512. Comece aqui
--steps — iterações de refinamento. Menos = mais rápido, qualidade menor. 20 é bom equilíbrio
--seed — número fixo para reproduzir o mesmo resultado. Troque para variar a geração
--output — onde salvar o arquivo PNG gerado
5.7 — Monitorando a geração em tempo real
Enquanto a imagem é gerada, abra um segundo terminal e monitore a RAM:
watch -n 2 free -h
Você vai ver a RAM enchendo enquanto o modelo carrega (~850 MB) e depois estabilizar
durante os steps de geração. Se a RAM física encher e o SWAP começar a ser usado,
a geração fica ainda mais lenta mas não para — é exatamente para isso
que criamos o SWAP de 8 GB no início.
5.8 — Usando o sd-server para geração via HTTP
Além do sd-cli, a compilação gerou também o sd-server, que
sobe um servidor HTTP local e permite gerar imagens via requisição web — útil para
integrar com scripts ou com a Web UI que vamos criar nas próximas partes:
cd ~/stable-diffusion.cpp/build
# Inicia o servidor na porta 8080
./bin/sd-server \
--model ~/sd-models/ggml-model-q4_0.gguf \
--port 8080 \
--host 0.0.0.0
Com o servidor rodando, você pode gerar imagens via curl de qualquer terminal
ou dispositivo na rede local:
# Gerar imagem via curl (de qualquer máquina na rede)
curl -X POST http://SEU_IP:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "a futuristic city at night, digital art",
"negative_prompt": "blurry, ugly",
"width": 256,
"height": 256,
"sample_steps": 20,
"seed": 42
}' \
--output imagem_via_api.png
✅ Dica importante: Não tente rodar o Ollama e o sd-server ao mesmo
tempo no N3700 com 2 GB. Cada um já usa boa parte da RAM disponível. Pare o serviço
do Ollama antes de iniciar o sd-server:
sudo systemctl stop ollama
Parte 6 — Moondream2: análise de imagens com IA local
O Moondream2 é um VLM — Vision Language Model — com apenas 1,6 bilhões
de parâmetros. Ele consegue olhar para uma imagem e fazer coisas que antes exigiam modelos
enormes: descrever o que vê, responder perguntas sobre o conteúdo, identificar objetos,
extrair texto presente na imagem e até interpretar gráficos simples. É um dos únicos
modelos visuais que roda de forma aceitável dentro de 2 GB de RAM — e funciona 100%
offline, sem mandar nada para nenhuma API.
A combinação mais interessante deste setup é gerar uma imagem com o
stable-diffusion.cpp e depois analisá-la com o Moondream2
— dois modelos de IA diferentes trabalhando em sequência, tudo no mesmo N3700 de 2 GB.
Lento, mas extraordinariamente capaz para o hardware.
6.1 — Instalando Python3 e pip3 corretamente no antiX
O antiX 23.1 já vem com Python 3, mas pode não ter o pip3 e o venv instalados. Execute:
Ambos devem retornar versões. Se o pip3 der "command not found" após a instalação,
feche e reabra o terminal ou execute
source ~/.bashrc.
🔧 Regra crítica para pip3 no antiX / Debian moderno:
Em qualquer instalação Python em Debian, Ubuntu ou antiX modernos, sempre use a flag
--break-system-packages junto com --user ao instalar
pacotes fora de um virtualenv. Sem essa flag, o pip3 vai recusar a instalação com
erro de "externally managed environment" (PEP 668).
6.2 — Criando um ambiente virtual isolado (virtualenv)
Para projetos Python com múltiplas dependências como o Moondream2, sempre crie um
virtualenv. Isso isola as bibliotecas do projeto do resto do sistema, evita conflitos
entre versões e facilita remover tudo se necessário:
cd ~
python3 -m venv moondream-env
source moondream-env/bin/activate
Quando o virtualenv está ativo, o terminal mostra
(moondream-env)
no início da linha. Isso confirma que você está dentro do ambiente isolado.
Dentro do virtualenv, o comando pip (sem o "3") funciona normalmente
e você não precisa da flag --break-system-packages.
ℹ️ Ativando o ambiente nas próximas sessões: O virtualenv precisa ser
ativado toda vez que você abre um novo terminal. Sempre execute
source ~/moondream-env/bin/activate antes de usar o Moondream2.
Para desativar quando terminar: deactivate.
6.3 — Instalando o Moondream2 e dependências
Com o virtualenv ativo, instale o pacote principal e a biblioteca de imagens:
# Certifique-se que o virtualenv está ativo (deve mostrar "(moondream-env)" no prompt)
source ~/moondream-env/bin/activate
pip install moondream Pillow
O download das dependências pode levar alguns minutos dependendo da sua conexão.
O próprio modelo (~1,7 GB) será baixado automaticamente na primeira
execução do script — não durante o pip install.
6.4 — Criando o script de análise de imagem
Crie o arquivo Python com o nano:
nano ~/analisa_imagem.py
Cole o código abaixo integralmente. Para colar no terminal do antiX use
Ctrl+Shift+V. Para salvar: Ctrl+O → Enter → Ctrl+X:
import moondream as md
from PIL import Image
import sys
import os
# ─── Verificação de argumentos ───────────────────────────────
if len(sys.argv) < 2:
print("Uso: python3 analisa_imagem.py /caminho/para/imagem.jpg")
print("Exemplo: python3 analisa_imagem.py ~/imagem_gerada.png")
sys.exit(1)
caminho_imagem = sys.argv[1]
if not os.path.exists(caminho_imagem):
print(f"Erro: arquivo '{caminho_imagem}' não encontrado.")
sys.exit(1)
# ─── Carregar modelo ─────────────────────────────────────────
# Na primeira execução baixa ~1,7 GB automaticamente
# Nas próximas usa o cache local em ~/.cache/moondream
print("Carregando modelo Moondream2... aguarde (pode demorar na primeira vez).")
try:
model = md.vl(model="moondream-2b-int8.mf")
except Exception as e:
print(f"Erro ao carregar o modelo: {e}")
sys.exit(1)
# ─── Carregar e codificar a imagem ───────────────────────────
print(f"\nAnalisando: {caminho_imagem}")
try:
imagem = Image.open(caminho_imagem)
imagem_enc = model.encode_image(imagem)
except Exception as e:
print(f"Erro ao abrir a imagem: {e}")
sys.exit(1)
# ─── Gerar descrição automática ──────────────────────────────
print("\nGerando descrição da imagem...")
try:
descricao = model.caption(imagem_enc)["caption"]
print("\n📷 Descrição:")
print(descricao)
except Exception as e:
print(f"Erro na descrição: {e}")
# ─── Loop interativo de perguntas ────────────────────────────
print("\n💬 Faça perguntas sobre a imagem.")
print(" Digite 'sair' ou pressione Ctrl+C para encerrar.\n")
while True:
try:
pergunta = input("Sua pergunta: ").strip()
if not pergunta:
continue
if pergunta.lower() in ["sair", "exit", "quit", "q"]:
print("Encerrando. Até mais!")
break
resposta = model.query(imagem_enc, pergunta)["answer"]
print(f"Resposta: {resposta}\n")
except KeyboardInterrupt:
print("\nEncerrado pelo usuário.")
break
except Exception as e:
print(f"Erro na pergunta: {e}")
6.5 — Rodando a análise de imagem
Ative o virtualenv e execute o script passando o caminho de qualquer imagem JPG ou PNG:
# Ativar o ambiente virtual
source ~/moondream-env/bin/activate
# Analisar a imagem gerada pelo Stable Diffusion na parte anterior
python3 ~/analisa_imagem.py ~/imagem_gerada.png
# Ou qualquer outra imagem do sistema
python3 ~/analisa_imagem.py ~/Downloads/foto.jpg
Na primeira execução, o Moondream vai baixar automaticamente o modelo
(~1,7 GB) para o cache em
~/.cache/moondream.
Aguarde o download completo. Nas execuções seguintes ele carrega direto do cache,
sem precisar de internet. A análise de uma imagem 256×256 leva de 1 a 3 minutos no N3700.
6.6 — Exemplos práticos de perguntas ao Moondream2
Depois que o modelo carrega e descreve a imagem, você entra no modo interativo.
Alguns exemplos do que você pode perguntar:
Pergunta
Tipo de análise
Funciona bem?
"What colors are dominant in this image?"
Análise de cor
✅ Sim
"Are there any people in this image?"
Detecção de objetos
✅ Sim
"What text can you read in this image?"
OCR / extração de texto
✅ Sim (texto legível)
"Describe the mood or atmosphere"
Análise emocional/estética
✅ Razoável
"What is the main subject?"
Identificação de foco
✅ Sim
"Is this a real photo or digital art?"
Classificação de tipo
⚠️ Parcial
Perguntas muito complexas em português
—
⚠️ Inglês funciona melhor
ℹ️ Dica de uso combinado: Gere uma imagem com o stable-diffusion.cpp,
depois analise-a com o Moondream2. É uma forma excelente de ver dois modelos de IA
completamente diferentes trabalhando em sequência — um cria, o outro interpreta.
Tudo local, tudo offline, no mesmo hardware de 2015.
6.7 — Liberando RAM entre as ferramentas
Com 2 GB de RAM, você não consegue rodar Ollama + Stable Diffusion + Moondream2
ao mesmo tempo. Use um de cada vez e gerencie a memória ativamente:
# Antes de usar o Moondream2, pare o Ollama
sudo systemctl stop ollama
# Verificar RAM disponível antes de começar
free -h
# Após terminar de usar o Moondream, sair do virtualenv
deactivate
# Reiniciar o Ollama se quiser voltar ao chat de texto
sudo systemctl start ollama
⚠️ Atenção com o cache do Moondream: O modelo ocupa ~1,7 GB no disco
em ~/.cache/moondream. Somado ao modelo do SD.cpp (~850 MB) e aos modelos
do Ollama (variável), você pode encher rapidamente um disco pequeno. Verifique o espaço
disponível periodicamente com df -h ~.
Parte 7 — Acessar o Ollama de outra máquina na rede local
Conteúdo novo
Por padrão, o Ollama escuta apenas em
127.0.0.1:11434
— ou seja, só a própria máquina consegue acessar. Se você quiser usar o chat a partir
de outro computador, notebook ou celular conectado na mesma rede Wi-Fi ou cabo, precisa
liberar o Ollama para escutar em todas as interfaces de rede. É um ajuste simples com
duas abordagens: temporária ou permanente.
7.1 — Liberando o acesso na rede (método temporário)
Para testar sem alterar configurações permanentes, pare o serviço e suba manualmente
com a variável de ambiente configurada:
# Parar o serviço atual
sudo systemctl stop ollama
# Subir manualmente escutando em todas as interfaces
OLLAMA_HOST=0.0.0.0 ollama serve
Enquanto esse terminal estiver aberto, o Ollama aceita conexões de qualquer dispositivo
na rede local. Fechar o terminal encerra o serviço.
7.2 — Liberando o acesso de forma permanente
Para que o Ollama sempre inicie escutando na rede, edite o arquivo de serviço systemd:
# Abrir o arquivo de configuração do serviço
sudo nano /etc/systemd/system/ollama.service
Encontre a seção [Service]
e adicione a linha de Environment logo abaixo dela:
[Service]
Environment="OLLAMA_HOST=0.0.0.0"
ExecStart=/usr/local/bin/ollama serve
# ... resto do arquivo permanece igual
Salve com Ctrl+O → Enter → Ctrl+X e recarregue o serviço:
sudo systemctl daemon-reload
sudo systemctl restart ollama
# Confirmar que o serviço reiniciou corretamente
sudo systemctl status ollama
7.3 — Descobrindo o IP da máquina antiX
Para acessar de outro dispositivo, você precisa saber o IP local da máquina com o Ollama:
ip a
Procure uma linha como
inet 192.168.1.10/24
na interface eth0 (cabo) ou wlan0 (Wi-Fi). O IP nesse exemplo
seria 192.168.1.10. Anote — você vai usar bastante nas próximas seções.
7.4 — Testando o acesso de outra máquina
De qualquer outro computador ou celular na mesma rede, teste no terminal ou navegador:
# Via curl (de outro terminal Linux/Mac/WSL)
curl http://192.168.1.10:11434/api/tags
# Via navegador — abra este endereço no browser de qualquer dispositivo da rede
# http://192.168.1.10:11434
Se retornar um JSON com a lista de modelos instalados, está funcionando.
7.5 — Liberando a porta no firewall (se necessário)
Se o acesso não funcionar mesmo com o Ollama configurado, o firewall pode estar bloqueando.
No antiX com ufw:
# Verificar se o ufw está ativo
sudo ufw status
# Se estiver ativo, liberar a porta do Ollama
sudo ufw allow 11434
# Confirmar
sudo ufw status verbose
⚠️ Segurança importante: Abrir o Ollama na rede local significa que
qualquer dispositivo conectado ao seu roteador pode usar seus modelos de IA sem
autenticação. Em redes domésticas isso é geralmente aceitável. Em redes corporativas
ou públicas, considere restringir por IP no firewall ou usar um proxy com autenticação
na frente do Ollama.
Parte 8 — Templates personalizados com Modelfile
Conteúdo novo
O Ollama permite criar modelos personalizados usando um Modelfile —
um arquivo de configuração que define o modelo base, o prompt de sistema fixo, o formato
de resposta e o "comportamento" do assistente. É como criar uma persona especializada
em cima de um modelo existente. Você define uma vez e chama sempre com o mesmo nome
customizado.
Um caso de uso prático: criar um assistente que sempre responde em português, sempre
segue um formato específico de resposta e sempre assume um perfil técnico — sem precisar
repetir isso em cada prompt manualmente.
8.1 — Criando seu primeiro Modelfile
Crie um arquivo chamado Modelfile em qualquer pasta:
nano ~/Modelfile
Cole o conteúdo abaixo. Este exemplo cria um assistente técnico que responde em
português e sempre segue uma estrutura de 3 partes:
FROM smollm2:1.7b
SYSTEM Você é um assistente técnico especializado. Sempre responda em português do Brasil.
Siga obrigatoriamente este formato em todas as respostas:
**Resumo:** uma frase curta respondendo diretamente
**Explicação:** o detalhamento técnico completo
**Próximo passo:** uma ação prática que o usuário pode tomar agora
Se alguma informação estiver faltando para responder bem, peça antes de inventar.
PARAMETER temperature 0.7
PARAMETER num_ctx 2048
O que cada instrução faz:
FROM — modelo base que você já tem instalado no Ollama
SYSTEM — prompt fixo que define o comportamento. O modelo lê isso antes de qualquer pergunta
PARAMETER temperature — controla criatividade. 0.0 = mais determinístico, 1.0 = mais criativo
PARAMETER num_ctx — tamanho da janela de contexto em tokens. 2048 é seguro para 2 GB
8.2 — Criando o modelo customizado
ollama create assistente-tecnico -f ~/Modelfile
O Ollama vai processar o Modelfile e criar o modelo com o nome
assistente-tecnico
— que agora aparece na sua lista de modelos local:
ollama list
8.3 — Usando o modelo customizado
# Abrir chat interativo com o modelo personalizado
ollama run assistente-tecnico
# Ou com prompt direto
ollama run assistente-tecnico "como funciona o SWAP no Linux?"
8.4 — Exemplos de Modelfiles especializados
Você pode criar quantos modelos customizados quiser — cada um com um perfil diferente.
Alguns exemplos úteis para o dia a dia:
Assistente de código Python:
FROM smollm2:1.7b
SYSTEM Você é um especialista em Python. Quando gerar código:
- Sempre inclua comentários explicativos em português
- Sempre inclua tratamento de erros (try/except)
- Sempre explique o que o código faz antes de mostrá-lo
- Nunca use bibliotecas externas sem avisar que precisam ser instaladas
PARAMETER temperature 0.3
Tradutor técnico EN↔PT:
FROM smollm2:1.7b
SYSTEM Você é um tradutor técnico especializado em tecnologia e programação.
Ao receber texto em inglês, traduza para português técnico brasileiro.
Ao receber texto em português, traduza para inglês técnico.
Preserve termos técnicos consagrados (como "commit", "deploy", "pipeline")
quando não há tradução estabelecida em português.
Nunca adicione explicações — só a tradução.
PARAMETER temperature 0.1
Revisor de texto:
FROM smollm2:1.7b
SYSTEM Você é um revisor de texto profissional para conteúdo técnico de blog.
Ao receber um texto, identifique e corrija:
1. Erros gramaticais e ortográficos
2. Frases confusas ou longas demais
3. Repetições desnecessárias
4. Linguagem robótica ou corporativa
Retorne o texto corrigido com as mudanças destacadas entre [colchetes].
PARAMETER temperature 0.2
8.5 — Gerenciando os modelos customizados
# Listar todos os modelos (incluindo os customizados)
ollama list
# Ver detalhes de um modelo customizado
ollama show assistente-tecnico
# Remover modelo customizado para liberar espaço
ollama rm assistente-tecnico
# Recriar após editar o Modelfile
ollama create assistente-tecnico -f ~/Modelfile
ℹ️ Dica importante: Modelos customizados criados com Modelfile
não ocupam espaço extra em disco — eles referenciam o modelo base
que você já tem instalado e apenas adicionam as configurações do Modelfile.
Você pode criar dezenas de perfis diferentes sem usar mais espaço.
8.6 — Simular "campos" com placeholders no prompt
Uma técnica prática é padronizar seus prompts com marcadores substituíveis —
uma espécie de formulário em texto puro. Crie um script bash simples para isso:
nano ~/prompt_form.sh
#!/bin/bash
# Script de formulário de prompt para Ollama — @CanalQb
echo "=== Formulário de Prompt ==="
read -p "Tarefa: " tarefa
read -p "Contexto: " contexto
read -p "Formato da resposta: " formato
PROMPT="Tarefa: $tarefa
Contexto: $contexto
Formato desejado: $formato
Responda seguindo exatamente o formato solicitado."
echo ""
echo "Enviando para o Ollama..."
echo ""
ollama run assistente-tecnico "$PROMPT"
chmod +x ~/prompt_form.sh
~/prompt_form.sh
Agora você tem um "formulário de terminal" que captura tarefa, contexto e formato
antes de enviar para o modelo. É simples, mas resolve 80% dos casos de uso do dia a dia
sem precisar de interface gráfica.
Parte 9 — Web UI com Flask: seu ChatGPT local
Conteúdo novo
Até aqui você usou o Ollama pelo terminal. Funciona, mas não é prático para o dia a dia —
especialmente se você quiser acessar de outro dispositivo da rede ou manter histórico
de conversas. A solução é criar uma interface web simples com Flask,
o microframework Python mais leve e direto que existe. Em menos de 100 linhas você tem
um chat funcional com memória, personas e templates salvos — rodando no antiX e acessível
de qualquer browser na sua rede local.
9.1 — Instalando o Flask e dependências
Use o virtualenv que já criamos (ou crie um novo separado para o projeto web):
# Criar virtualenv dedicado para a Web UI
cd ~
python3 -m venv webui-env
source webui-env/bin/activate
# Instalar Flask e requests
pip install flask requests
9.2 — Estrutura do projeto
Crie a estrutura de pastas do projeto:
mkdir -p ~/ollama-webui/templates
cd ~/ollama-webui
9.3 — Criando o arquivo de personas
As personas são perfis de assistente com instruções fixas. Crie o arquivo JSON:
nano ~/ollama-webui/personas.json
{
"Assistente Geral": "Você é um assistente prestativo que responde sempre em português do Brasil de forma clara e direta.",
"Dev Python": "Você é um especialista em Python. Sempre inclua código com comentários em português e tratamento de erros.",
"Tradutor EN-PT": "Traduza o texto recebido para português técnico brasileiro. Preserve termos técnicos consagrados.",
"Revisor de Blog": "Você revisa textos de blog técnico. Corrija gramática, simplifique frases longas e elimine linguagem robótica.",
"Analista Linux": "Você é um especialista em Linux/Debian. Explique comandos, diagnósticos e configurações de forma prática."
}
9.4 — Criando o arquivo de templates de prompt
nano ~/ollama-webui/templates_prompt.json
{
"Resumo de texto": "Resuma o seguinte texto em no máximo 5 pontos principais:\n\n{{texto}}",
"Explicação técnica": "Explique de forma clara e prática, com exemplos reais:\n\n{{conceito}}",
"Revisão de código": "Revise este código, identifique problemas e sugira melhorias:\n\n{{codigo}}",
"Tradução EN→PT": "Traduza para português técnico brasileiro:\n\n{{texto_ingles}}",
"Geração de script bash": "Crie um script bash que faça o seguinte:\n\n{{descricao}}\n\nInclua comentários e tratamento de erros."
}
9.5 — Backend principal (app.py)
nano ~/ollama-webui/app.py
from flask import Flask, render_template, request, jsonify, session
import requests
import json
import os
import uuid
app = Flask(__name__)
app.secret_key = os.urandom(24)
OLLAMA_URL = "http://localhost:11434/api/generate"
OLLAMA_CHAT = "http://localhost:11434/api/chat"
MODEL_NAME = "assistente-tecnico" # ou smollm2:1.7b se não criou Modelfile
# ─── Helpers para carregar JSONs ─────────────────────────────
def load_json(filepath, default={}):
try:
with open(filepath) as f:
return json.load(f)
except Exception:
return default
# ─── Memória de conversa por sessão (em RAM) ─────────────────
conversations = {}
def get_history(session_id):
return conversations.get(session_id, [])
def add_message(session_id, role, content):
if session_id not in conversations:
conversations[session_id] = []
conversations[session_id].append({"role": role, "content": content})
# Manter apenas as últimas 20 mensagens para não explodir a RAM
if len(conversations[session_id]) > 20:
conversations[session_id] = conversations[session_id][-20:]
# ─── Rota principal ──────────────────────────────────────────
@app.route("/")
def index():
personas = load_json("personas.json")
templates_prompt = load_json("templates_prompt.json")
if "session_id" not in session:
session["session_id"] = str(uuid.uuid4())
return render_template(
"index.html",
personas=personas,
templates_prompt=templates_prompt,
history=get_history(session["session_id"])
)
# ─── API de chat ─────────────────────────────────────────────
@app.route("/api/chat", methods=["POST"])
def chat():
data = request.json or {}
message = data.get("message", "").strip()
persona = data.get("persona", "Assistente Geral")
session_id = session.get("session_id", "default")
if not message:
return jsonify({"error": "Mensagem vazia"}), 400
personas = load_json("personas.json")
system = personas.get(persona, "Você é um assistente útil.")
add_message(session_id, "user", message)
# Montar histórico para o Ollama (formato /api/chat)
messages = [{"role": "system", "content": system}]
for msg in get_history(session_id):
messages.append({"role": msg["role"], "content": msg["content"]})
try:
res = requests.post(
OLLAMA_CHAT,
json={"model": MODEL_NAME, "messages": messages, "stream": False},
timeout=300
)
res.raise_for_status()
answer = res.json().get("message", {}).get("content", "Sem resposta.")
except requests.exceptions.ConnectionError:
return jsonify({"error": "Ollama não está rodando. Execute: sudo systemctl start ollama"}), 503
except Exception as e:
return jsonify({"error": str(e)}), 500
add_message(session_id, "assistant", answer)
return jsonify({"response": answer, "persona": persona})
# ─── Limpar histórico da sessão ──────────────────────────────
@app.route("/api/clear", methods=["POST"])
def clear_history():
session_id = session.get("session_id", "default")
conversations.pop(session_id, None)
return jsonify({"status": "ok"})
# ─── Listar modelos disponíveis ──────────────────────────────
@app.route("/api/models")
def list_models():
try:
res = requests.get("http://localhost:11434/api/tags", timeout=5)
models = [m["name"] for m in res.json().get("models", [])]
return jsonify({"models": models})
except Exception:
return jsonify({"models": [], "error": "Ollama offline"})
if __name__ == "__main__":
print("=== @CanalQb Ollama Web UI ===")
print("Acesse: http://localhost:5000")
print("Rede local: http://SEU_IP:5000")
app.run(host="0.0.0.0", port=5000, debug=False)
# Ativar o virtualenv
source ~/webui-env/bin/activate
# Entrar na pasta do projeto
cd ~/ollama-webui
# Iniciar o servidor
python3 app.py
O terminal vai mostrar o endereço de acesso. Abra no navegador do mesmo PC ou de
qualquer dispositivo na rede:
# No mesmo computador
http://localhost:5000
# De outro dispositivo na rede local (substitua pelo IP real)
http://192.168.1.10:5000
ℹ️ O que você acabou de criar: Uma interface web estilo ChatGPT,
com seleção de personas, templates de prompt, histórico de conversa na sessão e
acesso de qualquer dispositivo da rede — tudo rodando no antiX com o Ollama local.
Zero dependência de nuvem, zero custo recorrente.
Parte 10 — Agentes com internet e leitura de arquivos
Conteúdo novo
O passo final é dar ao seu assistente local acesso ao mundo externo: pesquisar na
internet, ler arquivos do disco e executar comandos do sistema. Isso transforma o
chat simples em um agente capaz de tomar ações reais — não apenas gerar texto.
import subprocess
import requests
import os
from bs4 import BeautifulSoup
# ─── Tool 1: Busca na web via DuckDuckGo ─────────────────────
def search_web(query):
"""Busca os 5 primeiros resultados no DuckDuckGo sem tracker."""
try:
url = f"https://duckduckgo.com/html/?q={requests.utils.quote(query)}"
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"}
res = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(res.text, "html.parser")
results = []
for a in soup.select(".result__a")[:5]:
title = a.get_text(strip=True)
href = a.get("href", "")
results.append(f"• {title}\n {href}")
return "\n\n".join(results) if results else "Nenhum resultado encontrado."
except Exception as e:
return f"Erro na busca: {e}"
# ─── Tool 2: Ler arquivo de texto ────────────────────────────
def read_file(path):
"""Lê e retorna o conteúdo de um arquivo de texto."""
path = path.strip().replace("~", os.path.expanduser("~"))
try:
if not os.path.exists(path):
return f"Arquivo não encontrado: {path}"
if os.path.getsize(path) > 50_000:
return "Arquivo muito grande (>50KB). Leia por partes."
with open(path, "r", encoding="utf-8", errors="replace") as f:
return f.read()
except Exception as e:
return f"Erro ao ler arquivo: {e}"
# ─── Tool 3: Executar comando do sistema ─────────────────────
COMANDOS_PERMITIDOS = ["ls", "pwd", "free", "df", "uname", "cat",
"echo", "date", "whoami", "ip", "ping", "ollama"]
def run_command(cmd):
"""Executa um comando shell. Restrito a comandos da lista segura."""
cmd = cmd.strip()
base_cmd = cmd.split()[0] if cmd else ""
if base_cmd not in COMANDOS_PERMITIDOS:
return f"Comando '{base_cmd}' não permitido. Permitidos: {', '.join(COMANDOS_PERMITIDOS)}"
try:
result = subprocess.run(
cmd, shell=True, capture_output=True,
text=True, timeout=10
)
output = result.stdout or result.stderr or "(sem saída)"
return output[:2000] # limitar tamanho da resposta
except subprocess.TimeoutExpired:
return "Timeout: comando demorou mais de 10 segundos."
except Exception as e:
return f"Erro ao executar: {e}"
# ─── Roteador de agente ──────────────────────────────────────
def agent_router(message):
"""
Detecta comandos especiais na mensagem e roteia para a tool correta.
Retorna None se não for um comando de agente (resposta normal via LLM).
Comandos reconhecidos:
buscar: → pesquisa DuckDuckGo
arquivo: → lê arquivo do disco
comando: → executa comando permitido
"""
msg = message.strip()
lower = msg.lower()
if lower.startswith("buscar:"):
query = msg[7:].strip()
if not query:
return "Informe o que buscar. Exemplo: buscar: ollama linux 2025"
return f"🌐 Resultados para '{query}':\n\n{search_web(query)}"
if lower.startswith("arquivo:"):
path = msg[8:].strip()
if not path:
return "Informe o caminho. Exemplo: arquivo: ~/meu_historico.txt"
conteudo = read_file(path)
return f"📄 Conteúdo de {path}:\n\n{conteudo}"
if lower.startswith("comando:"):
cmd = msg[8:].strip()
if not cmd:
return "Informe o comando. Exemplo: comando: free -h"
return f"💻 Saída do comando '{cmd}':\n\n{run_command(cmd)}"
return None # Sem comando especial — usa LLM normalmente
10.3 — Integrando os agentes no app.py
Edite o app.py para importar e usar o roteador de agentes na rota de chat:
# No topo do app.py, adicione o import:
from agents import agent_router
E dentro da função chat(), antes da chamada ao Ollama, adicione:
# Verificar se é um comando de agente primeiro
tool_result = agent_router(message)
if tool_result:
add_message(session_id, "user", message)
add_message(session_id, "assistant", tool_result)
return jsonify({"response": tool_result, "persona": "Agente"})
10.4 — Como usar os agentes no chat
Na interface web, use estes prefixos para acionar os agentes diretamente:
Prefixo
O que faz
Exemplo
buscar:
Pesquisa no DuckDuckGo e retorna os 5 primeiros resultados
buscar: ollama linux pc fraco 2025
arquivo:
Lê e exibe o conteúdo de qualquer arquivo de texto do disco
arquivo: ~/meu_historico.txt
comando:
Executa um comando shell seguro e retorna a saída
comando: free -h
(sem prefixo)
Resposta normal via LLM com a persona selecionada
como funciona o SWAP no Linux?
⚠️ Segurança dos agentes: O módulo já tem proteção contra comandos
perigosos — apenas os da lista COMANDOS_PERMITIDOS são executados.
Nunca adicione rm, sudo, chmod ou
dd a essa lista. Se for expor a Web UI na internet (e não apenas
na rede local), adicione autenticação antes de disponibilizar o endpoint de agentes.
10.5 — Resumo do que você construiu
🧠 Arquitetura completa do setup:
Navegador (qualquer dispositivo na rede local)
↓
Flask Web UI (porta 5000)
↓
Agent Router
├── buscar: → DuckDuckGo scraper
├── arquivo: → leitura de arquivos locais
├── comando: → shell com lista segura
└── (chat) → Ollama API (porta 11434)
↓
Modelo LLM local
(smollm2:1.7b / tinyllama / gemma3:1b)
↓
Resposta no chat
ℹ️ Nota Técnica: Todos os scripts, comandos e configurações deste guia
são para fins educacionais e uso pessoal. Teste sempre em ambiente controlado antes de
aplicar em produção. O autor não se responsabiliza por perda de dados durante
particionamento/instalação ou por uso indevido dos agentes em redes não autorizadas.
Compilei aqui todos os erros que aparecem com maior frequência neste setup — seja
pelos meus próprios testes, seja por relatos de quem tentou seguir tutoriais incompletos.
Se travou em algum ponto, a resposta provavelmente está nesta seção.
Sintoma / Erro
Causa provável
Solução
ollama: command not found
PATH não atualizado após instalação
source ~/.bashrc ou feche e reabra o terminal
Error: model not found no ollama run
Nome do modelo com typo ou modelo não baixado
Verifique com ollama list. Use Tab para autocomplete após ollama run
Processo morto pelo sistema durante geração
OOM killer — RAM esgotada sem SWAP
Crie o SWAP de 8 GB conforme a Parte 3. Confirme com free -h
cmake: command not found
cmake não instalado no antiX
sudo apt install -y cmake
./bin/sd: No such file or directory
Binário correto é sd-cli, não sd
Use ./bin/sd-cli. Confirme com ls bin
pip: command not found
No antiX/Debian o binário é pip3
Use pip3 sempre. Instale com sudo apt install python3-pip -y
Erro "externally managed environment" no pip3
PEP 668 — Debian bloqueia instalação global
Adicione --user --break-system-packages ou use virtualenv
Download do HuggingFace falha com 401
Repositório requer autenticação
Use huggingface-cli login com token ou tente URL alternativa com wget -c
Web UI retorna "Ollama não está rodando"
Serviço Ollama parado
sudo systemctl start ollama e aguarde 5 segundos
Web UI acessível localmente mas não da rede
Ollama escutando só em 127.0.0.1
Configure OLLAMA_HOST=0.0.0.0 conforme a Parte 7
Geração de imagem demora mais de 1 hora
SWAP sendo usado intensamente (HD lento)
Reduza para --steps 10 e resolução --width 128 --height 128
No module named 'moondream'
Virtualenv não ativado
source ~/moondream-env/bin/activate antes de executar
Moondream não baixa o modelo automaticamente
Sem conexão ou proxy bloqueando HuggingFace
Teste conexão com curl -I https://huggingface.co
Flask retorna 500 Internal Server Error
Erro no app.py ou Ollama offline
Verifique o log no terminal onde rodou python3 app.py
antiX não inicia pelo pendrive
Secure Boot ativo ou boot order errado
Desative Secure Boot na BIOS (aba Security) e coloque USB como primeiro na ordem de boot
Referência rápida — todos os comandos essenciais
Um cartão de referência para consultar sem precisar rolar o post inteiro.
Salve essa seção nos favoritos ou copie para um arquivo de texto na sua máquina.
Sistema e RAM
# Ver uso de RAM em tempo real
watch -n 2 free -h
# Ver espaço em disco
df -h ~
# Ver processos consumindo mais RAM
ps aux --sort=-%mem | head -10
# Ver tamanho dos modelos em disco
du -sh ~/.ollama/models/
du -sh ~/.cache/moondream/
du -sh ~/sd-models/
Ollama
# Status do serviço
sudo systemctl status ollama
# Iniciar / parar / reiniciar
sudo systemctl start ollama
sudo systemctl stop ollama
sudo systemctl restart ollama
# Listar modelos instalados
ollama list
# Baixar modelo
ollama pull smollm2:1.7b
# Chat interativo
ollama run smollm2:1.7b
# Prompt direto (sem abrir chat)
ollama run smollm2:1.7b "sua pergunta aqui"
# Ver modelos em execução
ollama ps
# Remover modelo
ollama rm tinyllama
# Criar modelo customizado
ollama create meu-modelo -f ~/Modelfile
# Liberar para rede local (temporário)
OLLAMA_HOST=0.0.0.0 ollama serve
Este script verifica automaticamente se todos os componentes do setup estão instalados
e funcionando corretamente. Cole, salve e execute sempre que algo não estiver se
comportando como esperado:
nano ~/verifica_setup.sh
#!/bin/bash
# ─────────────────────────────────────────────
# Script de diagnóstico do setup IA local
# @CanalQb — antiX Linux + Ollama + SD.cpp + Moondream
# ─────────────────────────────────────────────
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # sem cor
ok() { echo -e " ${GREEN}✅ $1${NC}"; }
fail() { echo -e " ${RED}❌ $1${NC}"; }
warn() { echo -e " ${YELLOW}⚠️ $1${NC}"; }
echo ""
echo "═══════════════════════════════════════════"
echo " 🔍 Diagnóstico do Setup IA Local @CanalQb"
echo "═══════════════════════════════════════════"
# ── RAM e SWAP ──────────────────────────────────
echo ""
echo "[ RAM e SWAP ]"
RAM_TOTAL=$(free -m | awk '/^Mem:/{print $2}')
SWAP_TOTAL=$(free -m | awk '/^Swap:/{print $2}')
RAM_LIVRE=$(free -m | awk '/^Mem:/{print $7}')
echo " RAM total: ${RAM_TOTAL} MB"
echo " RAM livre: ${RAM_LIVRE} MB"
echo " SWAP total: ${SWAP_TOTAL} MB"
[ "$SWAP_TOTAL" -ge 4096 ] && ok "SWAP adequado (${SWAP_TOTAL} MB)" || fail "SWAP insuficiente — crie pelo menos 4 GB (recomendado 8 GB)"
[ "$RAM_LIVRE" -ge 500 ] && ok "RAM livre suficiente para iniciar modelos" || warn "RAM livre baixa (${RAM_LIVRE} MB) — feche outros programas"
# ── Ollama ──────────────────────────────────────
echo ""
echo "[ Ollama ]"
if command -v ollama &>/dev/null; then
OLLAMA_VER=$(ollama --version 2>/dev/null | head -1)
ok "Ollama instalado: $OLLAMA_VER"
if systemctl is-active --quiet ollama 2>/dev/null; then
ok "Serviço Ollama rodando"
else
fail "Serviço Ollama parado — execute: sudo systemctl start ollama"
fi
MODELOS=$(ollama list 2>/dev/null | grep -v NAME | wc -l)
[ "$MODELOS" -gt 0 ] && ok "${MODELOS} modelo(s) instalado(s)" || warn "Nenhum modelo instalado — execute: ollama run smollm2:1.7b"
else
fail "Ollama não instalado — execute: curl -fsSL https://ollama.com/install.sh | sh"
fi
# ── Stable Diffusion ────────────────────────────
echo ""
echo "[ Stable Diffusion ]"
if [ -f ~/stable-diffusion.cpp/build/bin/sd-cli ]; then
ok "sd-cli compilado com sucesso"
else
fail "sd-cli não encontrado em ~/stable-diffusion.cpp/build/bin/sd-cli"
warn "Compile com: cd ~/stable-diffusion.cpp/build && make -j\$(nproc)"
fi
MODELO_SD=$(ls ~/sd-models/*.gguf 2>/dev/null | head -1)
if [ -n "$MODELO_SD" ]; then
TAMANHO=$(du -sh "$MODELO_SD" | cut -f1)
ok "Modelo SD encontrado: $(basename $MODELO_SD) (${TAMANHO})"
else
fail "Nenhum modelo .gguf encontrado em ~/sd-models/"
fi
# ── Python e virtualenvs ─────────────────────────
echo ""
echo "[ Python e Ambientes Virtuais ]"
if command -v python3 &>/dev/null; then
PY_VER=$(python3 --version)
ok "$PY_VER instalado"
else
fail "Python3 não instalado — execute: sudo apt install python3 -y"
fi
if command -v pip3 &>/dev/null; then
ok "pip3 disponível"
else
fail "pip3 não encontrado — execute: sudo apt install python3-pip -y"
fi
[ -d ~/moondream-env ] && ok "Virtualenv moondream-env encontrado" || warn "Virtualenv moondream-env não criado"
[ -d ~/webui-env ] && ok "Virtualenv webui-env encontrado" || warn "Virtualenv webui-env não criado"
# ── Web UI ──────────────────────────────────────
echo ""
echo "[ Web UI Flask ]"
if [ -f ~/ollama-webui/app.py ]; then
ok "app.py encontrado"
else
warn "app.py não encontrado em ~/ollama-webui/"
fi
if ss -tlnp 2>/dev/null | grep -q ':5000'; then
ok "Web UI rodando na porta 5000"
else
warn "Web UI não está rodando — execute: cd ~/ollama-webui && python3 app.py"
fi
# ── Rede ────────────────────────────────────────
echo ""
echo "[ Rede ]"
IP_LOCAL=$(ip -4 addr show scope global | grep -oP '(?<=inet )\d+\.\d+\.\d+\.\d+' | head -1)
[ -n "$IP_LOCAL" ] && ok "IP local: ${IP_LOCAL}" || warn "IP local não detectado"
if ss -tlnp 2>/dev/null | grep -q ':11434'; then
ok "Ollama API acessível na porta 11434"
else
warn "Porta 11434 não está aberta"
fi
# ── Espaço em disco ─────────────────────────────
echo ""
echo "[ Espaço em Disco ]"
DISCO_LIVRE=$(df -m ~ | awk 'NR==2{print $4}')
echo " Espaço livre em ~: ${DISCO_LIVRE} MB"
[ "$DISCO_LIVRE" -ge 5120 ] && ok "Espaço suficiente para modelos" || warn "Pouco espaço livre (${DISCO_LIVRE} MB) — considere liberar espaço"
OLLAMA_SIZE=$(du -sh ~/.ollama/models/ 2>/dev/null | cut -f1)
SD_SIZE=$(du -sh ~/sd-models/ 2>/dev/null | cut -f1)
MOON_SIZE=$(du -sh ~/.cache/moondream/ 2>/dev/null | cut -f1)
echo " Modelos Ollama: ${OLLAMA_SIZE:-0}"
echo " Modelos SD: ${SD_SIZE:-0}"
echo " Cache Moondream: ${MOON_SIZE:-0}"
echo ""
echo "═══════════════════════════════════════════"
echo " Diagnóstico concluído."
echo "═══════════════════════════════════════════"
echo ""
chmod +x ~/verifica_setup.sh
~/verifica_setup.sh
O script verifica RAM, SWAP, Ollama (versão, serviço e modelos), sd-cli, virtualenvs,
Web UI, rede e espaço em disco — tudo em segundos, com saída colorida mostrando o que
está ok e o que precisa de atenção.
Iniciando a Web UI automaticamente com o sistema
Se você usa a Web UI com frequência, pode configurar para ela iniciar automaticamente
toda vez que o antiX ligar — sem precisar abrir terminal e ativar o virtualenv manualmente.
Existem duas formas de fazer isso: via serviço systemd (mais robusto) ou via script de
inicialização do usuário (mais simples).
Método 1 — Script de inicialização do usuário (simples)
Crie um script de startup e adicione-o ao autostart do antiX:
nano ~/start_webui.sh
#!/bin/bash
# Aguarda o Ollama subir completamente antes de iniciar a Web UI
sleep 10
# Ativa o virtualenv e sobe o Flask em background
source ~/webui-env/bin/activate
cd ~/ollama-webui
nohup python3 app.py > ~/webui.log 2>&1 &
echo "Web UI iniciada. Log em ~/webui.log"
chmod +x ~/start_webui.sh
No antiX, adicione ao autostart via Menu → Preferências → Sessão e Inicialização
ou edite diretamente o arquivo:
nano ~/.config/autostart/webui-ollama.desktop
[Desktop Entry]
Type=Application
Name=Ollama Web UI
Exec=/home/SEU_USUARIO/start_webui.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
⚠️ Substitua SEU_USUARIO pelo seu nome de usuário real no antiX.
Descubra com o comando whoami.
Método 2 — Serviço systemd (robusto)
Para um setup mais profissional, crie um serviço systemd que reinicia automaticamente
em caso de falha:
sudo systemctl daemon-reload
sudo systemctl enable ollama-webui
sudo systemctl start ollama-webui
# Verificar status
sudo systemctl status ollama-webui
# Ver logs em tempo real
sudo journalctl -u ollama-webui -f
ℹ️ Vantagem do systemd: Com o método 2, a Web UI reinicia automaticamente
se o processo travar ou se o sistema reiniciar. O Requires=ollama.service
garante que o Ollama já esteja rodando antes da Web UI tentar se conectar a ele.
Próximos passos — para onde evoluir este setup
Com tudo que foi feito até aqui você já tem um servidor de IA local funcional.
Mas este setup é uma base — dá para evoluir bastante dependendo do que você precisar.
Aqui estão as direções mais práticas e como chegar lá:
Hardware
Adicionar mais RAM
Se o seu N3700 aceitar expansão (verifique no manual), ir de 2 GB para 4 GB
é a única atualização de hardware que muda completamente a experiência.
Com 4 GB você roda o Gemma3 4B e o SmolLM2 1.7B
sem tocar no SWAP, gerando respostas 3x mais rápidas.
Modelos
Explorar novos modelos leves
O ecossistema de modelos pequenos cresce todo mês. Acompanhe o
ollama.com/library
e filtre por tamanho. Modelos como Phi-3 mini e
Qwen2.5 0.5B são candidatos promissores para 2 GB de RAM.
Python
Memória persistente com SQLite
A Web UI atual guarda o histórico apenas em RAM — ao reiniciar, perde tudo.
O próximo passo natural é salvar as conversas em SQLite:
pip install flask-sqlalchemy e criar um modelo de banco para as mensagens.
IA
RAG — Responder sobre seus documentos
RAG (Retrieval-Augmented Generation) permite que o modelo responda perguntas
baseado em PDFs e documentos seus. Biblioteca indicada:
chromadb + sentence-transformers
com modelos de embedding leves como o all-MiniLM-L6-v2.
Rede
Acesso seguro pela internet
Para acessar sua Web UI de fora da rede local sem expor diretamente à internet,
use um túnel seguro. O Cloudflare Tunnel (gratuito) ou
o Tailscale são as opções mais simples — sem precisar abrir portas
no roteador.
Integração
Extensão de browser com a API
O Ollama expõe uma API compatível com OpenAI em
localhost:11434/v1. Qualquer ferramenta que aceita endpoint OpenAI
customizado — como o Open WebUI, extensões de browser ou
editores de código — consegue se conectar ao seu Ollama local.
✅ Compatibilidade OpenAI: O Ollama suporta a API compatível com OpenAI.
Isso significa que você pode apontar qualquer app que usa a API da OpenAI para o seu
servidor local trocando apenas a base URL:
http://localhost:11434/v1 com API key qualquer (o Ollama não valida).
Sem custo, sem limites de requisição, sem internet.
Perguntas frequentes (FAQ)
Antes de comentar com dúvidas, veja se ela já está respondida aqui — são as perguntas
que mais aparecem sobre este tipo de setup.
❓ Precisa de GPU para rodar IA local?
Não. GPU acelera muito, mas não é obrigatória. Tudo neste guia roda 100% em CPU.
O que muda sem GPU é a velocidade — textos levam minutos ao invés de segundos,
imagens levam horas ao invés de minutos. Para aprendizado e uso pessoal esporádico,
CPU é completamente viável.
❓ O Ollama funciona no antiX 32 bits?
Não. O Ollama só tem binários para arquitetura 64-bit (amd64). Use sempre a ISO
antiX 23.1 x64. Se seu processador for de 32 bits, infelizmente
o Ollama não tem suporte oficial — mas o stable-diffusion.cpp pode ser compilado
manualmente para 32 bits com limitações.
❓ Posso usar o mesmo setup no Raspberry Pi?
Sim, com adaptações. O Ollama tem suporte para ARM (Raspberry Pi 3, 4 e 5).
O Pi 4 com 4 GB de RAM funciona melhor que o N3700 para texto. Para imagem,
o stable-diffusion.cpp compila normalmente em ARM também. O antiX não tem versão ARM,
mas o Raspberry Pi OS (Debian-based) funciona como base equivalente.
❓ Os modelos falam português?
Modelos pequenos como SmolLM e TinyLlama entendem e respondem em português, mas
com qualidade menor que em inglês — treinaram com menos dados em PT. Para melhores
respostas em português, use o Gemma3 1B (melhor multilíngue nesta
faixa) ou defina no Modelfile: "Sempre responda em português do Brasil."
❓ O setup funciona sem internet após instalar?
Sim, 100% offline depois que os modelos estão baixados. O Ollama não faz
chamadas externas durante o uso — tudo fica local. A única exceção é a ferramenta
de busca web do agente (buscar:), que obviamente precisa de internet
para funcionar.
❓ Posso usar o Ollama com o VS Code?
Sim. Extensões como Continue e Ollama Autocomplete
aceitam endpoint customizado. Configure
http://localhost:11434 como base URL e selecione o modelo instalado.
Você terá autocomplete de código local, sem enviar nada para a nuvem.
❓ O SWAP não deixa o SSD mais lento?
Com swappiness=10 (configurado na Parte 3), o sistema só usa SWAP em
último caso. Na prática, para o SmolLM e TinyLlama, a RAM de 2 GB é suficiente e
o SWAP raramente é acionado. Para Gemma3 e modelos maiores, o SWAP é usado e sim,
o SSD trabalha mais — considere usar HD para o arquivo de swap se tiver.
❓ Como atualizo o Ollama para nova versão?
Execute o mesmo script de instalação novamente — ele detecta a versão anterior,
para o serviço, substitui o binário e reinicia tudo automaticamente:
curl -fsSL https://ollama.com/install.sh | sh
Seus modelos baixados são preservados.
Benchmarks reais no Pentium N3700 com antiX 23.1
Números reais medidos durante os testes. Os tempos podem variar dependendo do
armazenamento (SSD vs HD), temperatura do processador e carga de background.
Todos os testes foram feitos com o sistema recém reiniciado e sem outros processos
pesados rodando.
Modelos de texto (tokens/segundo)
Modelo
Tempo de carga
Velocidade (tok/s)
Resposta curta (~50 tok)
Resposta longa (~300 tok)
SWAP usado?
SmolLM 135M
~5s
8–12 tok/s
~5–7s
~25–40s
Não
SmolLM 360M
~8s
5–8 tok/s
~7–10s
~40–60s
Não
SmolLM2 1.7B
~20s
2–4 tok/s
~15–25s
~75–150s
Raramente
TinyLlama 1.1B
~25s
1–3 tok/s
~20–50s
~100–300s
Raramente
Gemma3 1B
~35s
1–2 tok/s
~30–60s
~150–300s
Às vezes
Gemma3 4B
~3–5min
~0.3–0.8 tok/s
~2–5min
~10–20min
Sim (intenso)
Geração de imagens (stable-diffusion.cpp)
Resolução
Steps
Tempo estimado
Qualidade
RAM usada
128×128
10
5–10 min
Baixa — para testar
~700 MB
256×256
20
15–40 min
Razoável — uso normal
~900 MB
512×512
15
~2–4 horas
Boa
~1,4 GB + SWAP
512×512
30
~4–8 horas
Muito boa
~1,4 GB + SWAP
Análise de imagens (Moondream2)
Operação
Tempo estimado
RAM usada
Carregamento do modelo (1ª vez — inclui download)
Download ~1,7 GB + ~3–5 min
—
Carregamento do modelo (cache local)
~60–120s
~1,4 GB
Encode de imagem (encode_image)
~30–60s
~1,5 GB
Geração de legenda (caption)
~60–120s
~1,5 GB
Pergunta sobre imagem (query)
~45–90s por pergunta
~1,5 GB
ℹ️ Contexto dos benchmarks: Estes números foram medidos no
Pentium N3700 (4 núcleos, 6W TDP, sem AVX2) com 2 GB de RAM, antiX 23.1 e SWAP
de 8 GB em SSD. Em processadores com AVX2 (praticamente qualquer Intel/AMD de
2013 em diante com mais núcleos) a velocidade pode ser 2–4x maior.
Usando o Ollama via API Python — automações e scripts
O Ollama expõe uma API REST em localhost:11434 que você pode usar em
qualquer script Python para automatizar tarefas. Alguns exemplos práticos:
resumir arquivos de log automaticamente, gerar descrições de imagens em lote,
ou criar um bot de terminal que responde perguntas sem abrir o chat interativo.
Script 1 — Chat simples via API (sem biblioteca extra)
nano ~/ollama_api.py
#!/usr/bin/env python3
"""
Exemplo de uso da API do Ollama via requests puro.
Não precisa do pacote ollama — só requests.
@CanalQb
"""
import requests
import json
import sys
OLLAMA_URL = "http://localhost:11434/api/chat"
MODELO = "smollm2:1.7b" # troque pelo modelo que tiver instalado
def chat(mensagem, historico=None, sistema=None):
"""Envia mensagem para o Ollama e retorna a resposta."""
mensagens = []
if sistema:
mensagens.append({"role": "system", "content": sistema})
if historico:
mensagens.extend(historico)
mensagens.append({"role": "user", "content": mensagem})
try:
resposta = requests.post(
OLLAMA_URL,
json={"model": MODELO, "messages": mensagens, "stream": False},
timeout=300
)
resposta.raise_for_status()
return resposta.json().get("message", {}).get("content", "")
except requests.exceptions.ConnectionError:
return "Erro: Ollama não está rodando. Execute: sudo systemctl start ollama"
except Exception as e:
return f"Erro: {e}"
# ── Modo de uso ───────────────────────────────────────────────
if __name__ == "__main__":
# Usa argumento da linha de comando se passado
if len(sys.argv) > 1:
pergunta = " ".join(sys.argv[1:])
print(chat(pergunta))
else:
# Chat interativo com histórico
print(f"Chat com {MODELO} | Ctrl+C para sair\n")
historico = []
sistema = "Você é um assistente técnico que responde em português."
while True:
try:
entrada = input("Você: ").strip()
if not entrada:
continue
resposta = chat(entrada, historico, sistema)
print(f"\nIA: {resposta}\n")
historico.append({"role": "user", "content": entrada})
historico.append({"role": "assistant", "content": resposta})
# Manter histórico pequeno para não explodir a RAM
if len(historico) > 10:
historico = historico[-10:]
except KeyboardInterrupt:
print("\nAté mais!")
break
# Usar como chat interativo
python3 ~/ollama_api.py
# Ou passar pergunta diretamente
python3 ~/ollama_api.py "como verificar o uso de disco no Linux?"
Script 2 — Resumir arquivo de texto automaticamente
nano ~/resume_arquivo.py
#!/usr/bin/env python3
"""
Resume qualquer arquivo de texto usando o Ollama local.
Uso: python3 resume_arquivo.py /caminho/arquivo.txt
@CanalQb
"""
import requests
import sys
import os
OLLAMA_URL = "http://localhost:11434/api/generate"
MODELO = "smollm2:1.7b"
def resumir(texto, max_chars=3000):
"""Envia texto truncado para resumo — respeita limite de contexto."""
texto_trunc = texto[:max_chars]
if len(texto) > max_chars:
texto_trunc += f"\n\n[... texto truncado em {max_chars} caracteres ...]"
prompt = f"""Resuma o seguinte texto em português, em no máximo 5 pontos principais.
Seja direto e objetivo.
TEXTO:
{texto_trunc}
RESUMO:"""
try:
res = requests.post(
OLLAMA_URL,
json={"model": MODELO, "prompt": prompt, "stream": False},
timeout=300
)
return res.json().get("response", "Sem resposta.")
except Exception as e:
return f"Erro: {e}"
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Uso: python3 resume_arquivo.py /caminho/arquivo.txt")
sys.exit(1)
caminho = sys.argv[1]
if not os.path.exists(caminho):
print(f"Arquivo não encontrado: {caminho}")
sys.exit(1)
with open(caminho, "r", encoding="utf-8", errors="replace") as f:
conteudo = f.read()
print(f"\nResumindo: {caminho} ({len(conteudo)} caracteres)\n")
print("─" * 50)
print(resumir(conteudo))
print("─" * 50)
# Exemplo — resumir seu próprio histórico do terminal
python3 ~/resume_arquivo.py ~/meu_historico.txt
Script 3 — Geração de imagens em lote com sd-cli
nano ~/gera_lote.sh
#!/bin/bash
# Gera múltiplas imagens em sequência com prompts diferentes
# @CanalQb — stable-diffusion.cpp em lote
SD_CLI=~/stable-diffusion.cpp/build/bin/sd-cli
MODELO=~/sd-models/ggml-model-q4_0.gguf
SAIDA=~/imagens_lote
mkdir -p "$SAIDA"
# Lista de prompts — adicione quantos quiser
PROMPTS=(
"a simple mountain landscape at sunset, digital art"
"a futuristic city with flying cars, cyberpunk style"
"a forest path with sunlight through trees, watercolor"
"abstract geometric shapes in blue and gold, minimal"
)
TOTAL=${#PROMPTS[@]}
echo "Gerando $TOTAL imagens em lote..."
echo "Pasta de saída: $SAIDA"
echo ""
for i in "${!PROMPTS[@]}"; do
NUM=$((i + 1))
PROMPT="${PROMPTS[$i]}"
ARQUIVO="$SAIDA/imagem_$(printf '%02d' $NUM).png"
echo "[$NUM/$TOTAL] Gerando: $PROMPT"
echo " Saída: $ARQUIVO"
$SD_CLI \
--model "$MODELO" \
--prompt "$PROMPT" \
--negative-prompt "blurry, low quality, ugly, distorted" \
--height 256 --width 256 \
--steps 20 \
--seed $((RANDOM)) \
--output "$ARQUIVO" 2>/dev/null
echo " ✅ Concluído"
echo ""
done
echo "═══════════════════════════════"
echo "Lote finalizado! $TOTAL imagens em $SAIDA"
ls -lh "$SAIDA"
chmod +x ~/gera_lote.sh
~/gera_lote.sh
⚠️ Geração em lote no N3700: Com 4 prompts e 256×256 pixels, o lote
completo pode levar de 1 a 3 horas dependendo do hardware. O script mostra progresso
a cada imagem. Ideal para deixar rodando durante a noite.
Backup e migração do setup para outro computador
Se você quiser mover este setup para outro PC ou fazer backup dos modelos e configurações
sem precisar baixar tudo novamente, aqui está o que você precisa preservar e como fazer.
O que fazer backup
O que
Onde fica
Tamanho típico
Prioridade
Modelos Ollama
~/.ollama/models/
600 MB – 3 GB por modelo
Alta — demora muito baixar
Modelos SD (.gguf)
~/sd-models/
~850 MB
Alta
Cache Moondream
~/.cache/moondream/
~1,7 GB
Alta — demora muito baixar
Projeto Web UI
~/ollama-webui/
~10–50 KB
Alta — seu código
Modelfiles e scripts
~/Modelfile, ~/*.sh, ~/*.py
Pequeno
Alta — configurações
stable-diffusion.cpp (compilado)
~/stable-diffusion.cpp/
~200–400 MB
Média — pode recompilar
Virtualenvs
~/moondream-env/, ~/webui-env/
~500 MB cada
Baixa — pode recriar
Script de backup completo
nano ~/backup_ia.sh
#!/bin/bash
# Backup do setup de IA local — @CanalQb
# Ajuste DESTINO para seu pendrive ou HD externo
DESTINO="/media/backup/ia_local_$(date +%Y%m%d)"
mkdir -p "$DESTINO"
echo "=== Backup IA Local @CanalQb ==="
echo "Destino: $DESTINO"
echo ""
backup_item() {
local ORIGEM="$1"
local NOME="$2"
if [ -e "$ORIGEM" ]; then
echo "Copiando $NOME..."
cp -r "$ORIGEM" "$DESTINO/" && echo " ✅ $NOME" || echo " ❌ Falhou: $NOME"
else
echo " ⚠️ Não encontrado: $ORIGEM (pulando)"
fi
}
backup_item ~/.ollama/models "Modelos Ollama"
backup_item ~/sd-models "Modelos SD"
backup_item ~/.cache/moondream "Cache Moondream"
backup_item ~/ollama-webui "Web UI"
backup_item ~/Modelfile "Modelfile"
backup_item ~/analisa_imagem.py "Script Moondream"
backup_item ~/ollama_api.py "Script API Python"
backup_item ~/verifica_setup.sh "Script diagnóstico"
backup_item ~/prompt_form.sh "Script formulário"
backup_item ~/personas.json "Personas"
backup_item ~/start_webui.sh "Script autostart"
echo ""
echo "Tamanho total do backup:"
du -sh "$DESTINO"
echo ""
echo "Backup concluído em $DESTINO"
chmod +x ~/backup_ia.sh
# Ajuste o DESTINO no arquivo antes de executar
~/backup_ia.sh
Se você chegou aqui sem familiaridade com alguns termos, este glossário explica de forma
prática o que cada coisa significa — sem academicismo desnecessário.
🧠 LLM (Large Language Model)
Modelo de linguagem de grande escala — o tipo de IA por trás do ChatGPT, Gemini e afins.
Funciona prevendo qual palavra/token vem depois de cada texto. "Large" refere-se ao
número de parâmetros (pesos) treinados, não necessariamente ao tamanho do arquivo.
⚡ Quantização
Técnica de compressão que reduz a precisão dos pesos do modelo (de float32 para int8
ou int4). Um modelo quantizado em 4 bits (Q4) usa ~4x menos RAM e disco que o original,
com pequena perda de qualidade. É o que torna possível rodar modelos em hardware modesto.
📄 GGUF
Formato de arquivo criado pelo projeto llama.cpp para armazenar modelos quantizados.
Substitui o formato GGML anterior. É o padrão usado pelo Ollama e pelo
stable-diffusion.cpp para modelos otimizados para CPU.
🔢 Token
A unidade básica de texto que um LLM processa. Não é necessariamente uma palavra —
pode ser uma sílaba, pontuação ou parte de uma palavra. Em inglês, 1 token ≈ 0,75
palavras. Em português, um pouco menos eficiente. "Tokens por segundo" mede a
velocidade de geração do modelo.
👁️ VLM (Vision Language Model)
LLM que também processa imagens como entrada, além de texto. O Moondream2 é um VLM —
ele recebe uma imagem + uma pergunta em texto e responde sobre o conteúdo visual.
É diferente de geração de imagem (que cria imagens a partir de texto).
🔄 RAG (Retrieval-Augmented Generation)
Técnica que combina busca vetorial com geração de texto. Você indexa seus documentos
(PDFs, arquivos) em um banco vetorial, e quando faz uma pergunta, o sistema busca
os trechos mais relevantes e os injeta no contexto do LLM antes de gerar a resposta.
Resultado: o modelo responde com base nos seus dados, não só no treinamento.
🧩 Embedding
Representação numérica de um texto (ou imagem) em um espaço vetorial de alta dimensão.
Textos com significados parecidos ficam "próximos" nesse espaço, o que permite busca
semântica. É a base do RAG — você busca documentos por proximidade de significado,
não por palavras exatas.
🔁 SWAP
Espaço em disco usado pelo Linux como extensão da RAM física quando esta esgota.
Muito mais lento que RAM (HD: ~100x mais lento; SSD: ~10x mais lento), mas impede
que processos sejam encerrados por falta de memória. Essencial para rodar modelos
grandes em hardware com pouca RAM.
📋 Modelfile
Arquivo de configuração do Ollama que define um modelo customizado a partir de um
modelo base. Especifica o prompt de sistema, parâmetros de geração (temperatura,
tamanho de contexto) e comportamento padrão. Similar a um Dockerfile, mas para
modelos de IA.
🌡️ Temperature
Parâmetro que controla o quão "criativo" ou "aleatório" é o modelo ao gerar texto.
0.0–0.3 = respostas mais determinísticas e previsíveis (bom para código e traduções).
0.7–1.0 = respostas mais criativas e variadas (bom para escrita criativa).
Padrão do Ollama: 0.8.
📐 OpenBLAS
Biblioteca de álgebra linear otimizada para CPUs (Basic Linear Algebra Subprograms).
Acelera operações de matriz que são o coração da inferência de modelos de IA e
de geração de imagens. Compilar o stable-diffusion.cpp com OpenBLAS melhora a
velocidade em CPUs Intel/AMD.
🐍 Virtualenv
Ambiente Python isolado que contém suas próprias instalações de pacotes,
independente do Python do sistema. Evita conflitos entre versões de bibliotecas
de projetos diferentes. Boa prática obrigatória para qualquer projeto Python sério.
Criado com python3 -m venv nome e ativado com source nome/bin/activate.
Parte 11 — Bot do Telegram integrado ao Ollama
Conteúdo novo
Este é um dos usos mais práticos de todo o setup: criar um bot no Telegram que responde
suas mensagens usando os modelos rodando no seu N3700. Você está no celular, manda uma
pergunta, e em alguns minutos recebe a resposta processada localmente na sua máquina —
sem passar por nenhuma API externa, sem custo, sem limite de mensagens.
Para isso funcionar, o antiX precisa ter acesso à internet (para receber as mensagens
do Telegram via polling). O Ollama processa localmente — só a comunicação com o Telegram
usa a rede.
11.1 — Criando o bot no Telegram
Abra o Telegram e busque por @BotFather
Envie o comando /newbot
Escolha um nome para o bot (ex: Meu Assistente Local)
Escolha um username que termine em bot (ex: meu_ollama_bot)
O BotFather retorna um token no formato:
1234567890:ABCDefGhIJKlmNOPqrsTUVwxyz — guarde este token
Para descobrir seu chat_id: envie qualquer mensagem para o bot
e acesse no navegador:
https://api.telegram.org/botSEU_TOKEN/getUpdates —
o número em "id" dentro de "chat" é o seu chat_id
⚠️ Nunca compartilhe seu token do BotFather. Com ele qualquer pessoa
pode controlar o bot e enviar comandos para o seu Ollama. Guarde-o como uma senha.
#!/usr/bin/env python3
"""
Bot do Telegram integrado ao Ollama local.
Envia mensagens pelo Telegram, recebe respostas do seu LLM no N3700.
@CanalQb
"""
import telebot
import requests
import json
import os
# ─── Configuração ─────────────────────────────────────────────
TELEGRAM_TOKEN = "SEU_TOKEN_AQUI" # token do BotFather
CHAT_ID_PERMITIDO = 123456789 # seu chat_id — bloqueia outros usuários
OLLAMA_URL = "http://localhost:11434/api/chat"
MODELO = "smollm2:1.7b"
SISTEMA = "Você é um assistente técnico que responde em português do Brasil. Seja direto e prático."
MAX_HIST = 10 # mensagens no histórico por usuário
bot = telebot.TeleBot(TELEGRAM_TOKEN)
# ─── Histórico de conversa por usuário ───────────────────────
historicos = {}
def get_historico(uid):
return historicos.get(uid, [])
def add_historico(uid, role, content):
if uid not in historicos:
historicos[uid] = []
historicos[uid].append({"role": role, "content": content})
if len(historicos[uid]) > MAX_HIST:
historicos[uid] = historicos[uid][-MAX_HIST:]
def limpar_historico(uid):
historicos.pop(uid, None)
# ─── Verificar autorização ───────────────────────────────────
def autorizado(message):
if message.chat.id != CHAT_ID_PERMITIDO:
bot.reply_to(message, "⛔ Acesso não autorizado.")
return False
return True
# ─── Chamar o Ollama ─────────────────────────────────────────
def chamar_ollama(uid, texto):
add_historico(uid, "user", texto)
mensagens = [{"role": "system", "content": SISTEMA}]
mensagens.extend(get_historico(uid))
try:
res = requests.post(
OLLAMA_URL,
json={"model": MODELO, "messages": mensagens, "stream": False},
timeout=300
)
resposta = res.json().get("message", {}).get("content", "Sem resposta.")
add_historico(uid, "assistant", resposta)
return resposta
except requests.exceptions.ConnectionError:
return "❌ Ollama offline. Execute: sudo systemctl start ollama"
except Exception as e:
return f"❌ Erro: {e}"
# ─── Handlers ────────────────────────────────────────────────
@bot.message_handler(commands=["start", "help"])
def cmd_start(message):
if not autorizado(message): return
bot.reply_to(message,
"🤖 *Ollama Bot — @CanalQb*\n\n"
"Envie qualquer mensagem e o LLM local responde.\n\n"
"Comandos:\n"
"/limpar — apaga o histórico da conversa\n"
"/modelo — mostra o modelo em uso\n"
"/status — verifica se o Ollama está rodando",
parse_mode="Markdown"
)
@bot.message_handler(commands=["limpar"])
def cmd_limpar(message):
if not autorizado(message): return
limpar_historico(message.chat.id)
bot.reply_to(message, "🗑 Histórico apagado.")
@bot.message_handler(commands=["modelo"])
def cmd_modelo(message):
if not autorizado(message): return
bot.reply_to(message, f"🧠 Modelo em uso: `{MODELO}`", parse_mode="Markdown")
@bot.message_handler(commands=["status"])
def cmd_status(message):
if not autorizado(message): return
try:
res = requests.get("http://localhost:11434/api/tags", timeout=5)
modelos = [m["name"] for m in res.json().get("models", [])]
lista = "\n".join(f"• {m}" for m in modelos) if modelos else "Nenhum instalado"
bot.reply_to(message, f"✅ Ollama online\n\nModelos instalados:\n{lista}")
except Exception:
bot.reply_to(message, "❌ Ollama offline ou inacessível.")
@bot.message_handler(func=lambda m: True)
def responder(message):
if not autorizado(message): return
uid = message.chat.id
texto = message.text.strip()
if not texto: return
# Avisa que está processando
processando = bot.reply_to(message, "⏳ Processando...")
resposta = chamar_ollama(uid, texto)
# Editar mensagem de "processando" com a resposta real
try:
bot.edit_message_text(
resposta,
chat_id=uid,
message_id=processando.message_id
)
except Exception:
bot.reply_to(message, resposta)
# ─── Iniciar polling ─────────────────────────────────────────
if __name__ == "__main__":
print(f"Bot Telegram iniciado — modelo: {MODELO}")
print("Pressione Ctrl+C para parar.")
try:
bot.infinity_polling(timeout=30, long_polling_timeout=20)
except KeyboardInterrupt:
print("Bot encerrado.")
11.4 — Configurando e iniciando o bot
Edite o arquivo e substitua os dois valores de configuração:
# Substituir no arquivo:
# TELEGRAM_TOKEN = "SEU_TOKEN_AQUI" → cole o token do BotFather
# CHAT_ID_PERMITIDO = 123456789 → cole seu chat_id real
# Ativar o virtualenv e rodar
source ~/webui-env/bin/activate
python3 ~/ollama_telegram_bot.py
Agora abra o Telegram, mande uma mensagem para o bot e aguarde. A primeira resposta
pode demorar enquanto o modelo carrega — as seguintes são mais rápidas pois o modelo
fica em memória.
ℹ️ O que você tem agora: Um assistente de IA acessível de qualquer
lugar do mundo pelo Telegram, processando tudo localmente no seu antiX. Histórico de
conversa por sessão, comandos de controle e proteção por chat_id para que só você
possa usar.
Parte 12 — Protegendo a Web UI com senha
Segurança
A Web UI Flask que criamos na Parte 9 não tem nenhuma proteção de acesso — qualquer
dispositivo na rede que souber o IP e porta consegue usar. Para uso doméstico simples
isso é aceitável, mas se você vai expor para internet ou quer mais controle, adicionar
uma senha básica leva menos de 10 linhas de código.
12.1 — HTTP Basic Auth (simples, sem banco de dados)
Edite o início do seu app.py adicionando as linhas marcadas:
from flask import Flask, render_template, request, jsonify, session
from flask_httpauth import HTTPBasicAuth # ← adicionar
from werkzeug.security import generate_password_hash, check_password_hash # ← adicionar
import requests, json, os, uuid
from agents import agent_router
app = Flask(__name__)
auth = HTTPBasicAuth() # ← adicionar
app.secret_key = os.urandom(24)
# ─── Usuários e senhas (troque os valores) ────────────────────
USUARIOS = { # ← adicionar
"admin": generate_password_hash("sua_senha_aqui"),
"convidado": generate_password_hash("outra_senha"),
}
@auth.verify_password # ← adicionar
def verify_password(usuario, senha):
if usuario in USUARIOS and check_password_hash(USUARIOS[usuario], senha):
return usuario
return None
# Proteger a rota principal com @auth.login_required
@app.route("/")
@auth.login_required # ← adicionar esta linha
def index():
# ... resto da função permanece igual
pass
# Proteger a API de chat também
@app.route("/api/chat", methods=["POST"])
@auth.login_required # ← adicionar esta linha
def chat():
# ... resto da função permanece igual
pass
Reinicie o servidor. Ao abrir o navegador agora, ele pede usuário e senha antes
de mostrar qualquer coisa. As credenciais ficam salvas no browser até você fechar
a sessão.
12.2 — Gerar hash da senha sem deixar em texto puro
Para não deixar a senha em texto legível no código, gere o hash antes e cole direto:
O output é algo como
scrypt:32768:8:1$.....
— cole esse hash diretamente no dicionário USUARIOS no lugar da chamada
generate_password_hash().
⚠️ HTTP Basic Auth não é criptografado sozinho. Para uso na internet,
coloque um proxy Nginx com HTTPS na frente ou use Cloudflare Tunnel (gratuito) que
adiciona TLS automaticamente. Na rede local sem internet, o Basic Auth já é suficiente.
Parte 13 — Automações com cron: IA trabalhando enquanto você dorme
Automação
O cron é o agendador de tarefas do Linux — permite executar scripts em horários
fixos sem interação manual. Combinado com o Ollama, você pode criar rotinas
automáticas que usam IA local para processar informações periodicamente:
resumir logs, analisar arquivos novos, gerar relatórios. O N3700 fica "trabalhando"
enquanto você não está usando o computador.
13.1 — Entendendo a sintaxe do cron
# Formato: minuto hora dia_do_mês mês dia_da_semana comando
# ┌───── minuto (0-59)
# │ ┌─── hora (0-23)
# │ │ ┌─ dia do mês (1-31)
# │ │ │ ┌ mês (1-12)
# │ │ │ │ ┌ dia da semana (0=Dom, 1=Seg... 6=Sáb)
# │ │ │ │ │
# * * * * * comando
# Exemplos:
# 0 8 * * * → todo dia às 08:00
# 0 22 * * 1-5 → dias úteis às 22:00
# 30 7 * * 0 → domingos às 07:30
# */30 * * * * → a cada 30 minutos
13.2 — Script: resumo diário dos logs do sistema
nano ~/cron_resumo_logs.sh
#!/bin/bash
# Resumo diário dos logs do sistema via Ollama
# @CanalQb — executado automaticamente pelo cron
source ~/webui-env/bin/activate
DATA=$(date +%Y-%m-%d)
LOG_SAIDA=~/logs_resumidos/resumo_${DATA}.txt
mkdir -p ~/logs_resumidos
# Coletar logs das últimas 24 horas
LOGS=$(journalctl --since "24 hours ago" --no-pager -p err..warning 2>/dev/null | tail -100)
if [ -z "$LOGS" ]; then
echo "$DATA — Nenhum erro ou aviso nos logs das últimas 24 horas." > "$LOG_SAIDA"
exit 0
fi
# Enviar para o Ollama via Python inline
python3 - < "$LOG_SAIDA"
import requests
logs = """$LOGS"""
prompt = f"""Analise estes logs do sistema Linux e gere um relatório em português com:
1. Principais erros encontrados
2. Possíveis causas
3. Ações recomendadas
LOGS:
{logs[:3000]}
RELATÓRIO:"""
try:
res = requests.post(
"http://localhost:11434/api/generate",
json={"model": "smollm2:1.7b", "prompt": prompt, "stream": False},
timeout=300
)
print(f"=== Relatório de Logs — $(date +%Y-%m-%d) ===\n")
print(res.json().get("response", "Sem resposta do modelo."))
except Exception as e:
print(f"Erro ao contatar Ollama: {e}")
EOF
echo "Resumo gerado: $LOG_SAIDA"
13.3 — Script: análise automática de imagens numa pasta
nano ~/cron_analisa_pasta.sh
#!/bin/bash
# Analisa automaticamente novas imagens numa pasta monitorada
# Gera arquivo de descrição .txt para cada imagem nova
# @CanalQb
PASTA_ENTRADA=~/imagens_para_analisar
PASTA_SAIDA=~/descricoes_geradas
mkdir -p "$PASTA_ENTRADA" "$PASTA_SAIDA"
source ~/moondream-env/bin/activate
for IMAGEM in "$PASTA_ENTRADA"/*.{jpg,jpeg,png,webp}; do
[ -f "$IMAGEM" ] || continue
NOME=$(basename "$IMAGEM" | sed 's/\.[^.]*$//')
SAIDA="$PASTA_SAIDA/${NOME}.txt"
# Pular se já foi analisada
[ -f "$SAIDA" ] && continue
echo "Analisando: $IMAGEM"
python3 - "$IMAGEM" > "$SAIDA" <<'EOF'
import moondream as md
from PIL import Image
import sys
try:
model = md.vl(model="moondream-2b-int8.mf")
img = Image.open(sys.argv[1])
enc = model.encode_image(img)
desc = model.caption(enc)["caption"]
print(desc)
except Exception as e:
print(f"Erro: {e}")
EOF
echo " Salvo em: $SAIDA"
done
deactivate
echo "Análise em lote concluída."
13.4 — Agendando os scripts no cron
# Abrir o editor de cron do usuário atual
crontab -e
Adicione as linhas abaixo ao final do arquivo. O cron usa o editor padrão do sistema
(geralmente nano no antiX):
# Dar permissão de execução aos scripts primeiro
chmod +x ~/cron_resumo_logs.sh
chmod +x ~/cron_analisa_pasta.sh
# Conteúdo a adicionar no crontab (crontab -e):
# Resumo de logs todo dia às 07:00
0 7 * * * ~/cron_resumo_logs.sh >> ~/logs_resumidos/cron.log 2>&1
# Análise de imagens novas a cada 2 horas
0 */2 * * * ~/cron_analisa_pasta.sh >> ~/descricoes_geradas/cron.log 2>&1
# Verificação do setup toda segunda-feira às 09:00
0 9 * * 1 ~/verifica_setup.sh >> ~/verifica_setup.log 2>&1
Salve e feche. Confirme que o cron registrou as tarefas:
# Ver tarefas agendadas
crontab -l
# Ver se o serviço cron está ativo no antiX
sudo systemctl status cron
ℹ️ Dica importante: Os scripts do cron rodam sem interface gráfica e
sem o PATH completo do seu usuário. Por isso os scripts usam caminhos absolutos
(~/) e ativam explicitamente o virtualenv com
source ~/nome-env/bin/activate. Sem isso, o Python e o pip não são
encontrados.
Parte 14 — Open WebUI: interface profissional para o Ollama
Alternativa avançada
O Open WebUI é a interface web mais usada com o Ollama no mundo —
open source, com visual estilo ChatGPT, suporte a múltiplos modelos, upload de arquivos,
histórico persistente, suporte a RAG, múltiplos usuários e muito mais. É uma alternativa
ao Flask caseiro que criamos, muito mais completa e polida.
A ressalva para o N3700 com 2 GB é clara: o Open WebUI em si consome de
300 a 600 MB de RAM apenas para rodar a interface — isso é RAM que
vai fazer falta para os modelos. Avalie se o seu uso justifica o custo de memória.
Característica
Flask caseiro (Parte 9)
Open WebUI
RAM usada pela interface
~30–50 MB
~300–600 MB
Instalação
~10 min
~15–30 min
Histórico persistente
Só na sessão
Sim (SQLite)
Upload de arquivos
Via agente
Sim (nativo)
RAG embutido
Não
Sim
Multiusuário
Básico
Sim (com login)
Streaming de resposta
Não
Sim
Recomendado para N3700 2GB
✅ Sim
⚠️ Com limitações
14.1 — Instalando o Open WebUI sem Docker (direto no Python)
O método sem Docker usa menos RAM que a versão containerizada:
# Criar virtualenv dedicado
python3 -m venv ~/openwebui-env
source ~/openwebui-env/bin/activate
# Instalar o Open WebUI
pip install open-webui
# Iniciar (na primeira vez baixa dependências adicionais)
open-webui serve
Na primeira inicialização, ele cria o banco de dados e baixa alguns assets.
Aguarde completar. Acesse em:
http://localhost:8080
Na tela inicial, crie sua conta de administrador. O Open WebUI detecta automaticamente
o Ollama em localhost:11434 e lista todos os modelos instalados.
14.2 — Configurando a URL do Ollama no Open WebUI
Se o Open WebUI não detectar o Ollama automaticamente:
Acesse Configurações → Conexões no menu do usuário
Em URL da API Ollama, coloque:
http://localhost:11434
⚠️ Monitoramento de RAM obrigatório: Ao usar o Open WebUI no N3700,
monitore o consumo de RAM com watch -n 2 free -h. Se a RAM livre cair
abaixo de 200 MB, reduza o modelo para SmolLM 135M ou pare o Open WebUI e use o
Flask caseiro que consome muito menos memória.
ℹ️ Minha recomendação para o N3700: Para hardware com 2 GB de RAM,
o Flask caseiro da Parte 9 é a escolha mais sensata no dia a dia — consome 10x menos
memória e deixa mais RAM livre para os modelos gerarem respostas mais rápido.
O Open WebUI brilha em máquinas com 8 GB+ de RAM. Se você conseguir expandir
para 4 GB, o equilíbrio já melhora bastante.
Mapa de portas e serviços do setup completo
Com tudo instalado, seu antiX pode estar rodando vários serviços simultaneamente.
Este é o mapa completo para você não se perder:
Serviço
Porta
URL de acesso
RAM
Iniciar
Ollama API
11434
http://IP:11434
~50–200 MB
sudo systemctl start ollama
Flask Web UI
5000
http://IP:5000
~30–50 MB
python3 app.py
sd-server (SD.cpp)
8080
http://IP:8080
~900 MB
./bin/sd-server --port 8080
Open WebUI
8080
http://IP:8080
~300–600 MB
open-webui serve
Bot Telegram
—
Via Telegram
~30 MB
python3 ollama_telegram_bot.py
⚠️ Conflito de porta 8080: O sd-server e o Open WebUI usam a mesma
porta 8080 por padrão. Nunca rode os dois ao mesmo tempo. Se precisar de ambos,
mude a porta de um deles:
./bin/sd-server --port 8081 ou
PORT=8090 open-webui serve.
Comentários
Comente só assim vamos crescer juntos!