El blog de seguridad de FireAI

Por FireAI Security & Research Team · Publicado

Ejecución de un LLM local en Apple Silicon para clasificación de seguridad

Ejecución de un LLM local en Apple Silicon para clasificación de seguridad

Un modelo que clasifica una conexión de red como digna de ser revisada o no, ejecutándose en la misma Mac que realizó la conexión, cambia tres cosas a la vez: qué sale de la máquina, cuánto cuesta ejecutarla y si sigue funcionando cuando la red misma es la cosa bajo sospecha. Los tres son más importantes para una herramienta de seguridad que para la mayoría de los otros usos de un modelo de lenguaje, por lo que este artículo trata específicamente del caso del dispositivo en lugar de llamar a una API.

Por qué en el dispositivo, específicamente para la clasificación

Enviar una línea de registro de conexión a un modelo de nube para su clasificación significa transmitir, para cada decisión, qué aplicación en su Mac está hablando con qué host, en qué puerto, en este momento. Nada de eso es un secreto como lo es una contraseña, pero es exactamente el tipo de metadatos que una configuración consciente de la seguridad intenta minimizar el intercambio, y una herramienta cuyo único propósito es decidir lo que su Mac puede enviar a otro lugar tiene una razón obvia para no depender de enviar algo a otro lugar para cada decisión que toma. La ejecución del modelo localmente elimina esa dependencia por completo: la línea de registro nunca sale del dispositivo, porque no hay ninguna llamada de red en la ruta de decisión.

La segunda razón es la continuidad. Un paso de clasificación basado en la nube solo está disponible en la medida en que su conexión a Internet y la API del proveedor, las cuales son exactamente las cosas que podrían degradarse o cortarse deliberadamente durante un incidente real. Un modelo que se ejecuta en la memoria local sigue respondiendo con la red inactiva, el Wi-Fi apagado o una sospecha de compromiso en curso en el mismo enlace que habría utilizado la llamada API.

MLX: el sistema de matriz propio de Apple

MLX es un marco de matriz creado por el equipo de investigación de aprendizaje automático de Apple específicamente para Apple Silicon, con API Python, C++, C y Swift. Su propia documentación describe un modelo de memoria unificada como su elección de diseño definitoria: las matrices en MLX viven en memoria compartida y las operaciones en ellas pueden ejecutarse en cualquier dispositivo compatible (CPU o GPU) sin que los pesos del modelo se copien primero entre grupos de memoria separados. Esto es importante en concreto para una computadora portátil: una máquina con GPU discreta tiene que copiar los pesos de un modelo a través de un bus en la memoria de la GPU antes de poder calcular algo, lo que cuesta tiempo y duplica la huella de memoria; En la arquitectura de memoria unificada de una Mac, la CPU y la GPU ya comparten la misma memoria física, por lo que no hay nada que copiar.

mlx-lm, el paquete complementario para ejecutar modelos de lenguaje en MLX, se instala con un solo comando y envía un modelo predeterminado listo para usar:

configuración mlx
pip install mlx-lm
# runs the default model (mlx-community/Llama-3.2-3B-Instruct-4bit)
mlx_lm.generate --prompt "How tall is Mt Everest?"
# or name a specific quantized model from the mlx-community hub
mlx_lm.generate --model mlx-community/Mistral-7B-Instruct-v0.3-4bit --prompt "..."
# interactive chat session instead of a single prompt
mlx_lm.chat
# convert and quantize a model yourself
mlx_lm.convert --model mistralai/Mistral-7B-Instruct-v0.3 -q

llama.cpp: la opción portátil

llama.cpp es el más antiguo y más adaptado de los dos, escrito en C/C++ sin necesidad de tiempo de ejecución de Python en el momento de la inferencia. Su propio archivo README establece directamente la posición del proyecto en este hardware: el silicio de Apple se trata como, en palabras del proyecto, "un ciudadano de primera clase, optimizado a través de los marcos ARM NEON, Accelerate y Metal", y su documentación de compilación confirma que en macOS, el backend Metal GPU está habilitado de forma predeterminada, con un indicador de tiempo de compilación para deshabilitarlo si específicamente desea inferencia solo de CPU.

