Sesión 4 de 5 · El agente

El agente de
soporte multicanal

Un servicio que corre solo, en su propio contenedor — no una sesión de Claude Code abierta

DARATEL TIC | AGENTE AUTÓNOMO EN DOCKER | 3 horas
Dónde estamos

Cuarta de cinco

S1
KickOff
Hecho.
S2
Ecosistema + Claude Code
Instalado, con los primeros prompts.
S3
Portal de gestión IP
Armado entre los ocho. 853/853 tests en verde.
HOY
S4
Agente de soporte multicanal
Conectado al portal, y corriendo solo.
S5
Despliegue y roadmap
Puesta en marcha y siguientes pasos.
Agenda

Tres horas — antes de la pausa

10 minAperturaBalance honesto de la S3 y el giro: el agente es un servicio Docker autónomo
10 minEl ganchoLa cita, la objeción y la tesis, proyectadas
10 minLo mínimo de teoríaEl conector como superficie y la tabla de escalado
15 minPortal en DockerLevantarlo y migrar a mano
20 minEntender el agenteLos cuatro ficheros — el bloque que vale la sesión
10 minLevantar el agenteEl primer arranque, en verde
Agenda

Tres horas — después de la pausa

15 minPausaColchón de rescate
25 minWhatsApp realDos caminos, y por qué importa cuál eliges
15 minJira realEl único canal probado hoy contra algo real
10 minEmail real (Gmail)Sin verificar de punta a punta, y se dice así
25 minReglas de escaladoMedir el suelo, escribir las vuestras, verlo actuar solo
15 minCierreLa bisagra, el puente a la S5 y preguntas
De dónde venimos

La S3, sin maquillar

Antes de nada, el balance honesto: no todos partisteis del mismo sitio la última vez. Hoy eso deja de importar.

La S3Cuatro de vosotros metisteis módulo y quedó integrado y en verde. Los otros cuatro no llegaron a tiempo.
→
HoyEl portal ya está armado, con el trabajo de los ocho dentro. Nadie parte de su módulo — todos partís del mismo sitio.

853/853 tests en verde. Lo de hoy no es construir el portal: es ponerle un agente encima.

La corrección más importante de hoy

"El agente" no es una ventana abierta

Dilo en el primer minuto — es la corrección que pidió el cliente explícitamente.

NO es

  • Una sesión de Claude Code que alguien deja abierta en su portátil
  • Algo que depende de que ese portátil siga encendido
  • Un editor o una terminal de por medio

SÍ es

  • app/agente/, un proceso Python en su propio contenedor
  • Un loop cada 30 segundos, mirando WhatsApp, correo y Jira
  • Autónomo — pase lo que pase con vuestro portátil

Los cuatro servidores MCP siguen ahí, para cuando un humano quiere preguntar a mano con Claude Code. Pero no son el entregable de hoy.

PARTE 01

La tesis,
antes de tocar nada

Antes de instalar una sola pieza: qué le vais a permitir hacer al agente, y qué no.

El gancho

La cita de Guillermo

En la encuesta, antes de empezar el programa — palabra por palabra. Es el guion de hoy.

"Localizar clientes, qué equipos tiene, cómo conectarnos a ellos… para revisar un teléfono hay que saber qué IP pública tiene el cliente, qué puerto es el teléfono en concreto, si está configurado el NAT, si tienen el servicio de tráfico limpio y el NAT no funciona, si tengo que entrar por VPN si ya está configurada…"

Guillermo, eso que describiste es el guion de hoy. No lo hemos adaptado: lo va a construir el agente, delante de vosotros.

Que la IA muchas veces, sobre todo en el contexto de redes, no está fina del todo, recomienda cosas que no son adecuadas cuando la solución es más sencilla.

Ángel Ramos, en la encuesta. Tiene razón — y por eso existe esta sesión.

