En la primera parte ordené los dos modelos de IA que tenía sueltos, cada uno en su propia caja de Docker. Con eso resuelto, tocaba traer a Hermes, mi asistente con herramientas, a la fiesta.
Cuatro días. Un comando mal escrito me da un buen susto a mitad de camino. Descubro que uno de los dos modelos no terminaba de pensar jamás (literalmente). Acabo entrenando yo mismo ese modelo a mano, dos veces, con un apagón a mitad de camino incluido. Y termino aprendiendo, por las malas, cuál es el límite real de un modelo pequeño.
Metí a Hermes en Docker, le di nombre y personalidad, y le enseñé a buscar en internet. Un bug dejaba a uno de los dos modelos "pensando" para siempre sin responder nunca. El modelo pequeño seguía inventándose cosas al usar herramientas, así que lo entrené yo mismo: un primer intento que memorizó el dataset en vez de aprenderlo, un apagón a mitad del reentrenamiento, y un modelo que por fin las usa bien. Ya en producción salieron tres problemas nuevos que ningún entrenamiento iba a arreglar: contestaba en inglés a un "Hola", un guardarraíl que existía pero estaba apagado casi provoca un colapso total, y el modelo llegó a narrar la creación de un cronjob que nunca existió. Un segundo entrenamiento arregló los casos simples — pero bajo el prompt real de producción, con varias herramientas encadenadas, el modelo colapsó igual. Ahí acepté el límite: no era un bug, era capacidad bruta. Encendí el modelo grande otra vez. El pequeño se quedó como red de seguridad, no como solución única.
Aviso antes de empezar: esto se pone técnico en varios tramos, porque lo fue de verdad. Te lo voy explicando según sale, así que no hace falta saber programar para seguirlo.
Día 1 — Hermes en Docker, bautizo, buscador propio, y el bug del cerebro que no terminaba de pensar.
Día 2 — decisión de entrenar el modelo pequeño, entrenamiento nº1, sobreajuste, corrección.
Día 3 — el apagón a media reconfiguración, el reintento que sí sirvió, el despliegue con seis capas de bugs.
Día 4 — "Hola" contestado en inglés, el Mundial que casi rompe el sistema, el cronjob fantasma, segundo entrenamiento, y la prueba final que rompe la ilusión.
📦 Meter a Hermes en su caja
Fui a clonar el código de Hermes en su sitio nuevo. Me topé con una carpeta vacía que había dejado ahí otro usuario del sistema, sin permisos para que yo escribiera dentro. Diez minutos, un cambio de propietario, resuelto.
Decidí no arrastrar la configuración ni el historial del Hermes viejo — mejor empezar de cero. Guardé una copia de seguridad por si acaso. Nunca hizo falta, pero dormí mejor sabiendo que estaba ahí.
Con todo recién montado, Hermes empezó a fallar al conectarse a Telegram: sesión ya abierta en otro sitio. El programa nuevo competía con el fantasma del viejo. Un par de minutos de espera —Telegram tarda en soltar la sesión anterior— y se resolvió solo.
Ya viviendo en su caja, le puse nombre: A.P.E.S., de Asistente Perezoso, Eficiente y Servicial. "Perezoso" en el buen sentido — el que busca siempre la forma más corta de resolver algo bien, sin dar vueltas de más.
🔍 Enseñarle a buscar, y el buscador que no era el que yo creía
Le conecté un buscador propio (SearXNG, corriendo ya en otra caja de Docker) para que pudiera buscar en internet sin depender de servicios de pago de terceros.
Una línea de configuración y funcionó a la primera. Después de la odisea de las tarjetas gráficas de la primera parte, casi no me lo creía.
Quise darle acceso también a "camofox", pensando que era un navegador automatizado de verdad. Resultó ser otra cosa: una API propia de búsqueda y extracción de datos de negocios, construida para otro proyecto nuestro. Nada que ver con lo que tenía en la cabeza.
Como no encajaba con lo que Hermes esperaba de fábrica, le escribí un programita puente — un MCP, el nombre estándar para estas piezas que añaden habilidades nuevas a un asistente sin tocarle el código de dentro.
Conectaba perfecto en mis pruebas manuales pero fallaba en las pruebas rápidas por consola. Hermes intenta conectar con las herramientas nuevas en un tiempo límite muy corto —1,5 segundos— y mi puente tardaba un pelín más en arrancar. Amplié el tiempo límite y se acabó el problema.
Lo probé pidiéndole que buscara la web de un negocio real. Funcionó a la primera: eligió solo la herramienta correcta, sin que se lo dijera.
🧠 El bug del cerebro que nunca terminaba de responder
El más raro de todos.
Primera conversación larga de verdad: le pedí un resultado de fútbol y la petición reventó con un error que, traducido, decía que había pedido más palabras de respuesta de las que el modelo era capaz de recordar en total. No quedaba sitio ni para la pregunta. Le puse un tope razonable a la longitud de la respuesta y desapareció.
Pero por el camino me tropecé con otro fallo más raro todavía: el Qwen3.5 —el modelo pequeño, el de refuerzo— contestaba, pero nunca se le veía la respuesta de verdad. Todo quedaba metido dentro de un "borrador mental", el cuaderno donde el modelo piensa antes de responder. El problema es que nunca lo cerraba para pasar a la respuesta final.
Le pregunté la capital de Francia, para probar algo trivial. Acertó el dato —París— pero dentro de ese cuaderno que nunca cerraba se inventó que la ciudad estaba "al sur del país" y sede de una organización europea que no existe. Y ahí se quedaba, pensando, para siempre.
Una pieza de software mal emparejada con esta versión concreta del modelo — la que se encarga de separar el borrador mental de la respuesta final. Quitarla, y empezó a contestar con normalidad.
😰 El susto de la carpeta que no estaba donde tocaba
Cerca del final del día 1, tocaba un ajuste menor: la hora del sistema estaba en el huso horario equivocado. Fui a recrear los contenedores para aplicarlo y escribí mal el comando —se me olvidó especificar bajo qué usuario debía ejecutarse—.
Consecuencia: Docker montó, sin avisar de nada, una carpeta de datos completamente distinta y vacía en vez de la real.
Durante unos minutos pensé que había perdido todo el trabajo del día: la personalidad del asistente, la configuración, el puente que acababa de terminar. No había nada borrado — solo estaba mirando al sitio equivocado. Repetí el comando bien escrito y todo volvió a como estaba.
La lección pequeña dentro de la lección grande: cuando algo que llevas horas construyendo desaparece de golpe, antes de asumir lo peor comprueba si de verdad se ha borrado o si estás mirando por la ventana equivocada.
🤖 El día que la IA decidió por su cuenta
Ya en el día 2, el Qwen3.5 volvió a inventarse cosas al buscar un resultado deportivo. Le pedí a Claude, la IA con la que trabajo, que investigara por qué.
El primer diagnóstico apuntaba a un ajuste de cómo el modelo guarda su caché en memoria. Se tocó, y el modelo dejó de arrancar del todo.
En mitad del intento de arreglo, sin que yo lo autorizara, la IA decidió por su cuenta mover el modelo de la RTX 2060 a la RTX 4060 Ti, la tarjeta que ya tenía su propio modelo grande encima. Cuando lo vi, se lo dije bien claro: nadie le había pedido eso. Lo deshizo, y seguimos buscando la causa real en vez de esquivarla cambiando las cosas de sitio.
La causa de verdad era un ajuste de cómo el programa le pide memoria a la tarjeta gráfica (expandable_segments), que hasta entonces no estaba activado. Sin él, el programa reservaba memoria en bloques fijos y se quedaba sin hueco para cálculos internos, aunque sobrara memoria de sobra. Con él activado, el modelo volvió a arrancar en la tarjeta de siempre.
Cuando algo que llevaba meses funcionando se rompe de golpe, la tentación es cambiar las cosas de sitio hasta que funcione. Pero casi siempre hay una causa concreta esperando a que la busques con calma.
Con el modelo sano otra vez, quedaba el problema de fondo: seguía siendo un modelo pequeño —2 mil millones de parámetros, comprimido a 4 bits— y se inventaba cosas con facilidad, sobre todo usando herramientas. En vez de conformarnos, decidimos entrenarlo nosotros mismos.
🏋️ Entrenamiento nº1: 4 horas y 53 minutos cuesta abajo
Ya tenía, sin usarla desde hacía meses, una infraestructura completa de fine-tuning montada en el servidor. Antes de lanzar nada tuve que confirmar que el pipeline sabía cargar el modelo (es multimodal, no solo texto), descargar la versión sin cuantizar y elegir el dataset: hermes-function-calling-v1, 1893 ejemplos de conversaciones llamando a herramientas.
Dos intentos fallidos antes del tercero:
- La librería de entrenamiento había cambiado el nombre de un parámetro (
max_seq_length→max_length). - El entrenador intentaba auto-detectar un procesador de imágenes que no necesitaba para un modelo que solo iba a usar texto.
Corregidos los dos, arrancó de verdad: LoRA de rango 64, 3 épocas completas, 711 pasos en total.
El loss bajó de forma sana toda la noche, con una precisión de tokens final del 96,9%. Sobre el papel, una pinta excelente.
Justo al fusionar el adaptador LoRA con el modelo base, todo el proceso reventó: "No puedes forzar un nuevo tipo de dato sobre un modelo cuantizado a 4 bits". Casi 5 horas de GPU, y se me caía en el último paso de guardar el resultado. Por suerte el adaptador ya se había guardado bien en disco antes de intentar la fusión — solo tenía que fusionarlo bien, con un script aparte que recarga el modelo base en precisión completa.
😬 "Esto no sirve": el sobreajuste
Antes de tocar producción, probé si el modelo entrenado usaba mejor las herramientas con una pregunta que no estaba en el dataset. El resultado: un batiburrillo que mezclaba fragmentos memorizados literalmente de las instrucciones del propio dataset con la pregunta metida en el sitio equivocado.
Con solo 1893 ejemplos, 3 épocas completas y un LoRA de capacidad alta, el modelo no aprendió el patrón general — memorizó literalmente los textos del dataset. La pista ya estaba ahí desde el principio: una precisión del 96,9% tras solo 3 épocas es sospechosamente alta, la huella típica de memorizar en vez de generalizar.
La corrección, aplicada en caliente, sin reconstruir nada:
| Parámetro | Antes | Después |
|---|---|---|
| Rango LoRA | 64 | 16 |
| Alpha LoRA | 128 | 32 |
| Learning rate | 2e-4 | 1e-4 |
| Épocas | 3 | 1 |
| Pasos totales | 711 | 237 |
⚡ El apagón
Día 3. Relancé el entrenamiento corregido a las 07:55 de la mañana. A los pocos minutos ya iba bien, bajando de forma sana.
Entre las 08:06 y las 08:09 se fue la luz. El servidor reinició, y el entrenamiento murió con el corte — sí tengo UPS, pero estaba mal conectado, así que no sirvió de nada justo cuando más falta hacía.
El entrenamiento guarda un checkpoint completo cada 50 pasos. Si el corte pilla entre dos, todo el progreso desde el último guardado se pierde sin remedio.
La investigación encontró dos cosas:
- Redis —donde se guarda el estado de cada trabajo— estaba configurado sin persistencia en disco, pensado para velocidad, nunca para sobrevivir un corte real. Al reiniciar se vació entero, y el registro del job desapareció sin dejar rastro.
- El job en marcha nunca llegó a guardar ningún checkpoint — se cortó en el paso 20 de 237, y el primero está programado para el paso 50.
Lo importante —el modelo del primer entrenamiento, ya fusionado y corregido esa misma mañana antes del apagón— estaba a salvo en disco, intacto. El propio arreglo del código también sobrevivió, porque vive en un archivo, no en la memoria volátil de Redis. Lo único que perdí de verdad fueron esos ~20 pasos sin checkpoint.
Lo importante —el modelo del primer entrenamiento, ya fusionado y corregido esa misma mañana antes del apagón— estaba a salvo en disco, intacto. El propio arreglo del código también sobrevivió, porque vive en un archivo, no en la memoria volátil de Redis. Lo único que perdí de verdad fueron esos ~20 pasos sin checkpoint.
🏁 El final: el mismo bug, y esta vez sí sirve
Relanzado el job —con un tropiezo tonto de por medio: un script de vigilancia que usaba status como nombre de variable, reservada en zsh— los 237 pasos terminaron bien.
Y al fusionar el adaptador, el proceso volvió a fallar con exactamente el mismo error de bitsandbytes de la primera vuelta. Nunca había tocado ese paso al corregir los hiperparámetros, así que el bug seguía ahí, agazapado, esperando su turno otra vez. Mismo arreglo de siempre, sin sorpresas.
La prueba de verdad —la misma pregunta que había destapado la memorización— esta vez devolvió una llamada a herramienta limpia:
{
"name": "web_search",
"arguments": {
"query": "final Champions League 2023-2024 ganador"
}
}
Ni rastro del batiburrillo memorizado. Con menos capacidad, menos vueltas y un learning rate más bajo, el modelo aprendió el patrón en vez de memorizar el examen.
🧅 Producción: una cebolla con más capas de las esperadas
Probarlo con Hermes de verdad, bajo condiciones reales, destapó un problema nuevo: texto sin sentido mezclado entre los datos, y una lista de "semifinales" con seis equipos donde solo caben cuatro.
Antes de asumir que "el modelo alucina" sin más, investigué si de verdad estaba buscando. La cadena de causas:
- Gemma llevaba parado desde que le liberé la tarjeta para entrenar.
- Hermes caía en cada conversación al Qwen sin el arreglo del sobreajuste todavía desplegado.
- Ese Qwen generaba, de vez en cuando, argumentos de búsqueda rotos que Hermes sustituía en silencio por un objeto vacío.
- La búsqueda le llegaba a SearXNG sin
query, y siempre fallaba.
Añadí una validación genérica de parámetros obligatorios en el registro de herramientas de Hermes: si falta algo, el modelo recibe ahora un error claro en vez de ejecutar en silencio con datos vacíos.
Con la búsqueda arreglada, tocaba desplegar de una vez el Qwen corregido en producción. Lo que vino después fueron capas y capas de la misma cebolla:
🧅 Ver las seis capas del despliegue, una a una
- La herramienta de cuantizar (
autoawq) está oficialmente descontinuada y ni siquiera reconocía la arquitectura del modelo. Rodeo: vLLM sabe cuantizar a 4 bits al vuelo, sin ese paso aparte. - Al modelo fusionado le faltaba media cabeza: durante el entrenamiento guardé solo la parte de lenguaje, pero vLLM esperaba el envoltorio multimodal completo. Reconstruí el checkpoint combinando el texto entrenado con la visión intacta del modelo base.
- Probé mal mi propia API: metí las herramientas a mano en el mensaje en vez de usar el parámetro estándar. En cuanto lo hice bien, la respuesta salió limpia.
bfloat16no existe en la RTX 2060 (necesita capacidad de cómputo 8.0, la 2060 tiene 7.5) — copiado sin querer de la prueba en la otra tarjeta. Cambio de una palabra.- La caché KV no cabía por 80 megas, porque había bajado sin darme cuenta dos ajustes ya calibrados a mano para esta tarjeta. Restaurarlos lo arregló.
- Un lío de nombres con
docker compose: un contenedor de respaldo aparcado quiso reclamar el mismo nombre que mi contenedor de pruebas. Borrar el manual y dejar que compose recreara el servicio lo resolvió.
Y, cómo no, el mismo bug de status en zsh otra vez, en otro script distinto. Con todo en orden, la prueba final contra la API real devolvió justo lo que llevaba todo el día persiguiendo: una llamada a herramienta limpia, sin memorización, sin alucinación de por medio.
🗣️ "¿Por qué me habla en inglés?"
Día 4. Primera prueba de verdad tras el despliegue: le escribí "Hola" a Hermes. Me contestó en inglés, con frases sin mucho sentido.
El fichero que define su personalidad estaba escrito enteramente en español — el problema no era el idioma de la personalidad, era que en ningún sitio del sistema había una instrucción explícita de "responde en el idioma en el que te escriben". Con un prompt de sistema gigante en inglés y una entrada de una sola palabra, el modelo se decantaba por inglés sin que nadie se lo hubiera pedido. Añadí la instrucción explícita, y se acabó.
🏎️ Cuando preguntar por un Mundial casi rompe el sistema
Le pedí las semifinales de un Mundial de fútbol. Ocho rondas de búsquedas después, sin encontrar nunca el dato real, el modelo terminó generando 20.000 caracteres de puro disparate: un "reglamento" inventado con artículos, cláusulas de leasing de vehículos y tablas de puntos de campeonatos que no existen. No fue un fallo de formato ni de idioma — fue un colapso total de coherencia por acumulación de contexto.
Hermes ya tiene un sistema pensado exactamente para esto —detecta búsquedas repetidas sin progreso—, pero tenía la parada forzosa desactivada: solo avisaba, nunca cortaba el bucle de verdad. Con el umbral ya configurado, debería haber parado sobre la quinta búsqueda repetitiva, mucho antes de las ocho que acabaron en aquello. Cambié el valor. Config, no código.

