kagent + Agent Substrate: agentes en producción con sandboxes que duermen
El agente que pasa el 95% del día esperando
En mi último post sobre kagent les conté cómo desplegar agentes de IA como recursos nativos de Kubernetes. En ese momento Agent Substrate aún no estaba disponible; hoy sí, y cambia por completo la forma en que kagent ejecuta los agentes.
Un agente pasa más del 95% de su tiempo esperando al LLM o a un humano. En el modelo “un agente, un Pod”, ese Pod reserva CPU y memoria 24/7. Con cientos de agentes en sandbox, el billing cloud crece y sigue creciendo.
Agent Substrate: la plomería
Agent Substrate es un runtime y scheduler open source que corre sobre Kubernetes, fundado por Google y mantenido junto con Solo.io (repo). Cada sesión de agente es un Actor, y un pool chico de Pods precalentados, los Workers, ejecuta solo los Actors que están trabajando. Cuando un Actor queda ocioso, Substrate guarda su RAM y su filesystem en S3 o GCS y libera el Worker; con la siguiente petición lo restaura en cualquier Worker libre.
- ate-api-server: scheduler y ciclo de vida de los Actors.
- atenet-router: DNS + proxy L7 que reanuda el Actor y le enruta la petición.
- atenet-egress: salida default-deny que inyecta las credenciales.
- atelet: DaemonSet que sube y baja los snapshots.
El proyecto anuncia hasta 1.000 Actors por Worker y una demo de ~250 Actors sobre 8 Pods. Son objetivos de diseño; todavía no hay benchmarks publicados.
kagent 1.0: qué cambió
kagent 1.0 deja atrás los Deployments y ejecuta todas las sesiones sobre Substrate (The future of kagent). La migración desde 0.x es manual. El modelo nuevo vive en el grupo api.kagent.dev (core concepts):
| Recurso | Qué define | Dónde vive |
|---|---|---|
| Harness | Cómo se ejecuta: motor, imagen, WorkerPool y bucket | CRD |
| AgentTemplate | Qué hace: modelo, prompt, tools MCP y skills | CRD |
| Agent | Une un AgentTemplate con un Harness | CRD |
| Session | Una conversación en curso | PostgreSQL |
| Actor | El proceso en sandbox que la ejecuta | Substrate |
El controller compila cada Agent en un ActorTemplate inmutable, y cada Session queda fijada a esa revisión, igual que un ReplicaSet con su pod-template-hash.
Arquitectura: cómo encajan las piezas
kagent es la capa con la que hablan desarrolladores y plataformas; Substrate decide dónde corre cada agente y cuánto cuesta mantenerlo.
Solo los Actors con un turno en curso ocupan un Worker. El resto duerme en el object store hasta que llega la siguiente petición y el router lo reanuda.
Manos a la obra con kind - dejamos EKS para la próxima ;)
Para probarlo no hace falta una nube: alcanza con kind. Sigo la guía oficial de kagent 1.x y algunos consejos de la guía con kind del equipo de kagent. Ojo: kagent 1.0 está en RC y Substrate es pre-1.0, así que esto es para laboratorio.
Requisitos:
- Unos 8 vCPU y 16 GB de RAM para Docker. Yo lo probé en una Mac con Apple Silicon y Docker Desktop, y funcionó con un fix de una línea que explico en los retos reales.
- kind v0.32 o superior y la imagen
kindest/node:v1.37.0, porque Substrate usa las APIs de Pod Certificates de Kubernetes 1.37. - Una API key gratuita de Google AI Studio (aistudio.google.com), sin tarjeta de crédito. Vamos a usar Gemini 3.8 Flash, el Flash más reciente, que entra en el free tier.
1
2
3
4
5
6
7
kind create cluster --name kagent --image kindest/node:v1.37.0
kubectl api-resources --api-group=certificates.k8s.io # deben aparecer podcertificaterequests y clustertrustbundles
curl https://raw.githubusercontent.com/kagent-dev/kagent/refs/heads/main/scripts/get-kagent | bash -s -- --version v1.0.0-rc1
export GOOGLE_API_KEY="<tu-api-key-de-ai-studio>"
kagent install --namespace kagent # instala Substrate, PostgreSQL de desarrollo y kagent
kubectl get workerpools -n kagent # kagent-default debe tener READY=1
Después creamos un ModelConfig que apunte a Gemini, como indica la documentación de kagent para Gemini:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubectl create secret generic kagent-gemini -n kagent --from-literal GOOGLE_API_KEY=$GOOGLE_API_KEY
kubectl apply -f - <<EOF
apiVersion: api.kagent.dev/v1alpha3
kind: ModelConfig
metadata:
name: gemini-model-config
namespace: kagent
spec:
apiKeySecret: kagent-gemini
apiKeySecretKey: GOOGLE_API_KEY
model: gemini-3.8-flash
provider: Gemini
gemini: {}
EOF
Dos cosas a tener en cuenta del free tier: tiene límites de peticiones por minuto y por día, y Google puede usar esas peticiones para mejorar sus productos, así que no le pasen datos sensibles. Si gemini-3.8-flash responde que está saturado o se agota la cuota, cambien a gemini-3.5-flash-lite, que tiene su propio contador.
En kind, los snapshots van al object store que trae el chart de Substrate (RustFS), así que no hay que configurar ningún bucket. El primer agente son tres recursos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: api.kagent.dev/v1alpha3
kind: Harness
metadata: { name: sre-harness, namespace: kagent }
spec:
kagent: {}
workload:
image: ghcr.io/kagent-dev/kagent/golang-adk@sha256:d20d411759d7bb549f00b7d6e8d669b91694aeb57dce847dcd2cbb68aa0d45f3
args: ["-filepath", "/data/config"] # fix: ver "Los retos reales"
substrate:
workerPoolRef: { name: kagent-default }
snapshotPolicy: { location: s3://ate-snapshots/kagent/ }
---
apiVersion: api.kagent.dev/v1alpha3
kind: AgentTemplate
metadata: { name: sre-assistant, namespace: kagent }
spec:
modelConfig: { name: gemini-model-config }
systemPrompt: Eres un SRE conciso. Responde con comandos concretos.
---
apiVersion: api.kagent.dev/v1alpha3
kind: Agent
metadata: { name: sre-agent, namespace: kagent }
spec:
template: { ref: { name: sre-assistant } }
harnessRef: { name: sre-harness }
1
2
3
kubectl apply -f sre-agent.yaml
kubectl get agents.api.kagent.dev -n kagent # esperen a que sre-agent esté Ready (~1 min mientras crea el golden snapshot)
kubectl port-forward -n kagent service/kagent-ui 8082:8080
A partir de acá todo pasa en la UI de kagent, en http://localhost:8082.
En Agents, la pantalla resume el modelo de 1.0: un AgentTemplate (qué hace) más un Harness (dónde y cómo corre) forman un Agent, y cada chat es una Session que corre como un Actor de Substrate. En la pestaña Harnesses aparece nuestro sre-harness con el runtime kagent y la imagen fijada por digest.
En Substrate está la parte que más me gusta. Con el agente ya creado, el golden snapshot está listo (Templates ready 1/1), pero Actors running 0/1 y Workers busy 0/1: el agente existe y no ocupa nada. El pool kagent-default corre la imagen ateworker-gvisor:v1.0.0-rc1, y el template sre-agent aparece en fase Ready con su golden snapshot.
Una forma sencilla de probarlo:
- Abran
sre-agenty pregunten: “How do I troubleshoot a pod in CrashLoopBackOff?”. - Mientras responde, refresquen Substrate: Workers busy pasa a 1/1. Al terminar el turno vuelve a 0/1, porque el Actor quedó suspendido en el object store.
- Pregunten: “What if the pod is in another namespace?”. La respuesta llega con el contexto intacto, porque Substrate restauró el Actor desde su snapshot.
- Bonus: el ícono de guardar junto a la caja de mensajes crea un checkpoint, que la UI llama Snapshot. Desde esa marca pueden hacer Fork y arrancar una conversación nueva exactamente desde ese punto.
Dos consejos de la guía con kind: hagan el laboratorio de una sola vez, porque los certificados internos pueden expirar si el clúster queda inactivo cerca de 24 horas, y bórrenlo al terminar con kind delete cluster --name kagent.
Cuando quieran pasar a EKS, los cambios principales son un clúster 1.37, snapshots en S3 con EKS Pod Identity y PostgreSQL en RDS.
Los retos reales
- Deriva de versiones: las guías mezclan
AgentHarnessv1alpha2 conHarnessv1alpha3 y charts de Substrate de 0.0.8 a 0.2.0. Incluso elapiVersiondel ModelConfig cambió: la documentación dicekagent.dev/v1alpha3, pero en 1.0.0-rc1 esapi.kagent.dev/v1alpha3. Regla de oro: no actualicen Substrate por su cuenta; usen la versión que fija cada release de kagent (versiones) y verifiquen conkubectl api-resources. - El egress hace MITM del TLS:
atenet-egresstermina HTTPS para filtrar por hostname. Esto rompe el certificate pinning y bloquea UDP, HTTP/3 y SSH. Los rechazos solo aparecen enkubectl logs -n ate-system deploy/atenet-egress -c agentgateway. - Credenciales: el gateway usa la identidad SPIFFE del Actor y reemplaza un header placeholder por la API key real. Si la petición no trae el placeholder, sale sin credencial y el LLM responde 401.
- EKS privados: los nodos descargan los assets de gVisor desde una URL pública de GCS; sin salida a Internet hay que alojar una copia.
El que me pasó a mí: el agente nunca despertaba. El golden snapshot fallaba con GoldenActorCrashed, porque el wakeup probe (/readyz en el puerto 8081) daba “connection refused” a los 30 segundos y Substrate reintentaba en loop. Los logs del atelet mostraban arm64 y gVisor, así que primero sospeché de mi Mac. Pero no era eso.
La pista real estaba en los logs del Worker (kagent-default-*):
1
materialize agent config in /config: ... mkdir /config: permission denied
El binario /app corre como el usuario no root 65532 e intenta crear /config en la raíz del filesystem, que pertenece a root. Crashea antes de levantar el servidor HTTP, así que el probe nunca recibe respuesta. Con /app --help vi que acepta -filepath, y lo apunté a /data, el durable dir que Substrate monta y que sí es del usuario 65532:
1
2
3
workload:
image: ghcr.io/kagent-dev/kagent/golang-adk@sha256:d20d41...
args: ["-filepath", "/data/config"]
Después de borrar y recrear el agente para forzar un nuevo golden snapshot, arrancó a la primera. La lección como SRE: antes de culpar a la plataforma, lean los logs del Worker, no solo los del control plane.
Conclusión
kagent pone la experiencia de desarrollo y Substrate la plomería. Con kind lo prueban en una tarde, y con EKS 1.37 ya disponible el salto a AWS es corto. las posibilidades son muchas, pero si hay algo claro aqui es que este tipo de herramientas estan definiendo nuevos estandares sobre coom correr agentes de una forma segura y eficiente.
Definitivamente el futuro de los workloads agénticos está en Kubernetes.



