Llamo por teléfono a mi homelab y me atiende un agente que opera la infra
Conecté mis labs sueltos —RAG multimodal, WhatsApp, K3s, avatar— detrás de un agente de voz al que llamo por Telegram. Ve mi pantalla, ejecuta kubectl, navega la web y delega lo pesado a Claude Code. La arquitectura, la demo y los tradeoffs honestos del approach on-prem.
- voice
- gemini-live
- telegram
- ai
- devops
- kubernetes
- k3s
- homelab
- self-hosted
- rag
- agents

TL;DR
Hace meses que vengo armando labs sueltos en el homelab: un RAG multimodal, un router de WhatsApp, un switchboard de GPU, Claude Code corriendo en K3s, un avatar con lip-sync. Cada uno resolvía una cosa y vivía por su cuenta. Esta semana los enchufé todos detrás de un mismo agente de voz —clau— y la forma de hablarle es literal: una llamada de Telegram.
Lo interesante no fue el agente de voz en sí (de eso hay mil). Fue que, una vez conectados todos los labs, clau dejó de ser “un voice bot” y se transformó en la interfaz principal para operar la infra. Le hablo, ve mi pantalla, me muestra la suya, ejecuta kubectl sobre mi clúster, navega la web, y si algo le queda grande, lo delega a otro agente con más permisos y vuelve con la respuesta hablada.
Este post explica la arquitectura, el patrón “voice agent como capa de orquestación”, y los tradeoffs honestos de armar esto on-prem.
El problema: labs que no se hablaban entre sí
El último año fui sumando piezas al homelab, cada una con su propósito:
| Lab | Qué hace | Stack |
|---|---|---|
| RAG multimodal | Busca sobre mis docs/imágenes | FastAPI + Qdrant + Gemini |
| WhatsApp Router | Envía/recibe mensajes WA | Evolution API + ws-router |
| GPU Switchboard | Conmuta perfiles de LLM en la 3090 | daemon propio en el AI Server |
| claupod | Claude Code para razonamiento largo | K3s sobre Proxmox |
| Avatar realtime | Cara con lip-sync | MuseTalk + TensorRT |
El problema clásico: cada uno tenía su propia forma de invocarse. Para hacer algo “completo” terminaba con tres terminales abiertas, un chat de WhatsApp y un dashboard. La fricción no estaba en las capacidades —estaban todas— sino en el pegamento entre ellas.
La idea: el agente de voz como capa de orquestación
clau vive en mi workstation, levanta una llamada real de Telegram cuando la llamo (o me llama ella, de forma proactiva), y habla con voz propia vía Gemini Live. Hasta acá, un voice agent más.
El cambio de fase fue conectarle, uno por uno, los labs que ya tenía. Cada lab se expone como una tool nativa del agente. Hoy son del orden de 50 tools nativas, más la posibilidad de delegar lo pesado a Claude Code. Todo on-prem, con mis credenciales, mi RAG y mis skills.
La diferencia con un chatbot está en dos capacidades que no veo tan seguido en agentes:
- Ve lo que hago. Le comparto mi pantalla o mi cámara y razona sobre eso.
- Me muestra lo que hace. Comparte su terminal o su browser dentro del video de la llamada, así trabajamos los dos sobre lo mismo.
Cómo funciona, en la práctica
En el video le hago una llamada y le pido que se muestre. El recorrido real:
Opera el clúster. Le pido que comparta la terminal y ejecute comandos:
kubectl get nodes
# k3s-master, k3s-worker-01, ubuntu → todos Ready
kubectl get ns
# agents, cloud, default, kube-system, rag-multimodal, ...
kubectl -n cloud get pods
# atlassian-mcp, evolution-api, message-fileserver Running; algunos Jobs Completed
No solo ejecuta: ve el resultado en su propia terminal compartida y dialogamos sobre lo que aparece. Es operar la infra conversando.
Navega la web. Le digo “abrime mi sitio y andá al dashboard de Grafana”. Comparte el browser (Chromium + Playwright), navega, intenta loguearse —el password va sin que el modelo lo vea jamás— y cuando entra, le pido un screenshot del panel y me lo manda por WhatsApp. La integración con WA no es un extra: es parte del mismo flujo de la llamada.
Consulta el RAG, multimodal. Sobre el RAG monté un detalle que me gustó: en paralelo a la búsqueda vectorial clásica (Qdrant + embeddings de Google), guardo la misma información en una estructura tipo LLM-as-a-wiki (el approach que comentaba Karpathy). Una capa arriba analiza la consulta: si es genérica/abierta, navega la wiki; si es muy específica, va a los vectores; o combina las dos —primero un acercamiento por wiki, después la búsqueda fina. Le mandé el logo de Apple por WhatsApp y me devolvió la descripción que generó. La frase clave para dispararlo es “usá el rack para…”.
Agenda y consulta servicios. Crea un evento en Google Calendar, me dice el estado del servicio de FLUX en el AI Server, mira la cámara y me describe el setup (sí, también encontró el mate).
El patrón que más me sirvió: delegación
No todo lo resuelve clau en la llamada. Para tareas que necesitan razonamiento largo o permisos que el agente de voz no tiene, delega a claupod —una instancia de Claude Code corriendo en K3s—. claupod hace el trabajo (incluso SSH a servidores externos configurados para sacar un dato puntual), devuelve el resultado, y clau me lo comunica hablado en la misma llamada.
Tarda un poco más porque la tarea es delegada, pero el patrón es lo bueno: el agente de voz es la cara conversacional; los agentes con permisos hacen el trabajo sucio. La voz nunca toca credenciales sensibles; orquesta.
El avatar (y por qué tiene delay)
El último gimmick es que clau aparece con un avatar de webcam en tiempo real: un MuseTalk con TensorRT generando el lip-sync sobre el AI Server. Es honestamente lo más caro de todo el pipeline —necesita mucho procesamiento en tiempo real—, así que arrastra ~1 segundo de desfase con el audio. El video sí es realtime; la sincronía fina es lo que queda por mejorar. Lo dejo como está: cumple para mostrar la idea, no para producción.
Tradeoffs honestos
- La latencia es del modelo, no de la red. Medí el round-trip a claupod: el 99% del tiempo es Gemini razonando (4-8s según tools); la red interna es <5ms. Optimizar acá pasa por el prompt y las tools, no por la infra.
- Hay que domar al modelo. Gemini Live dispara tools al azar ante preguntas confusas y a veces alucina identificadores. Se arregla con reglas explícitas e inviolables en el prompt y fallbacks, pero es trabajo fino.
- GPU compartida = warmups. Todo cuelga de una sola 3090 con time-multiplexing. El primer call a un modelo recién swapeado paga el costo de carga. Para mi caso (un usuario, yo) está perfecto; para concurrencia real, no.
- El browser no maneja 2FA/SSO. En el video se ve: el login a Grafana con doble factor lo frena. Es un límite conocido del browser share.
- Avatar con desfase ~1s. Ya mencionado.
Nada de esto es “revolucionario”. Es pegamento bien puesto sobre piezas que ya funcionaban, y para un homelab de un solo usuario rinde muchísimo.
Stack
- Voz: Gemini Live (voz Aoede) sobre una llamada de Telegram (hydrogram + pytgcalls).
- Orquestación: tools nativas en el agente + delegate a Claude Code (claupod) en K3s.
- Infra: K3s sobre Proxmox, NAS para PVs, una RTX 3090 con GPU Switchboard.
- RAG: FastAPI + Qdrant + embeddings de Google + capa LLM-as-a-wiki.
- Mensajería: Evolution API + ws-router (WhatsApp).
- Browser: Chromium + Playwright (CDP screencast headless).
- Avatar: MuseTalk + TensorRT.
- STT/TTS auxiliar: Whisper + Piper self-hosted.
Cierre
La conclusión que me llevo: el valor no estaba en construir un agente más, sino en darle a los labs que ya tenía una única puerta de entrada conversacional. Una llamada telefónica terminó siendo la mejor interfaz para operar el homelab mientras camino por la casa.
Quedan cosas para mejorar (el desfase del avatar, domar mejor el modelo, manejar SSO en el browser), así que va a haber más videos.
Demo completa funcionando en vivo 👉 Ver en YouTube
Si estás armando algo parecido o querés discutir el approach on-prem, escribime — me copa charlarlo.