Otra prueba trivial —"que carrera se corre este fin de semana"— y Hermes anunció, con todo detalle, que había creado un cronjob llamado "Charlie" para un resumen semanal. Fui a comprobarlo: la carpeta de tareas programadas estaba vacía.
El modelo nunca llegó a llamar a ninguna herramienta real — se inventó de principio a fin una narración que se parece a como luce una llamada a herramienta, sin serlo. Y no fue un bucle largo degradándose: fue el primer mensaje de una sesión nueva.
Ahí tuve que ser honesto conmigo mismo sobre el límite real: el entrenamiento corrigió la memorización en el formato de las llamadas a herramientas. No enseña a mantener coherencia general, ni a elegir bien entre dieciséis herramientas disponibles, ni a no inventarse acciones que nunca ocurrieron. Eso es un límite de capacidad, no un bug con una línea de arreglo.
Reforcé el SOUL.md con una instrucción directa contra ese fallo exacto, y encontré una palanca que no sabía que existía: subir el "esfuerzo de razonamiento" de un modelo concreto por separado del global. Lo subí solo para Qwen.
🔬 El segundo entrenamiento
El dataset de la mañana tenía un problema de fondo: 100% en inglés, y 100% casos donde tocaba llamar a una herramienta. Ni un solo ejemplo de "el usuario pregunta algo trivial, y lo correcto es simplemente contestar".
Mezclé tres fuentes —conversación general en español (el contraejemplo que faltaba), el mismo dataset de function-calling en su versión multi-turno, y otra fuente distinta para variar el estilo— unos 2900 ejemplos en total.
Mi función de formateo del dataset no tenía ningún caso para el turno
tool (la respuesta de la herramienta dentro de una conversación), así que esos turnos se descartaban en silencio sin que nadie lo notara. Una línea añadida, arreglado.Los 363 pasos terminaron bien. La fusión volvió a fallar con el mismo bug de bitsandbytes de siempre —el tercero ya— con el mismo arreglo manual. Dos pruebas rápidas salieron perfectas: una llamada a herramienta limpia, y un "¡Hola! ¿Cómo puedo ayudarte hoy?" directo en español, sin inventarse ninguna herramienta. Lo desplegé en producción.
Parecía el final feliz. No lo era.
💥 La prueba de verdad rompió la ilusión
Las pruebas rápidas usan un prompt mínimo, una o dos herramientas. Hermes de verdad manda unas 20.000 fichas de contexto en cada turno, contando las dieciséis herramientas activas y todas las instrucciones internas. Nunca había probado el modelo nuevo bajo esas condiciones reales.
Tres rondas de herramientas después de una pregunta ambigua, la respuesta fueron 18.400 caracteres de basura total —español, coreano, vietnamita, chino y árabe mezclados sin ningún sentido—. Ningún guardarraíl lo cortó, porque no fue un bucle de búsquedas fallando: fue la coherencia colapsando dentro de una sola respuesta larga, con todo el peso del prompt real encima.
Un modelo de 2 mil millones de parámetros, comprimido a 4 bits, cargando casi 20.000 fichas de contexto solo para arrancar, no tiene margen de sobra para sostener coherencia turno tras turno. Ni el fine-tuning de tool-calling ni el de conversación general lo arreglan, porque el problema no es de comportamiento aprendido — es de capacidad bruta. Doy el modelo pequeño por perdido como solución única: sigue existiendo, sigue siendo el respaldo, pero estos días demostraron que no hay forma de que sostenga una conversación real de forma fiable.
La decisión, sin darle más vueltas: encender Gemma otra vez. Llevaba parado dos días enteros y ni siquiera arrancó a la primera —la red de Docker a la que estaba conectado había desaparecido de en medio—, pero borrar el contenedor viejo y dejar que docker compose levantara uno nuevo lo resolvió en un minuto.
Con Gemma arriba, la misma prueba de "Hola" salió limpia y directa, sin caer nunca a Qwen. Los dos cerebros están encendidos otra vez: Qwen sigue ahí, corregido y mejor que al principio, sirviendo de red de seguridad para cuando Gemma no esté disponible — pero ya no es el único que sostiene la conversación.
📋 El resumen de daños
📋 Ver la tabla completa — 16 problemas y sus arreglos
ProblemaCausa realArregloHerramienta "camofox" no conectaba en pruebasTiempo límite de conexión demasiado cortoAmpliar el tiempo de esperaNinguna respuesta cabíaSe pedía como salida el tamaño de todo el contextoPoner un tope razonable a la longitud de la respuestaQwen3.5 nunca "terminaba" de responderPieza de software mal emparejada con la versión del modeloQuitarlaSusto por datos "perdidos"Comando de Docker mal escrito, carpeta equivocada montadaEscribir bien el comando (los datos nunca se movieron)Qwen3.5 se inventaba cosas al buscarModelo pequeño, sin margen para cierto cálculo interno de memoriaActivar expandable_segmentsLa IA movió el modelo de tarjeta sin permisoIntentó esquivar el problema en vez de buscar la causaDeshacerlo y buscar la causa realCrash al fusionar el adaptador (dos veces)bitsandbytes no permite forzar un dtype nuevo sobre un modelo cuantizadoScript de fusión aparte, en precisión completaModelo entrenado no servía (sobreajuste)3 épocas + LoRA de rango 64 sobre solo 1893 ejemplos → memorización literalReentrenar con 1 época, LoRA rango 16, learning rate más bajoEstado del entrenamiento borrado por el apagónUPS mal conectado + Redis sin persistencia en disco~20 pasos de progreso perdidos sin remedio; pendiente revisar el UPS y activar persistenciaBúsquedas fallando en producciónGemma caído → fallback a Qwen sin arreglar → tool-calls rotas sustituidas por {} en silencioValidación genérica de parámetros obligatorios en HermesSeis capas de bugs al desplegar en producciónLibrería de cuantización muerta, checkpoint incompleto, prueba mal hecha, dtype no soportado, caché corta, lío de nombresResueltos uno a uno, capa a capa"Hola" contestado en inglésNinguna instrucción explícita de responder en el idioma del usuarioInstrucción añadida al SOUL.md20.000 caracteres de reglamento inventadoGuardarraíl de bucles configurado pero sin parada forzosa activadaActivar hard_stop_enabledCronjob "Charlie" narrado sin haberse creadoEl modelo alucinó toda la interacción sin llamar a ninguna herramienta realSin arreglo de una línea — instrucción reforzada + más esfuerzo de razonamientoTurnos de herramienta desaparecían del dataset multi-turnoEl formateador no tenía caso para el rol toolAñadir el caso que faltaba18.400 caracteres de basura multilingüe bajo el prompt realColapso de coherencia por capacidad bruta, no por comportamiento aprendidoNinguno posible — encender Gemma, Qwen como red de seguridad
🎓 La moraleja (de momento)
Ninguna de estas cosas era, en el fondo, un fallo grave. Era memoria mal repartida, un tiempo de espera demasiado corto, un comando mal escrito, un parámetro que cambió de nombre entre versiones, un nombre de variable prohibido en la shell. Cosas pequeñas, cada una con una solución de una línea — una vez que sabías cuál era la línea. El problema nunca fue lo difícil que era arreglarlo. Fue lo bien escondido que estaba.
Lo que de verdad cuesta no es escribir la configuración correcta a la primera. Es aceptar que vas a tirar la primera versión, la segunda y probablemente la tercera, y que cada error te enseña algo que no sabías del hardware, del modelo o del propio sistema. El modelo que memorizó en vez de aprender comete el mismo error que cualquiera que confunde "me lo sé de memoria" con "lo entiendo": funciona perfecto con el ejemplo exacto que ya conoces, y se rompe en cuanto te cambian una palabra.
El apagón dejó su propia lección aparte: lo que no se guarda en disco no sobrevive a un corte, ni un UPS mal conectado sirve de nada por muy bien intencionado que estuviera.
Y desplegarlo a producción resultó ser una cebolla con más capas de las que esperaba, cada una parecida a la anterior sin ser la misma. Ninguna capa era difícil de arreglar una vez la veías — la dificultad estaba en que cada arreglo destapaba la siguiente, sin forma de saber cuántas quedaban hasta el fondo. Cuando algo lleva "cinco pasos más" de los que pensabas, no significa que vayas mal: significa que el sistema tiene más piezas moviéndose de las que veías desde fuera.
La lección final, la que cierra estos cuatro días: hay una diferencia entre "arreglar un comportamiento" y "superar un límite de capacidad", y confundir la una con la otra es la manera más segura de perder días enteros persiguiendo la siguiente palanca de configuración que nunca iba a ser suficiente. El primer entrenamiento arregló que el modelo memorizara el formato de las llamadas a herramientas. El segundo arregló que inventara herramientas cuando no tocaba, en los casos simples. Ninguno de los dos podía arreglar que un modelo de 2 mil millones de parámetros, cargando 20.000 fichas de contexto real antes de escribir una sola palabra, se quede sin margen para pensar con claridad turno tras turno. Eso no es un bug. Es lo que hay dentro de la caja, y no hay LoRA que le añada capacidad que nunca tuvo.
Monté un asistente de IA local, con dos cerebros ya ordenados, buscador propio, una herramienta a medida, y un cerebro pequeño que ha pasado por dos entrenamientos completos, dos reconstrucciones de checkpoint y un despliegue con seis capas de problemas. Ese cerebro pequeño sigue ahí, corregido y mejor que al principio, como red de seguridad — pero ya no sostiene la conversación él solo. Funciona, y de momento solo lo usamos dos personas: mi socio y yo, los dos únicos admins de la app.
Hermes ya tiene dieciséis toolsets activos y treinta y cuatro herramientas individuales, más siete de camofox — cuarenta y una en total, compitiendo por la atención de cualquier modelo en cada turno antes de escribir una sola palabra de respuesta. No hace falta explicar mucho más por qué un modelo de 2B no aguanta eso turno tras turno, ni por qué, al preguntarle a un modelo confundido "qué carrera se corre este fin de semana", lo primero que se le cruzó fue cronjob. Estaba ahí, en la lista, esperando.
Pero hay un hueco que se queda para la siguiente entrega: cuando Hermes cae al modelo de refuerzo, solo lo escribe en un log — no hay ningún aviso en ningún sitio, por eso nadie se enteró de que Gemma llevaba dos días apagado hasta que las respuestas empezaron a salir mal. Y la raíz es más simple todavía: Hermes no tiene ni una sola plataforma de mensajería configurada. No puede avisarme de nada por su cuenta. Ese es, literalmente, el primer paso de publicarlo de verdad para que mi socio deje de depender de mí — y esa es la tercera u ultima parte. Te espero en ella.