antirez/ds4 - Motor de inferencia local de DeepSeek V4 Flash para Metal
(github.com/antirez)- Motor de inferencia local dedicado a DeepSeek V4 Flash optimizado para GPU Apple Metal, con una implementación nativa en C enfocada en un solo modelo en lugar de un runner GGUF genérico
- DeepSeek V4 Flash tiene pocos parámetros activos, por lo que ofrece alta velocidad, y en modo thinking genera tramos de razonamiento cinco veces más cortos que otros modelos
- Permite inferencia local de contexto largo gracias a una ventana de contexto de 1 millón de tokens y un caché KV extremadamente comprimido, con soporte para persistencia del caché KV en disco
- Incluye un servidor HTTP API compatible con OpenAI y Anthropic, por lo que puede integrarse de inmediato con distintos agentes de código como Claude Code, opencode y Pi
- Está construido sobre la base de llama.cpp y el ecosistema GGML, y es un proyecto desarrollado con el fuerte apoyo de GPT 5.5 para tareas de programación
Descripción general del proyecto y filosofía de diseño
ds4.ces un pequeño motor de inferencia nativo dedicado a DeepSeek V4 Flash, no un runner GGUF genérico ni un wrapper de otro runtime- La ruta principal es un ejecutor de grafos Metal especializado para DeepSeek V4 Flash, e incluye carga específica de DS4, renderizado de prompts, estado KV y código de integración con la API del servidor
- Hay muchos proyectos excelentes en el campo de la inferencia local, pero a medida que siguen apareciendo nuevos modelos, la atención se dispersa
- Este proyecto se enfoca deliberadamente en un modelo a la vez, e incluye validación oficial de vectores (logits), pruebas de contexto largo e integración con agentes
- La visión de la inferencia local es que funcionen juntos estos tres elementos: A) un motor de inferencia con API HTTP incluida, B) un GGUF optimizado para un motor específico y C) pruebas y validación mediante implementaciones de agentes de código
- Es solo para Metal; existe la posibilidad de soporte para CUDA en el futuro, pero no está confirmada
- La ruta de CPU existe solo para verificación de exactitud y, debido a un bug actual en la implementación de memoria virtual de macOS, ejecutar el código de CPU provoca un kernel crash
- Fue desarrollado con el fuerte apoyo de GPT 5.5, mientras que los humanos lideraron las ideas, las pruebas y la depuración
Por qué DeepSeek V4 Flash se convirtió en un motor aparte
- Tiene pocos parámetros activos, por lo que ofrece mayor velocidad de inferencia
- En modo thinking genera tramos de razonamiento de aproximadamente 1/5 frente a otros modelos, y la longitud de ese tramo es proporcional a la complejidad del problema
- Incluso en situaciones donde otros modelos son imprácticos en modo thinking, DeepSeek V4 Flash sí puede usarse
- Soporta una ventana de contexto de 1 millón de tokens
- Con una escala de 284B parámetros, conoce más información en la frontera del conocimiento que modelos de 27B o 35B
- Esto puede comprobarse en preguntas sobre programas de TV italianos, política y temas similares
- La calidad de redacción en inglés e italiano está al nivel de modelos casi frontier
- Su caché KV está extremadamente comprimido, permitiendo inferencia local de contexto largo, y además soporta persistencia del caché KV en disco
- Si se cuantiza de una manera especial, funciona bien incluso con cuantización de 2 bits, por lo que puede ejecutarse en una MacBook con 128GB de RAM
- Se espera que DeepSeek publique en el futuro una versión actualizada de V4 Flash
Agradecimientos a llama.cpp y GGML
- Aunque ds4.c no enlaza con GGML, existe sobre el camino abierto por el proyecto llama.cpp
- Los kernels, formatos de cuantización, el ecosistema GGUF y el conocimiento de ingeniería hard-won de llama.cpp fueron referencias esenciales
- Parte del código a nivel fuente se conserva o aplica bajo licencia MIT: layouts y tablas de cuantización GGUF, lógica CPU de quant/dot y ciertos kernels Metal, entre otros
- El archivo LICENSE mantiene los avisos de copyright de los autores de GGML
Pesos del modelo
- Solo funciona con el GGUF de DeepSeek V4 Flash publicado específicamente para este proyecto; archivos DeepSeek/GGUF arbitrarios no son compatibles
- La cuantización de 2 bits usa un esquema de cuantización asimétrica
- Solo se cuantizan los expertos MoE: up/gate usa
IQ2_XXSy down usaQ2_K - Los demás componentes, como expertos compartidos, proyecciones y routing, no se cuantizan para garantizar la calidad
- Solo se cuantizan los expertos MoE: up/gate usa
./download_model.sh q2descarga el modelo para máquinas con 128GB de RAM, y./download_model.sh q4para máquinas con 256GB o más- Se descarga desde Hugging Face (
antirez/deepseek-v4-gguf) y soporta reanudar descargas parciales concurl -C -
- Se descarga desde Hugging Face (
./download_model.sh mtppermite descargar un GGUF con soporte opcional de speculative decoding- La ruta MTP/speculative decoding sigue siendo experimental y por ahora solo ofrece una ligera mejora de velocidad
Benchmarks de velocidad
- Cifras de una sola ejecución del CLI Metal con
--ctx 32768,--nothink, greedy decoding y-n 256 - MacBook Pro M3 Max, 128GB (q2)
- Prompt corto: prefill 58.52 t/s, generación 26.68 t/s
- Prompt de 11709 tokens: prefill 250.11 t/s, generación 21.47 t/s
- q4: N/A por falta de memoria
- Mac Studio M3 Ultra, 512GB (q2)
- Prompt corto: prefill 84.43 t/s, generación 36.86 t/s
- Prompt de 11709 tokens: prefill 468.03 t/s, generación 27.39 t/s
- Mac Studio M3 Ultra, 512GB (q4)
- Prompt corto: prefill 78.95 t/s, generación 35.50 t/s
- Prompt de 12018 tokens: prefill 448.82 t/s, generación 26.62 t/s
Uso del CLI
- La opción
-pejecuta un prompt de una sola vez; si se ejecuta sin-p, entra en modo de chat interactivo multiturno - El CLI interactivo mantiene la transcripción de chat renderizada y checkpoints KV Metal en vivo, de modo que cada turno extiende la conversación anterior
- Comandos útiles:
/help,/think,/think-max,/nothink,/ctx N,/read FILE,/quit- Con Ctrl+C se interrumpe la generación actual y se regresa al prompt
- El modo predeterminado es thinking; puede cambiarse a modo de respuesta directa con
/nothinko--nothink - Puede activarse la ruta opcional MTP con
--mtp MTP.gguf --mtp-draft 2- Solo resulta útil con greedy decoding y usa una confidence gate (
--mtp-margin) para evitar aceptar partes lentas
- Solo resulta útil con greedy decoding y usa una confidence gate (
Servidor
- Puede ejecutar un servidor HTTP local compatible con OpenAI/Anthropic
- Es solo para Metal y mantiene en memoria un único grafo/checkpoint KV mutable
- Si un cliente stateless vuelve a enviar una versión más larga del mismo prompt, puede reutilizar el prefijo compartido
- El parseo de solicitudes y los sockets se ejecutan en el hilo del cliente, pero la inferencia en sí se serializa mediante un único worker Metal
- Actualmente el servidor no agrupa varias solicitudes independientes, y las solicitudes concurrentes esperan en orden
-
Endpoints soportados
GET /v1/models,GET /v1/models/deepseek-v4-flashPOST /v1/chat/completions,POST /v1/completions,POST /v1/messages
-
/v1/chat/completions(compatible con OpenAI)- Soporta
messages,max_tokens/max_completion_tokens,temperature,top_p,top_k,min_p,seed,stream,stream_options.include_usage,tools,tool_choice - El esquema de herramientas se renderiza en el formato de herramientas DSML de DeepSeek, y las llamadas DSML generadas se convierten de vuelta a tool calls de OpenAI
- Soporta
-
/v1/messages(compatible con Anthropic)- Endpoint para clientes estilo Claude Code
- Soporta
system,messages,tools,tool_choice,max_tokens,temperature,top_p,top_k,stream,stop_sequencesy control de thinking - El uso de herramientas se devuelve como bloques Anthropic
tool_use
- Ambas API soportan streaming SSE, y en modo thinking el proceso de razonamiento se transmite con un formato de API nativo
Integración con clientes agente
- ds4-server puede integrarse con agentes locales de código que usan chat completions compatibles con OpenAI
- Al ejecutar la cuantización de 2 bits (81GB) en 128GB de RAM, una ventana de contexto de 100k~300k tokens es adecuada
- El contexto completo de 1M tokens usa unos 26GB de memoria (de los cuales el indexador comprimido usa cerca de 22GB)
- Puede evitarse el límite de tokens configurando un límite de salida de
384000(el modelo puede generar hasta 384k tokens) -
Integración con opencode
- Se configura agregando los elementos provider y agent en
~/.config/opencode/opencode.json - Establecer
baseURLenhttp://127.0.0.1:8000/v1
- Se configura agregando los elementos provider y agent en
-
Integración con Pi
- Agregar la configuración del provider en
~/.pi/agent/models.json - Incluye opciones de compatibilidad como el formato thinking de DeepSeek, soporte para reasoning effort y uso de streaming usage
- Puede establecerse como modelo predeterminado en
~/.pi/agent/settings.json
- Agregar la configuración del provider en
-
Integración con Claude Code
- Usa el endpoint compatible con Anthropic y un script wrapper
~/bin/claude-ds4para configurar variables de entorno - Establecer
ANTHROPIC_BASE_URLal servidor local y todas las variables de modelo endeepseek-v4-flash - Claude Code envía al inicio un prompt grande de unas 25k tokens, por lo que es indispensable activar
--kv-disk-dir- Después del primer prefill costoso, el caché KV en disco reutiliza el prefijo guardado y evita reprocesar todo el prompt en sesiones posteriores
- Usa el endpoint compatible con Anthropic y un script wrapper
Modo Thinking
- DeepSeek V4 Flash soporta tres modos: non-thinking, thinking y Think Max
- El valor predeterminado del servidor es el modo thinking
- Puede solicitarse Think Max con
reasoning_effort=max, pero solo se aplica cuando el tamaño de contexto es lo suficientemente grande según las recomendaciones de la model card- En contextos pequeños hace fallback a thinking normal
reasoning_effort=xhighde OpenAI se mapea a thinking normal, no a Think Max- Si se necesita una respuesta directa, puede usarse
thinking: {"type":"disabled"},think:falseo un alias de modelo non-thinking comodeepseek-chat
Caché KV en disco
- La API de chat/completion es stateless, así que los clientes agente reenvían la conversación completa en cada solicitud
- ds4-server procesa esto comparando el flujo de tokens renderizado con el prefijo de tokens almacenado en caché
- El checkpoint activo en memoria maneja la sesión actual
- El caché KV en disco es un mecanismo para conservar prefijos útiles entre cambios de sesión y reinicios del servidor
- Actualmente solo hay un caché KV activo en memoria; si una nueva sesión no relacionada lo reemplaza, la sesión anterior solo podrá reanudarse sin reprocesamiento si quedó registrada en el caché KV en disco
- Se activa con
--kv-disk-diry--kv-disk-space-mb -
Clave del caché y estructura de archivos
- La clave del caché es un hash SHA1 de los IDs exactos de token, no del texto en bruto
- Cada ID de token se hashea como entero little-endian de 32 bits y el nombre del archivo es
<sha1>.kv - Se escribe con I/O normal de
read/write; no usammappara evitar mapeos VM adicionales en un proceso que ya mapea el modelo
-
Layout del archivo de caché en disco
- Header fijo KVC de 48 bytes: magic("KVC"), versión, bits de cuantización de routed expert, motivo de guardado, cantidad de tokens en caché, contador de hits, tamaño de contexto, tiempos Unix de creación/último uso y cantidad de bytes del payload de sesión DS4
- Texto renderizado: texto decodificado por el tokenizador del prefijo de tokens cacheado (solo para observación, no se usa como clave)
- Payload de sesión DS4: comienza con 13 campos u32 little-endian, incluyendo magic("DSV4"), versión del payload, tamaño de contexto, tamaño del chunk de prefill, capacidad del anillo KV, etc.
- Guarda IDs de tokens del checkpoint, logits float32 para el siguiente token, filas comprimidas de atención por capa, filas KV raw del sliding window en vivo, filas KV de capas comprimidas y tensores de compressor frontier, entre otros
-
Cuándo se guardan los checkpoints
cold: después de que un prompt inicial largo alcanza un prefijo estable, antes de generarcontinued: cuando el prefill o la generación avanzan el intervalo configuradoevict: antes de que una solicitud no relacionada reemplace la sesión activa en memoriashutdown: cuando el servidor se cierra normalmente
- En los guardados cold se recorta un pequeño sufijo de tokens y se alinea con el límite del chunk de prefill para evitar errores futuros de retokenización en los límites BPE
- Valores predeterminados: prefijo mínimo de 512 tokens, máximo de 30000 tokens para guardado cold, recorte de cola de 32 tokens y alineación a chunks de 2048 tokens
- Por defecto, los checkpoints pueden reutilizarse entre variantes routed-expert de 2 y 4 bits si el prefijo de tokens coincide
- Puede restringirse a la misma cuantización con
--kv-cache-reject-different-quant
- Puede restringirse a la misma cuantización con
Backend
- El backend predeterminado es Metal (
--metal) - También existe una ruta de referencia/depuración por CPU (
--cpu), pero no está orientada a producción- El servidor es solo para Metal y la implementación optimizada está en la ruta de grafos Metal
- Licencia MIT, implementación en C/Objective-C/Metal
1 comentarios
Comentarios en Hacker News
Lo probé junto con Claude Code en una base de código existente y, aunque es un modelo cuantizado a 2 bits, parece cumplir su función
El procesamiento del prompt tarda unos minutos, pero la edición real es bastante rápida, a más de 20 tokens/segundo
En tareas pequeñas logró explorar el código, aplicar cambios y escribir tests, pero no pudo corregir una observación menor
Lo peor fue que, mientras resolvía otro problema, alucinó y trajo una conversación paralela sobre “The Duck”. Supongo que era uno de los ejemplos del prompt inicial de Claude Code
Antes hice algo muy parecido para el modelo Qwen3. Solo ejecuta Qwen3, soporta solo algunas cuantizaciones, carga desde GGUF y usa una inferencia optimizada iterativamente por Claude
Todo el proyecto cabe en unos pocos archivos y es fácil de entender, así que lo hice para que estudiantes aprendieran experimentando con cosas como agregar estrategias de decodificación o abliteration. Los frameworks famosos son grandes y complejos, difíciles de hackear, y muchos proyectos educativos se quedan en cosas antiguas como GPT-2
Empezó como algo educativo, pero desde entonces no se me va una idea de la cabeza: ¿qué tal crear un motor de inferencia hiperoptimizado para una combinación específica de GPU+modelo? Las GPU son caras y cada vez más difíciles de conseguir, así que si eliminas suficiente abstracción y apuntas directamente al hardware/modelo exacto, parece que se podría optimizar bastante
El problema es que, cuando el modelo queda obsoleto, hay que rehacerlo todo desde cero
En plataformas menos populares todavía hay margen para ganar rendimiento fácilmente, pero no queda mucho espacio para obtener mejoras mucho mayores creando un ejecutor de modelos hiperoptimizado para una familia específica de GPU. Los cálculos clave ya los manejan kernels muy optimizados para cada GPU
También hay forks de llama.cpp optimizados para correr mejor en ciertas arquitecturas de CPU, pero salvo que los mantenedores no se pongan de acuerdo, suele ser mejor invertir tiempo en upstreamear esas mejoras que en hacer un ejecutor separado para un modelo+GPU concreto
https://codegolf.stackexchange.com/questions/215216/high-thr...
¿Se podrían hacer las multiplicaciones de matrices pesadas con amplificadores operacionales? ¿Y este enfoque analógico podría ser mucho más eficiente que las limitaciones de la representación en bits?
Ahora que la IA moderna ya puede hacer incluso optimización de kernels, creo que más gente debería intentar construir por sí misma una mejor inferencia adaptada a su hardware
Tengo una vieja W7900 (RDNA3), y además de sus 48 GB de VRAM, sobre el papel tiene cifras bastante decentes como 123 FP16 TFLOPS/INT8 TOPS y 864 GB/s de ancho de banda de memoria. Pero tanto el soporte de AMD para ROCm como el de llama.cpp han sido famosamente malos
Hace poco empecé a ajustar modelos W8A8-INT8 para usar esta tarjeta como endpoint dedicado de agentes/coding. Corrí unas 800 iteraciones automáticas durante varios días y probé varios modelos frontier/SOTA; sorprendentemente, Kimi K2.6 rindió muy bien. Como resultado, frente a las mejores cifras de llama.cpp con Qwen3.6 MoE, el prefill fue 20% más rápido y el decode 50% más rápido
Ahora sigo profundizando en optimizaciones de MTP y DFlash, y los resultados me están gustando bastante; después quiero probar Gemma 4
Aun así, llama.cpp quizá no tenga el mejor rendimiento, pero sí logra ejecutar la mayoría de modelos de forma consistente. Le falta MTP y parece tener problemas de invalidación de caché en modelos híbridos, pero al menos sé qué cosas corren y cuáles no
Los inferencers basados en Python terminan mezclando uv/venv, mi venv, el entorno del sistema, Python y las librerías, al punto de que ya casi haría falta un agente para averiguar qué es lo que realmente está corriendo. Sé que es problema de habilidad o error del usuario, pero ya no me queda tiempo para eso
Aunque no esté perfecto, si lo subes a GitHub o Hugging Face, otros agentes pueden empezar desde ahí en lugar de desde cero. Eso hice con Ling-2.6-flash (107B-A7B4 MoE), y es el LLM más grande que puedo correr de forma práctica en mi otro hardware para LLM local, un M2 Max
Aunque MTP no funcione bien, sigue siendo una mejora frente a que el llama.cpp actual no pueda correr Ling-2.6-flash en absoluto. La discusión está en https://huggingface.co/inclusionAI/Ling-2.6-flash/discussion..., la cuantización de 4 bits en https://huggingface.co/ljupco/Ling-2.6-flash-GGUF y la rama en https://github.com/ljubomirj/llama.cpp/tree/LJ-Ling-2.6-flas...
Creo que llama.cpp podría haber dado un soporte para PC mucho mejor. Parte del problema será el mal soporte de los vendors, pero sorprende que, con tantos usuarios, no se vea más inferencia optimizada para PCs estándar
Está realmente genial. Me da curiosidad ver qué pasaría si alguien se enfocara durante varios meses en optimizar a fondo un solo modelo open source
No solo el serving de inferencia, sino también la optimización del harness y workflows a medida, para ver cuánto se puede cerrar la brecha en esas carencias que los modelos frontier pueden inferir o deducir, pero que los open source traen de base por límites de tamaño o entrenamiento
Operar Kimi 2.6 a una velocidad decente de tokens/segundo cuesta 20 mil dólares al mes, y para vender esos tokens con ganancia, el hardware tendría que costar menos de mil dólares al mes
Si estás apostando tu capacidad a un futuro en el que billonarios regalan tokens a 1/10 o 1/20 del costo, o a la fantasía de que modelos open source competentes entren en hardware de consumo, entonces ya estás acabado
Hay un dato divertido, interesante y bastante revelador. Mi MacBook M3 Max sube hasta 50 W de consumo cuando DS4 genera tokens a máxima velocidad
Si DS4 Flash alcanza un pico de 50 W y tiene 280B parámetros, ¿entonces DS4 Pro con 1.6T parámetros rondaría los 300 W? Los GPT 5 y Opus actuales se sienten más bien como algo de unos 500 W
Cuando uso Claude Code y el modelo se queda dando vueltas por su cuenta de forma verbosa, ¿es razonable imaginar que en algún datacenter está quemando 500 W?
En la Mac Studio no se puede pedir una opción de más de 96 GB de RAM. Lo mismo pasa con la M3 Ultra o la M4 Max. No sé si será solo en Australia
En cambio, en la MacBook Pro sí se puede elegir 128 GB con la Mac M5
https://www.apple.com/au/shop/buy-mac/mac-studio
Apple quizá prefirió no ponerle precio antes que enfrentarse a polémicas por sobreprecio o quejas por falta de stock
Eliminaron todas las configuraciones de Mac Studio por encima de 96 GB y también la Mac mini base. Incluso hay rumores de que están evaluando sacar del mercado la configuración base de Neo
Parece ser su forma de lidiar con restricciones de capacidad fabril y suministro de RAM
No sé si me perdí algún benchmark o meta motivacional simple
Supongo que esto permite ir más rápido que usando la cadena de herramientas normal, o correr modelos más grandes e inteligentes, pero no parece quedar claro cuánto mejora ya hoy, o cuánto se espera que mejore, frente a esa línea base
Si uno conociera esas comparaciones, probablemente podría calcularlo con los números mostrados, claro
Muy impresionante. Pero sí se ve raro que con entradas grandes tarde unos 4 minutos en empezar a responder
Yo no uso LLM en hardware Mac, pero me sorprende bastante y parece una barrera importante para el uso real
Aunque, para uso normal, la explicación sobre el caché lo vuelve mucho más entendible. Claude Code suele mandar un prompt inicial grande de unas 25k tokens antes de empezar algo útil, y si dejas activado
--kv-disk-dir, después del primer prefill costoso el caché KV en disco puede reutilizar el prefijo ya guardado sin volver a procesar todo el promptPero en una M3 Ultra el prefill va casi a 500 tokens/segundo, así que ya entra en terreno perfectamente usable. En una M3 Max hace falta un poco más de paciencia, pero funciona bien, y si usas pi agent, como imprime el proceso de pensamiento, en vez de esperar te quedas leyendo un chain of thought sin censura
Ayer subí a X un video usándolo en una M3 Max, y escupe tokens a una velocidad bastante aceptable
En una MacBook, los LLM grandes tienen una velocidad de generación de tokens aceptable, pero el problema es leer el contexto
No la lectura incremental con caché KV, como en una sesión de chat, sino cuando tienen que leer una entrada grande, como al pegar archivos grandes. Ahí puede tardar minutos
Probablemente eso lo deja bastante lejos de la inteligencia original que ofrece un proveedor cloud
Aun así, muestra mejor lo posible que puede ser un LLM local en un workflow tipo agente
¿Existe alguna arquitectura que no dependa de volver a inyectar todo el historial de conversación? Algo como LLM recurrentes, por ejemplo?