Todos los artículos
20 min de lectura

Ejecutando Hermes Agent con 8 GB de VRAM cuando la comunidad dice que el mínimo son 16 GB

Benchmarks medidos, las matemáticas de la caché KV que hacen que 8 GB encajen y un kit de instalación para Windows 11 que lleva a una RTX 3060 Ti de cero a cadenas de llamadas a herramientas contra un modelo 9B alojado en local, con un solo comando de PowerShell.

AI Side Projects Open Source Linux

El Discord de Hermes hizo una encuesta en abril de 2026: 16 GB+ de VRAM es el nivel cómodo para trabajo agéntico, 24 GB+ es “lo mejor”. Mi escritorio tiene una RTX 3060 Ti con 8 GB. Quería saber cuánto se puede vivir por debajo de esa línea, así que pasé un fin de semana escribiendo un kit de instalación para Windows 11 que lleva la máquina de “acabo de descargar un zip” a “el agente está encadenando llamadas a herramientas en una terminal”. Por el camino aprendí muchas cosas pequeñas sobre dónde está el suelo real; y Carnice-9b, un modelo 9B corriendo en una tarjeta supuestamente demasiado pequeña, me sorprendió más que nada que haya probado este año.

Este artículo trata de todo eso: las matemáticas de inferencia que hacen que 8 GB quepan, la fricción específica de Windows que casi mató el proyecto, y cómo se siente realmente cuando un modelo pequeño alojado localmente maneja limpiamente tareas de agente de varios pasos.

Qué significa “ejecutar Hermes localmente”

Antes de que las matemáticas sean interesantes, ayuda saber qué se ejecuta dónde. Hermes Agent es el framework de agentes de código abierto de Nous Research — la TUI con la que interactúas, el sistema de skills, la memoria persistente, la lógica de llamadas a herramientas. Nada de eso hace inferencia por sí mismo. Habla el protocolo OpenAI Chat Completions y envía cada prompt al endpoint que configures.

En una configuración cloud típica, ese endpoint es api.openai.com o api.anthropic.com y pagas por token. Ir local significa simplemente cambiar el endpoint por http://127.0.0.1:8080/v1 y levantar un proceso que hable el mismo protocolo al otro lado. Ese proceso es llama-server, parte de llama.cpp — un único binario que carga un archivo de modelo GGUF, expone una API HTTP compatible con OpenAI y ejecuta las matemáticas del transformer en tu GPU.

Así que el stack local real son tres piezas:

  1. La capa de drivers: el driver de NVIDIA en Windows, el toolkit CUDA dentro de WSL, y el shim WSL-CUDA que los conecta.
  2. El motor de inferencia: un binario llama-server compilado con CUDA que mapea en memoria un archivo .gguf y expone /v1/chat/completions en localhost.
  3. El agente: Hermes, apuntando a ese endpoint de localhost, haciendo todo el trabajo de skills/herramientas/memoria.

Todo en el kit existe para hacer que esas tres piezas se levanten en una instalación limpia de Windows 11 sin que tengas que aprender qué son antes. Pero saber qué son hace que el “por qué” de cada decisión de abajo quede mucho más claro.

Por qué mi tarjeta no debería haber funcionado

Hermes se niega a arrancar por debajo de 64K de contexto. Ese suelo existe por una razón real: el agente reinyecta constantemente definiciones de herramientas, conversación reciente, recuperaciones de memoria y metadatos de skills en cada turno del modelo. Por debajo de 64K, el agente olvida a qué herramientas tiene acceso a mitad de tarea. Por encima de 64K, las cosas funcionan en su mayoría. 64K es el trato.

Pero 64K de contexto en un modelo 9B es donde 8 GB de VRAM dejan de ser teóricos y empiezan a ser un rompecabezas. Las matemáticas se dividen en tres bloques:

QuéCuánto en un modelo 9B
Pesos del modelo (Q4_K_M)~5,2 GB
Caché KV a 64K, FP16~5,5 GB
Scratch de inferencia + runtime CUDA~0,5 GB

Eso suma ~11,2 GB en una tarjeta que tiene 8 GB totales, y eso antes de que el framebuffer del escritorio, el navegador y Discord hayan reclamado sus pocos cientos de megabytes. La configuración por defecto que la documentación de llama.cpp te guía a usar no cabe en una 3060 Ti, ni de lejos.

El “16 GB mínimo” de la comunidad es, en esa luz, completamente honesto.

El truco de la caché KV que hace que 8 GB encajen

