Chega de orçamento de RAM. Esta lição é o roteiro de comandos: clonar e compilar o cérebro, baixar os pesos, subir os dois servidores e plugar o seu cliente. Cada passo marcado com ✓ foi rodado neste M5 Max nesta sessão — não é "deveria funcionar", é "rodou, aqui está a saída". Ao fim você terá dois endpoints OpenAI-compatíveis na sua própria máquina: um cérebro em :8000 e visão em :8081.
São duas trilhas paralelas que terminam no mesmo lugar: o seu cliente. A trilha do cérebro (DS4 nativo, Metal) e a trilha da visão (MLX, em Python). Nenhuma depende da outra para subir. O diagrama marca com ✓ o que já foi provado nesta sessão e com ⏳ o único passo demorado (o download dos 81 GB).
Tudo, menos o download, foi executado e verificado nesta sessão. As duas trilhas não se bloqueiam: você pode subir a visão enquanto os 81 GB do cérebro ainda baixam.
git clone + make do DS4 rodaram até o fim neste Mac: 5 binários Metal gerados, exit 0. E o mlx_vlm.server da visão está no ar agora em :8081, respondendo a chamadas reais (a saída está mais abaixo). O que não foi feito ao vivo é só o download de 81 GB — por tempo, não por dúvida.
O DS4 (de antirez) compila nativo com Metal. A regra de ouro: nunca make cpu num Apple Silicon — isso joga fora a GPU e a memória unificada, justamente o que torna o Mac viável. O build padrão (make) já mira o Metal.
# 1) clonar o repositório do cérebro git clone https://github.com/antirez/ds4 && cd ds4 # 2) compilar com Metal — NUNCA `make cpu` num Apple Silicon make # ✓ provado nesta sessão: 5 binários Metal gerados, exit 0 # 3) baixar os pesos q2 (~81 GB, o download é resumível) ./download_model.sh q2-imatrix # 4) servir — KV cache em disco para folgar a RAM ./ds4-server --ctx 100000 --kv-disk-dir ~/.ds4/kv --kv-disk-space-mb 8192 # perf medida (README do DS4, classe M5 Max): gen 25–34 t/s · prefill 87–463 t/s
--kv-disk-dir? O KV cache cresce com o contexto. Empurrá-lo para disco (aqui 8 GB de teto) mantém os 81 GB de pesos + sistema dentro da folga de 20%, mesmo em contextos longos. É o detalhe que separa "rodou" de "estourou a RAM".O resultado é simples de visualizar: uma máquina, dois servidores HTTP. O cérebro fala OpenAI e Anthropic em :8000; a visão fala OpenAI em :8081/v1. Para o seu cliente, são só duas base-URLs.
Dois processos, duas portas, zero nuvem. Ambos OpenAI-compatíveis, então qualquer cliente que aceite uma base-URL customizada conecta nos dois.
A trilha da visão usa o uv para um venv isolado e instala o mlx-vlm direto do Git. O servidor sobe um endpoint OpenAI em :8081. Abaixo, o setup e — o que importa — a resposta real capturada nesta sessão, com os timings medidos.
# 1) venv isolado e ativado (uv é rápido e reprodutível) uv venv ~/.venv-mlxvlm && source ~/.venv-mlxvlm/bin/activate # 2) instalar o mlx-vlm a partir do main uv pip install "git+https://github.com/Blaizzy/mlx-vlm" # 3) subir o servidor de visão (OpenAI-compatível) em :8081 python -m mlx_vlm.server \ --model mlx-community/Qwen3-VL-30B-A3B-Instruct-4bit \ --port 8081 # ✓ rodando agora nesta sessão
# chamada real ao endpoint OpenAI-compatível da visão curl -s 127.0.0.1:8081/v1/chat/completions -d '{ "model":"mlx-community/Qwen3-VL-30B-A3B-Instruct-4bit", "messages":[{"role":"user","content":"Descreva o conceito de memória unificada."}] }' # resposta real (medida nesta sessão): "A memória unificada é uma arquitetura em que CPU e GPU compartilham o mesmo pool físico de memória, eliminando cópias entre dispositivos e permitindo que modelos grandes sejam acessados diretamente pela GPU com baixa latência." # timings reais: prompt 88 t/s · gen 62 t/s · peak 18,4 GB
Esses números não são do README de ninguém: foram medidos nesta máquina, agora. O servidor processou o prompt a 88 t/s, gerou a 62 t/s e nunca passou de 18,4 GB de pico — bem dentro do orçamento de visão da Lição 08. Numa chamada one-shot (generate) a geração chegou a 122 t/s.
Antes de plugar o cliente, vale o mapa de estado. Cada peça da config tem um selo: ✓ se já foi construída/está rodando, ⏳ se ainda depende do download. Só uma peça está em ⏳.
| Componente | Onde | Estado | Evidência |
|---|---|---|---|
| clone + make (DS4) | cérebro · :8000 | ✓ pronto | 5 binários Metal, exit 0 (esta sessão) |
| pesos q2 (81 GB) | cérebro · disco | ⏳ baixando | download resumível |
| venv + mlx-vlm | visão · :8081 | ✓ pronto | instalado (esta sessão) |
| mlx_vlm.server | visão · :8081 | ✓ no ar | curl real · gen 62 t/s · pico 18,4 GB |
O que acontece quando o seu cliente faz uma pergunta? Uma troca HTTP enxuta, em loopback, sem sair da máquina. O diagrama segue uma requisição do cliente até os tokens de volta.
A mesma forma de sequência vale para os dois endpoints; muda só a porta e qual modelo carrega. Os timings do cérebro são da classe M5 Max (README do DS4); os da visão foram medidos acima.
Esta é a parte mais simples — e a mais satisfatória. Como ambos os servidores falam OpenAI, qualquer cliente que aceite uma base-URL aponta para o 127.0.0.1. A api-key pode ser qualquer string: não há autenticação real num servidor local.
# cérebro (DeepSeek) — para coding/agente base-url = http://127.0.0.1:8000 model = deepseek-v4-flash # o id que o ds4-server expõe api-key = qualquer-coisa # local não valida a chave # visão (Qwen3-VL) — para imagens/screenshots base-url = http://127.0.0.1:8081/v1 model = mlx-community/Qwen3-VL-30B-A3B-Instruct-4bit api-key = qualquer-coisa # exemplo: variáveis que o Claude Code / Codex / opencode entendem export OPENAI_BASE_URL=http://127.0.0.1:8000 export OPENAI_API_KEY=local
No modo recomendado (um Mac), o cérebro fica residente e a visão só carrega quando chega uma imagem. A troca é de segundos graças ao mmap, e o pico nunca cruza o teto seguro. Esta é a Lição 08 traduzida em linha do tempo.
Nunca há dois modelos grandes na RAM ao mesmo tempo: pausa um, roda o outro, volta. O preço é a latência do reload (segundos); o prêmio é nunca estourar os 128 GB.
make cpu é o erro a evitar ao compilar o DS4 num M5 Max?make cpu joga isso fora. Nesta sessão, make (padrão) gerou 5 binários Metal com exit 0.api-key pode ser qualquer string. Recusa de conexão vem de servidor não iniciado, base-url/porta errada ou o download ainda em curso.