¿Qué hizo tu agente anoche? Cómo conectar Hermes con OpenTelemetry


Te despiertas, abres el portátil y el trabajo de «solo tocar la config» que dejaste corriendo toda la noche salió unos dólares más caro de lo habitual. /status te dice el total de tokens y nada más. Lo que de verdad quieres saber es qué llamada al modelo fue lenta, qué tool reintentó tres veces y qué aprobación se quedó ahí dos minutos esperando un clic. Hermes no trae esa línea de tiempo por llamada, pero el plugin de la comunidad hermes-otel cubre justo ese hueco: conecta Hermes con OpenTelemetry y convierte un turno en un trace que puedes ir desplegando capa por capa.

Las dos superficies de observabilidad integradas no cubren «esta llamada concreta»

Hermes ya incluye dos. La primera es el monitoreo del gateway (monitoring.export.otlp), que vigila la salud del gateway y del cron y por diseño no toca el contenido: ni prompts, ni llamadas a tools, ni tokens. La segunda es el plugin Langfuse incluido, que registra traces por ejecución pero solo los envía a Langfuse.

hermes-otel es la tercera superficie, la de nivel de ejecución, y convive con las dos anteriores. Se engancha a los hooks de ciclo de vida del host e instala su propio TracerProvider, así que nunca toca los objetos privados del provider que construye el exportador del core. Los dos conjuntos de datos no se pisan entre sí.

Instalar: un comando y un reinicio del gateway

El plugin está en el catálogo curado (nivel comunitario, fijado en 1.18.2):

hermes plugins install hermes_otel
hermes plugins enable hermes_otel

Hermes 0.21 y posteriores instalan las tres dependencias opentelemetry-* del plugin en su propio virtualenv y las vuelven a aplicar después de cada hermes update. Reinicia cualquier gateway que esté en marcha cuando termines.

¿Versión anterior a 0.21, o lo instalaste con --no-deps? Añade las dependencias a mano:

<hermes venv>/bin/pip install -r ~/.hermes/plugins/hermes_otel/requirements.txt

Cómo se ve un turno cuando lo despliegas

El plugin mapea los eventos del host a spans, más o menos así:

agent / cron                       <- el span raíz de todo el turno
├── skill.{name}                   <- una skill cargada, abierta hasta que termina el turno
└── llm.{model}                    <- un run_conversation
    ├── api.{model}                <- un round-trip HTTP, con tokens y finish reason
    │   ├── tool.{name}            <- cada llamada a tool: argumentos, resultado, desenlace
    │   └── approval.{pattern}     <- esperando a una persona o al smart guardian
    └── subagent.{role}            <- delegate_task, el trace del hijo se anida aquí

Los spans api.* llevan un atributo hermes.query_source que indica si la llamada vino del propio bucle del agente (main), de un hijo delegado (subagent) o se hizo en nombre del agente (auxiliary: titulado, compresión, agregación de Mixture-of-Agents), con hermes.auxiliary.task nombrando la tarea. Los spans de tool llevan hermes.tool.outcome: completed, error, timeout, blocked o cancelled. Los atributos se emiten tanto en las convenciones de OpenInference como en las de OTel GenAI, así que Phoenix y Langfuse funcionan sin mapeo personalizado.

Configúralo con un YAML — o con una sola variable de entorno

Si solo usas un backend, no necesitas ningún archivo de configuración:

export OTEL_PHOENIX_ENDPOINT=http://localhost:6006/v1/traces

Si usas varios, crea ~/.hermes/hermes_otel.yaml:

project_name: hermes-agent
backends:
  - type: phoenix
    endpoint: http://localhost:6006/v1/traces      # solo traces
  - type: lgtm
    endpoint: http://localhost:4318/v1/traces      # traces + métricas + logs
    metrics: true
  - type: honeycomb
    api_key: ${HONEYCOMB_API_KEY}                  # ${VAR} se expande al cargar
capture_logs: true

Los backends se alimentan en paralelo, cada uno con su propia cola de exportación. El directorio docker-compose/ del repo trae un stack listo para cada backend que puedas autoalojar, así que levantar Phoenix o Jaeger en local es el camino más corto al primer trace.

Privacidad: por defecto va el contenido completo, así que pásate a preview

Lee esta parte primero. El valor por defecto, content_capture: full, envía el prompt y la respuesta completos de cada llamada al modelo, además de los argumentos y la salida de las tools recortados a 1200 caracteres, al backend que hayas configurado.

Valor Qué sale del proceso
full Prompt y respuesta completos, contenido de tools recortado a 1200 caracteres
preview Solo vistas previas recortadas
off Nada de contenido, solo la estructura

Sin ningún backend configurado, el plugin mantiene un almacén SQLite local bajo $HERMES_HOME para su dashboard y no envía nada. Si usas varios profiles, recuerda que cada uno tiene su propia instalación, ajustes y telemetría, y que un gateway multiplexado traza cada profile que sirve bajo su propio nombre.

Pregunta desde el chat, sin tocar ningún backend

El plugin incluye una skill, hermes_otel:observability, con una herramienta de consulta por terminal. Una vez instalado puedes preguntar directamente «¿por qué fue lento mi último turno?» o «¿cuánto costó esta sesión?», y responderá leyendo el almacén local en vivo: no hace falta backend.

python3 ~/.hermes/plugins/hermes_otel/skills/observability/scripts/otel.py trace last

La salida es una cascada legible con la duración de cada span, tokens, aciertos de caché y finish reason. Entre los subcomandos hay status, traces, span, sessions, stats, metrics, logs y un sql de solo lectura, todos con --json, --since y --source.

Cuándo conviene saltárselo

  • Si lo único que quieres saber es si el gateway sigue vivo, el monitoreo de gateway integrado es más ligero y por diseño no toca el contenido.
  • Si Langfuse es tu único backend, el plugin Langfuse incluido ya te cubre.
  • En una máquina pequeña con muchas tareas diminutas, los spans por turno tienen un coste. Empieza con preview y sampling durante una semana antes de dejarlo fijo.

Dos límites con todas las letras: enlazar los spans del servidor MCP dentro del trace del agente está implementado del lado del plugin pero aún depende del soporte del host, y el plugin falla en abierto: sus exportadores corren en hilos propios, así que no es un log de auditoría. Si necesitas evidencia que sobreviva a un crash, conserva el almacén local o exporta a un backend que controles tú.

Si todavía estás eligiendo plugins, empieza por cómo funciona el catálogo de plugins: hermes plugins search te dice a qué commit está fijado un plugin. Para recorrer estos spans en un workspace de navegador, Hermes Workspace se lleva bien con él, y los detalles de los comandos están en la referencia de hermes plugins.