Si ejecutás una gateway LLM como proceso de larga duración, cada alta de modelo o cambio de routing se convierte en un potencial evento de despliegue. Esa es la unidad de cambio equivocada. La configuración no es código, y un pipeline de CI es la herramienta equivocada para enviar un system prompt nuevo a las cuatro de la tarde mientras el agente de un cliente se comporta mal.
El patrón al que vuelvo es mecánicamente simple: servicio ECS para la gateway, Parameter Store como registro de modelos, un poll loop en el medio. Casi todo el esfuerzo de diseño consiste en decidir qué cuenta como configuración y qué cuenta como código, y mantener ambos en rutas de despliegue distintas sin excepciones.
SSM como registro de modelos
aws ssm put-parameter.DIAL Core, DIAL Chat y el DIAL Bedrock Adapter guardan sus rosters de modelos y tablas de routing en SSM Parameter Store. Los contenedores leen esos valores al arrancar y verifican cambios en un intervalo configurable — sin rebuild, sin reemplazo de la task de ECS.
Esto separa dos responsabilidades que habitualmente se confunden: el provisionamiento de infraestructura (gestionado por Terraform y aplicado a través de CI) y la configuración de modelos (gestionada por operadores con aws ssm put-parameter). Cada uno tiene su propio ciclo de despliegue y su propio radio de impacto. La imagen es inmutable. La configuración es mutable. El poll loop es la API entre ambos.
El layout de parámetros al que llegué:
/dial/<env>/models/<model-id> # JSON: provider, region, weights, agreement
/dial/<env>/routes/<route-id> # JSON: model-id, priority, fallback chain
/dial/<env>/prompts/<prompt-id> # JSON: template, variables, version pin
/dial/<env>/limits/<tenant-id> # JSON: tokens/min, requests/min, RPM ceiling
Cada hoja es un documento JSON único. El tier "Advanced" de SSM te da 8 KB por parámetro e historial de versiones de fábrica — los dos importan para este caso de uso. Cuando algo se rompe, aws ssm get-parameter-history es tu audit log.
El reload loop, en 30 líneas
El poll loop es poco glamoroso y ese es el punto. Pseudocódigo:
# dentro del proceso de la gateway
import asyncio, json, boto3, hashlib, logging
ssm = boto3.client("ssm")
log = logging.getLogger(__name__)
current_hash: dict[str, str] = {}
async def reload_loop(prefix: str, interval: int = 30):
while True:
try:
params = ssm.get_parameters_by_path(
Path=prefix, Recursive=True, WithDecryption=True
)["Parameters"]
for p in params:
h = hashlib.sha256(p["Value"].encode()).hexdigest()
if current_hash.get(p["Name"]) != h:
apply(p["Name"], json.loads(p["Value"]))
current_hash[p["Name"]] = h
log.info("reloaded", extra={"param": p["Name"], "version": p["Version"]})
except Exception:
log.exception("reload failed; keeping last-known-good")
await asyncio.sleep(interval)
Tres cosas para notar. Primero, el loop nunca tira abajo el proceso ante un fallo — loguea y reintenta. Last-known-good en memoria siempre es mejor que un reinicio durante un incidente. Segundo, la comparación por hash hace que un parámetro sin cambios sea un no-op aunque SSM lo haya devuelto. Tercero, cada recarga escribe una línea de log estructurada, que es la única superficie de auditoría que los operadores necesitan a las tres de la mañana.
Qué cambia cuando dejas de tratar los prompts como código
El efecto inmediato es la velocidad de iteración. Los equipos de agentes medían prompt a producción en horas con el modelo anterior — abrir un PR, conseguir review, esperar CI, esperar a que ECS drene y reemplace tasks. Con hot-reload, ese mismo cambio se reduce a minutos: aws ssm put-parameter --overwrite, esperar un intervalo de poll, ver la línea de log que confirma. La gateway no necesitaba saberlo — un evento de recarga es solo una consulta a un parámetro.
El efecto más sutil es la claridad operacional. Cuando falla un acuerdo de modelo o una regla de routing produce un resultado inesperado, la primera pregunta es "¿qué cambió?". Con la configuración respaldada en SSM, la respuesta es una sola llamada a la API: aws ssm get-parameter-history. Sin git blame, sin arqueología en logs de despliegue, sin correlacionar una revisión de task definition con un SHA de commit cruzando dos repos.
El modelo de proceso de larga duración también invierte la relación de radio de impacto. Una entrada de modelo mal configurada afecta únicamente al routing de ese modelo. Los operadores pueden hacer rollback de un parámetro sin tocar la definición de la task, la imagen ni el comportamiento de ningún otro modelo. La unidad de cambio es un único documento JSON, y la unidad de rollback es el mismo único documento JSON.
El trade-off: la consistencia eventual es real
La contra honesta es que este diseño es eventualmente consistente y el intervalo de poll es una superficie de fallo real. Si empujás un cambio de routing y un cliente golpea la gateway en el mismo segundo, le toca el routing viejo. Si empujás un prompt roto y te das cuenta veinte segundos después, veinte segundos de tráfico vieron el prompt roto. Un redeploy sincrónico no tiene esta propiedad — cuando las tasks nuevas están healthy, las viejas ya no existen.
Hice el trade por dos razones. La primera es que el alternativo redeploy tampoco es realmente sincrónico: tiene su propia ventana de propagación durante el drenaje de ECS, más el tiempo de rebuild, más la latencia de CI. La ventana del hot-reload está acotada por el intervalo de poll, que puedo afinar. La segunda es que la ruta de rollback es más rápida que la ruta de despliegue — empujar la versión previa del parámetro es una sola llamada CLI, mientras que revertir una task definition gestionada por Terraform implica un revert PR y otro ciclo de despliegue completo.
Si tu tolerancia a la consistencia eventual es cero, este patrón está mal para vos. Probablemente querés una gateway sincrónica con feature flags y cutover inmediato, asumiendo la ruta de despliegue más larga como costo. Para la mayoría de los equipos, la ventana de consistencia eventual medida en segundos está bien por debajo de la latencia humana de detección de "el prompt está mal", y el trade es favorable.
Contraargumento: pero el equipo de compliance quiere control de versiones
El pushback más fuerte que escucho es que los prompts de producción deberían vivir en Git porque Git es el sistema de registro que los auditores reconocen. La respuesta no es mantener los prompts en Git como ruta de despliegue; es espejar las escrituras de parámetros hacia un historial de Git de forma asincrónica, para que el registro auditable exista sin acoplar el despliegue al merge.
Una regla pequeña de EventBridge sobre eventos Parameter Store Change que commitea el nuevo parámetro a un repo de auditoría de solo lectura le da al equipo de compliance lo que quiere y a los operadores lo que necesitan. Son más piezas que "ponelo en Git", pero no acopla las responsabilidades equivocadas para entregar el artefacto correcto.
Así que
Si mantenés una gateway LLM y tus cambios de prompt pasan por CI, listá cada cambio que enviaste el mes pasado que tocó solo configuración y preguntate cuántos de ellos habrían ganado con una ruta de cinco minutos en vez de una de cincuenta. Después mirá los fallos que requirieron un redeploy completo para volver atrás. Las dos listas tienden a ser más largas de lo que la gente espera.
La migración es lo suficientemente chica como para prototipar en una semana: elegí un prompt, pasalo a SSM, agregá el reload loop, apuntá una ruta al valor resuelto desde SSM, mandalo. O el equipo de operaciones empieza a pedir el segundo prompt, o no lo hace y volvés atrás. El costo de averiguarlo es bajo.
[VERIFY: los parámetros SSM Standard tienen un techo de 4 KB; el tier Advanced extiende a 8 KB y es requerido para parameter policies — confirmar los límites exactos contra la página de pricing actual de AWS Systems Manager antes de citar textualmente.]
Comentarios
Enviando…