Rama JARVIS-GREEN, independiente de master (el nucleo naranja queda intacto). - README propio del carril verde con diagrama de arquitectura y capturas del panel. - verde/: arranque, panel web, puente de voz (escucha local con faster-whisper + habla con la voz del naranja), pruebas (07 voz, 08 escucha), config y notas. - Integracion RAG: nucleo/saber/busca_cli.py + skill buscar-en-apuntes, para que el agente consulte el mismo indice que el nucleo. - Todo local (127.0.0.1); sin datos personales (rutas y modelo de GPU scrubeados).
38 KiB
Notas del carril verde
Lo medido, lo resuelto y lo que queda abierto.
Fechas: 15 de agosto de 2026 (instalación, en frío) y 16 de agosto (primer arranque real). Lo de la primera fecha salía de leer código y mirar el disco; lo de la segunda está ejecutado.
EL SUELO DE 64k: el tope que invalidó la medida anterior (16 ago)
(Esto va primero porque cambia la conclusión de la sección siguiente.)
arrancar.sh midió bien: 24576 era el mayor contexto que cabía en la GPU
con las 32 capas en la tarjeta, 3301 MiB, y con eso el modelo emitía tool_calls
válidos. Todo correcto y todo inútil, porque al abrir Hermes:
Model qwen3.5:4b-verde has a context window of 24,576 tokens, which is below
the minimum 64,000 required by Hermes Agent.
Es un tope duro del arnés, no un aviso: agent/model_metadata.py:405,
MINIMUM_CONTEXT_LENGTH = 64_000, comprobado en agent/agent_init.py:2656.
No se ve en frío — hermes prompt-size no lo menciona — y solo salta al
construir el agente.
Las dos salidas que se descartaron, y por qué
- La puerta de atrás de
lmstudio. El mismoifse salta siprovider == "lmstudio"ycontext_lengthestá puesto a mano. No se usa: ese proveedor llama a{server}/api/v1/models, la API nativa de LM Studio, que ollama no tiene, y además intenta precargar el modelo por su cuenta (ensure_lmstudio_model_loaded). Fingir ser otro servidor rompería más adelante y de forma más confusa. - Mentir en el
config.yamlponiendo 64000 con el modelo a 24576. Es la peor de las tres: Hermes creería tener 64k, no comprimiría hasta acercarse, y ollama iría tirando los tokens más viejos — que son el prompt de sistema y los esquemas de las 19 herramientas. El síntoma sería que el agente deja de llamar herramientas a mitad de sesión, sin un solo error en el log.
Lo que se hizo: subir el contexto de verdad y pagar capas
Con num_ctx 65536 fijo, medido el 16 de agosto:
num_gpu |
Resultado | En tarjeta | Total |
|---|---|---|---|
| 32 | cudaMalloc failed |
— | — |
| 24 | cudaMalloc failed |
— | — |
| 16 | carga | 2795 MiB | 5784 MiB |
| 8 | carga | 1772 MiB | 5784 MiB |
| 0 | carga | 0 MiB | 5368 MiB |
O sea que 64k sí cabe en esta máquina, pero con la mitad de la red en la CPU y unos 3 GB en RAM. Lo que eso cuesta en tiempo está más abajo, en "Cuánto tarda de verdad".
La palanca para recuperar capas —OLLAMA_FLASH_ATTENTION=1 +
OLLAMA_KV_CACHE_TYPE=q8_0, que parte el KV caché por la mitad— sigue sin
aplicarse: es del servicio de ollama, no del modelo, y afectaría también al
naranja. Esa decisión es del usuario.
Lección de método, que es la misma que ya aparece dos veces en este fichero: se midió a fondo el límite de la máquina y no se comprobó el límite del software que iba encima. Los dos son topes; solo uno se estaba mirando.
Cuánto tarda de verdad (16 ago)
Medido con hermes -z, que es una pregunta y a callar, contra el modelo a
num_ctx 65536 con 16 de 32 capas en GPU.
| Escalón | Vueltas | Tiempo |
|---|---|---|
| 1 · "hola, ¿quién eres?" — en frío | 0 herramientas | 2 min 38 s |
| 2 · "¿cuánto espacio libre queda?" | 1 herramienta | 48,6 s |
| 3 · "lee verde/README.md y resúmelo" | 4 (dos fallidas + recuperación) | 3 min 29 s |
3 bis · igual, con terminal.cwd arreglado |
1 | 1 min 58 s |
3 ter · igual, ya con AGENTS.md |
2 (una fallida + recuperación) | 4 min 54 s |
| 4 · "cuál de los .md de verde/ es el más largo" | 4 encadenadas | 3 min 54 s |
| 5 · "un cartel en arte ascii que ponga JARVIS" | 3 (una salió a la red) | 4 min 58 s |
| 6 · "di en voz alta…" | 1 (text_to_speech) |
3 min 37 s |
| (pwd suelto, caché caliente, una llamada) | 1 | 13,2 s |
Esa diferencia de tres veces no es ruido, y es lo más útil que se midió. El primer turno paga el prefill entero de los ~20.500 tokens del prompt fijo con media red en la CPU. A partir de ahí, ollama cachea el prefijo y las vueltas siguientes solo procesan lo nuevo — y eso que la 2 hace dos llamadas al modelo (pedir la herramienta y redactar con su resultado) y aun así tarda un tercio.
O sea que la cifra que importa para decidir si esto se usa no es la del primer arranque. Es la de régimen. Igual que la primera transcripción de whisper tarda ~4 s por arrancar el servidor y luego se queda en ~930 ms.
El escalón 2 acertó, además: 663G libres de 1,9T al 63 %, que es
exactamente lo que dice df -h /.
Y el 3 enseñó algo que no se estaba buscando. Falló dos veces seguidas —
partió mal la frase y pidió cat ~/lee-verde/README.md, que no
existe— y en vez de inventarse una respuesta cambió de estrategia: search_files
para localizar el fichero, y read_file con la ruta absoluta ya correcta. El
resumen final es fiel al README.
Eso es exactamente lo que se venía a probar de este carril: recuperarse de una herramienta que devuelve error. El núcleo naranja, con sus acciones rápidas, o acierta a la primera o no hay segunda. Las dos vueltas de más son las que explican los 3 min 29 s.
El punto flaco del 4B: las rutas relativas
Con terminal.cwd ya arreglado se repitió el escalón 3, y salió peor:
1 min 58 s y una sola llamada, read_file sobre
COFRE/CODERS/JARVIS/README.md. Se comió el verde/. Resumió el README de
la raíz —el del proyecto entero— con un "JARVES" de propina y la frase cortada a
medias.
La comparación entre las dos pasadas es lo interesante:
| Ruta base | Qué hizo | Resultado | |
|---|---|---|---|
Con cwd en $HOME |
mal | falló, buscó, se corrigió | acertó |
Con cwd en el repo |
bien | fue directo a una ruta plausible | falló |
O sea: cuando la herramienta le devuelve un error, se recupera; cuando le
devuelve un fichero que existe pero no es el pedido, no tiene forma de notarlo
y lo resume tan campante. Es el mismo patrón del error de ~/README.md,
y la razón de que estos fallos no se vean: no hay error que mirar.
Lo que apunta a la mejora obvia si este carril sigue adelante: un AGENTS.md en
la raíz del repo describiendo la disposición de carpetas. Hermes ya carga
ficheros de contexto, así que el sitio está.
Escrito el AGENTS.md, el escalón 3 pasa — pero no por lo que parece
Se escribió (<raíz>/AGENTS.md: los tres carriles, el árbol de carpetas y la
regla de que las rutas relativas salen de la raíz del repo). Cuesta 2.490 B
del prompt fijo, que sube a 84.740 B ≈ 21.185 tokens de los 65.536: quedan
44.351 para conversar.
Tercera pasada del escalón 3, 4 min 54 s, y el resumen es del fichero correcto por fin. Recoge incluso el aviso de la fuga a internet que se añadió hoy al README.
Pero el modelo volvió a alucinar la ruta. Su primera llamada fue
read_file ~/COFRE/CODERS/JARVIS-verde/zapier-connection.py
que no existe y no se parece a nada de lo pedido. Recibió File not found y
entonces acertó con JARVIS/verde/README.md.
O sea que el AGENTS.md no evitó el disparate; le dio con qué recuperarse.
Es la tercera vez que sale el mismo patrón en esta sesión y ya no es anécdota:
| Lo que recibe la herramienta | Qué hace el 4B |
|---|---|
| Un error | cambia de estrategia y suele acertar |
| Un fichero que existe pero no es | lo resume tan contento |
La lección práctica para este carril: más vale una ruta que falle que una que
acierte por accidente. Y en la prosa sigue descuidado — aquí se inventó un
"98 % local", llamó API de terceros a pyfiglet (que es una librería local; la
API era asciified.thelicato.io) y le dio la vuelta a la frase del objetivo.
El escalón 4, que era el que decidía: pasa
"dime cuál de los .md de la carpeta verde/ es el más largo" — 3 min 54 s, cuatro llamadas encadenadas, respuesta correcta:
| # | Llamada | Qué pasó |
|---|---|---|
| 1 | search_files con pattern: "*.md" |
rg: regex parse error |
| 2 | search_files con pattern: "\\.md$" |
corrigió el patrón él solo |
| 3 | terminal: find … -name "*.md" |
los cinco ficheros |
| 4 | terminal: bucle con wc -l sobre cada uno |
las cinco cifras |
Y concluyó: verde/NOTAS.md con 625 líneas. Es el correcto.
Esto es lo que justificaba el carril entero. No hay una herramienta que conteste esa pregunta: hay que listar, medir y razonar sobre el resultado de la vuelta anterior, y saber cuándo parar. El núcleo naranja no puede hacerlo — sus 150 acciones rápidas o aciertan a la primera o no hay segunda.
La letra pequeña, que no cambia el veredicto pero conviene tener escrita: al
enumerar los ficheros los ordenó mal (puso PLAN-ARRANQUE con 181 por encima de
ESCALERA con 208) y etiquetó el README de verde/ como "categoría raíz". O
sea que el razonamiento de la cadena es bueno y la prosa de alrededor es
descuidada. Para una orden hablada da igual; para un informe, no.
EL BLOQUEO DEL ARRANQUE: no cabe la pregunta
(15 ago, tarde. Esto es lo más importante del fichero.)
Se preparó el primer arranque y apareció un tope que no era el que se
anticipaba. No es que qwen3.5:4b no sepa llamar herramientas: es que no le
cabe la pregunta. Medido con hermes prompt-size:
| Prompt de sistema de Hermes | 26.281 B |
| Esquemas de las 19 herramientas | 55.978 B |
| Fijo, antes de decir nada | 82.259 B ≈ 20.500 tokens |
num_ctx de qwen3.5:4b-jarvis |
4.096 |
Cinco veces por encima. Y hay un segundo tope escondido: num_predict 300,
que el naranja hereda del modelo BASE (no lo pone su Modelfile). 300 tokens
cortan el JSON de una llamada a herramienta por la mitad.
Los dos están bien puestos para el naranja —respuestas habladas cortas, VRAM para whisper— y son justo lo contrario de lo que pide un arnés agéntico.
Y encima, la trampa que avisaba la documentación: /api/show reporta
qwen35.context_length = 262144, el máximo del modelo, no los 4096
configurados. Hermes se habría creído que tenía 262k. El síntoma habría sido
respuestas truncadas y llamadas rotas, y la conclusión natural —"el 4B no vale
para esto"— habría sido falsa.
La variante verde, ya creada
verde/qwen3.5-4b-verde.Modelfile → ollama create qwen3.5:4b-verde.
qwen3.5:4b-jarvis no se tocó.
| Parámetro | Valor | Por qué |
|---|---|---|
num_gpu |
32 | todas las capas, con el naranja cerrado |
num_ctx |
24576 | 32768 no cabe (ver abajo) |
num_predict |
4096 | 300 cortaría una tool call |
temperature |
0.2 | emitir JSON no es escribir prosa |
Trampa del Modelfile: ollama no admite comentarios en la misma línea que
un PARAMETER. Esto falla:
PARAMETER num_gpu 32 # todas las capas
→ Error: invalid int value [32 # todas las capas]
Los comentarios van en su propia línea.
Lo medido de VRAM
| Configuración | Resultado |
|---|---|
num_ctx 32768, 32 capas |
cudaMalloc failed: out of memory |
num_ctx 24576, 32 capas |
pendiente: hay que medirlo con la tarjeta libre |
Escalera si 24576 tampoco entra, en orden de menos a más invasivo: num_ctx 16384 (el suelo: por debajo no cabe el prompt fijo con holgura) → num_gpu 24
→ OLLAMA_FLASH_ATTENTION=1 + OLLAMA_KV_CACHE_TYPE=q8_0.
Cuidado con el último: OLLAMA_KV_CACHE_TYPE es del servicio, no del
modelo. Se pone en el systemd de ollama y afectaría también al naranja la
próxima vez que arranque. Si se usa, hay que anotarlo aquí y en el Modelfile del
naranja.
Desde dónde se lanza importa, y mucho
| Directorio de arranque | Prompt de sistema |
|---|---|
~/COFRE/CODERS/JARVIS |
26.281 B |
~/.hermes/hermes-agent |
55.261 B |
La diferencia son 30 KB: el AGENTS.md de 81 KB del propio repo de Hermes,
que se cuela como fichero de contexto y encima sale truncado con un aviso. A
mano es fácil olvidarlo y sacar medidas que no comparan nada.
Y el cd NO basta (16 ago)
Esto se descubrió subiendo la escalera y es peor que lo anterior, porque no
deja ni un error. verde-launcher.sh hacía cd "$RAIZ" antes de llamar a
Hermes, que parece suficiente y no lo es: Hermes restaura el directorio de
trabajo apuntado en la sesión y se salta aquel desde el que lo lanzas.
El síntoma: se le pide "lee verde/README.md y resúmelo en una línea" y devuelve
Gitleaks es una herramienta SAST diseñada para detectar y prevenir secretos codificados a mano en repositorios git.
Parece el modelo alucinando de manera espectacular. No lo es. En la base de sesiones se ve la llamada real:
terminal {"command": "cat ~/README.md | head -50"}
Resolvió verde/README.md contra ~, ahí hay un README.md que es el
de Gitleaks, lo leyó entero y lo resumió bien. La herramienta funcionó, el
modelo entendió, el resumen es correcto — y la respuesta es inútil.
Lo que NO lo arregla, probado
--in DIR no vale en modo -z. Parece la bandera exacta —entra en el
directorio y se salta la restauración, mientras que --no-restore-cwd solo
hace lo segundo— y con ella el fallo se repitió igual. El motivo está en
hermes_cli/main.py:11324: el modo de una sola pregunta sale por
_run_and_exit_oneshot(...) y termina el proceso ahí, sin llegar nunca a
cmd_chat, que es la única función donde se atiende --in. Se ignora en
silencio. En el TUI sí funciona, y por eso verde-launcher.sh la pasa.
Un cd delante tampoco. Comprobado del modo más directo posible:
pwd del shell : ~/COFRE/CODERS/JARVIS
pwd del agente : ~
Y no es que el modelo se lo invente: el ls -d */ que ejecutó por su cuenta
devolvió BIKEPARK/ COFRE/ ctf/ Downloads/…, o sea el $HOME de verdad.
Lo que sí lo arregla
terminal.cwd en ~/.hermes/config.yaml, que de fábrica viene como "." y
que su propia documentación describe como "el directorio actual donde ejecutas
hermes". No lo es.
terminal:
cwd: "~/COFRE/CODERS/JARVIS"
Verificado: con eso el prompt de sistema pasa a decir
working directory: ~/COFRE/CODERS/JARVIS. Está replicado en
verde/config.yaml y lo comprueba pruebas/03_config.sh.
Ojo, que esto ATA el carril a este repositorio. Es lo correcto para un banco
de pruebas que se compara contra el naranja, y es una limitación real si algún
día se quiere que el verde mire ~/BIKEPARK o ~/oasis. Con la raíz en $HOME
además se ahoga: un search_files de *.md devolvía sobre todo
site-packages/ de los venv de entrena/.
Cómo se diagnosticó, que es reutilizable: -z imprime solo el texto final,
sin las llamadas a herramienta, así que desde fuera no se ve nada. Todo queda en
~/.hermes/state.db, tabla messages, columnas tool_calls y content:
python3 -c "
import sqlite3
c = sqlite3.connect('file:$HOME/.hermes/state.db?mode=ro', uri=True)
for r in c.execute('SELECT role, tool_name, tool_calls, content FROM messages ORDER BY rowid DESC LIMIT 10'):
print(r)
"
Sin mirar ahí, este fallo se atribuye al modelo y se acaba cambiando de modelo para nada.
Por qué se paró: dataset.py
El arranque en GPU quedó aplazado, no descartado. La gráfica la tiene un
trabajo tuyo: JARVIS/entrena/dataset.py, que genera pares pregunta-respuesta
de COFRE con qwen3.5:4b-jarvis. Son 7-10 horas (tu propio README lo dice) y
va por 100/2261 fragmentos.
Detalle que confunde y conviene tener escrito: mientras ese script corra,
ollama stop qwen3.5:4b-jarvis parece no funcionar. Sí funciona — descarga
el modelo — pero la siguiente petición del script lo vuelve a cargar en
segundos. No es un fallo de ollama.
Comprobado que los ollama stop no rompieron la generación: siguió de 199 a
224 ejemplos en 75 s.
Retomar cuando termine
# 1. confirmar que dataset.py acabó y la tarjeta está libre
pgrep -f 'entrena/dataset.py' || echo "terminado"
nvidia-smi --query-gpu=memory.used --format=csv,noheader # ~140 MiB
# 2. medir si 24576 cabe
curl -s http://127.0.0.1:11434/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"qwen3.5:4b-verde","messages":[{"role":"user","content":"ok"}],"max_tokens":5}'
curl -s http://127.0.0.1:11434/api/ps | python3 -m json.tool | grep -E 'size_vram|context_length'
# 3. decirle a Hermes el contexto REAL (si no, cree que son 262144)
# en ~/.hermes/config.yaml, bloque model:, en las lineas canonicas (~82/~95)
# default: "qwen3.5:4b-verde"
# context_length: 24576
# 4. LA PRUEBA DECISIVA, antes de abrir el TUI: ¿emite tool_calls?
curl -s http://127.0.0.1:11434/v1/chat/completions -H 'Content-Type: application/json' -d '{
"model":"qwen3.5:4b-verde",
"messages":[{"role":"user","content":"¿Cuánto espacio libre queda en el disco?"}],
"tools":[{"type":"function","function":{"name":"ejecuta_comando",
"description":"Ejecuta un comando de shell",
"parameters":{"type":"object","properties":{"comando":{"type":"string"}},"required":["comando"]}}}]}' \
| python3 -m json.tool
El paso 4 separa dos preguntas que juntas se contestan mal: ¿sabe el modelo
emitir una llamada? y ¿funciona Hermes?. Si sale tool_calls con JSON
válido, lo que falle después es de Hermes. Si contesta en prosa, abrir el TUI
solo daría un fallo más confuso.
Después, la escalera del plan: "hola" → una herramienta → ficheros → varias vueltas encadenadas (el escalón que separa un chatbot de un agente) → skills. La voz al final, y la escucha la última de todas: los 839 MiB de pico de whisper contra una tarjeta que tendrá el modelo casi entero.
Estado de la gráfica
| VRAM | Quién | |
|---|---|---|
| Antes de instalar | 878 MiB de 4096 | tres whisper-server tuyos, del naranja |
| Después de instalar | 127 MiB de 4096 | nadie; tus whisper acabaron solos |
Instalar no cargó nada en la tarjeta. Comprobado con
nvidia-smi --query-compute-apps y con /api/ps de Ollama, que devolvió
{"models": []} en todo momento.
De paso, un dato que puede interesarte del naranja: llegaste a tener tres
whisper-server a la vez, ~880 MiB cada uno, 2648 MiB en total sobre una
tarjeta de 4096. Es el escenario exacto del CUDA error: out of memory que
documenta el README. No hice nada al respecto —es tu desarrollo en marcha— pero
queda anotado.
Verificación en frío, ejecutada
| Comprobación | Resultado |
|---|---|
hermes en PATH |
~/.local/bin/hermes |
Tamaño de ~/.hermes |
2,2 GB |
| Caché de Playwright | 622 MB |
Credenciales con valor en .env |
0 |
| Tokens de mensajería | 0 (por eso el gateway nunca arrancó) |
URLs de nube activas en config.yaml |
0 |
| Puertos fuera de loopback | ninguno |
| Modelos cargados en Ollama | ninguno |
| Puente de voz | funciona: 0,34 s para 3,2 s de audio, 22050 Hz mono |
Las 11 líneas con contenido del .env no son claves: son TERMINAL_TIMEOUT,
BROWSER_SESSION_TIMEOUT, banderas de depuración y similares.
Trampa: claves YAML duplicadas en config.yaml
Esto casi cuela y merece quedar escrito. El config.yaml que genera el
instalador declara provider y base_url dos veces dentro del bloque
model:: una arriba, junto a default, y otra ~45 líneas más abajo. En YAML la
última repetición gana.
Al configurar Ollama arriba, el fichero parecía correcto leyéndolo, y al parsearlo salía esto:
model.provider: auto
model.base_url: https://openrouter.ai/api/v1
Es decir: el carril habría salido por OpenRouter creyendo yo que iba a localhost. Se arregló dejando los valores solo en las líneas de abajo, las canónicas.
La lección, que vale para todo este fichero: con 1890 líneas y 121 activas, no basta con leerlo. Hay que parsearlo:
python3 -c "import yaml;d=yaml.safe_load(open('$HOME/.hermes/config.yaml'));print(d['model'])"
Trampa: la instalación de Chromium se cuelga
Le pasa a más gente (issue #35166 del repo). El zip se descarga entero y la
extracción se queda parada: 50 minutos de reloj con 1 segundo de CPU y cero
crecimiento en ~/.cache/ms-playwright/. El timeout del instalador mata al
padre y deja procesos huérfanos reteniendo un __dirlock, así que el reintento
también falla.
Se resolvió a mano, y si vuelve a pasar tras un hermes update, esta es la
receta:
pkill -f oopDownloadBrowserMain; pkill -f 'playwright install'
rm -rf ~/.cache/ms-playwright/chromium-1208 ~/.cache/ms-playwright/__dirlock
mkdir -p ~/.cache/ms-playwright/chromium-1208
unzip -q /tmp/playwright-download-*/playwright-download-chromium-*.zip \
-d ~/.cache/ms-playwright/chromium-1208/
touch ~/.cache/ms-playwright/chromium-1208/INSTALLATION_COMPLETE
El INSTALLATION_COMPLETE es lo que Playwright mira para dar el navegador por
bueno (lib/server/registry/index.js:1345). Sin él lo reinstala cada vez.
Quedaron los tres componentes, verificados con playwright install --dry-run:
| Tamaño | |
|---|---|
chromium-1208 |
364 MB |
chromium_headless_shell-1208 |
254 MB |
ffmpeg-1011 |
4,9 MB |
El binario responde: Google Chrome for Testing 145.0.7632.6.
Resuelto: tu voz afinada SÍ se puede reutilizar
Era la pregunta abierta del plan, y salió el mejor de los tres desenlaces.
Hermes tiene un proveedor de TTS por comando que no está documentado en su
web. Está en tools/tts_tool.py, bloque "Custom command providers":
tts:
provider: mi-voz
providers:
mi-voz:
type: command
command: "... {output_path} {input_path}"
output_format: wav
Marcadores: {input_path}, {text_path}, {output_path}, {format},
{voice}, {model}, {speed}. Ejecuta con shell=True.
Eso permite llamar al mismo config/jarvis-piper.sh que usa el naranja, así que
el verde suena exactamente igual: mismo binario Piper 1.2.0, mismo .onnx,
misma cadena de audio. Sin esto la comparación de voz entre carriles no habría
valido nada.
Dos detalles que obligaron a escribir voz-verde.sh en vez de meter el comando
directo en el YAML:
- El entorno se limpia.
_run_command_ttsllama ahermes_subprocess_env(inherit_credentials=False), así queJARVIS_PIPER_VOZyJARVIS_PIPER_TONOno llegarían. Se ponen dentro del adaptador. - Las interfaces no encajan.
jarvis-piper.shquiere el texto como argumento; Hermes entrega un fichero. El adaptador hace de puente.
Qué voz, y ojo con el README
voz-verde.sh usa davefx a tono 1.0, que es lo que tiene el núcleo hoy
(nucleo/boca/voz.py: POR_DEFECTO = "davefx", JARVIS_PIPER_TONO = "1.0").
El README.md del repo describe sharvard + 0,86 = 106 Hz como "la puesta".
Eso era el JARVIS sobre Newelle. El verde copia al núcleo, que es contra quien
se va a comparar, no al README. Si algún día el núcleo vuelve a sharvard, hay
que cambiarlo aquí también.
De paso: el Piper de Hermes es el GPL, no el tuyo
El proveedor piper de serie usa OHF-Voice/piper1-gpl (GPL-3.0). El tuyo es
el original rhasspy/piper 1.2.0, MIT, archivado en octubre de 2025. Los dos
son software libre; no son el mismo paquete. Como usamos el comando y no el
proveedor de serie, el verde habla con tu Piper MIT.
El cerebro: lo que hay que vigilar
Configurado como proveedor custom contra http://127.0.0.1:11434/v1 con
qwen3.5:4b-jarvis. Ollama ya habla OpenAI, no hace falta puente.
Pero la propia guía de Hermes avisa de algo que hay que probar. Su tabla de
modelos locales dice, literalmente, que para trabajo agéntico completo hace
falta gemma4:31b, y marca sin tool calling a todo lo de 9B para abajo:
| Modelo (su tabla) | Tool calling |
|---|---|
gemma4:31b |
Sí |
gemma2:27b |
No |
gemma2:9b |
No |
llama3.2:3b |
No |
Su tabla no lista qwen3.5, que sí hace tool calling — el commit "nucleo: el cerebro con tool calling nativo" lo demuestra. Pero lo demuestra en tu arnés, no en el de Hermes, que inyecta muchas más definiciones de herramientas por vuelta. Así que:
- Es lo primero que hay que comprobar al arrancar: si el 4B llama herramientas bien dentro de Hermes o se atraganta.
- Si se atraganta, esta máquina tiene 62 GB de RAM: un modelo grande cabe,
aunque iría por CPU (~2-5 tok/s según su guía, 30-120 s por respuesta). Para
eso está
HERMES_API_TIMEOUT=1800documentado enenv.ejemplo. qwen3.5:9b(6,14 GB) es el escalón intermedio, y solo con el naranja cerrado.
Aviso de contexto: si se define num_ctx propio en Ollama, hay que poner el
mismo en Hermes. Su /api/show reporta el contexto máximo del modelo, no el
configurado, así que Hermes puede creer que tiene más sitio del que hay.
Antes de comparar los dos carriles: no midas peras y manzanas
El naranja tiene 150 acciones rápidas que se saltan el modelo: "¿cuánto
espacio queda?" es un df, no una inferencia. Hermes no tiene ese atajo —
allí toda orden pasa por el cerebro, con las herramientas, las skills y la
memoria en contexto en cada vuelta.
El verde va a medir más lento, y no será culpa del modelo. Para comparar de
verdad hay que usar solo las frases que hoy no encajan en el catálogo y acaban
en el cerebro: son las únicas donde los dos hacen lo mismo. Las hay anotadas en
config/jarvis-sin-accion.jsonl.
Pendiente: dependencias de sistema de Playwright
El instalador pidió sudo para las librerías de sistema de Chromium, se declinó
(no hay sudo sin contraseña) y degradó solo: instaló el binario en la caché
del usuario. Chromium arrancará si las librerías ya están; si se queja, esto es
lo que hay que correr una vez:
cd ~/.hermes/hermes-agent && sudo npx playwright install-deps chromium
Qué se descargará el primer día que arranques
Nada de esto ha pasado todavía. Que no sorprenda:
| Cuándo | Cuánto | |
|---|---|---|
Modelo de whisper small |
primera transcripción | ~500 MB en disco, 839 MiB de pico en GPU |
| Modelo de Ollama | ninguno — 4b-jarvis ya está |
0 |
| Voces de Piper | ninguna — usamos las tuyas | 0 |
stt.local.unload_after_idle_seconds: 300 está puesto justo por los 839 MiB:
suelta whisper tras cinco minutos de silencio y le devuelve el hueco al modelo.
Cuesta una recarga en la frase siguiente.
Se instaló también el driver de Computer Use
Va en la instalación completa. Es el que permitiría a un agente controlar
ratón y teclado del escritorio, no solo el navegador. Instalado no es lo mismo
que activo — hoy no hay nada que lo invoque — pero conviene saber que está antes
de dar permisos a algo. Si se quiere fuera: --skip-computer-use.
Dato que sí importa en esta máquina: ese driver no viene de Nous. El
instalador lo trae con un curl | bash contra terceros:
https://raw.githubusercontent.com/trycua/cua/main/libs/cua-driver/scripts/install.sh
O sea que el envoltorio instalar.sh audita el instalador de Hermes, pero ese
instalador se descarga y ejecuta otro script de otro repo sin que nadie lo mire.
Con COFRE en esta máquina, merece una lectura antes del próximo hermes update.
Está en ~/.hermes/hermes-agent, y el binario resultante también.
Las 104 pruebas del naranja: NO se ejecutaron
El plan decía correr pruebas/ejecutar.sh para comprobar que el naranja seguía
dando el mismo número. No se hizo, y a propósito: esa suite abre la
aplicación y carga el modelo, que es exactamente lo que se pidió no hacer
mientras el naranja estaba en desarrollo.
Queda pendiente para cuando pares de desarrollar:
cd ~/COFRE/CODERS/JARVIS/pruebas && ./ejecutar.sh
En su lugar se comprobó lo que sí se podía en frío: que la gráfica quedó libre y
que el verde no escribió fuera de su sitio. Todo lo suyo vive en verde/, en
~/.hermes/, más tres ficheros de escritorio (~/.local/share/icons/jarvis-verde.png,
~/.local/share/applications/jarvis-verde.desktop) y la caché de Playwright.
Los 13 ficheros que git status da por modificados son todos anteriores a
esta sesión: el más reciente es config/set_persona.py a las 13:22 y esto
empezó a las 16:09. Y los ficheros nuevos que aparecen en nucleo/ después de
esa hora —arranca.sh, estado.py, ventana.sh, pruebas/prueba_cara.py…—
son tu desarrollo del naranja de esta tarde: no existían cuando se listó esa
carpeta al empezar.
La duda del 4B: resuelta, y a favor (16 ago)
Era la pregunta abierta del carril — la guía de Hermes marca sin tool calling
todo lo de 9B para abajo y recomienda gemma4:31b para trabajo agéntico.
qwen3.5:4b llama herramientas bien dentro de Hermes. Comprobado primero
contra el endpoint pelado (pruebas/04_tool_calling.sh, tres casos) y después
en el arnés completo, con las 19 herramientas en contexto:
| Caso | Resultado |
|---|---|
| Pide herramienta cuando hace falta | terminal {"command": "df -h ."} |
| Acierta la herramienta y sus dos argumentos | escribe_fichero {"ruta": …, "contenido": …} |
| No llama cuando no hace falta ("¿cuánto son dos más dos?") | contesta 4 en prosa |
El tercero importa tanto como los otros: un modelo que llama siempre es tan inútil como uno que no llama nunca, y con una sola pregunta no se distinguen.
El escalón 6: habla, y con la voz del naranja (16 ago)
text_to_speech → proveedor jarvis-piper → voz-verde.sh →
config/jarvis-piper.sh. Comparado el WAV que salió de Hermes con el que genera
la prueba 07_voz.sh llamando al adaptador a pelo:
| Hermes | Directo | |
|---|---|---|
| Frecuencia | 22050 Hz | 22050 Hz |
| Canales | 1 | 1 |
| Bits | 16 | 16 |
Idénticos, que era el objetivo: mismo binario Piper 1.2.0 MIT, mismo .onnx,
mismo tono. La comparación de voz entre los dos carriles vale, que es para
lo que se montó el adaptador.
De propina, sin que nadie se lo pidiera contestó "Perfecto, señor" y "Todos
mis sistemas están listos para sus órdenes". El registro de JARVIS sale del
SOUL.md de Hermes, no de la persona del naranja, y aun así coincide.
Queda el juicio que no es medible: si suena bien. El fichero está en
~/.hermes/cache/audio/.
EL ESCALÓN 5 ROMPIÓ LA PREMISA: el agente se fue a internet solo (16 ago)
Esto es lo más serio de la sesión de estreno y hay que leerlo antes que nada.
Se le pidió "hazme un cartel en arte ascii que ponga JARVIS" — a propósito sin nombrar ninguna skill, que es lo que medía el escalón. Lo que hizo:
| # | Llamada | Resultado |
|---|---|---|
| 1 | skill_view {"name": "ascii-art"} |
eligió la skill sola ✓ |
| 2 | python3 -m pyfiglet "JARVIS" --font slant |
No module named pyfiglet |
| 3 | curl https://asciified.thelicato.io/api/v2/ascii?text=JARVIS |
salió a internet |
Nadie le dijo que podía. La skill menciona pyfiglet, pyfiglet no está instalado, y el modelo resolvió el hueco buscando el servicio equivalente en la web y mandándole el texto a un tercero.
Por qué no lo cazaba nada
pruebas/06_privacidad.sh pasa, y seguirá pasando: comprueba configuración
—cero credenciales, cero URLs de nube en el YAML, nada escuchando fuera de
loopback— y esto no es configuración, es conducta en marcha. El agente tiene
una terminal y la terminal tiene curl. No hay ajuste que lo impidiera.
Es una diferencia de fondo con el naranja, y conviene que quede escrita: el naranja ejecuta un catálogo cerrado de 150 acciones. El verde decide qué ejecutar, y ese es justo su valor y justo su riesgo. No se puede tener lo uno sin lo otro.
Lo que se puede hacer, por orden de menos a más invasivo
- Instalar lo que las skills dan por hecho (
pip install pyfiglet). Quita el motivo, no la capacidad. Es parche, no solución. - Apagar las skills que tiran de red:
hermes skills disable <nombre>. - Recortar herramientas en
platform_toolsetsdelconfig.yaml, que ya estaba apuntado como palanca para el contexto y sirve para esto también. - Cortar por debajo: regla de cortafuegos para el proceso, o terminal en
contenedor sin red.
hermes egressno vale para esto — es iron-proxy, un proxy que INYECTA credenciales en las salidas, no un bloqueo.
No he aplicado ninguna. Cualquiera de ellas cambia lo que el carril sabe hacer, y eso es una decisión del usuario, no una corrección técnica.
Y el cartel salió mal
Aparte de todo lo anterior: el banner que curl devolvió era correcto, y al
copiarlo a su respuesta final el modelo lo destrozó. Mismo patrón que la
lista de ficheros del escalón 4: el 4B razona bien la cadena y es descuidado
reproduciendo texto literal largo. 4 min 58 s en total.
El escalón 7 (la escucha) cabe, y por un motivo que sorprende (16 ago)
El plan lo dejaba para el final temiendo un CUDA error: out of memory: whisper
small pide 839 MiB en el pico de la transcripción y la tarjeta son 4096.
Medido con el verde cargado y trabajando:
| MiB | |
|---|---|
| Tarjeta | 4096 |
| Ocupado por el modelo verde (65536 ctx, 16 capas) | 2902 |
| Libre | 1062 |
Pico de whisper small |
839 |
| Margen | 223 |
Cabe. Y lo que sorprende es que el verde deja más sitio que el naranja:
2902 MiB contra los 2941 del reparto automático de ollama para
qwen3.5:4b-jarvis. Bajar 16 capas a la CPU libera tarjeta, así que el precio
que se paga en velocidad se cobra en VRAM.
Sigue siendo justo —223 MiB— y falta la prueba de verdad, que necesita micrófono. Pero la aritmética que hacía temer el escalón ya no dice lo que decía.
Para probar cuando arranques
- Tema del panel web.
dashboard.themeaceptadefault,midnight,ember,mono,cyberpunk,rose. No he podido ver sus paletas (el panel va empaquetado), perocyberpunkomonoson los candidatos a pegar con el verde. El TUI no tiene tema: ahí el color lo poneverde-launcher.shcon secuencias OSC. - Los escalones 5, 6 y 7 de
ESCALERA.md: una skill, la voz dentro de Hermes y la escucha por micrófono. Los tres piden criterio u oído, o el micrófono, así que no se automatizan. - Si merece la pena la palanca del KV caché.
OLLAMA_FLASH_ATTENTION=1+OLLAMA_KV_CACHE_TYPE=q8_0partiría el caché por la mitad y devolvería capas a la tarjeta, que es justo lo que hoy cuesta el tiempo de respuesta. No se ha aplicado: es del servicio de ollama y afectaría también al naranja.
Noche del 16-17 ago: voz de entrada, RAG y frontal a nivel del naranja
Encargo: subir el verde al nivel del naranja en voz, RAG y frontal. Todo local, nada a gitea (pendiente de revisión).
La escucha, resuelta y en local (escalón 7 cerrado sin micro)
El TUI de Hermes no tiene micrófono, y su STT nativo (stt: del config) es para
notas de voz de mensajería — saldrían de la máquina. Así que se puentea:
verde/escucha_transcribe.py— WAV → texto con faster-whispersmall(el mismo que declara el config), en CPU a propósito: el 4B ocupa la tarjeta y el margen es de 223 MiB; whisper en CPU tarda ~12 s por frase y no arriesga OOM. No llama a ninguna API.verde/escucha-verde.sh— pulsar-para-hablar: graba (pw-record/arecord/parec) → transcribe →hermes chat --reasoning none -q→ responde;--hablalo dice con la voz del verde,--seguidoencadena turnos.verde/pruebas/08_escucha.sh— 8/8. Bucle TTS→STT sin micro: genera voz con el07, la transcribe y comprueba que vuelven las palabras clave. Mide y verifica que el transcriptor no referencia APIs remotas.
Medido: "hola jarvis verde cuanto espacio libre queda en el disco" → "¿Cuánto espacio libre queda en el disco?" (solo pierde "jarvis"→"harvis", nombre raro).
Latencia honesta: la ESCUCHA va bien (~12 s CPU). El cuello es el 4B (minutos por turno). El puente sirve para PROBAR que el verde oye, no como diario.
RAG del naranja, disponible en el verde
El agente ya puede consultar el mismo índice que el núcleo (14.949 entradas):
nucleo/saber/busca_cli.py— CLI sobresaber.busca(texto o--json).- Skill local
~/.hermes/skills/research/buscar-en-apuntes/— le dice al agente cuándo y cómo buscar (herramienta de terminal, local).hermes skills list:buscar-en-apuntes · research · local · enabled.
Skills de ECC (el ganador del hackathon de Claude)
Instaladas 4 por hermes skills install (escaneadas SAFE): security-review,
git-workflow, python-testing, coding-standards. Total 82 skills. El prompt
fijo apenas sube (29 KB, lejos de 64k). Probada security-review con el 4B:
checklist correcto, 4 min 30 s en frío.
Noche: expansión del RAG + reentrenamiento (auto mode, local, sin gitea)
Contenido añadido al índice PERSONAL (nunca al público sin revisar licencias):
- Strix (usestrix/strix, agente IA de pentesting) clonado en CODERS; sus docs (README, docs/, AGENTS, benchmarks) indexados → 49 fragmentos.
- 6 awesome lists más: sysadmin, cli-apps, security, pentest, shell, linux (catálogo pasa de 385 a 740 fragmentos). En awesome.py, publico=False.
- Apps instaladas de esta máquina: nucleo/saber/instalado.py lee los .desktop (146 apps) + apt-mark showmanual (22 fragmentos). Personal por definición.
- Índice personal: ~49 MB. Reindex raw hecho; contenido recuperable ya.
Reentrenamiento nocturno: nucleo/saber/enriquece/reentrena_noche.sh encadena síntesis de ganchos del contenido nuevo → reindex → eval → refresco COFRE (cosecha+sintetiza) → reindex final. Corre detached, log en datos/reentrena_noche.log.
Pendiente de revisar por la mañana: licencias de las 6 awesome nuevas antes de subir nada; y decidir qué de todo esto va al índice público / gitea.
Cierre de la noche (07:52) — números y decisiones
Dos rondas de reentrenamiento. Índice final: 64 MB, 19.578 entradas (12.260 preguntas-gancho, 1.383 catálogo, 168 apps, 459 metodología incl. strix).
Retrieval (eval_set ampliado a 57 casos = 45 pentest + 12 amplitud):
- Ronda 1: hit@1 77%, hit@5 96%
- Ronda 2 (+osint/devops/docker/go/python): hit@1 75%, hit@5 96%
- Amplitud sola (apps/self-hosted/cli/linux/strix): hit@1 75%, hit@5 100%
Intercambio medido: pentest puro pasó de 82% (antes de la noche) a ~75% de hit@1; hit@5 se mantiene 95-96%. Es el precio de la amplitud. Las listas gigantes (awesome-go, 3197 items → 322 fragmentos; osint con algún artefacto de TOC) son las que más ruido meten y menos aporta a consultas por voz.
Decisiones para la mañana:
- Licencias de las 12 awesome lists antes de publicar nada (ahora todas publico=False = solo índice personal).
- Si se prioriza precisión pentest: quitar awesome-go (y quizá awesome-python) recuperaría 1-2 pts de hit@1 sin perder casi cobertura útil.
- Qué del contenido nuevo va al índice público / gitea (hoy: nada subido).