Varias sesiones y varias VMs¶
Desde 0.14.0.
Un MicroVM con tu agente atiende pocas sesiones a la vez. Cuando necesitas más,
deploy_agent() te da una flota (AgentFleet): varios MicroVMs de la misma
imagen y un reparto de sesiones que hace el cliente, sin ningún servidor de
Rayito en medio. Con los valores por defecto es un solo VM, igual que
AgentProject.deploy().
Las opciones¶
Cada opción tiene su clave en rayito-agent.toml, su argumento en
ServeOptions y ScalingOptions, y su bandera en rayito agent deploy.
| Opción | Qué hace | Por defecto | Límite |
|---|---|---|---|
max_concurrency (--max-sessions) |
sesiones a la vez en un VM | 1 | las conexiones del tamaño del VM: 7 con 2 GB, 15 con 4 GB, 31 con 8 GB |
queue_max (--queue) |
peticiones que esperan en la cola del host cuando el VM está lleno | 0 (un 429 al momento) | 256 |
queue_timeout_seconds (--queue-timeout) |
lo que espera una petición en la cola | 30 s | 900 s |
min_vms (--min-vms) |
VMs siempre vivos | 1 | |
max_vms (--max-vms) |
VMs como máximo | 1 | 64 |
warm_vms (--warm) |
plazas aparcadas (suspendidas) que absorben un pico | 0 | max_vms - min_vms |
sessions (--sessions) |
shared (varias sesiones por VM) o dedicated (un VM por sesión) |
shared |
|
scale_in_after_seconds (--scale-in-after, desde 0.17) |
inactividad tras la que un VM por encima de min_vms se termina solo |
900 s | 60 s |
idle (--idle) |
inactividad tras la que un VM se suspende y se reanuda al llamarlo | sin suspensión | ver Pausar y reanudar |
El límite de max_concurrency sale de las conexiones que admite el MicroVM
(8 por vCPU de base) menos una que el servicio se reserva para /ping: con 1 GB
se cuenta una vCPU, un valor conservador. Subir la memoria sube el tope.
Lo más corto es el fichero (las claves que no pongas las decide la detección):
# rayito-agent.toml
[serve]
max_concurrency = 2
queue_max = 4
[scaling]
min_vms = 1
max_vms = 3
warm_vms = 1
sessions = "shared"
[image]
memory_mib = 4096
from rayito import AgentDeployOptions, ScalingOptions, deploy_agent
fleet = deploy_agent(
"./mi-agente",
options=AgentDeployOptions(
bucket="amzn-s3-demo-bucket",
scaling=ScalingOptions(min_vms=1, max_vms=3, warm_vms=1, sessions="shared"),
),
)
try:
first = fleet.invoke("Hola")
again = fleet.invoke("¿Qué te dije?", session_id=first.session_id)
print(again.text)
finally:
fleet.stop()
import { deployAgent, ScalingOptions } from "rayito";
const fleet = await deployAgent("./mi-agente", {
options: {
bucket: "amzn-s3-demo-bucket",
scaling: new ScalingOptions({ minVms: 1, maxVms: 3, warmVms: 1, sessions: "shared" }),
},
});
try {
const first = await fleet.invoke("Hola");
const again = await fleet.invoke("¿Qué te dije?", { sessionId: first.sessionId });
console.log(again.text);
} finally {
await fleet.stop();
}
Sesiones compartidas y dedicadas¶
shared(por defecto): una sesión nueva va al VM con hueco; si todos están llenos, a una plaza caliente; si no hay, a un VM nuevo mientras no llegues amax_vms; y si ya estás enmax_vms, espera en la cola.dedicated: cada sesión nueva tiene su propio MicroVM y un VM nunca atiende a dos sesiones. Es lo que quieres si el agente guarda estado en el disco del VM o si no te fías de que dos usuarios compartan proceso.
El id de sesión y volver a una conversación¶
Con max_vms > 1, AgentResult.session_id es un identificador que nombra su
VM (rs1.<sandbox>.<sesión>): pasarlo de vuelta lleva la llamada al mismo VM sin
consultar a nadie. Con max_vms = 1 el id de sesión no cambia. Desde otro
proceso, AgentFleet.connect(nombre, access_token=...) reconstruye la flota con
los MicroVMs no terminados de su imagen sin reanudar ninguno, y un id de sesión
de la primera llamada sigue llevando a su VM. Para que las respuestas de la
flota reconectada también devuelvan el asa, pasa el mismo scaling con el que
la desplegaste.
Desde 0.17, una flota reconectada sólo con su nombre y el token también puede
crear VMs y arrancar plazas calientes: lee la receta
que guardó deploy_agent(). Sin receta (una flota de 0.16, o desplegada con
--no-save-recipe) y sin project=, la flota enruta y consulta pero no puede
crear un VM: la primera sesión que necesite uno lanza
InvalidArgumentException; pasa entonces el proyecto
(AgentProject.detect(carpeta) o AgentProject.load(...)).
Un id de sesión que elijas tú (el hilo de un chat) sólo se recuerda dentro del
proceso que desplegó la flota. Para encontrarlo desde otro proceso en modo
dedicated, activa el índice opcional de metadatos
(Índice de metadatos, con su
coste); sin él, un id desconocido empieza un VM nuevo y la respuesta trae el
identificador enrutable que debes guardar.
La receta de la flota¶
Desde 0.17.0.
Tras arrancar la flota, deploy_agent() (y rayito agent deploy) guarda su
receta en Parameter Store, en /rayito/agents/<nombre>/recipe: el proyecto
tal como quedó tras las banderas (--env, --secret-env-from, --allow-out,
las pasarelas), la versión de la imagen, los presets de las pasarelas, el
escalado y si se renuevan las claves. Sólo lleva nombres de secretos, nunca
sus valores ni el access token.
Con ella, AgentFleet.connect(nombre, access_token=...) desde cualquier
proceso (o rayito agent invoke|status|stop <nombre>) crea VMs iguales a los
del despliegue, abre las plazas calientes con sus pasarelas y status() da el
escalado real. Un scaling= o project= explícitos ganan a la receta, y
use_recipe=False (useRecipe: false) la ignora: la flota es la de 0.16.
- Estado.
FleetStatus.scaling_source(scalingSource) dice de dónde sale el escalado:explicit,project,recipeodefault. Condefault(sin proyecto ni receta)rayito agent statusimprimemembers: N (escalado desconocido: sin proyecto ni receta)en vez de unmin 1, max 1que no describe la flota. - Borrado.
fleet.stop()yrayito agent stop <nombre>borran la receta si este proceso la guardó o la leyó;stop(keep_recipe=True)(--keep-recipe) la conserva. - Avisos. Un fallo al guardarla nunca hace fallar el despliegue: deja un
aviso con un código (
recipe_access_denied,recipe_not_saved,recipe_too_large) en el log y enwarningsde--json. Al leerla, sin permiso esrecipe_access_denied, y una receta ilegible o un Parameter Store que no responde (sin endpoint de VPC para SSM, limitación de tasa) esrecipe_unreadable. Una receta que pedía renovar la clave de Bedrock no la activa en quien conecta: sinrenew_gateway_keys=Trueel aviso esrecipe_renewal_ignoredy no se renueva nada. Un nombre de flota.o..no tiene receta (recipe_not_saved); en todos los casos la flota sigue como en 0.16, yrayito agent stop <nombre>termina sus VMs (si tampoco puede borrar la receta, lo dice al final con un error). Trasstop, Parameter Store tarda unos 30 s en dejar crear otro parámetro con el mismo nombre: undeployinmediato avisarecipe_not_saved. - Tamaño. Una receta va en JSON si cabe en 4 096 bytes (un parámetro
estándar) y, si no, comprimida; si ni así cabe (instrucciones muy largas), el
aviso es
recipe_too_largey se borra la anterior. --envy los secretos.rayito agent deployrechaza un--envcuyo nombre parece un secreto (*_TOKEN,*_KEY,*_SECRET,*_PASSWORD..., con un segmentoPASS,PASSWD,PAT,CREDENTIALoCREDENTIALS, comoDB_PASSoGITHUB_PAT, o conAUTHoBEARERcomo último segmento o junto aTOKEN,KEYoHEADER, comoX_AUTHoAUTH_HEADER;AUTH_MODEoAUTHOR_NAMEpasan): usa--secret-env NOMBRE=<secreto>o--secret-env-from <secreto>, así la receta sólo guarda el nombre. Es un cambio incompatible con 0.16 para*_SECRET. Ojo: la regla es de la bandera--env; los valores deenvdel proyecto ([env]derayito-agent.tomloenv=de la biblioteca) se guardan tal cual en la receta, legibles por quien tengassm:GetParametersobreparameter/rayito/agents/*. Nada secreto enenv, tampoco una URL con credenciales: va ensecret_env.- Quién puede escribirla. La receta decide la imagen, la salida a red,
envy el upstream y los secretos de cada pasarela de todo proceso que conecta por nombre: escribir enparameter/rayito/agents/*equivale a poder desplegar. Por esoSandboxLauncherPolicysólo la lee y la borra, yssm:PutParameterestá enImagePublisherPolicyyCallerPolicy.
Coste y activación
- Activa:
deploy_agent()/rayito agent deploycontra MicroVMs, por defecto. Es la única función de Rayito encendida por defecto (ADR-034): el parámetro estándar no cuesta y su petición de KMS entra en la capa gratuita. - Recursos y llamadas AWS: un parámetro
SecureStringestándar por flota (PutParameteral desplegar,GetParameteral reconectar sin proyecto,DeleteParameteral parar), cifrado con la clave gestionadaaws/ssm. - Coste aproximado: los parámetros estándar no tienen cargo; cada escritura y cada lectura es una petición de KMS (20 000 gratis al mes, después $0,03 por 10 000). Consultado el 2026-10-22: Systems Manager y KMS.
- IAM:
ssm:PutParameter(quien despliega:CallerPolicyoImagePublisherPolicy),ssm:GetParameteryssm:DeleteParametersobreparameter/rayito/agents/*(la sentenciaAgentRecipede IAM); ningún permiso de KMS. - Cómo apagarla:
AgentDeployOptions(save_recipe=False)(saveRecipe: false,--no-save-recipe). - Ejemplo:
rayito agent deploy ./mi-agente --no-save-recipe.
Plazas calientes y pasarelas¶
Una plaza aparcada nace sin las pasarelas del proyecto. Desde 0.17, la flota
las abre al tomarla, también cuando la plaza la aparcó otro proceso (una flota
reconectada con la receta o con project=), con el mismo camino que
SandboxPool.take(gateways=); si no puede abrirlas, termina la plaza y usa
otro VM. Antes, una flota reconectada podía arrancar el agente sin la pasarela
del modelo y había que desplegar con --warm 0.
La cola y el 429¶
Con max_concurrency = 1 y queue_max = 1, de tres peticiones simultáneas
una corre, una espera su turno y la tercera recibe 429 con Retry-After. El
cliente de la flota respeta ese Retry-After hasta queue_timeout_seconds y,
pasado el plazo, lanza CapacityException (code = "agent_capacity";
CapacityError en TypeScript).
Reducir, suspender y reiniciar¶
- Reducir: los VMs por encima de
min_vmsllevan un plazo descale_in_after_secondsque la flota renueva cada vez que los usa; un VM ocioso se termina solo (elrayddel VM ya sabe terminarse), sin ninguna llamada del plano de control. Mientras atiende alguna invocación, el plazo nunca es menor que el de la petición, y sólo vuelve ascale_in_after_secondscuando termina la última, también si falla. Los cambios de plazo de cada VM van en orden: soltar una invocación nunca acorta el de otra que empezó entre medias. Un VM acortado que pasa a ser base recupera toda su vida hasta el tope derayd, aunque la flota no lo conozca (pide el máximo yraydresponde con su tope); desde 0.17.1. Para queraydacepte ese plazo, una flota que puede crecer (max_vms > min_vms) crea todos sus VMs y plazas con plazo lógico (on_timeout="kill"ymax_lifetime=serve.timeout_seconds + 60); un VM base sigue viviendoserve.timeout_seconds. Una flota de tamaño fijo crea sus VMs como siempre. Hasta 0.16 la reducción no ocurría: los VMs se creaban sin plazo lógico yraydrechazaba elset_timeout. Un VM creado así (por 0.16) no se reduce: la flota avisa una vez; termínalo constop()o vuelve a desplegar. - Suspender:
idlesuspende un VM sin uso y lo reanuda al llamarlo; un VM suspendido no cobra cómputo, sólo el almacenamiento de su snapshot. - Reiniciar: si la app del usuario se cae, el host la relanza con espera
creciente (hasta 3 veces en 5 minutos). Mientras tanto
/pingdiceHealthyBusyy las invocaciones reciben503conRetry-After. Pasado el presupuesto,/pingfalla, el marcador de fallo llevaapp_crashloopy la flota sustituye el VM; las llamadas en curso de ese VM fallan conSandboxException(code="agent_vm_lost").
Coste y activación
- Activa:
deploy_agent()con más de un VM posible (max_vms > 1) o con plazas calientes; con los valores por defecto es un solo VM. - Recursos y llamadas AWS:
min_vmsMicroVMs en marcha,warm_vmsplazas aparcadas en un pool, y los VMs extra que cree el reparto hastamax_vms;list-microvmspara encontrar los miembros de la flota. - Coste aproximado: cada VM en marcha cuesta lo de un despliegue (≈ $0,126 por hora con 2 GB, $0,25 con 4 GB); una plaza aparcada sólo paga el almacenamiento de su snapshot (≈ $0,011 por día); un VM suspendido, igual. Precios de lista, us-east-1, consultados el 2026-10-06: Precios.
- IAM: los de
Sandbox.create(),Sandbox.list(),Sandbox.connect()ySandbox.kill(); con índice de sesiones, los del índice de metadatos. - Cómo apagarla:
fleet.stop()orayito agent stop <nombre>terminan todos los VMs de la flota (y borran su receta); sin llamar adeploy_agent()no se crea nada, y los valores por defecto no dejan plazas ni VMs extra. Por nombre sólo se terminan VMs que son de un agente: unHealthsin token lee las etiquetas de losRUNNING, y si la imagen tiene alguno que no las lleva, no se termina ninguno de los suspendidos (no se pueden sondear sin reanudarlos). - Ejemplo:
deploy_agent("./mi-agente", options=AgentDeployOptions(scaling=ScalingOptions(max_vms=3))).
Medido en AWS real
En la aceptación de 0.14 (2026-10-16, us-east-1, Claude Haiku 4.5) los quince
agentes de ejemplo se desplegaron sin tocar su código, en Python y en
TypeScript, sin opciones y con 4096 MiB, 2 sesiones por VM, cola de 2 y de
1 a 2 VMs con una caliente. Cinco llamadas a la vez se repartieron entre dos
VMs; por la URL de un solo VM, el sobrante recibió 429. Reanudar un VM
suspendido y hacer la primera llamada tardó entre 1,9 y 4,0 s. Todo el
ciclo (65 imágenes) costó unos $5 y terminó con el inventario de AWS igual
que al empezar. Detalle en AWS_API_NOTES.md (Q170 a Q172).
En AgentCore no hay flota¶
Con --target agentcore el agente corre en
Amazon Bedrock AgentCore Runtime, que crea una
microVM por sesión y escala solo: min_vms, max_vms, warm_vms y el resto
de las opciones de esta página que dimensionan una flota no existen allí. Si las
pasas en la llamada es un error (option_not_supported) y, si vienen del
fichero de proyecto, un aviso: Rayito no las aproxima en silencio. invoke(),
stream() y el session_id sí son los mismos. La tabla completa está en
Desplegar en AgentCore.
Ver también¶
- Despliega tu agente: la guía completa y el fichero de proyecto.
- Desplegar en AgentCore: el mismo agente en Amazon Bedrock AgentCore Runtime.
- Límites: las cuotas y los topes de esta página.
- Pool: las plazas aparcadas que usa una flota.