Publicado el 5 min de lectura

Compara versiones de prompts de IA con una prueba de regresión en Python

En esta página

Un prompt de IA modificado clasifica correctamente cinco de seis incidencias en vez de cuatro. ¿Lo publicas? No si el nuevo error envía a la cola general a un cliente que no puede acceder. Una regresión es un caso que antes se resolvía bien y ahora falla; el promedio puede ocultarlo.

Crea una comparación local en Python que enumere mejoras, muestre regresiones y bloquee una regresión crítica elegida de antemano. Las predicciones sintéticas guardadas no requieren cuenta, paquetes ni datos de clientes. Estas cifras demuestran el comprobador, no una mejora del modelo.

Dos filas de fichas de evaluación con marcas de acierto verde azuladas y cruces ámbar; una lupa destaca una ficha de fallo coral.

1. Fija los casos y la regla de aceptación

Usa las mismas entradas y respuestas revisadas de forma independiente para ambas versiones del prompt. Este es el conjunto de incidencias de prueba:

ID del caso Texto de la incidencia Etiqueta esperada
reset Mi enlace para restablecer la contraseña ha caducado. access
charge Me habéis cobrado dos veces. billing
refund ¿Dónde está mi reembolso? billing
locked_out Nadie de nuestro equipo puede iniciar sesión. access
setup ¿Dónde está la guía de configuración? general
invoice Por favor, reenviad mi factura. billing

Marca locked_out como crítico antes de ver los resultados. Nuestra regla de demostración no permite reducir el total de aciertos ni que un caso crítico antes correcto empiece a fallar. Las regresiones no críticas siguen visibles para revisión. Es una política de comparación elegida, no un criterio universal para desplegar sistemas.

La guía de evaluación de Anthropic recomienda pruebas específicas de la tarea, casos límite y calificación automatizada cuando resulte adecuada. La coincidencia exacta de etiquetas sirve para esta tarea limitada; no evalúa la calidad de respuestas abiertas.

En una comparación real, guarda las salidas con IDs estables y registra ambos prompts, el identificador del modelo, los parámetros de generación y la fecha de recogida. Cambia solo el prompt si quieres aislar ese cambio. Revisa las etiquetas ambiguas antes de puntuar; nuestro tutorial sobre clasificación de incidencias explica las reglas de asignación y las comprobaciones por clase.

2. Compara las predicciones guardadas por ID

Guarda esto como compare_prompts.py y ejecútalo con Python 3.9 o posterior. Sustituye los diccionarios de predicciones por tus exportaciones revisadas. Exigir que los conjuntos de IDs coincidan exactamente evita que casos ausentes o adicionales alteren la comparación sin avisar.

expected = {
    "reset": "access", "charge": "billing", "refund": "billing",
    "locked_out": "access", "setup": "general", "invoice": "billing",
}
critical = {"locked_out"}
baseline = {
    "reset": "access", "charge": "general", "refund": "general",
    "locked_out": "access", "setup": "general", "invoice": "billing",
}
candidate = {
    "reset": "access", "charge": "billing", "refund": "billing",
    "locked_out": "general", "setup": "general", "invoice": "billing",
}


def compare(before, after):
    labels = {"access", "billing", "general"}
    if not expected or not critical <= expected.keys():
        raise ValueError("invalid reference cases")
    for name, predictions in (("baseline", before), ("candidate", after)):
        if predictions.keys() != expected.keys():
            raise ValueError(f"{name}: case IDs must match exactly")
        if any(not isinstance(v, str) or v not in labels
               for v in predictions.values()):
            raise ValueError(f"{name}: unknown label")

    old = {key: before[key] == label for key, label in expected.items()}
    new = {key: after[key] == label for key, label in expected.items()}
    improved = sorted(key for key in expected if not old[key] and new[key])
    regressed = sorted(key for key in expected if old[key] and not new[key])
    critical_regressions = sorted(critical.intersection(regressed))
    blocked = sum(new.values()) < sum(old.values()) or bool(critical_regressions)
    print(f"baseline={sum(old.values())}/{len(expected)}")
    print(f"candidate={sum(new.values())}/{len(expected)}")
    print(f"improved={improved}")
    print(f"regressed={regressed}")
    print(f"critical_regressions={critical_regressions}")
    print("gate=" + ("BLOCK" if blocked else "PASS"))
    return 1 if blocked else 0


if __name__ == "__main__":
    raise SystemExit(compare(baseline, candidate))
python3 compare_prompts.py

3. Valida el criterio de bloqueo, no solo la puntuación

Los datos incluidos muestran:

baseline=4/6
candidate=5/6
improved=['charge', 'refund']
regressed=['locked_out']
critical_regressions=['locked_out']
gate=BLOCK

El proceso termina con código 1 y sin stderr. El fallo es intencional: dos correcciones de facturación compensan numéricamente un error de acceso, pero el caso que empieza a fallar es crítico. La documentación de Python sobre códigos de salida explica por qué un código distinto de cero permite detectar fallos en una automatización.

Ejecuta estas comprobaciones adicionales desde el mismo directorio:

python3 - <<'PYTEST'
from compare_prompts import baseline, candidate, compare

fixed = dict(candidate, locked_out="access")
assert compare(baseline, fixed) == 0

missing = dict(fixed)
del missing["invoice"]
try:
    compare(baseline, missing)
except ValueError as error:
    print(error)
else:
    raise AssertionError("missing case was accepted")
PYTEST

El conjunto corregido muestra candidate=6/6, regressed=[], critical_regressions=[] y gate=PASS. La comprobación del caso ausente imprime candidate: case IDs must match exactly. El comando completo termina con código 0 y sin stderr. Ambos conjuntos de prueba se comprobaron localmente con Python 3.11.

4. Resuelve comparaciones engañosas

  • IDs diferentes: une las exportaciones mediante IDs estables, no por posición de fila. Investiga los casos ausentes en lugar de puntuar únicamente la intersección sin avisar.
  • Etiqueta desconocida: revisa la ortografía y el formato de salida acordado. El comprobador rechaza etiquetas desconocidas en vez de adivinar.
  • El criterio se cumple pese a errores existentes: esta regla comprueba el cambio, no la calidad absoluta. Comparar la referencia consigo misma permite pasar con 4/6; añade requisitos mínimos de exactitud y por clase antes del despliegue.
  • Los resultados varían entre ejecuciones: puntuar salidas guardadas es determinista; generarlas con un modelo puede no serlo. Repite la recogida para ambos prompts y examina con qué frecuencia falla cada caso crítico. No presentes esta demostración de seis casos como evidencia estadística.

Separa los casos de desarrollo de los de evaluación final. La guía de scikit-learn sobre fuga de información explica por qué los datos de prueba no deben dirigir la elección del modelo; aplica esa separación también al ajustar prompts. Nuestro comprobador de coincidencias entre entrenamiento y prueba ayuda a encontrar textos repetidos, aunque no puede certificar su independencia.

Si las salidas son probabilidades en vez de etiquetas, añade una comprobación con la puntuación de Brier; una lista de regresiones no mide la calidad de las probabilidades.

Conclusión práctica

Conserva la puntuación global, pero pregunta siempre qué casos cambiaron. Fija primero la regla, compara IDs idénticos y revisa los casos críticos que empiezan a fallar antes de aceptar una nueva versión del prompt. Superar esta comparación es un punto de control, no una autorización para desplegar.

Artículos relacionados

Últimos artículos