antirez/ds4 - Motor local de inferência DeepSeek V4 Flash para Metal
(github.com/antirez)- Motor local de inferência dedicado ao DeepSeek V4 Flash otimizado para GPUs Apple Metal, com implementação nativa em C focada em um único modelo, e não um runner GGUF genérico
- O DeepSeek V4 Flash tem poucos parâmetros ativos, oferecendo alta velocidade, e no modo thinking gera trechos de raciocínio cerca de 1/5 do tamanho de outros modelos
- Suporta janela de contexto de 1 milhão de tokens e um cache KV extremamente comprimido, permitindo inferência de contexto longo localmente, com suporte a persistência de cache KV em disco
- Inclui um servidor HTTP API compatível com OpenAI e Anthropic, permitindo integração imediata com vários agentes de código, como Claude Code, opencode e Pi
- Construído sobre as bases de llama.cpp e do ecossistema GGML, e desenvolvido com o forte apoio de codificação do GPT 5.5
Visão geral do projeto e filosofia de design
ds4.cé um pequeno motor nativo de inferência dedicado ao DeepSeek V4 Flash, não um runner GGUF genérico nem um wrapper de outro runtime- O caminho principal é um executor de grafo Metal especializado no DeepSeek V4 Flash, incluindo carregamento específico do DS4, renderização de prompt, estado KV e código de integração da API do servidor
- Há muitos projetos excelentes na área de inferência local, mas o surgimento constante de novos modelos acaba dispersando a atenção
- Este projeto se concentra intencionalmente em um modelo por vez, fazendo inclusive verificação oficial de vetores (logits), testes de contexto longo e integração com agentes
- A visão da inferência local é que três elementos trabalhem juntos: A) um motor de inferência com API HTTP, B) um GGUF otimizado para um mecanismo específico e C) testes e validação por meio de implementações de agentes de código
- É exclusivo para Metal; há possibilidade futura de suporte a CUDA, mas nada está definido
- O caminho de CPU existe apenas para verificação de corretude e atualmente causa kernel crash ao executar o código de CPU devido a um bug na implementação de memória virtual da versão atual do macOS
- O projeto foi desenvolvido com o forte apoio do GPT 5.5, enquanto as pessoas conduziram ideias, testes e depuração
Por que criar um motor separado para o DeepSeek V4 Flash
- Tem poucos parâmetros ativos, oferecendo inferência mais rápida
- No modo thinking, gera trechos de raciocínio cerca de 1/5 do tamanho de outros modelos, e o tamanho desse trecho é proporcional à complexidade do problema
- Mesmo em situações em que outros modelos ficam impraticáveis no modo thinking, o DeepSeek V4 Flash continua utilizável
- Suporta janela de contexto de 1 milhão de tokens
- Com 284B de parâmetros, sabe mais coisas na fronteira do conhecimento do que modelos de 27B e 35B
- A diferença pode ser observada em perguntas sobre programas de TV italianos, política etc.
- A qualidade de escrita em inglês e italiano fica em nível de modelo quase fronteira
- O cache KV é extremamente comprimido, tornando possível inferência de contexto longo em computadores locais, com suporte a persistência de cache KV em disco
- Com quantização especial, funciona bem até em quantização de 2 bits, podendo rodar em um MacBook com 128GB de RAM
- Espera-se que a DeepSeek lance no futuro uma versão atualizada do V4 Flash
Agradecimentos ao llama.cpp e ao GGML
- O ds4.c não faz link com GGML, mas existe sobre o caminho desbravado pelo projeto llama.cpp
- Os kernels do llama.cpp, os formatos de quantização, o ecossistema GGUF e o conhecimento de engenharia hard-won foram referências essenciais
- Parte do código-fonte em nível de source foi mantida ou aplicada sob licença MIT: layouts e tabelas de quantização GGUF, lógica de quant/dot em CPU, certos kernels Metal etc.
- O arquivo LICENSE mantém os avisos de copyright dos autores do GGML
Pesos do modelo
- Só funcionam os GGUF do DeepSeek V4 Flash publicados especificamente para este projeto; arquivos arbitrários de DeepSeek/GGUF não são compatíveis
- A quantização de 2 bits usa quantização assimétrica
- Apenas os especialistas MoE (experts) são quantizados: up/gate em
IQ2_XXS, down emQ2_K - Os demais componentes, como especialistas compartilhados, projeção e roteamento, não são quantizados para garantir a qualidade
- Apenas os especialistas MoE (experts) são quantizados: up/gate em
- Baixe o modelo para máquinas com 128GB de RAM com
./download_model.sh q2e para máquinas com 256GB ou mais com./download_model.sh q4- O download é feito via Hugging Face (
antirez/deepseek-v4-gguf), com suporte a retomada parcial usandocurl -C -
- O download é feito via Hugging Face (
- Também é possível baixar com
./download_model.sh mtpum GGUF com suporte opcional a speculative decoding- O caminho MTP/speculative decoding ainda é experimental e atualmente oferece apenas um pequeno ganho de velocidade
Benchmark de velocidade
- Números de uma única execução da CLI Metal com
--ctx 32768,--nothink, decodificação greedy e-n 256 - MacBook Pro M3 Max, 128GB (q2)
- Prompt curto: prefill 58.52 t/s, geração 26.68 t/s
- Prompt de 11709 tokens: prefill 250.11 t/s, geração 21.47 t/s
- q4: N/A por falta de memória
- Mac Studio M3 Ultra, 512GB (q2)
- Prompt curto: prefill 84.43 t/s, geração 36.86 t/s
- Prompt de 11709 tokens: prefill 468.03 t/s, geração 27.39 t/s
- Mac Studio M3 Ultra, 512GB (q4)
- Prompt curto: prefill 78.95 t/s, geração 35.50 t/s
- Prompt de 12018 tokens: prefill 448.82 t/s, geração 26.62 t/s
Como usar a CLI
- Use a opção
-ppara executar um prompt one-shot; se rodar sem-p, entra no modo de chat interativo multi-turno - A CLI interativa mantém a transcrição renderizada da conversa e checkpoints KV Metal em tempo real, de modo que cada turno estende o diálogo anterior
- Comandos úteis:
/help,/think,/think-max,/nothink,/ctx N,/read FILE,/quit- Use Ctrl+C para interromper a geração atual e voltar ao prompt
- O padrão é o modo thinking; use
/nothinkou--nothinkpara mudar para o modo de resposta direta - É possível ativar o caminho especulativo MTP opcional com
--mtp MTP.gguf --mtp-draft 2- Só é útil com decodificação greedy, usando o confidence gate (
--mtp-margin) para evitar aceitar partes lentas
- Só é útil com decodificação greedy, usando o confidence gate (
Servidor
- É possível executar um servidor HTTP local compatível com OpenAI/Anthropic
- É exclusivo para Metal e mantém na memória um único grafo/checkpoint KV mutável
- Se um cliente stateless reenviar uma versão mais longa do mesmo prompt, é possível reaproveitar o prefixo compartilhado
- O parsing das requisições e os sockets rodam na thread do cliente, mas a inferência em si é serializada por meio de um único worker Metal
- Atualmente o servidor não faz batching de várias requisições independentes; requisições simultâneas aguardam em fila
-
Endpoints suportados
GET /v1/models,GET /v1/models/deepseek-v4-flashPOST /v1/chat/completions,POST /v1/completions,POST /v1/messages
-
/v1/chat/completions(compatível com OpenAI)- Suporta
messages,max_tokens/max_completion_tokens,temperature,top_p,top_k,min_p,seed,stream,stream_options.include_usage,tools,tool_choice - O schema de ferramentas é renderizado no formato de ferramentas DSML do DeepSeek, e as chamadas de ferramenta DSML geradas são convertidas de volta em tool calls OpenAI
- Suporta
-
/v1/messages(compatível com Anthropic)- Endpoint para clientes no estilo Claude Code
- Suporta
system,messages,tools,tool_choice,max_tokens,temperature,top_p,top_k,stream,stop_sequencese controle de thinking - O uso de ferramentas é retornado em blocos Anthropic
tool_use
- Ambas as APIs suportam streaming SSE e, no modo thinking, o processo de raciocínio é transmitido em formato de API nativa
Integração com clientes de agentes
- O ds4-server pode se integrar a agentes locais de código que usam chat completions compatível com OpenAI
- Ao rodar quantização de 2 bits (81GB) em 128GB de RAM, uma janela de contexto entre 100k e 300k tokens é adequada
- O contexto completo de 1M de tokens usa cerca de 26GB de memória (o indexador comprimido sozinho usa cerca de 22GB)
- É possível evitar limite de tokens configurando o limite de saída em
384000(o modelo pode gerar até 384k tokens) -
Integração com opencode
- Configure adicionando itens de provider e agent em
~/.config/opencode/opencode.json - Defina
baseURLcomohttp://127.0.0.1:8000/v1
- Configure adicionando itens de provider e agent em
-
Integração com Pi
- Adicione a configuração do provider em
~/.pi/agent/models.json - Inclui opções de compatibilidade para o formato thinking da DeepSeek, suporte a reasoning effort e suporte a usage em streaming
- Pode ser definido como modelo padrão em
~/.pi/agent/settings.json
- Adicione a configuração do provider em
-
Integração com Claude Code
- Usa o endpoint compatível com Anthropic, com um script wrapper
~/bin/claude-ds4para definir variáveis de ambiente - Defina
ANTHROPIC_BASE_URLpara o servidor local e todas as variáveis de modelo comodeepseek-v4-flash - O Claude Code envia inicialmente um grande prompt de cerca de 25k tokens, então é essencial ativar
--kv-disk-dir- Após o primeiro prefill caro, o cache KV em disco reaproveita o prefixo salvo, evitando reprocessar o prompt inteiro em sessões seguintes
- Usa o endpoint compatível com Anthropic, com um script wrapper
Modo thinking
- O DeepSeek V4 Flash suporta três modos: non-thinking, thinking e Think Max
- O padrão do servidor é o modo thinking
- É possível solicitar Think Max com
reasoning_effort=max, mas isso só se aplica quando o tamanho de contexto é suficientemente grande conforme a recomendação do model card- Em contextos pequenos, há fallback para thinking normal
reasoning_effort=xhighdo OpenAI é mapeado para thinking normal, não para Think Max- Se for necessária resposta direta, use
thinking: {"type":"disabled"},think:falseou um alias de modelo non-thinking comodeepseek-chat
Cache KV em disco
- As APIs de chat/completion são stateless, então clientes agentes reenviam a conversa inteira a cada requisição
- O ds4-server processa isso comparando o fluxo de tokens renderizado com o prefixo de tokens armazenado em cache
- O checkpoint ativo em memória atende à sessão atual
- O cache KV em disco é o mecanismo para preservar prefixos úteis entre trocas de sessão e reinicializações do servidor
- Atualmente existe apenas um cache KV ativo na memória; se uma nova sessão sem relação o substituir, a sessão anterior só poderá ser retomada sem reprocessamento se tiver sido gravada no cache KV em disco
- Ative com
--kv-disk-dire--kv-disk-space-mb -
Chave de cache e estrutura de arquivos
- A chave de cache é o hash SHA1 dos IDs exatos dos tokens, não do texto bruto
- Cada ID de token é hasheado como inteiro little-endian de 32 bits, e o nome do arquivo é
<sha1>.kv - A gravação usa I/O comum de
read/write, semmmap(evitando mapeamentos VM adicionais em um processo que já mapeia o modelo)
-
Layout do arquivo de cache em disco
- Cabeçalho fixo KVC de 48 bytes: magic("KVC"), versão, bits de quantização do expert roteado, motivo do salvamento, número de tokens em cache, contagem de hits, tamanho de contexto, timestamps Unix de criação/último uso e número de bytes do payload de sessão DS4
- Texto renderizado: texto decodificado pelo tokenizador do prefixo de tokens em cache (para observação, não usado como chave)
- Payload de sessão DS4: começa com 13 campos
u32little-endian, incluindo magic("DSV4"), versão do payload, tamanho de contexto, tamanho do chunk de prefill, capacidade do anel KV etc.- Armazena IDs dos tokens do checkpoint, logits float32 para o próximo token, número de linhas de atenção comprimida por camada, linhas KV ativas da janela deslizante raw, linhas KV das camadas comprimidas e tensores compressor frontier, entre outros
-
Quando os checkpoints são salvos
cold: depois que um prompt inicial longo atinge um prefixo estável, antes da geraçãocontinued: quando o prefill ou a geração avança no intervalo configuradoevict: antes que uma requisição não relacionada substitua a sessão ativa em memóriashutdown: quando o servidor é encerrado normalmente
- No salvamento cold, um pequeno sufixo de tokens é removido e o resultado é alinhado ao limite do chunk de prefill para evitar erros futuros de retokenização em fronteiras BPE
- Padrões: prefixo mínimo de 512 tokens, máximo de 30000 tokens em cold save, trimming de 32 tokens na cauda e alinhamento em chunks de 2048 tokens
- Por padrão, checkpoints podem ser reutilizados entre variantes de experts roteados em 2 bits e 4 bits se o prefixo de tokens coincidir
--kv-cache-reject-different-quantpermite restringir a reutilização apenas à mesma quantização
Backend
- O backend padrão é Metal (
--metal) - Também existe um caminho de CPU para referência/debug (
--cpu), mas ele não é alvo de produção- O servidor é exclusivo para Metal, e a implementação otimizada está no caminho de grafo Metal
- Licença MIT, implementação em C/Objective-C/Metal
1 comentários
Comentários no Hacker News
Testei isso junto com o Claude Code na minha base de código atual, e parece cumprir bem o papel mesmo sendo um modelo quantizado em 2 bits
O processamento do prompt leva alguns minutos, mas a edição de fato é bem rápida, acima de 20 tokens/s
Em tarefas pequenas, conseguiu explorar o código, aplicar alterações e escrever testes, mas não conseguiu corrigir uma observação trivial
O pior é que, ao resolver outro problema, trouxe por alucinação uma conversa paralela sobre “The Duck”. Imagino que seja um dos exemplos do prompt inicial do Claude Code
Eu já tinha feito algo muito parecido para o modelo Qwen3. Roda só Qwen3, suporta apenas algumas quantizações, carrega de GGUF e usa inferência otimizada iterativamente pelo Claude
O projeto era pequeno, com só alguns arquivos, fácil de entender, feito para estudantes aprenderem experimentando com coisas como adicionar estratégias de decodificação ou abliteration. Frameworks famosos são grandes e complexos demais para hackear, e projetos educacionais muitas vezes ficam presos a coisas antigas como GPT-2
Começou como projeto educacional, mas fiquei com uma ideia martelando na cabeça: e se eu fizesse um motor de inferência ultraotimizado para uma combinação específica de GPU + modelo? GPU é caro e está cada vez mais difícil de conseguir, então parece que daria para otimizar bastante removendo abstrações e ajustando tudo diretamente ao hardware/modelo exato
O problema é que, quando o modelo ficar obsoleto, teria que refazer tudo do zero
Em plataformas menos populares ainda existe algum espaço fácil para ganhar desempenho, mas não sobra muito espaço para obter ganhos muito melhores criando um executor de modelos ultraotimizado para uma família específica de GPUs. Os cálculos principais já são tratados por kernels altamente otimizados para cada GPU
Também existem forks do llama.cpp otimizados para rodar melhor em certas arquiteturas de CPU, mas, se não houver divergência entre mantenedores, costuma ser melhor gastar tempo fazendo merge dessas melhorias upstream do que criando um executor separado para um modelo+GPU específicos
https://codegolf.stackexchange.com/questions/215216/high-thr...
Será que a multiplicação de matrizes pesada poderia ser feita com amplificadores operacionais? E será que essa abordagem analógica poderia ser muito mais eficiente do que os limites da representação em bits?
Agora que a IA mais recente consegue até fazer otimização de kernels, acho que mais gente deveria tentar construir por conta própria inferências melhores para o próprio hardware
Tenho uma W7900 antiga (RDNA3) e, além dos 48GB de VRAM, os números no papel são bem bons: 123 FP16 TFLOPS/INT8 TOPS e 864GB/s de largura de banda de memória. Mas tanto o suporte da AMD ao ROCm quanto o suporte do llama.cpp eram notoriamente ruins
Recentemente comecei a ajustar modelos W8A8-INT8 para usar essa placa como endpoint dedicado para agentes/código. Rodei cerca de 800 iterações automáticas ao longo de alguns dias e testei vários modelos frontier/SOTA; surpreendentemente, o Kimi K2.6 se saiu muito bem. No fim, comparado aos melhores números do llama.cpp para o Qwen3.6 MoE, o prefill ficou 20% mais rápido e o decode 50% mais rápido
Agora sigo aprofundando otimizações de MTP e DFlash, com resultados bem satisfatórios, e depois quero tentar o Gemma 4
Mesmo assim, o llama.cpp talvez não tenha o melhor desempenho, mas consegue executar a maioria dos modelos de forma consistente. Parece faltar MTP e há problemas de invalidação de cache em modelos híbridos, mas pelo menos dá para saber o que roda
Os inferidores baseados em Python viram uma mistura de uv/venv, meu venv, ambiente do sistema, Python e bibliotecas; chega ao ponto de parecer que eu precisaria de um agente só para descobrir o que de fato está rodando. Sei que é limitação minha ou erro de usuário, mas não tenho mais tempo para gastar nisso
Mesmo que não esteja perfeito, se você publicar no GitHub ou no Hugging Face, outros agentes podem partir daí em vez de começar do zero. Fiz isso com o Ling-2.6-flash (107B-A7B4 MoE), que também é o maior LLM que consigo rodar de forma prática no meu outro hardware para LLM local, um M2 Max
Mesmo que o MTP não funcione direito, já é melhor do que o llama.cpp atual simplesmente não conseguir rodar o Ling-2.6-flash. A discussão está em https://huggingface.co/inclusionAI/Ling-2.6-flash/discussion..., a quantização 4 bits em https://huggingface.co/ljupco/Ling-2.6-flash-GGUF e o branch em https://github.com/ljubomirj/llama.cpp/tree/LJ-Ling-2.6-flas...
Acho que o llama.cpp poderia ter dado um suporte muito melhor a PCs. Parte disso talvez seja culpa do suporte ruim dos vendors, mas, com tanta gente usando, ainda assim surpreende não haver mais inferência otimizada para PCs padrão
Muito legal. Fico curioso para ver o que acontece quando alguém passa vários meses otimizando intensamente um modelo open source
Não só no serving de inferência, mas também em otimização de harness e workflows sob medida, para ver o quanto isso consegue reduzir a lacuna em áreas nas quais modelos frontier conseguem inferir e derivar melhor, enquanto modelos open source ficam atrás por limitações de tamanho ou treinamento
Operar o Kimi 2.6 em uma velocidade decente de tokens/s custa 20 mil dólares por mês, e para vender esses tokens com lucro o custo de hardware teria que ficar abaixo de 1 mil dólares por mês
Se você está apostando sua capacidade num futuro em que bilionários gentilmente vendem tokens por 1/10 ou 1/20 do custo, ou em que modelos open source competentes caem no colo do hardware de consumo, então já era
Há um dado divertido, interessante e bastante revelador: meu MacBook M3 Max chega a 50W de consumo quando o DS4 gera tokens na velocidade máxima
Se o DS4 Flash atinge pico de 50W e tem 280B parâmetros, então o DS4 Pro, com 1,6T parâmetros, ficaria em algo como 300W? GPT 5 e Opus atuais parecem algo perto de 500W
Quando uso Claude Code e o modelo fica se alongando sozinho, faz sentido pensar que em algum datacenter ele está queimando 500W?
No Mac Studio, já não dá para pedir opções acima de 96GB de RAM. O mesmo vale para M3 Ultra e M4 Max. Não sei se é algo só da Austrália
Já no MacBook Pro dá para configurar 128GB com o Mac M5
https://www.apple.com/au/shop/buy-mac/mac-studio
Talvez a Apple tenha preferido simplesmente não precificar isso a enfrentar a polêmica de cobrança abusiva ou a reação pela falta de estoque
Removeram todas as configurações de Mac Studio acima de 96GB e também o Mac mini básico. Há rumores de que também avaliam tirar do mercado a configuração base do Neo
Parece ser a forma como estão lidando com restrições de capacidade fab e oferta de RAM
Talvez eu tenha perdido um benchmark ou objetivo motivacional mais simples
Imagino que isso seja mais rápido do que usar a cadeia de ferramentas normal, ou que permita rodar modelos maiores e mais inteligentes, mas não parece estar muito claro qual é a melhora já obtida ou esperada em relação à linha de base
Dá para calcular isso com os números apresentados, se você conhecer os comparativos relevantes, mas enfim
Muito impressionante. Mas o fato de parecer levar uns 4 minutos para começar a responder com entradas grandes soa estranho
Não uso LLM em hardware Mac, mas isso me surpreende bastante e parece um obstáculo grande para uso prático
Por outro lado, para uso normal, a explicação sobre cache deixa isso bem mais compreensível. O Claude Code costuma mandar um prompt inicial enorme, algo como 25k tokens, antes de começar qualquer tarefa útil, e com
--kv-disk-dirativado o cache KV em disco pode reutilizar prefixes já salvos depois do primeiro prefill caro, sem precisar processar o prompt inteiro de novoMas no M3 Ultra a velocidade de prefill chega perto de 500 tokens/s, então já entra numa faixa bem prática. No M3 Max exige um pouco mais de paciência, mas funciona bem, e com o pi agent você ainda vê o processo de raciocínio sendo exibido, então, em vez de esperar, fica lendo chain of thought sem censura
Ontem publiquei no X um vídeo usando isso no M3 Max, e ele cospe tokens numa velocidade bem boa
Em MacBook, LLM grande até tem velocidade de geração de tokens aceitável, mas o problema é a leitura do contexto
Não é a leitura incremental com cache KV, como numa sessão de chat, e sim quando precisa ler uma entrada grande, como ao colar um arquivo grande. Aí pode levar minutos
Então provavelmente está bem longe da inteligência original oferecida por um provedor de nuvem
Ainda assim, mostra melhor o potencial de LLMs locais em workflows com agentes
Existe alguma arquitetura que não dependa de reenviar todo o histórico da conversa? Tipo LLMs recorrentes?