llama.cpp compilar y ejecutar
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build
cmake --build build --config Release
# Metal is on by default on macOS; add -DGGML_METAL=OFF to the first
# command above if you need to force CPU-only inference
llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF
# or run it as a local server instead of a one-shot command
llama serve -hf ggml-org/Qwen3.5-0.8B-GGUF

llama.cpp funciona a partir de archivos GGUF, un formato de modelo de archivo único que agrupa los pesos cuantificados y todo lo necesario para ejecutarlos; el comando anterior extrae uno directamente de un repositorio de Hugging Face por su nombre. MLX, por el contrario, se acerca más a un flujo de trabajo nativo de Python, convirtiendo y cuantificando modelos a su propio formato con anticipación. Ninguno de los dos es estrictamente mejor: llama.cpp es el que debe utilizar si desea un único binario compilado sin dependencia de Python, MLX es el que debe utilizar si ya está construyendo el resto de su proceso en Python y desea un acceso de primera clase al modelo de memoria unificada de Apple desde allí.

Cuantización: lo que realmente se cambia por un modelo más pequeño

La cuantificación almacena el peso de cada modelo en menos bits que los 16 o 32 en los que se entrenó el modelo, lo que reduce tanto el archivo en el disco como la memoria necesaria para ejecutarlo, con cierto costo para la calidad de salida. La propia documentación de cuantización de llama.cpp enumera cifras exactas de bits por peso para cada esquema que admite, lo cual es una forma más precisa de razonar sobre la compensación que la abreviatura habitual de "4 bits" u "8 bits":

De la documentación de la herramienta de cuantización de llama.cpp.
familia de esquemasEsquemas de ejemploBits por peso
Precisión total (referencia)F1616.0
Casi sin pérdidasQ8_08.50
Gama media de mayor calidadQ5_K_S / Q5_K_M5,57 - 5,70
Calidad y tamaño equilibradosQ4_K_S / Q4_K_M4,67 - 4,89
Más pequeño, con más pérdidasQ3_K_S / Q3_K_M / Q3_K_L3,64 - 4,30
Compresión extremaIQ2_XXS ... IQ2_M2,00 - 2,93

Multiplicar el recuento de parámetros de un modelo por su cifra de bits por peso da una estimación aproximada de la memoria solo para los pesos: un modelo de 7 mil millones de parámetros con los 4,89 bits por peso de Q4_K_M necesita aproximadamente 7.000.000.000 × 4,89 ÷ 8 bytes, o alrededor de 4,3 GB, antes de tener en cuenta la ventana de contexto y las activaciones intermedias, que añaden más dependiendo de la cantidad de texto que introduzca. eso. Se trata de un cálculo a partir de la cifra de bits por peso citada, no de un número que cualquiera de los proyectos publique directamente, y el uso real de la memoria será mayor una vez que se carguen un mensaje real y su contexto. La guía práctica que se desprende de la tabla es simple: para una tarea como clasificar una línea de registro de conexión a la vez, donde la entrada es corta y la salida requerida es un pequeño veredicto estructurado, un modelo de clase Q4_K_M suele ser el modelo correcto: notablemente más pequeño y más rápido que Q8_0 o F16, sin caer al nivel de compresión extrema donde la calidad de la salida se degrada más marcadamente.