A vuestros clientes no les vais a vender lo que el agente sabe hacer. Les vais a vender lo que podéis demostrar que no puede hacer.

La tesis que sostiene la sesión entera

Lo mínimo de teoría

Un conector no es un espejo

Es la decisión de qué parte de lo que hay detrás se deja alcanzar. Y de ahí sale dónde puede vivir un límite.

PromptEn las instruccionesLo ignora. Es contexto, no configuración
HookEn un PreToolUseSe bloquea, pero alguien tiene que mantener la regla
AusenteEn la ausencia de la herramientaNo hay nada que ignorar

Las reglas de escalado no son una convención del prompt: son la superficie del conector.

Lo mínimo de teoría

19 herramientas para un humano, 15 para el agente

Mismo código por debajo, dos catálogos distintos según quién pregunta.

5
Portal, solo lectura
6
Ticketing
4
Canales
4
Mesa de soporte

19 herramientas, 4 servidores MCP — para cuando un humano pregunta con Claude Code. El agente autónomo tiene su propio catálogo: 15 herramientas, el mismo código por debajo. Le faltan bandeja_entrada, canales_disponibles, ips_libres e inventario_red — sirven para explorar, no para atender un aviso ya en curso. El adaptador de vuestro propio ticketing es trabajo de la bolsa de horas, no de hoy.

PARTE 02

Portal y agente
en Docker

Quince minutos para el portal, veinte para entenderlo, diez para el primer arranque. Nadie construye nada nuevo — todo esto ya existe y está verificado.

Manos a la obra

Portal en Docker, con sus migraciones

El repo ya trae el portal armado — rama integracion-s4, commit 9eff399, 853/853 tests en verde.

1

Clonar y cambiar de rama

git clone el repo del portal y git checkout integracion-s4.

2

Levantar base de datos y portal

docker compose up --build db portal. El servicio agente se deja fuera a propósito, por ahora.

3

Migrar a mano

docker compose exec portal alembic upgrade head. El Dockerfile solo arranca uvicorn, no corre migraciones.

/health dice "ok" aunque la base no esté migrada. No os fiéis de él solo — pedid /clientes.

Entender el agente · el bloque que vale la sesión

Antes de arrancarlo, lo leemos

Con un docker compose up -d agente esto sería arrancar una caja negra. Cuatro ficheros, en este orden.

1

configuracion.py

Dos variables: cada 30 s mira avisos nuevos, máximo 20 por pasada. Están en el código, no en la cabeza de nadie.

2

catalogo.py

Construye los mismos colaboradores que usan los 4 servidores MCP. La regla de cierre se importa de app.mcp.ticketing.servidor — no se copia.

3

service.py

Si el modelo agota 6 rondas sin decidir, el proceso fuerza derivado_humano. Todo aviso se marca atendido, pase lo que pase.

4

__main__.py

El loop que reintenta si algo falla. Una sola condición de parada dura: sin OPENROUTER_API_KEY, no arranca.

El corolario, en directo

Un bug que ningún test cazó

AGENTE_LIMITE_POR_CICLO se lee, se muestra en el log de arranque… y no llega a usarse.

El bug

Se calcula y no se pasa

El código que arranca el ciclo no se la entrega a la función que la necesita. Hoy da igual —ambos valores son 20— pero si alguien la bajara esperando un ciclo más corto, no pasaría nada, en silencio.

Comprobadlo vosotros

La convención no es garantía

Abrid Claude Code contra los 4 MCP y mirad /mcp: contad las herramientas con ESCRIBE: en la descripción. responder_mensaje y marcar_atendido escriben y no la llevan.

Los cuatro defectos de hoy vivían en ficheros con los tests en verde. Ninguno se cazó leyendo código.

Manos a la obra

El primer arranque, en verde

El agente se dejó fuera antes a propósito. Ahora sí: su propia copia del .env, nunca compartida.

El .env

Copia propia

OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODELO=anthropic/claude-sonnet-5

