Una plataforma mínima viable para la experimentación de agentes de IA: pruebas A/B y ejecuciones en sombra
Cómo decidir si la versión B del agente debe reemplazar a A, utilizando definiciones de pipeline, asignación local, eventos de exposición, trazas, evaluación en entorno aislado, ejecuciones en segundo plano y medidas de control.
Fuentes: A minimum viable experimentation platform for AI agents
Juzgar si una nueva versión del agente supera a la en producción es más difícil de lo que parece. En fase de desarrollo, el proceso es manejable: se utilizan unos casos de prueba, se ejecutan ambas versiones, se analizan algunos registros, otro modelo evalúa los resultados y se decide si el cambio merece más trabajo. Las cosas se complican cuando el objetivo no es una solicitud o una sola llamada al modelo, sino un sistema cuyos componentes cambian según sus propios horarios e interactúan de maneras que no siempre son obvias.
Imagínese un agente de soporte al cliente compuesto por estas partes:
agent runtime
memory and retrieval
system prompt
model
tools
guardrails
application logic
Supongamos que la estrategia de recuperación cambia y la tasa de finalización de tareas aumenta en el conjunto de prueba. Es posible que el agente sea realmente mejor. También podría estar realizando más llamadas al modelo, lo que incrementa los costos en un 20 por ciento, comportándose de manera diferente con usuarios reales, o beneficiándose de algún otro componente que casualmente cambió en el mismo período. En ese caso, la tarea parece menos una evaluación de modelos y más un experimento.
A continuación se presenta un esbozo de la plataforma más sencilla en la que sería cómodo confiar para responder a una pregunta bastante básica:
should version B replace version A?
Esto solo tiene sentido a cierta escala. Un agente único, veinte casos de prueba revisados manualmente y unos pocos usuarios internos no necesitan la mayor parte de sus funcionalidades. La plataforma cobra sentido cuando varios equipos modifican modelos, prompts, memoria, herramientas e implementaciones de agentes de forma independiente, los experimentos se ejecutan sin cesar, y alguien debe aprobar eventualmente un cambio en producción con pruebas que sigan siendo claras incluso meses después. Una plataforma de agentes a nivel empresarial encaja en esa descripción.
Qué es realmente B
El primer problema es básico. Supongamos que un experimento indica:
A = agent-v17
B = agent-v18
¿Qué es exactamente agent-v18? Si la instrucción cambia durante el experimento, se reconstruye el índice de recuperación y la pasarela del modelo cambia su modelo predeterminado, lo que hace que la etiqueta diga muy poco sobre lo que se evaluó. Esto afecta especialmente a los agentes, ya que el comportamiento observado al final proviene de todo el proceso y no de una sola invocación a un modelo aislado.
Por lo tanto, la definición inmutable del proceso es el tratamiento que se está evaluando. Puede verse así:
pipeline:
name: support-agent
version: 18
components:
runtime:
artifact: agent-runtime@sha256:...
memory:
artifact: memory-service@sha256:...
configuration:
strategy: hybrid
top_k: 8
model:
route: support
model: provider/model-x
prompt:
artifact: sha256:...
tools:
- artifact: customer-lookup@sha256:...
- artifact: refund-tool@sha256:...
El formato exacto del artefacto apenas importa. Lo que realmente cuenta es que una variante pueda reconstruirse como una configuración de sistema, de modo que meses después alguien pueda indicar qué se suponía que debía ejecutar el usuario asignado a B. Esto también mantiene la plataforma de experimentación independiente de cualquier cambio que ocurra. Un experimento podría comparar:
model X vs model Y
Otro podría comparar:
memory top_k=5 vs top_k=8
Y un tercero podría enfrentar entre sí dos implementaciones de agentes completamente diferentes. Desde el punto de vista del experimento, cada una de estas es simplemente una variante del flujo de trabajo. Cuando comienza un experimento, esas definiciones se congelan. Si es necesario cambiar el tratamiento, se crea una nueva iteración del experimento, de modo que el significado histórico de B nunca se reescribe en silencio.
La asignación no debe convertirse en una dependencia de producción
A continuación surge la pregunta de quién recibe A y quién recibe B. Un diseño tentador consiste en que cada solicitud pida a un servicio central de experimentación su tratamiento correspondiente. Esto lleva a que la plataforma de experimentación forme parte del presupuesto de disponibilidad y latencia de la propia aplicación, lo cual es un mal intercambio para un sistema cuyo propósito principal es la medición.
La primera versión utiliza hashing determinista. La capa de control envía su configuración actual a un SDK que se encuentra dentro de la aplicación, y la elección del algoritmo se realiza allí mismo, de forma local. Esto genera una propiedad útil en caso de fallo: si el servicio de experimentos deja de funcionar, la plataforma no puede iniciar nuevos experimentos ni cambiar las asignaciones temporalmente, pero la aplicación permanece intacta y el SDK sigue funcionando con su última configuración válida.
Para la versión MVP, bastan unas pocas unidades de aleatorización:
request
user
session
workflow_instance
El último existe porque los agentes no necesariamente viven tanto tiempo como una solicitud web. Un agente puede comenzar una migración de repositorio hoy, esperar una aprobación externa y continuar mañana. Si la asignación se volviera a calcular a partir de la configuración de mañana, el mismo flujo de trabajo podría comenzar en una implementación y terminar en otra, lo que dificultaría innecesariamente tanto las operaciones como el análisis. Por lo tanto, la asignación permanece fija:
workflow_instance = migration-8291
variant = B
pipeline = agent-v18
y viaja con el flujo de trabajo cada vez que se reanuda.
La asignación difiere de la exposición
Otra distinción cobra importancia cuando el tratamiento se encuentra dentro de una tubería de agentes. Llegar a un grupo de tratamientos no garantiza que el tratamiento se haya ejecutado.
Tomemos dos implementaciones de memoria que están siendo probadas. Un usuario llega a la versión B, la nueva versión de memoria, pero el agente responde sin realizar ninguna búsqueda en la memoria. La asignación existe, pero el componente sometido a pruebas nunca tuvo la oportunidad de influir en el resultado. Estos dos casos deberían considerarse eventos separados:
ASSIGNMENT
user-18271 → B
EXPOSURE
memory-v2 retrieval started
Esto también proporciona información sobre la salud del experimento. Además del número de usuarios asignados aleatoriamente a cada grupo, se puede examinar el panorama de exposición:
assigned 10,428
exposed 8,912
exposure rate 85.46%
Si A y B muestran tasas de exposición inesperadamente diferentes, esa diferencia merece atención en primer lugar.
Surge un problema relacionado cuando el tratamiento asignado no es el que finalmente se ejecuta. Supongamos que B elige el modelo Y, el modelo Y devuelve un 503 y el gateway recurre al modelo X. El registro muestra lo siguiente:
assigned_variant = B
realized_model = X
fallback_reason = provider_unavailable
Y la ejecución se mantiene en B.
Esto sigue la regla de intención de tratamiento, ITT. Cada sujeto se analiza en el grupo al que fue asignado aleatoriamente, incluso si lo que realmente se ejecutó difiere. La solución de respaldo aquí forma parte de la experiencia de B, y eliminar tales solicitudes posteriormente reconfiguraría la población utilizando un evento que ocurrió después de la asignación aleatoria. Los datos de exposición y tratamiento realzado siguen siendo valiosos, sobre todo como señales de salud y diagnóstico, a menos que el análisis indique lo contrario.
El rastreo explica lo que sucedió
El resultado de un experimento indica si un resultado cambió, pero no dice nada sobre por qué. Para averiguar el motivo, OpenTelemetry es la opción más adecuada, no un nuevo protocolo de rastreo. La capa de experimentación solo necesita añadir un poco de contexto adicional a cada span:
experiment.id
experiment.version
experiment.variant
experiment.assignment_id
pipeline.id
pipeline.version
execution.purpose
Con eso en su lugar, un resultado agregado como este:
B has a lower task completion rate
puede convertirse en una solicitud mucho más útil:
show failed B traces
y las trazas revelan si el nuevo pipeline utiliza un contexto más débil, vuelve a intentar las llamadas a la herramienta, cambia a un modelo diferente, se queda atascado en bucles o simplemente sigue una ruta más larga para llegar al mismo resultado.
Una traza aún no constituye el registro del experimento. Explica bien cómo se desarrolló una ejecución, pero los resultados experimentales pueden persistir más tiempo. Un cliente podría hablar con un agente hoy y renovar una suscripción una semana después, mucho tiempo después de que la traza original haya finalizado. Por lo tanto, un flujo de eventos de experimento más reducido funciona junto con el seguimiento:
assignment
exposure
outcome
feedback
evaluation
El rastro registra la ejecución. El flujo de eventos se encarga del control contable que relaciona la randomización con los resultados que llegan posteriormente.
La evaluación sin conexión utiliza el mismo proceso
Antes de que B llegue a los usuarios finales, debe poder manejar escenarios conocidos. En el caso de los agentes, un escenario no debería reducirse a:
prompt
expected answer
dado que el resultado interesante a menudo no es el texto devuelto, sino el estado del entorno posteriormente. Un escenario de soporte podría escribirse de esta manera:
scenario:
id: duplicate-charge-001
input:
message: >
I was charged twice for order 9811.
environment:
fixture: duplicate-charge-customer
expected:
refund_count: 1
ticket_status: resolved
limits:
max_turns: 15
max_tool_calls: 20
max_cost: 0.50
El mismo escenario se aplica entonces a la tubería A y a la tubería B, posiblemente varias veces, ya que las ejecuciones de los agentes son estocásticas y dos ejecuciones con la misma configuración pueden seguir trayectorias diferentes a partir del mismo estado inicial. El MVP utiliza un entorno aislable reinitializable con servicios respaldados por fixtures o bases de datos de prueba. Construir una funcionalidad general de reproducción de secuencias múltiples se deja mejor para más adelante.
La reproducción solo es posible mientras el candidato solicite interacciones que ya hayan sido grabadas. Supongamos que B toma una decisión diferente en la tercera vuelta y llama a una herramienta que la trayectoria original nunca utilizó. Entonces no existe ningún mundo grabado que contenga esa rama. El resultado honesto es:
REPLAY_DIVERGED
Una respuesta simulada que da la impresión de que la ejecución sigue siendo una repetición es un error. Un entorno aislado con estado permite que ambos agentes se desarrollen de forma natural mientras se controlan sus condiciones iniciales, lo cual constituye una mejor primera aproximación para la evaluación real de agentes.
La ejecución en sombra llena el vacío
Existe un paso útil entre el entorno aislado controlado y exponer a los usuarios al sistema B: ejecutar B de forma asíncrona sobre una copia de las entradas reales de producción sin mostrar sus resultados. Las escrituras y los efectos secundarios externos se desactivan o se redirigen a un entorno aislado. Esto proporciona entradas similares a las de producción y permite al equipo observar:
latency
cost
tool usage
model fallbacks
guardrail failures
judge scores
trajectory differences
antes de que cualquier usuario vea el tratamiento.
Los “Shadow Runs” no pueden mostrar si B mejora el resultado más importante, ya que los usuarios nunca ven a B y por lo tanto no pueden reaccionar ante él. Son un paso intermedio: entradas realistas y comportamiento en tiempo de ejecución, sin ninguno de los riesgos del producto que conlleva un experimento en vivo.
No toda verificación necesita un juez basado en LLM
Cuando el agente termina un escenario, el resultado aún necesita ser evaluado, por lo que la primera versión cuenta con dos tipos de evaluadores.
El primer tipo es determinista. Cuando una tarea consiste en emitir un único reembolso y resolver un ticket, basta con inspeccionar el entorno:
refund_count == 1
ticket_status == resolved
Preguntar a otro modelo si la tarea probablemente se completó no aporta nada cuando se puede verificar el estado resultante.
El segundo tipo es basado en modelos, para propiedades que resisten las afirmaciones:
helpfulness
clarity
tone
quality of explanation
Un juez basado en LLM ayuda con eso, pero el juez se convierte en otro componente versionado y nunca en una fuente anónima de verdad:
evaluator
evaluator version
judge model
judge prompt
rubric
Cada puntuación generada mantiene esa procedencia. También se mantiene un pequeño conjunto de datos de calibración etiquetados por humanos, y el evaluador se compara periódicamente con ellos. Un evaluador basado en LLM es un instrumento de medición, y cuando el instrumento cambia, es importante que sus lecturas también cambien. Los evaluadores agentes que navegan, utilizan herramientas o buscan pruebas adicionales son útiles y probablemente inevitables en algunos dominios, pero no tienen lugar en la primera versión.
Estadísticas, manteniéndolas simples
Los sistemas de experimentación avanzados ofrecen un alto grado de sofisticación. Un punto de partida mejor es un pequeño conjunto de métodos implementados correctamente, en lugar de un impresionante menú estadístico que pocos usuarios comprenden. La primera versión admite:
A/B allocation
binary metrics
continuous metrics
95% confidence intervals
sample ratio mismatch detection
fixed-horizon analysis
Eso es suficiente para responder a muchas preguntas útiles, y un resumen del resultado podría ser:
Experiment
support-agent-v18
Randomization
user
Primary metric
ticket resolution
A 81.4%
B 85.1%
Difference
+3.7 percentage points
95% CI
[...]
Experiment health
SRM PASS
La discrepancia en la proporción de muestras requiere atención especial, ya que los resultados nunca deben parecer fiables cuando la propia randomización parece estar defectuosa. Si el plan preveía una división equitativa y la división observada difiere claramente, el experimento se considera defectuoso y eso es todo; no se muestra ningún ganador junto a un ícono de advertencia.
Las medidas de control operativas forman parte de esa misma capa de calidad. Una variante puede fallar en términos de costos sin fallar técnicamente. Imagine un bucle de solicitudes que envía llamadas desde:
4 model calls
En:
47 model calls
Y la respuesta puede seguir siendo HTTP 200, incluso cuando hay una respuesta correcta. Por eso se necesitan límites máximos:
max cost / execution
max tokens / execution
max agent turns
max tool calls
max variant spend / hour
Cruzar un casquillo puede detener esa ejecución o interrumpir la exposición adicional a la variante. Un informe de salud podría indicar lo siguiente:
Variant B
SRM PASS
Error rate PASS
Cost / request +312%
Circuit breaker TRIPPED
New exposure PAUSED
Un interruptor de circuito no es una conclusión estadística. Es un mecanismo de seguridad operativa que evita que un tratamiento problemático consuma tráfico y presupuesto mientras el experimento aún está recopilando suficientes observaciones como para llegar a alguna conclusión estadística.
Los métodos más sofisticados pueden esperar: CUPED, pruebas secuenciales, grupos de control, capas de experimentos, randomización por clústeres, asignación adaptativa y otros. El modelo de datos debe dejar espacio para ellos, pero la versión inicial solo necesita ejecutar correctamente experimentos A/B simples.
Qué construir primero
Con la mayoría de los elementos posponibles eliminados, la plataforma se vuelve manejable. Necesita un servicio de control que contenga:
- un registro de experimentos
- un registro de pipelines
- un registro de conjuntos de datos
- un registro de evaluadores
- definiciones de métricas
En la parte superior se encuentra el SDK, que descarga la configuración del experimento y la asigna de manera determinista en el lado del cliente. Se necesita una ruta de ingestión para:
assignment
exposure
outcome
feedback
evaluation
También se ingieren rastros de OpenTelemetry. Las opciones razonables de almacenamiento son PostgreSQL para el estado del plano de control, NATS JetStream o Kafka para eventos, ClickHouse para análisis de experimentos y rastros, y almacenamiento de objetos compatible con S3 para archivos voluminosos. Un ejecutor offline procesa un conjunto de datos en entornos A y B que pueden reiniciarse.
Hay muchas cosas deliberadamente ausentes. La versión MVP no aborda la virtualización arbitraria de entornos con estado, las interacciones sutiles entre experimentos, la deriva en los evaluadores, el análisis automático de causas raíz, los efectos duraderos del tratamiento ni la atribución causal entre varios agentes, y puede funcionar sin ellas. Lo que es importante desde el principio son aquellas cosas cuya corrección resulta complicada una vez que los datos históricos dependen de ellas:
what was randomized
what treatment was assigned
what treatment actually ran
what pipeline version produced the execution
what outcome was measured
how that outcome was evaluated
Si esas identidades sobreviven a la primera versión, la plataforma puede volverse mucho más completa sin alterar el significado de sus experimentos anteriores.
La idea clave es que la evaluación offline, la ejecución en segundo plano y los experimentos en tiempo real pueden compartir definiciones de pipelines, registros detallados y herramientas de evaluación, aunque cada uno responde a su propia pregunta. Las ejecuciones offline revelan cómo se comportan A y B en escenarios controlados. Las ejecuciones en segundo plano muestran cómo B maneja entradas reales mientras los usuarios no se ven afectados. Los experimentos en tiempo real indican qué cambia cuando se divide aleatoriamente a usuarios reales. Los registros detallados registran lo que ocurre dentro de cada ejecución y ayudan a interpretar los resultados, pero un diagnóstico a nivel de registro nunca debe considerarse una estimación causal. Juntos, estos elementos permiten crear un MVP que pueda responder de manera fiable a tres preguntas:
What exactly did we run?
What happened when we ran it?
Did assigning users to B improve the outcome we care about?
Todo lo demás puede desarrollarse alrededor de eso.
Leer más
- Los logs de auditoría de agentes necesitan 12 campos: qué registran realmente seis marcos — Un recuento estático de seis marcos para agentes frente a un borrador de registro de auditoría con 12 campos: la cobertura promedio es de 5 de 12, y ninguno registra evidencias de manipulación ni el nivel de confianza.
- Ingeniería de grafos para agentes de codificación AI: contexto, contratos y reparación — Qué enseñó un grafo de agentes basado en contratos a un desarrollador: contexto delimitado, validación determinista, reparación dirigida y cuándo está justificado el costo adicional de procesamiento.