Lo primero que aprendes cuando te sientas con los números es que los pesos del modelo no son el problema. Un modelo de 5,2 GB en una tarjeta de 8 GB deja ~2,8 GB de margen: suficiente, si los otros costes fueran fijos. El problema es la caché KV: el bloc de notas que va guardando las claves y valores de atención por cada token en la ventana de contexto. Escala con capas × cabezas KV × dimensión por cabeza × longitud de contexto × tipo de dato, y ese último término —el tipo de dato usado para almacenar cada valor— es donde se gana la guerra:

Clase de modeloKV FP16 a 64KKV Q8KV Q4
9B~5,5 GB~3 GB~1,5–2 GB

No es una errata. Cuantizar la caché KV de floats de 16 bits a enteros de 4 bits lleva el coste corriente de ~5,5 GB a menos de 2 GB, con una pérdida de calidad casi imperceptible en la práctica para cargas de agente. La forma de las matemáticas:

Coste KV por token ≈ 2 (K y V) × capas × num_kv_heads × head_dim × bytes

Dos cosas mantienen ese número pequeño específicamente para Qwen3.5-9B — y juntas son lo que hace viable el nivel de 8 GB:

  • Grouped Query Attention. Qwen3.5-9B tiene muchas más cabezas de query que de KV — las claves y valores se comparten entre grupos de queries, lo que recorta la caché aproximadamente 4× frente a una arquitectura de atención densa del mismo tamaño. Sin GQA, este mismo modelo 9B a 64K necesitaría más bien 24 GB solo para KV, y ninguna cantidad de cuantización inteligente lo metería en una 3060 Ti.
  • Cuantización Q4 de la propia caché. Otra reducción de ~4× almacenando enteros de 4 bits en lugar de floats de 16 bits, con una pérdida de calidad casi imperceptible en la práctica para cargas de agente.

El efecto combinado deja el coste corriente alrededor de ~1,5 GB en Q4 frente a ~5,5 GB en FP16: las cifras de la tabla de arriba.

Cada invocación de llama-server en el kit fuerza --cache-type-k q4_0 --cache-type-v q4_0. Este par de flags es la diferencia entre quedarte sin memoria al arrancar y que el modelo cargue limpio con ~500 MiB de margen para el resto del sistema: apretado, pero real.

Flash Attention: la otra mitad del desbloqueo

El otro flag que cada script fuerza es --flash-attn on. Flash Attention no es tanto un cambio algorítmico como un cambio en el patrón de acceso a memoria: en lugar de materializar la matriz de atención completa (que escala con el cuadrado de la longitud de secuencia) y luego hacer el softmax, recorre la matriz en bloques que caben en memoria compartida y nunca tiene la matriz entera en VRAM a la vez.

Para nuestros propósitos, el efecto práctico es que la memoria de trabajo del cálculo de atención se reduce aproximadamente a la mitad; y a 64K de contexto, “aproximadamente la mitad” es la diferencia entre una respuesta fluida y un OOM a mitad de token. Con Flash Attention apagado, el ahorro de la caché KV Q4 se come en parte en buffers transitorios de atención y 8 GB deja de caber otra vez.

El kit autodetecta tu tarjeta en el momento de instalar y elige la precisión KV en consecuencia: Q4 en tarjetas de 8 GB, Q8 en 10–12 GB, F16 por encima. Flash Attention siempre activado. Nadie debería tener que aprender el contenido de esta sección solo para lanzar un agente.

Por qué WSL2, no Windows nativo

Hermes incluye un instalador nativo de Windows. Nous lo llama “early beta”. Al instalador de Linux lo llaman “battle-tested”. Misma máquina, misma GPU, mismo agente: la diferencia es qué ruta del código ha pisado la comunidad.

Los problemas concretos que evitas usando WSL2:

  • Parsers de llamadas a herramientas: Hermes tiene parsers por modelo que traducen la salida en texto del modelo a tool_calls estructurados. Estos se han desarrollado y probado contra rutas de archivo de Linux y semántica de procesos POSIX. La build nativa de Windows tiene sus propias copias, con bordes más ásperos.
  • Sistema de skills: muchos de los skills incorporados de Hermes invocan herramientas Unix instalables con aptripgrep, ffmpeg, jq, cosas que el agente usa para leer repositorios, parsear medios, manipular datos. En Windows nativo hay que instalarlas y meterlas en el PATH por separado, y el Git Bash incluido que Hermes usa para encontrarlas tiene rarezas.
  • Watch de archivos: los skills monitorean cambios mediante semántica POSIX inotify. WSL2 la tiene; Win32 tiene otras y el shim no es perfecto.