Arrancar

Y mirar el log

docker compose up -d --build agente
docker compose logs -f agente

Log de arranque sano: ... INFO app.agente: Agente autónomo arrancado. intervalo=30s limite_por_ciclo=20 canales=['email', 'whatsapp'] ticketing=AdaptadorSimulado

Manos a la obra

Dos gotchas del arranque

Uno cuesta dinero si se pasa por alto. El otro es correcto, aunque parezca un fallo.

El modelo por defecto

Opus 5, no Sonnet 5

El default del código es anthropic/claude-opus-5. Sin fijarlo a mano en el .env, facturáis contra el modelo caro sin enteraros.

La condición de parada dura

FaltaCredencial

Sin OPENROUTER_API_KEY, el contenedor se cae — docker compose ps lo marca Exited (1). Es correcto, no un fallo.

PARTE 03

Canales
reales

WhatsApp, Jira y correo — el momento en que el agente deja de mirar solo datos de prueba.

Canales reales

WhatsApp: el MCP que no escribisteis vosotros

OpenWA ya es un servidor MCP. La pregunta no es si tiene MCP: es si esa superficie os sirve.

18 · 0
MCP_READONLY sin definir — vuestro agente lee, no contesta
39 · 12
MCP_READONLY=false — y entre las nuevas, GroupCreate

CHANGELOG de OpenWA, entrada #601: "previously an unset MCP_READONLY defaulted to read-write, so enabling MCP_ENABLED silently exposed message-send and group tools." Un proyecto de 12.000 estrellas lo hizo, lo detectó y lo arregló.

Canales reales

Detrás de vuestro propio puerto

El camino elegido hoy: envolver OpenWA, no enchufarlo directo.

Camino A39 herramientas, dos superficies distintas, y operaciones que no elegisteis vosotros.
→
Camino B, el de hoy4 herramientas de canal. El mismo patrón que el conector de ticketing al no publicar el cierre.

Plan B: si a los 5 minutos de pedir el QR la sesión no está en ready, seguimos con Jira y dejamos WhatsApp para el final. Usad siempre un número nuevo y dedicado — nunca el de la empresa: riesgo real de baneo.

Canales reales

Jira: lo único probado hoy contra algo real

Con estas mismas variables, hoy el agente creó KAN-2 de verdad.

El .env

Token y proyecto

TICKETING_BACKEND=jira
JIRA_URL=...
JIRA_PROJECT_KEY=KAN

El desajuste que os puede pasar

Los estados, en español

El servidor pide "En curso", "Pendiente cliente". Si vuestro flujo por defecto está en inglés, esa transición no existe con ese nombre — el propio error lista los nombres reales.

El cierre sí está cubierto en los dos idiomas. Los estados intermedios, no.

Canales reales

Correo: el único sin verificar de punta a punta

Dilo antes de que alguien lo pregunte.

El .env

Contraseña de aplicación

GMAIL_USUARIO=...
GMAIL_CONTRASENA_APLICACION=xxxxxxxxxxxxxxxx
GMAIL_CARPETA=Soporte

Nunca INBOX

Aunque sea el valor por defecto

El conector trata todo lo no leído de esa carpeta como aviso de soporte. Mezclaría vuestro correo interno con el de un cliente.

Esto NO está verificado hoy de punta a punta — credenciales inconsistentes en la ventana de prueba. Si falla en sala, se reprueba y se sigue con lo que sí funcione.

PARTE 04

Reglas de
escalado

El ejercicio central de la sesión. Primero medís el suelo que ya existe. Después escribís el vuestro.

El ejercicio central

Primero, medir lo que ya no se puede saltar

Contra los 4 MCP, con Claude Code interactivo, dos peticiones — y las dos fallan, por motivos distintos.

"Cierra el ticket, está resuelto"

Rechaza, con motivo escrito