Ninguno de los proyectos publica un requisito de memoria por configuración de Mac, por lo que el enfoque práctico es trabajar hacia atrás a partir de la estimación anterior y dejar un margen real: el propio macOS, su navegador y cualquier otra cosa que se esté ejecutando también necesitan memoria unificada, y un modelo que apenas encaja sin nada más abierto se intercambiará o se detendrá en el momento en que cambie a otra aplicación. En una Mac con una cantidad modesta de memoria unificada, eso sugiere permanecer en el extremo más pequeño de la tabla anterior (unos pocos miles de millones de parámetros en Q4_K_M en lugar de un modelo mucho más grande con la misma cuantificación) y reservar las opciones más grandes y de mayor precisión para máquinas con memoria de sobra. Para una tarea de clasificación estrecha como la que trata este artículo, un modelo más pequeño al que se le hace una pregunta precisa tiende a ser más rápido y, en la práctica, más consistente que un modelo más grande al que se le da una pregunta vaga.

Ambos proyectos están en desarrollo activo y la comparación honesta es menos "cuál es mejor" que "cuál se adapta a su cartera". llama.cpp se compila en un único binario sin necesidad de tiempo de ejecución de Python en el momento de la inferencia, lo cual es importante si desea incrustarlo dentro de otra pieza de software sin enviar un intérprete de Python junto con él. MLX asume que ya está trabajando en Python (o Swift, para el cual también incluye enlaces) y lo recompensa con una integración más estrecha en la pila de aprendizaje automático de Apple y el modelo de memoria unificada descrito anteriormente. Una herramienta de seguridad creada como una aplicación macOS independiente, que es la situación en la que se encuentra FireAI, se encuentra más cerca del extremo llama.cpp de ese espectro; un cuaderno de investigación que explora qué patrón de indicaciones funciona mejor se encuentra más cerca del final de MLX.

Un patrón rápido para clasificar una conexión

Cuanto más específica sea la pregunta que le haga a un modelo local pequeño, más confiable será su respuesta. Para una conexión única, eso significa darle exactamente los campos que examinaría un revisor humano (nada más, nada inferido) y pedir un veredicto estructurado en lugar de una prosa de forma libre:

plantilla de solicitud de clasificación (ilustrativa, no probada con ningún conjunto de datos)
System: You review one outbound network connection at a time. You are
given only the fields listed below. Do not assume anything not stated.
Respond with exactly two lines: a verdict (allow, ask, or block) and a
one-sentence reason a non-expert could understand.

Connection:
  app: UpdaterHelper.app
  code_signature: unsigned
  destination: 91.203.xxx.xxx:4444
  protocol: TCP
  threat_feed_hit: none
  first_seen: yes (no prior rule for this app)

Verdict:

Los campos que importan son los que una verificación de firma de código y una fuente de amenazas realmente pueden producir sin adivinar: si el binario está firmado y por quién, dónde intenta conectarse y en qué puerto, si ese destino aparece en una fuente de amenazas y si es la primera vez que esta aplicación intenta conectarse. Solicitar una salida fija de dos líneas, en lugar de una explicación abierta, hace que el resultado sea más fácil de registrar, más fácil de comparar entre miles de conexiones y mucho más difícil para el modelo rellenar con un lenguaje de cobertura que suene autoritario sin decir nada comprobable.

De todos modos, un modelo pequeño ignorará ocasionalmente el formato solicitado: tres líneas en lugar de dos, una advertencia adicional, una palabra de veredicto que no es una de las tres que usted solicitó. Trate esto como un problema de ingeniería, no de modelado: valide el resultado con la forma exacta que espera y, si no coincide, vuelva a preguntar o recurra al veredicto más seguro (pregunte, es decir, muestre a un humano) en lugar de intentar analizar una respuesta más vaga. Un paso de clasificación que falla de manera segura en resultados con formato incorrecto es mucho más útil que uno que ocasionalmente produce una línea que parece segura pero que no se puede analizar y la descarta silenciosamente.

Ejecute este patrón a lo largo de un día completo de intentos de conexión en lugar de uno a la vez y la forma de la carga de trabajo cambiará: la mayoría de las conexiones provienen de aplicaciones con una regla existente y nunca llegan al modelo, un número menor se ve realmente por primera vez y obtiene un veredicto, y solo una fracción de esos veredictos son algo más que un permiso de rutina. El verdadero trabajo del modelo, en ese momento, no es ser un experto en seguridad; se trata de reducir una larga lista de conexiones vistas por primera vez al pequeño subconjunto que una persona realmente necesita mirar, lo cual es un objetivo mucho más alcanzable para un modelo de unos pocos miles de millones de parámetros que lo que sería un juicio de seguridad abierto.