El coste de ir por WSL2 es aproximadamente cero. La pasarela CUDA de NVIDIA para WSL significa que el driver de Windows hace el trabajo real de la GPU, y Ubuntu dentro de WSL2 le habla a través de un shim especial en /usr/lib/wsl. El rendimiento de inferencia está dentro de un pequeño porcentaje respecto a Linux nativo. No haces dual-boot, no pierdes tu GPU, no pagas nada por la indirección.

Esta es también la razón por la que nunca debes instalar el driver NVIDIA de Linux dentro de WSL. Entra en conflicto con la pasarela. El kit instala solo el toolkit CUDA (cuda-nvcc-12-4, cuda-cudart-dev-12-4, etc.) — las piezas del SDK que llama.cpp necesita en tiempo de compilación. El driver se queda en Windows, donde le toca estar.

Las trampas de Ubuntu 24.04

Tres cosas cambiaron entre Ubuntu 22.04 y 24.04 que rompieron cada guía de instalación de Hermes escrita en 2024 o antes. Saber por qué existe cada una las hace menos exasperantes:

libtinfo5 ya no está. Ubuntu 24.04 solo trae libtinfo6, la librería ncurses más nueva. cuda-toolkit-12-4, el metapaquete al que todo el mundo recurre, depende transitivamente de nsight-systems-2023.4.4, que sigue enlazado contra libtinfo5. Instalar el metapaquete en 24.04 tira un error de dependencias sin solución. El arreglo es saltarse el metapaquete e instalar solo las piezas que llama.cpp necesita de verdad: el compilador NVCC, las cabeceras del runtime CUDA, el compilador en tiempo de ejecución NVRTC, cuBLAS (para multiplicación de matrices) y cuRAND. Nsight es un profiler. No lo necesitamos.

PEP 668 está activo. Python 3.12 en 24.04 marca el Python del sistema como “externally managed”, lo que significa que pip install --user falla con un aviso sobre pisar paquetes gestionados por el sistema. La jugada correcta es pipx, que instala cada herramienta CLI de Python en su propio venv aislado y crea symlinks de los entry points en tu PATH. El kit usa pipx para instalar la CLI de Hugging Face y otras herramientas que antes se instalaban con pip.

huggingface-cli se renombró a hf. Versiones recientes del paquete huggingface_hub convirtieron el binario antiguo en un shim de deprecación que imprime un aviso y sale con código distinto de cero. Cualquier script que lo invocara sin comprobar el exit code se rompe silenciosamente. Los descargadores del kit usan hf download en todas partes.

Ninguna de estas es difícil. Las tres son completamente imposibles de buscar de antemano: solo las encuentras chocándote con ellas.

Carnice-9b: la parte que de verdad me sorprendió

Aquí está el modelo que hizo que el proyecto entero mereciera la pena.

Carnice-9b es una variante de Qwen3.5-9B afinada para Hermes. “Afinada para Hermes” hace mucho trabajo en esa frase: significa que el modelo se ha entrenado específicamente sobre la gramática de llamadas a herramientas y razonamiento agéntico que el framework de Hermes espera. Las llamadas a herramientas salen en el sobre XML correcto. Los planes de varios pasos salen estructurados. El modelo “sabe” que es un agente de una forma en la que los modelos instruct base no.

En Q4_K_M el GGUF ocupa 5,23 GiB en disco. Los metadatos del GGUF lo identifican como Carnice_V1_9B_Hermes_Stage2_Merged, modelo base Qwen/Qwen3.5-9B, arquitectura qwen35. Con la caché KV Q4 y los flags de Flash Attention del kit, mi 3060 Ti reporta 7.519 / 8.192 MiB usados con el modelo cargado y un contexto de 64K calentado: exactamente 506 MiB de margen, que resulta ser suficiente para el framebuffer del escritorio si no tienes a Chrome devorando VRAM de fondo.

La velocidad de generación en mi máquina es de 52–53 tokens por segundo, medida a lo largo de media docena de peticiones incluyendo la tarea multi-herramienta de abajo. Los números salieron notablemente consistentes: predicted_per_second en el campo timings del propio servidor se mantuvo entre 51,7 → 53,5 tok/s cada vez. La evaluación del prompt corre mucho más rápido (40–570 tok/s dependiendo de cuánto del prompt está en caché). No hay lag perceptible entre “pulso enter” y “el modelo empieza a responder”.

