carril verde (Hermes Agent): documentacion, panel, voz local y RAG
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).
This commit is contained in:
parent
b1f8118272
commit
1e3e5ca20d
32 changed files with 3697 additions and 161 deletions
136
verde/pruebas/03_config.sh
Executable file
136
verde/pruebas/03_config.sh
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env bash
|
||||
# La configuracion de Hermes, PARSEADA.
|
||||
#
|
||||
# Esta prueba existe por un fallo concreto que casi cuela. El config.yaml que
|
||||
# genera el instalador declara `provider` y `base_url` DOS VECES dentro del
|
||||
# bloque model:, una arriba y otra ~45 lineas mas abajo. En YAML gana la ultima.
|
||||
# Al configurar Ollama en las de arriba el fichero PARECIA correcto leyendolo, y
|
||||
# al parsearlo salia provider=auto, base_url=https://openrouter.ai/api/v1: el
|
||||
# carril habria salido por OpenRouter creyendo nosotros que iba a localhost.
|
||||
#
|
||||
# Con 1890 lineas y 121 activas, leer el fichero no demuestra nada. Aqui todo se
|
||||
# comprueba con yaml.safe_load, que es lo mismo que hace Hermes al arrancar.
|
||||
|
||||
cd "$(dirname "$0")" && . ./comun.sh
|
||||
|
||||
titulo "El cerebro"
|
||||
|
||||
afirma "existe $CONFIG" test -f "$CONFIG"
|
||||
|
||||
comprueba "el modelo por defecto es el verde" "$(config_model default)" "$MODELO"
|
||||
|
||||
# Sin esto Hermes se cree lo que dice /api/show, que son 262144 —el maximo del
|
||||
# modelo— y no los 24576 configurados. El sintoma serian respuestas truncadas y
|
||||
# llamadas rotas, y la conclusion natural ("el 4B no vale") seria falsa.
|
||||
CTX_YAML=$(config_model context_length)
|
||||
CTX_REAL=$(parametro "$MODELO" num_ctx)
|
||||
comprueba "Hermes sabe el contexto REAL, no el que reporta /api/show" \
|
||||
"$CTX_YAML" "$CTX_REAL"
|
||||
|
||||
# Los dos numeros tienen que ser iguales Y llegar al suelo de 64k. Poner aqui
|
||||
# 65536 con el modelo a 24576 pasaria el arranque y romperia en marcha: Hermes
|
||||
# no comprimiria hasta acercarse a 64k y ollama iria tirando los tokens mas
|
||||
# viejos, que son el prompt de sistema y los esquemas de las herramientas. El
|
||||
# agente dejaria de llamar herramientas a mitad de sesion, sin un solo error.
|
||||
mayor_que "y ese contexto llega al suelo de Hermes" "${CTX_YAML:-0}" 63999
|
||||
|
||||
BASE=$(config_model base_url)
|
||||
case "$BASE" in
|
||||
http://127.0.0.1:*|http://localhost:*)
|
||||
echo " $(verde OK) el cerebro apunta a esta maquina ($BASE)"; _ok=$((_ok + 1)) ;;
|
||||
*)
|
||||
echo " $(rojo FALLO) base_url NO es local: $BASE"; _fallo=$((_fallo + 1)) ;;
|
||||
esac
|
||||
|
||||
titulo "Cero nube"
|
||||
|
||||
# Se recorre el YAML entero, no solo el bloque model:. Cualquier http(s) que no
|
||||
# apunte al propio equipo es una salida potencial y tiene que estar justificada.
|
||||
FUERA=$(python3 - "$CONFIG" <<'PY'
|
||||
import re, sys, yaml
|
||||
|
||||
LOCAL = re.compile(r'^https?://(127\.0\.0\.1|localhost|0\.0\.0\.0|\[::1\])')
|
||||
URL = re.compile(r'https?://[^\s"\']+')
|
||||
fuera = []
|
||||
|
||||
def anda(nodo, ruta=""):
|
||||
if isinstance(nodo, dict):
|
||||
for k, v in nodo.items():
|
||||
anda(v, f"{ruta}.{k}" if ruta else str(k))
|
||||
elif isinstance(nodo, list):
|
||||
for i, v in enumerate(nodo):
|
||||
anda(v, f"{ruta}[{i}]")
|
||||
elif isinstance(nodo, str):
|
||||
for u in URL.findall(nodo):
|
||||
if not LOCAL.match(u):
|
||||
fuera.append(f"{ruta} = {u}")
|
||||
|
||||
anda(yaml.safe_load(open(sys.argv[1])))
|
||||
for f in fuera:
|
||||
print(f)
|
||||
PY
|
||||
)
|
||||
if [ -z "$FUERA" ]; then
|
||||
echo " $(verde OK) ninguna URL de nube activa en el config"; _ok=$((_ok + 1))
|
||||
else
|
||||
echo " $(rojo FALLO) hay URLs que salen de esta maquina:"
|
||||
printf '%s\n' "$FUERA" | sed 's/^/ /'
|
||||
_fallo=$((_fallo + 1))
|
||||
fi
|
||||
|
||||
titulo "Desde donde mira el agente"
|
||||
|
||||
# `cwd: "."` de fabrica NO es "el directorio desde el que lanzas hermes", por
|
||||
# mucho que lo diga su documentacion: el agente arranca en $HOME. Con la raiz
|
||||
# ahi, "lee verde/README.md" acaba leyendo $HOME/README.md —el de
|
||||
# Gitleaks— y resumiendolo perfectamente. Ningun error, respuesta inutil.
|
||||
CWD_AGENTE=$(python3 -c "
|
||||
import yaml
|
||||
print(yaml.safe_load(open('$CONFIG')).get('terminal', {}).get('cwd', ''))" 2>/dev/null)
|
||||
comprueba "el agente arranca en la raiz del proyecto" "$CWD_AGENTE" "$RAIZ"
|
||||
|
||||
titulo "El lanzador entra donde debe"
|
||||
|
||||
# Un `cd` antes de llamar a hermes NO basta: Hermes restaura el directorio que
|
||||
# tenga apuntado en la sesion. El sintoma no parece de rutas — se le pide "lee
|
||||
# verde/README.md y resumelo" y devuelve un resumen impecable de OTRO fichero,
|
||||
# sin un solo error — asi que se comprueba estaticamente.
|
||||
if grep -q -- '--in "\$RAIZ"' "$VERDE/verde-launcher.sh"; then
|
||||
echo " $(verde OK) verde-launcher.sh pasa --in y no confia en el cd"; _ok=$((_ok + 1))
|
||||
else
|
||||
echo " $(rojo FALLO) verde-launcher.sh no pasa --in: Hermes abrira en otro sitio"
|
||||
_fallo=$((_fallo + 1))
|
||||
fi
|
||||
|
||||
titulo "La voz apunta a la del naranja"
|
||||
|
||||
# Sin esto la comparacion de voz entre carriles no diria nada: Hermes se
|
||||
# descargaria su propia voz de Piper (la GPL de OHF-Voice) en vez de usar el
|
||||
# binario MIT y el .onnx que ya tiene el naranja.
|
||||
ORDEN=$(python3 -c "
|
||||
import yaml
|
||||
t = yaml.safe_load(open('$CONFIG')).get('tts', {})
|
||||
p = t.get('providers', {}).get(t.get('provider', ''), {})
|
||||
print(p.get('command', ''))
|
||||
" 2>/dev/null)
|
||||
case "$ORDEN" in
|
||||
*/verde/voz-verde.sh*)
|
||||
echo " $(verde OK) TTS por comando -> voz-verde.sh"; _ok=$((_ok + 1)) ;;
|
||||
*)
|
||||
echo " $(rojo FALLO) el TTS no apunta al adaptador: $ORDEN"; _fallo=$((_fallo + 1)) ;;
|
||||
esac
|
||||
|
||||
stt_local() { # $1 = clave dentro de stt.local
|
||||
python3 -c "
|
||||
import yaml
|
||||
print(yaml.safe_load(open('$CONFIG')).get('stt', {}).get('local', {}).get('$1', ''))
|
||||
" 2>/dev/null | tr -d '\n'
|
||||
}
|
||||
|
||||
comprueba "la escucha es la local de Hermes" "$(stt_local model)" "small"
|
||||
|
||||
# 839 MiB de pico contra una tarjeta con 3301 MiB de modelo dentro: whisper
|
||||
# tiene que soltar la VRAM cuando calla, o la siguiente frase da OOM.
|
||||
menor_que "whisper suelta la VRAM tras callar (s)" "$(stt_local unload_after_idle_seconds)" 601
|
||||
|
||||
resumen
|
||||
Loading…
Add table
Add a link
Reference in a new issue