llama.cpp - despliegue y plantilla de arranque

Cómo preparar cualquier modelo GGUF y su script de arranque para un servidor local con llama.cpp y CUDA. El procedimiento vale para cualquier equipo; los ejemplos asumen 16 GB de VRAM y se ajustan con la formula del paso 1.


Reglas de oro (KV cache y contexto)

OrdenIntentoContextoKV cacheCuándo
1º128K-c 131072q8_0Siempre que el modelo + KV + overhead quepan en VRAM
2º64K-c 65536q8_0Si el modelo es más grande y 128K ya no cabe
3º64K (o menos)-c 65536 o inferiorq4_0Si de ninguna manera cabe con q8

La idea: siempre q8 y el máximo contexto que quepa (128K → 64K), y solo se baja KV a q4 como último recurso. La KV cache q4 tiene pérdida mínima en modelos ya cuantizados, pero q8 es la prioridad.

Regla de VRAM: si el total modelo + KV + ~2 GB de overhead es menor que la VRAM libre, se usa offload completo (-ngl 999). Si no cabe ni a 64K q4, se plantea bajar la cuantización del propio modelo (Q4 en vez de Q8) o usar -cmoe/CPU.


Procedimiento paso a paso (para cualquier modelo)

Paso 1 — Comprobar arquitectura, MTP y dimensionar la KV

Con la librería gguf de Python (instalada):

from gguf import GGUFReader
r = GGUFReader(r"<RUTA_MODELOS>/<MODELO>.gguf")
 
def val(k):
    if k not in r.fields: return None
    return r.fields[k].parts[r.fields[k].data[0]].tolist()
 
# Prefijo de arquitectura: "llama", "qwen2", "qwen3moe", "gemma4", ...
arch = bytes(val("general.architecture")).decode()
layers = val(arch + ".block_count")
hkv    = val(arch + ".attention.head_count_kv")
hd     = val(arch + ".attention.key_length")
 
print("arquitectura:", arch, "| capas:", layers, "| KV heads:", hkv, "| head_dim:", hd)
kv_q8 = 2 * layers * hkv * hd / 1e9          # GB por token (q8 = 1 byte/valor)
print(f"KV q8: {kv_q8*1024:.3f} MB/token | 128K={kv_q8*131072:.2f} GB | 64K={kv_q8*65536:.2f} GB")
 
# MTP: hay tensores MTP/draft?
mtp = [t.name for t in r.tensors if "mtp" in t.name.lower() or "draft" in t.name.lower()]
print("MTP:", "SOPORTADO" if mtp else "NO soportado")
  • Si MTP: SOPORTADO → se puede añadir --spec-type draft-mtp --spec-draft-n-max 2 (y el -m del drafter en --model-draft si es un archivo aparte).
  • Si no → se omite MTP y ya está.
  • El KV por token es la cifra clave para el Paso 2.

Paso 2 — Elegir contexto y KV según las reglas

Suma al tamaño del GGUF (el peso en GB del archivo) la KV elegida y ~2 GB de overhead. Con 16 GB de VRAM (≈14,6 GB libres en la practica):

Tamaño GGUFContextoKVTotal aprox.Resultado
≤ ~9 GB128Kq8modelo + 3.8 + 2✅ 128K q8
~9–12 GB64Kq8modelo + 1.9 + 2✅ 64K q8
~12–14 GB64Kq4modelo + 1.0 + 2✅ 64K q4
> ~14 GBbajar quant / -cmoe——replantear

La tabla es orientativa para modelos densos típicos. La fuente de verdad es la fórmula (Paso 1): compara modelo + KV + 2 GB contra la VRAM libre (nvidia-smi --query-gpu=memory.free --format=csv). Los MoE hibridos solo hacen atencion completa en una de cada cuatro capas, asi que su KV real es varias veces menor que la formula.

Paso 3 — Elegir binario y puerto

  • Binario CUDA: %USERPROFILE%\.unsloth\llama.cpp\build\bin\Release\llama-server.exe (build más reciente, soporta MTP/jinja/CORS). Alternativa: %USERPROFILE%\AI\llama.cpp-cuda\llama-server.exe.
  • Puerto libre: elige uno que no use ningun otro servicio (ss -tulpn o netstat -ano). Si vas a tener varios modelos, reserva un rango y documentalo.

Paso 4 — Crear el .bat con la plantilla

Copia la plantilla de abajo, sustituye SU_CARPETA, SU_MODELO.gguf, SU_PUERTO y los flags de contexto/KV según el Paso 2. Guárdalo junto al modelo. Opcionalmente añádelo al menú AI\models\start-model.bat (ver más abajo).


Plantilla .bat (copiar/pegar)

Guarda como start-NOMBRE.bat en la carpeta del modelo:

@echo off
REM ============================================================
REM  NOMBRE DEL MODELO (QUANT) - Servidor local llama.cpp (GPU)
REM  Arquitectura : <arch> (ver Paso 1)
REM  MTP          : <SOPORTADO | NO soportado>
REM  KV cache     : <q8_0 | q4_0>
REM  Contexto     : <131072 | 65536> - <cabe | no cabe> en VRAM 16 GB
REM  GPU          : <VRAM> GB, offload completo (-ngl 999)
REM  Puerto       : SU_PUERTO
REM  Arrancar a MANO: ejecutar este .bat
REM ============================================================
 
set MODEL=<RUTA_MODELOS>\<MODELO>.gguf
set LLAMA=%USERPROFILE%\.unsloth\llama.cpp\build\bin\Release\llama-server.exe
 
set CTX=131072          REM 65536 si el modelo es grande (regla 2)
set CTK=q8_0            REM q4_0 si no cabe con q8 (regla 3)
set CTV=q8_0            REM igual que CTK
 
echo.
echo  Arrancando NOMBRE DEL MODELO...
echo  Modelo  : %MODEL%
echo  Context : %CTX% (%CTX%/1024 K)
echo  KV      : K=%CTK% V=%CTV%
echo  GPU     : <VRAM> GB (offload completo)
echo  Puerto  : SU_PUERTO
echo  MTP     : <SOPORTADO | no soportado>
echo.
 
"%LLAMA%" ^
  -m "%MODEL%" ^
  -ngl 999 -fa on ^
  -c %CTX% --cache-type-k %CTK% --cache-type-v %CTV% ^
  --no-context-shift ^
  --jinja ^
  --temp 0.7 --top-p 0.80 --top-k 20 ^
  --cors-origins localhost ^
  --alias SU_ALIAS ^
  --port SU_PUERTO ^
  --host 127.0.0.1
 
echo.
echo  Servidor detenido.
pause

Flags MTP (SOLO si el Paso 1 dice que está soportado): añade justo tras -m "%MODEL%":

  --spec-type draft-mtp --spec-draft-n-max 2 ^

Si el drafter es un archivo aparte (mtp-*.gguf), añade además:

  --model-draft "%USERPROFILE%\AI\models\SU_CARPETA\mtp-XXXX.gguf" ^

Añadirlo al menú start-model.bat

En %USERPROFILE%\AI\models\start-model.bat (el gestor DSH):

  1. Añade una línea al menú: echo N) NOMBRE (QUANT) (puerto SU_PUERTO).
  2. Cambia [1-X] a [1-X+1] en el set /p.
  3. Añade if "%OPT%"=="N" goto :load_N.
  4. Añade al final un bloque :load_N:
:load_N
call :KILL
echo [*] Arrancando NOMBRE en http://localhost:SU_PUERTO ...
"%USERPROFILE%\.unsloth\llama.cpp\build\bin\Release\llama-server.exe" ^
  -m "<RUTA_MODELOS>\<MODELO>.gguf" ^
  -ngl 999 -fa on ^
  -c 131072 --cache-type-k q8_0 --cache-type-v q8_0 ^
  --no-context-shift --jinja ^
  --temp 0.7 --top-p 0.80 --top-k 20 ^
  --cors-origins localhost ^
  --alias SU_ALIAS --port SU_PUERTO --host 127.0.0.1
echo.
echo [*] NOMBRE detenido. Pulse una tecla para volver al menu.
pause >nul
goto :MENU

:load_N empieza con call :KILL para asegurar que solo corre un modelo a la vez (mata el llama-server anterior).


Ejemplo resuelto — WhiteRabbitNeo V3 7B (Q8_0)

  • Arquitectura: qwen2 (Qwen2.5-Coder-7B), 28 capas, 4 KV heads, head_dim 128.
  • KV: 2 × 28 × 4 × 128 = 28 KB/token → 128K q8 = 3.76 GB.
  • Cabe: modelo Q8 (8.1 GB) + KV 3.76 + 2 overhead ≈ 12 GB < 14.6 GB → 128K + q8 (regla 1). MTP no soportado.
  • Puerto: 8085. Archivo: AI\models\7B Neo Rabbit\start-whiterabbitneo.bat y entrada 12 del menú.

Referencia rápida — flags

FlagQué haceValor habitual
-mRuta al GGUF%USERPROFILE%\AI\models\...
-ngl 999Offload completo a GPU999
-fa onFlash Attention (ahorra VRAM, más rápido)on
-cTamaño de contexto131072 / 65536
--cache-type-k/vCuantización de la KV cacheq8_0 / q4_0
--jinjaUsa el chat template Jinja del GGUFon
--cors-origins localhostBlinda CORS contra drive-by (seguridad)localhost
--port / --hostPuerta y bindSU_PUERTO / 127.0.0.1
--spec-type draft-mtpDecodificación especulativa MTP (solo si soporta)draft-mtp