Lo que de verdad no esperaba:

Las llamadas a herramientas funcionan de verdad. La sabiduría convencional de los últimos dos años ha sido que necesitas 30B+ parámetros para function calling fiable: que los modelos pequeños alucinan nombres de herramientas, se equivocan con los esquemas de argumentos y olvidan a qué herramientas tienen acceso a mitad de una tarea de varios pasos. Carnice-9b no hace esas cosas. Para comprobarlo, escribí un arnés de cuatro herramientas exponiendo read_file, parse_json, calculate y write_file, y le pedí a Carnice: leer /tmp/carnice_test/order.json, calcular el total con impuestos (precio × cantidad × (1 + tasa)), escribir el resultado a un archivo nuevo y reportar de vuelta. Hizo toda la tarea en cuatro turnos / 6,8 segundos a ~53 tok/s: read_filecalculate(49.99 * 3 * (1 + 0.21))write_file(...) → un resumen en lenguaje natural citando la ruta del archivo y el número. El archivo final en disco decía 181.4637. Los sobres de las llamadas a herramientas estaban estructurados correctamente, los argumentos cumplían los esquemas, y hasta decidió saltarse mi herramienta redundante parse_json porque la salida de read_file ya era JSON válido. Ese último detalle fue el momento en que empecé a tomarme el modelo en serio.

La deriva de formato del primer turno frente a los siguientes. Hay un detalle real que merece conocerse. En el primer turno, Carnice emite llamadas a herramientas en el formato canónico que el parser --jinja de llama-server sabe leer, y aterrizan limpiamente en el campo tool_calls compatible con OpenAI. En turnos posteriores — después de devolver un resultado de herramienta — a veces emite la llamada en un sobre XML ligeramente distinto (<tool_call><function=...><parameter=...>) dentro de reasoning_content en lugar de content, y el parser de llama-server no captura esa variante. Por esto Hermes incluye sus propios parsers de llamadas a herramientas en el lado del cliente. El modelo está produciendo la llamada correcta; el arnés tiene que ser tolerante con el formato. Mi script de pruebas reproduce esto exactamente — primera llamada: parseada nativamente; llamadas dos y tres: recuperadas por un parser de regex de 30 líneas en el lado del arnés. Sin ese parseado tolerante, la cadena se atasca en el paso uno.

El detalle del modo de pensamiento. Carnice hereda el comportamiento de dos modos de Qwen3: razona en un campo reasoning_content durante unos cientos de tokens antes de emitir la respuesta real. Esto es una feature, no un bug — pero significa que presupuestos cortos de max_tokens fallan silenciosamente. Una petición con max_tokens=80 para “saluda” nunca llega al saludo; los ochenta tokens se gastan pensando y content vuelve vacío. Pon max_tokens a 1500–2000 para cualquier petición con llamadas a herramientas y la realidad vuelve a su sitio.

El flag --jinja está haciendo dos trabajos a la vez. Sin él, llama-server usa una plantilla de chat por defecto que no expone correctamente los tokens del modo de pensamiento del modelo ni enruta las llamadas a herramientas al campo estructurado. Probé los dos casos: con --jinja apagado, el modelo emite 80 tokens de razonamiento y cero tokens de contenido en cualquier prompt que normalmente disparase razonamiento. Con --jinja encendido, el razonamiento aterriza en reasoning_content, la llamada a herramienta aterriza en tool_calls, y todo aguas abajo simplemente funciona.

Este es el desbloqueo que hace viable el nivel de 8 GB. Sin modelos pequeños afinados para Hermes como Carnice, necesitarías un modelo clase 30B que no cabe en una 3060 Ti y te verías forzado al fallback en la nube. Con Carnice + --jinja + un arnés que captura la deriva de formato en los turnos siguientes, la ruta local es real.

La configuración que hace que las llamadas a herramientas funcionen

Algunos detalles pequeños de configuración de Hermes me costaron una tarde y merecen estar documentados:

“No inference provider configured” — el error más confuso del proyecto. El servidor está corriendo, el modelo está cargado, /v1/models devuelve el nombre correcto, Hermes conecta, y luego se niega a hablar. La causa: Hermes trata OPENAI_API_KEY="" o OPENAI_API_KEY=none como “ningún proveedor configurado”. Solo necesita una string no vacía. El kit pone sk-local-no-auth. Cualquier literal sirve.