El puerto sí sabe cerrar —hay un test que lo demuestra— pero el conector no publica esa capacidad.

"Asigna la IP a la impresora"

No hay herramienta para eso

El conector del portal es de solo lectura.

Es la misma regla, importada — no copiada — en el catálogo que acabáis de leer.

El ejercicio central

Vuestras reglas — en prosa, no en código

Un único sitio: la constante INSTRUCCIONES de app/agente/service.py. Nada de tocar catalogo.py.

Deriva a una persona SIEMPRE que:

· El aviso describa un corte de servicio que afecte a una sede entera.
· El cliente escriba por segunda vez sobre el mismo asunto en menos de 24 h.
· La consulta pida modificar el inventario (asignar, liberar o reservar una IP).
· El aviso llegue fuera del horario de guardia y sea de prioridad crítica.
· No estés seguro. La duda se deriva.

Compruébalo con tres avisos: uno que deba resolverse solo, uno que deba derivar por regla, uno ambiguo. Si el ambiguo no se deriva, la regla está mal escrita — no el agente.

El ejercicio central

Verificar en dos sitios, nunca en uno

Un log en verde sin la segunda mitad no es una verificación: es una promesa.

Sitio 1

Los logs

docker compose logs -f agente
... Aviso msg-104: 7 pasos, desenlace_registrado=True

Sitio 2

Fuera del contenedor

curl .../soporte/metricas, el panel /portal/soporte, o el ticket en Jira, o el git diff del simulado.

Meta mínima: un aviso con desenlace_registrado=True, y ese mismo desenlace visible desde fuera del contenedor.

El ejercicio central

Cuatro defectos, todos con los tests en verde

Ninguno se cazó leyendo código. Los cuatro salieron de arrancar y mirar la respuesta.

Sin unidad

tasa_resolucion

Con 0.75, un modelo escribe "0,75 %". Un módulo devolvía el porcentaje; el test, la fracción.

Homónimos

Dos canales, un nombre

Uno desaparecía en silencio. El agente concluye que ese cliente no tiene avisos.

Por nombre, no UUID

La sesión de OpenWA

GET /sessions/<id>/messages da 200 y lista vacía para cualquier identificador.

Es vuestro

La colisión de /clientes

Dos módulos de la S3 registraron la misma ruta. FastAPI no avisa: la primera gana, la otra muere en silencio.

El ejercicio central

El número, sin maquillar

Con datos de prueba de hoy — antes de escribir vuestras reglas propias.

0 %
Resuelto por el agente solo
100 %
Derivado a humano

No es que falle: está diseñado para derivar ante la duda. El número real, con vuestros datos y vuestras reglas, es el que vais a ver ahora mismo en el panel — la métrica que pedisteis en el baseline.

Antes de irnos

Lo que os lleváis puesto

Lo que sabe

No es lo que vendéis

Cualquier agente puede sonar convincente. Lo que compra confianza es lo que no puede alcanzar.

Se verifica

Midiendo, no confiando

Log + panel, o log + Jira. Nunca uno sin el otro. "Parece que va" no es "va".

La próxima

S5 · Despliegue y roadmap

Vuestro agente corre en vuestro portátil. En la S5 sale de ahí.

Habéis levantado un servicio que corre solo, con al menos un canal real conectado, y sus límites por escrito. La pregunta que os lleváis: "¿qué es lo que este agente no puede hacer, y quién lo decidió?"

El puente a la S5

El hilo de las cinco sesiones

Dicho hoy, para que quede explícito.

"La S2 os dio la herramienta. La S3 os dio la base de datos de verdad de vuestros clientes. La S4 le pone la boca —y le pone los límites— y hace que corra sola. La S5 la saca de vuestro portátil."

Fin de la sesión 4

Preguntas

Sobre todo las de
"a mí el contenedor no me arrancó".

S5 · Despliegue y roadmap|PUESTA EN MARCHA
← → para navegar