Cansei de arrumar minha pasta Downloads: montei um "zelador" com IA local que faz isso por mim

Depois de passar a semana inteira lidando com Active Directory, Entra ID e tickets de infraestrutura, no fim de semana eu gosto de resolver um problema bem mais egoísta: a minha própria bagunça digital. Minha pasta de Downloads virou, com o tempo, um cemitério de PDFs com nome documento (3).pdf, prints de tela chamados Captura de tela 2026-...png, planilhas, instaladores e boletos — tudo misturado, sem nenhuma lógica.

Já tinha brincado antes de montar um assistente de IA local para outras coisas, então resolvi reaproveitar a mesma ideia para um problema bem mais mundano: um script que observa a pasta Downloads, entende (de verdade, lendo o conteúdo, não só o nome) o que cada arquivo é, e move ele para o lugar certo — sozinho, rodando o tempo todo em segundo plano, sem nada saindo da minha máquina.

A ideia por trás do projeto

O conceito não é novo — encontrei pelo menos três projetos de código aberto que fazem exatamente isso, usando a mesma combinação de peças: uma biblioteca Python chamada watchdog para vigiar a pasta[1], e um modelo de IA rodando localmente via Ollama para decidir a categoria de cada arquivo[2][3]. A ideia central é simples e elegante:

  1. O watchdog detecta quando um arquivo novo aparece na pasta.
  2. O script extrai um pedaço do conteúdo — texto de PDFs, OCR de imagens, texto puro de .txt/.docx.
  3. Esse conteúdo (mais o nome do arquivo) vai para um modelo de linguagem rodando local, via Ollama, pedindo para ele responder com uma categoria entre as que eu defini.
  4. O script cria a subpasta se não existir e move o arquivo.

O detalhe que me convenceu a fazer isso localmente, e não com alguma IA de nuvem: boletos, comprovantes, documentos pessoais e prints de conversas privadas passam por esse pipeline todos os dias. Com Ollama, o texto extraído nunca sai da própria máquina — o modelo responde via uma API local, sem nenhuma chamada externa[4][5].

Montando a base: Ollama

O primeiro passo é instalar o Ollama e baixar um modelo pequeno, que já é suficiente para esse tipo de classificação simples (não precisa de um modelo gigante para decidir se um PDF é "nota fiscal" ou "contrato"):

# instala o Ollama (Linux/WSL) curl -fsSL https://ollama.com/install.sh | sh # baixa um modelo leve, bom o suficiente para classificação de texto ollama pull llama3.2:3b 

Isso sobe um serviço local expondo uma API HTTP em http://localhost:11434, que qualquer script Python pode chamar com a biblioteca requests[4][5].

O "zelador": monitorando a pasta com watchdog

A segunda peça é o watchdog, uma biblioteca Python que observa eventos do sistema de arquivos — criação, modificação, remoção — e dispara callbacks[1]. A estrutura básica de um observador é direta:

from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import time class NovoArquivoHandler(FileSystemEventHandler): def on_created(self, event): if not event.is_directory: classificar_e_mover(event.src_path) if __name__ == "__main__": handler = NovoArquivoHandler() observer = Observer() observer.schedule(handler, "/caminho/para/Downloads", recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join() 

Esse handler dispara classificar_e_mover sempre que um arquivo novo aparece. É aqui que a IA entra.

A função que "pensa": classificar com o modelo local

A função classificar_e_mover faz três coisas: extrai um pedaço do conteúdo (texto puro, ou pdftotext para PDF, ou OCR via pytesseract para imagem), monta um prompt curto pedindo uma resposta em categoria fixa, e chama a API local do Ollama:

import requests, json, shutil, os CATEGORIAS = ["Notas Fiscais", "Comprovantes", "Trabalho", "Contratos", "Imagens", "Outros"] def classificar(nome_arquivo, trecho_conteudo): prompt = f"""Classifique o arquivo abaixo em UMA destas categorias: {CATEGORIAS}. Responda apenas em JSON: {{"categoria": "..."}} Nome do arquivo: {nome_arquivo} Conteúdo (trecho): {trecho_conteudo[:1500]} """ resp = requests.post("http://localhost:11434/api/generate", json={ "model": "llama3.2:3b", "prompt": prompt, "stream": False, "format": "json" }) dados = json.loads(resp.json()["response"]) categoria = dados.get("categoria", "Outros") return categoria if categoria in CATEGORIAS else "Outros" def classificar_e_mover(caminho_arquivo): nome = os.path.basename(caminho_arquivo) trecho = extrair_trecho(caminho_arquivo) # lê PDF/txt ou faz OCR, conforme a extensão categoria = classificar(nome, trecho) destino_dir = os.path.join(os.path.dirname(caminho_arquivo), categoria) os.makedirs(destino_dir, exist_ok=True) shutil.move(caminho_arquivo, os.path.join(destino_dir, nome)) print(f"[OK] {nome} -> {categoria}") 

O parâmetro "format": "json" força o Ollama a responder em JSON válido, o que evita um bocado de dor de cabeça tentando fazer parsing de texto livre.

As travas de segurança que realmente importam

Rodar um script que move arquivos sozinho, sem supervisão, é o tipo de coisa que dá muito errado se feita sem cuidado. As lições mais úteis que peguei estudando implementações já prontas desse padrão[3]:

  • Modo "dry run" por padrão. O script só deve listar o que faria; mover de fato exige uma flag explícita, por exemplo --apply.
  • Log de reversão. Cada movimentação vai para um arquivo .jsonl com origem e destino, para dar undo se a IA classificar algo errado.
  • Nunca sobrescrever. Se já existe um arquivo com o mesmo nome no destino, acrescenta um sufixo (_v2, _v3) em vez de substituir.
  • Ignorar downloads em andamento. Arquivos .crdownload/.part e qualquer coisa modificada nos últimos 60 segundos ficam de fora, para não mexer em download que ainda não terminou.
  • Categoria fixa. O modelo só pode escolher entre as categorias que eu defini — qualquer resposta fora da lista cai em "Outros", em vez de criar pastas aleatórias.

Com essas travas, mesmo que o modelo erre a classificação de um arquivo de vez em quando (e erra, principalmente com imagens sem muito texto), o pior cenário é um arquivo na pasta errada — nunca um arquivo perdido ou sobrescrito.

Rodando de verdade

Depois de testar em modo dry-run por alguns dias e ajustar as categorias ao meu próprio padrão de bagunça, deixei o script rodando como um serviço em segundo plano. O resultado prático: a pasta Downloads para de crescer feito bola de neve, os comprovantes ficam juntos (o que ajuda demais na hora de organizar o financeiro do mês), e eu não preciso mais abrir aquela pasta para arrastar arquivo por arquivo manualmente.

É um projeto pequeno, mas captura bem o que eu acho mais interessante de usar IA local no dia a dia: resolver um problema chato e repetitivo, sem mandar nenhum dado pessoal para fora da minha máquina, e sem depender de assinatura de nenhum serviço de nuvem.

Principais pontos

  • watchdog + Ollama é uma combinação simples e já testada por vários projetos abertos para automatizar organização de arquivos com IA local.
  • O modelo não precisa ser grande — um modelo pequeno (3B) já resolve bem uma classificação em categorias fixas.
  • Forçar resposta em JSON e restringir a lista de categorias evita que a "criatividade" do modelo crie bagunça nova.
  • Dry-run, log de reversão e proteção contra sobrescrita são obrigatórios antes de deixar qualquer script mover arquivos sozinho.
  • Nada sai da sua máquina: todo o pipeline — leitura, classificação e decisão — roda local.

📚 Recursos recomendados

Sources

[1] https://python-watchdog.readthedocs.io/en/stable/quickstart.html — Watchdog quickstart docs
[2] https://github.com/pneelamr/ai-file-organizer — AI File Organizer (watchdog + Ollama + OCR)
[3] https://github.com/laiba-Ashfaq/lazy-downloads-tidier — lazy-downloads-tidier (Ollama local LLM classifier)
[4] https://medium.com/@prithasaha_62327/i-built-a-local-ai-downloads-organizer-with-python-and-ollama-82c0f865a319 — Local AI Downloads Organizer with Python and Ollama
[5] https://ollama.com — Ollama - run LLMs locally