El nombre del modelo no debe llevar el prefijo custom/. Algunas guías antiguas sugieren custom/carnice-9b como nombre del modelo en Hermes. Hermes ahora parsea la barra como una ruta de proveedor y busca un proveedor llamado “custom”. carnice-9b a secas es lo que quieres, coincidiendo exactamente con lo que el endpoint /v1/models de llama-server reporta.

--jinja no es negociable, y el kit lo incluye. Cubierto en profundidad arriba; la versión corta es que sin --jinja, llama-server cae a una plantilla de chat por defecto que no reconoce la división razonamiento/contenido de Carnice. El modelo se gasta todo su presupuesto de tokens en modo razonamiento y nunca llega a una respuesta utilizable. Los scripts de arranque pasan --jinja por defecto; esta entrada solo existe por si forkeas el kit y el flag desaparece, para que sepas dónde mirar.

La salida elegante

No todo el mundo va a tener una ejecución local limpia. A veces el modelo se equivoca con las llamadas a herramientas igualmente. A veces 8 GB son demasiado apretados para lo que quieres hacer. El kit trae un docs/FALLBACKS.md que existe precisamente para que el proyecto no acabe en frustración: puedes mantener toda tu instalación de Hermes — tus skills, tu memoria, la memoria muscular de la TUI — y cambiar el backend de inferencia a Ollama Cloud (tier gratis) o OpenRouter (~$2–5/mes por una hora de trabajo con agente al día) en unos dos minutos.

La obsesión con lo “totalmente local” es sobre todo privacidad y aprendizaje, no dinero. El fallback en la nube existe para que una experiencia local frustrante no queme el proyecto entero — y para que puedas aislar “¿está mal mi configuración de Hermes?” de “¿es mi modelo local el problema?” con un solo comando en la TUI.

Lo que de verdad aprendí

El nivel de 8 GB es real pero está apretado. En mi 3060 Ti, Carnice + 64K de contexto se queda en 7.519 / 8.192 MiB usados — el 92 % de la capacidad de la tarjeta, con poco más de 500 MiB de margen. El “16 GB mínimo” de la comunidad es un consejo honesto para quien quiera que la experiencia “simplemente funcione” sin este tipo de toqueteo. No es un suelo duro — al menos no a 8 GB. No he probado nada más pequeño; si tienes una tarjeta de 6 GB y quieres contarlo, me encantaría leer tu artículo.

La mayor parte del desbloqueo viene de cuatro flags de configuración, no del número de parámetros:

  • --cache-type-k q4_0 y --cache-type-v q4_0 — la caché KV Q4, la mayor victoria de VRAM por sí sola.
  • --flash-attn on — atención en bloques, reduce a la mitad la memoria de trabajo a 64K de contexto.
  • --jinja — activa la plantilla de chat propia del modelo, que es la que enruta los tokens de pensamiento y las llamadas a herramientas a los campos de respuesta correctos.

Lo más interesante que aprendí es sobre el tamaño del modelo. El modelo mental de 2024 — “necesitas 30B+ para trabajo de agente” — ya está desfasado para modelos pequeños afinados para un arnés concreto. Carnice, en una tarjeta que la comunidad de Hermes decía que no debía funcionar, completó una tarea de cuatro herramientas en menos de siete segundos con llamadas estructuradas limpias y un resumen final coherente. El afinado específico para Hermes está haciendo más de lo que el número de parámetros sugeriría. Probablemente hay una lección generalizable aquí: a medida que los fine-tunes se vuelven más específicos para un arnés, los requisitos de hardware de ese arnés bajan más rápido de lo que predeciría la ley de escalado por tamaño de modelo.

La letra pequeña: esa carrera limpia de cuatro herramientas solo se mantuvo limpia porque el arnés de pruebas era tolerante con una deriva de formato XML en los turnos siguientes. Hermes-sobre-Carnice se ocupa de esto por ti. llama-server solo no lo hace, no en cada turno, y un fork que arranque los parsers del lado del cliente de Hermes se chocará con la pared al tercer tool call.

Si tienes una 3060 Ti, una 4060 o cualquier tarjeta NVIDIA de 8 GB cogiendo polvo en una torre, el kit está en GitHub en blugart-dev/hermes-local-setup. La instalación entera es un comando de PowerShell, con un reinicio en medio. Prueba Carnice. A ver si te sorprende como me sorprendió a mí.

Ignacio María Muñoz Márquez

Ignacio María Muñoz Márquez

Senior Game Programmer

Artículos relacionados