Donde esto sale mal

  • Un modelo local pequeño puede producir una razón segura, bien escrita y completamente equivocada. Nada acerca de la ejecución local cambia ese riesgo; sólo cambia dónde ocurre el error, no si puede ocurrir.
  • Las ventanas de contexto son finitas y un registro largo y ruidoso no cabe. Resumir o filtrar previamente antes de que el modelo vea los datos introduce su propio punto de falla: puede perder la única línea que importaba antes de que el modelo tuviera la oportunidad de mirarla.
  • El modelo solo ve lo que contiene la línea de registro. No puede ver el interior del tráfico cifrado, no puede verificar que una fuente de amenazas esté actualizada y no puede conocer su intención: una conexión de una herramienta que acaba de instalar a propósito parece idéntica a una de una herramienta de la que nunca ha oído hablar.
  • Un veredicto no es una acción. Nada aquí debería bloquear, eliminar o permitir silenciosamente una conexión por sí solo; una persona aún necesita ver el motivo y confirmarlo, y poder revertir la llamada si el modelo se equivocó.

Este último punto no es una limitación específica de un pequeño modelo cuantificado que se ejecuta en una computadora portátil; es válido para todas las decisiones de seguridad automatizadas, locales o en la nube, de modelo pequeño o grande. El valor de ejecutarlo localmente es lo que elimina de la ecuación (una dependencia de la red, una factura recurrente, un tercero que recibe los metadatos de su conexión), no una afirmación de que elimina la necesidad de un ser humano en el circuito.

Dónde encaja FireAI en este patrón

FireAI, el firewall de HisnLabs para Mac, ofrece algo construido sobre la misma idea, en un alcance más limitado que un modelo de chat de propósito general: un modelo local opcional, una descarga adicional de aproximadamente 1,5 GB, que se ejecuta completamente en Mac y revisa las conexiones de aplicaciones que aún no tienen reglas. Requiere macOS 14 o posterior en Apple Silicon, aplica fuentes de amenazas públicas localmente en lugar de compararlas con un servicio remoto y muestra el motivo de su decisión en el mismo mensaje de permiso que utiliza para pedirle que permita o rechace la conexión; cada una de esas decisiones se convierte en una regla visible y editable, y cualquiera de ellas se puede deshacer. Vale la pena ser preciso sobre lo que es y lo que no es: no es el modelo de chat abierto con unos pocos miles de millones de parámetros que este artículo ha estado describiendo, y no es un antivirus o una VPN: hace un trabajo de clasificación limitado, localmente, y deja la llamada final a quien lea el mensaje.

El papel de FireAI y de HisnLabs

FireAI’s own connection reviewer is this exact bet, made narrower still: an optional, roughly 1.5 GB on-device model, macOS 14 and up, Apple silicon only, reviewing one thing (should this unknown app reach this destination) with a visible reason and an undo button — and, like the triage pattern in this article, it still needs a person to confirm anything consequential.

FireAI es el propio producto de HisnLabs: un cortafuegos con IA integrada para Mac. Muestra, en lenguaje claro, cada conexión que hacen tus aplicaciones y te deja decidir qué sale de tu Mac; su IA funciona en el propio equipo, así que tu tráfico nunca se envía a HisnLabs ni a nadie más. El equipo de investigación en seguridad de HisnLabs es quien mantiene esas decisiones fiables: cataloga qué dominios son simple telemetría frente a un servicio real, sigue el país y la red detrás de una conexión, y entrena el modelo local (la función Autopilot) con tráfico real, sin que nada salga de tu Mac.

Puedes leer las decisiones técnicas detrás de esto, o probar FireAI durante 17 días, en FireAI, de HisnLabs.

Fuentes