Evaluación automatizada de LLM: Cómo crear un control de calidad de CI/CD que realmente funcione

Una guía práctica para integrar la evaluación de LLM en CI/CD, de modo que las regresiones de calidad se detecten antes de su lanzamiento y no después de que se acumulen los tickets de soporte.

>Loading the Elevenlabs Text to Speech AudioNative Player...
Resumir este artículo
Resumen: La evaluación automatizada de LLM es un pipeline de CI/CD donde cada cambio en un prompt, versión de modelo o configuración de recuperación activa una ejecución de evaluación frente a un conjunto de datos de referencia versionado. Se diferencia de la automatización de pruebas estándar en dos aspectos: las comprobaciones son probabilísticas, no deterministas, y el conjunto de datos es parte del sistema.

Un cambio en el prompt se lanza el jueves. El ingeniero lo probó con cinco ejemplos y parecía mejor. Dos lunes después, la cola de soporte tiene 60 tickets sobre un patrón de respuesta específico: la categoría exacta que el cambio en el prompt debía solucionar, pero que ahora falla en un caso extremo diferente. No hay historial de evaluación, ni línea base, ni forma de saber cuándo comenzó el fallo o qué versión del prompt lo causó.

Esto no es un problema de pruebas. Es un problema de infraestructura. Las herramientas para evitarlo no son complicadas; la mayoría de los equipos simplemente nunca las construye.

Por qué la automatización de la evaluación de LLM difiere de la automatización de pruebas estándar

La automatización de pruebas estándar ejecuta comprobaciones deterministas. Una función devuelve el valor esperado o no lo hace. Una prueba que falla es inequívocamente incorrecta. La solución está en el código.

La automatización de la evaluación de LLM ejecuta comprobaciones probabilísticas. Un juez LLM que califica la fidelidad es, en sí mismo, un modelo: puede producir falsos negativos. Una ejecución de evaluación que produce una puntuación de fidelidad de 0.82 no es incorrecta de la misma manera que lo es una prueba unitaria fallida; es una estimación, y la estimación tiene márgenes de error. El pipeline debe manejar esto de manera diferente: seguir tendencias en lugar de puntuaciones puntuales, detectar regresiones en conjunto en lugar de fallos en casos aislados y derivar los casos dudosos a revisión humana en lugar de tratarlos como bloqueos definitivos.

La segunda diferencia: el conjunto de datos es parte del sistema. En las pruebas estándar, las entradas de prueba están fijadas por la especificación. En la automatización de la evaluación de LLM, el conjunto de datos de referencia evoluciona con el tiempo a medida que cambia el alcance del producto, se descubren modos de fallo y el equipo añade cobertura para nuevos tipos de consultas. La gestión del conjunto de datos es un problema de ingeniería de primer nivel, no una preocupación secundaria.

Los tres disparadores que deben ejecutar la evaluación automáticamente

No todos los cambios de código necesitan una ejecución de evaluación. Tres cambios sí, y la mayoría de los demás no.

Cambios en la versión o configuración del modelo. Cualquier cambio en la versión del modelo, el proveedor del modelo o la configuración de inferencia (temperatura, tokens máximos, top-p) activa una ejecución de evaluación completa. Los proveedores de modelos actualizan los modelos subyacentes sin incrementar el nombre de la versión; los equipos cambian de proveedor por razones de coste o latencia. Ambos alteran la calidad de la salida de formas que solo una evaluación con un conjunto de referencia puede detectar. Un pipeline de CI que no se activa con los archivos de configuración del modelo pierde la fuente más común de regresiones silenciosas.

Cambios en los prompts. Cada edición de un prompt —prompt del sistema, ejemplos few-shot, instrucciones de formato de salida— activa una ejecución de evaluación frente al conjunto de datos de referencia completo. Un cambio en el prompt que mejora el rendimiento en el escenario objetivo degrada de forma fiable el rendimiento en casos extremos que el desarrollador no tuvo en cuenta. Los casos extremos son exactamente lo que los conjuntos de datos de referencia están diseñados para cubrir.

Cambios en la configuración de recuperación para sistemas RAG. La estrategia de fragmentación (chunking), el modelo de incrustación (embedding), el re-ranker, la asignación de la ventana de contexto y los umbrales de similitud afectan a lo que recibe el modelo y, por tanto, a lo que genera. Cada cambio activa una evaluación de componentes sobre la calidad de la recuperación y una evaluación integral de todo el pipeline.

Los cambios en la infraestructura, las actualizaciones de dependencias y los cambios en el código de la aplicación que no afecten a estas tres áreas pueden omitir la ejecución de la evaluación. Limitar el disparador a las rutas de archivo correctas mantiene el pipeline rápido y evita la fatiga de evaluación.

Diseño del arnés, lo que realmente requiere la consistencia

El arnés de evaluación es el código que carga los ejemplos, llama al modelo, llama al juez y agrega las puntuaciones. La consistencia es su propiedad innegociable: la misma entrada debe producir la misma puntuación en todas las ejecuciones, de modo que las diferencias de puntuación entre ejecuciones reflejen cambios reales en la calidad y no variaciones del arnés.

Es necesario fijar seis parámetros para garantizar la coherencia:

  1. Versión del modelo de evaluación y del snapshot. Fija gpt-4o-2024-11-20, no gpt-4o. Los proveedores de modelos actualizan sus versiones bajo el mismo nombre. Un evaluador sin fijar es un instrumento de medición que cambia entre ejecuciones.
  2. Temperatura del evaluador en 0. Una temperatura distinta de cero introduce variaciones entre ejecuciones en las tareas de clasificación. Esta varianza es pequeña en cada ejecución, pero se acumula como ruido al analizar las tendencias de puntuación a lo largo de decenas de despliegues.
  3. Versión del prompt del evaluador. Versiona el prompt del evaluador en git junto con el código de la aplicación. Cualquier cambio en el prompt altera el instrumento de medición. Las tendencias de puntuación que abarcan un cambio en el prompt carecen de sentido; necesitarías volver a ejecutar la línea base con el nuevo evaluador antes de interpretar la diferencia.
  4. Configuración de tamaño de lote y concurrencia. La limitación de tasa y el comportamiento de reintento, si varían entre ejecuciones, afectan a qué ejemplos fallan o agotan el tiempo de espera, lo que altera la puntuación agregada.
  5. Semilla aleatoria para el orden de los ejemplos. Aleatoriza el orden de los ejemplos (algunos modelos de evaluación muestran efectos de posición en lotes largos) y luego fija la semilla para que las ejecuciones sean reproducibles.
  6. Versión del conjunto de datos. Cada ejecución de evaluación registra contra qué commit del conjunto de datos de referencia se realizó. Una mejora en la puntuación que coincide con un cambio en el conjunto de datos no es una mejora de calidad.


import hashlib
import json
from anthropic import Anthropic

client = Anthropic()

def run_eval(
    dataset_path: str,
    dataset_commit: str,
    app_prompt_version: str,
    judge_prompt_version: str,
    model: str = "claude-sonnet-4-6",           # application model
    judge_model: str = "claude-opus-4-7",        # judge model — different family
    judge_snapshot: str = "claude-opus-4-7",     # pin the snapshot
    seed: int = 42,
) -> dict:
    with open(dataset_path) as f:
        examples = [json.loads(line) for line in f]

    import random
    rng = random.Random(seed)
    rng.shuffle(examples)

    results = []
    for ex in examples:
        response = client.messages.create(
            model=model,
            max_tokens=1024,
            temperature=0,                        # deterministic application output
            system=app_prompt_version,
            messages=[{"role": "user", "content": ex["query"]}],
        )
        output = response.content[0].text

        verdict = client.messages.create(
            model=judge_snapshot,
            max_tokens=256,
            temperature=0,                        # deterministic judge
            system=judge_prompt_version,
            messages=[{"role": "user", "content": json.dumps({
                "query": ex["query"],
                "context": ex.get("context", ""),
                "response": output,
            })}],
        )
        results.append({
            "example_id": ex["id"],
            "verdict": verdict.content[0].text,
            "dataset_version": dataset_commit,
            "judge_prompt_hash": hashlib.sha256(judge_prompt_version.encode()).hexdigest()[:8],
        })

    return aggregate(results)

Los metadatos registrados por ejecución —commit del conjunto de datos, hash del prompt del evaluador, versión del modelo, commit de la aplicación— son lo que permite diagnosticar regresiones. Sin ellos, sabes que la calidad cayó, pero no sabes qué cambió.

Versionado del conjunto de datos

El conjunto de datos de referencia es tan importante como el código de la aplicación. La mayoría de los equipos lo versionan de forma informal —una carpeta compartida, quizás un CSV con una fecha en el nombre del archivo— y descubren el coste de esto cuando no pueden distinguir si un cambio en la puntuación se debe a una modificación en los datos o a un cambio real en la calidad.

Tres reglas de control de versiones que evitan los fallos más comunes:

Las adiciones requieren revisión de código. Añadir un ejemplo al conjunto de datos de referencia cambia lo que mide la puntuación. Un nuevo ejemplo que el modelo actual falla es una regresión real que antes no se detectaba; añadirlo debe ser una decisión deliberada, documentada en un PR. Un nuevo ejemplo que el modelo actual supera es una mejora en la cobertura; también debe documentarse. El mensaje de commit "ejemplos añadidos" no es suficiente.

Las eliminaciones suponen un riesgo para la producción. Eliminar un ejemplo de referencia que el modelo actual no logra resolver hace que las puntuaciones mejoren sin que el sistema sea realmente mejor. Trate la eliminación como un cambio bloqueante que requiere una justificación explícita: ¿qué ha cambiado en los requisitos del producto para que este caso de fallo ya no sea relevante?

Los cambios en la verdad fundamental requieren justificación. Actualizar el resultado esperado de un ejemplo altera la medición. La justificación correcta: los requisitos de calidad han cambiado, por lo que el resultado esperado también. La justificación incorrecta: el modelo genera este resultado ahora, así que actualizamos el resultado esperado para que coincida. Este segundo patrón —ajustar la verdad fundamental al comportamiento del modelo— es común, fácil de pasar por alto en las revisiones y destruye silenciosamente la capacidad de la evaluación para detectar regresiones.

Formato de almacenamiento: JSON Lines (.jsonl), un ejemplo por línea, versionado en git. Los archivos JSONL muestran diferencias claras en las solicitudes de extracción (pull requests), a diferencia de los CSV. Almacene el conjunto de datos en el mismo repositorio que el código de la aplicación que lo utiliza, de modo que los cambios en los prompts y en los datos aparezcan en la misma diferencia de la PR.

Diseño de umbrales

Un umbral binario —lanzar si la puntuación de fidelidad supera 0,85, bloquear si no lo hace— falla de dos formas predecibles. Si se establece demasiado estricto, se activa con el ruido, generando falsas alarmas que el equipo termina ignorando. Si se establece demasiado laxo, no detecta regresiones reales. La mayoría de los equipos lo calibran para minimizar las falsas alarmas, lo que significa dejarlo lo suficientemente laxo como para que rara vez se active, lo que a su vez implica que tampoco detecta las regresiones reales.

Las tasas de fallo aceptables por categoría gestionan esto mejor. La estructura:

  • Fallos bloqueantes — cualquier tasa activa un bloqueo de despliegue. Estos son los fallos que hacen que el producto sea activamente perjudicial: violaciones de seguridad, infracciones de políticas o fallos en el formato de salida que rompen los sistemas posteriores. Tolerancia cero, porque incluso un solo fallo de este tipo que llegue a un usuario es un incidente de producción.
  • Puertas de regresión — la puerta se activa según el cambio en la tasa de fallo, no según la tasa absoluta. Una tasa de fallo de fidelidad que aumenta del 3% al 5% es una regresión que merece ser bloqueada. Una tasa de fallo estable del 5% podría ser el punto operativo aceptable del producto, establecido cuando se lanzó y el equipo decidió que era tolerable. Establecer la puerta basándose en el delta evita falsas alarmas por tasas de fallo estables, al tiempo que detecta regresiones genuinas.
  • Señales de advertencia — registradas y rastreadas, pero no bloqueantes. Degradaciones menores de calidad, regresiones en casos límite, aumentos en la varianza de la longitud de respuesta. Revise semanalmente y bloquee si la tendencia persiste durante tres semanas consecutivas.

El error: un único umbral, demasiado estricto, que se activa constantemente y termina desactivado para la segunda semana. Los umbrales de tres niveles con diferentes gravedades crean una puerta que bloquea lo que importa e ignora lo que no.

Integración con CI

Un flujo de trabajo de GitHub Actions que ejecuta la evaluación en cada PR que modifique un prompt o la configuración del modelo:



name: LLM Eval Gate

on:
  pull_request:
    paths:
      - "prompts/**"
      - "config/model.yaml"
      - "config/retrieval.yaml"

jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Install dependencies
        run: pip install anthropic==0.40.0 python-dotenv

      - name: Run eval harness
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python eval/run.py \
            --dataset eval/golden.jsonl \
            --dataset-commit ${{ github.sha }} \
            --app-commit ${{ github.sha }} \
            --output eval/results/${{ github.run_id }}.json

      - name: Check thresholds and post PR comment
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          python eval/check_thresholds.py \
            --results eval/results/${{ github.run_id }}.json \
            --baseline eval/baseline.json \
            --pr-number ${{ github.event.pull_request.number }}

      - name: Store results
        run: |
          python eval/store_results.py \
            --results eval/results/${{ github.run_id }}.json \
            --store eval/history/


El formato de los comentarios en las PR es tan importante como el control de calidad en sí. Un comentario que solo muestra la puntuación actual no aporta información útil a los revisores. Muestra el desglose de la puntuación por categoría, la diferencia respecto a la línea base y, en caso de fallo bloqueante, los IDs específicos de los ejemplos fallidos para que el ingeniero pueda investigar sin tener que volver a ejecutar la evaluación localmente.



## LLM Eval Results — PR #482

| Category       | Baseline | This PR | Delta  | Status  |
|----------------|----------|---------|--------|---------|
| Faithfulness   | 0.91     | 0.87    | -0.04  | ⚠️ Gate |
| Answer relevance | 0.88   | 0.89    | +0.01  | ✅ Pass  |
| Safety         | 1.00     | 1.00    | 0.00   | ✅ Pass  |

**Regression gate triggered on Faithfulness** (delta exceeds 0.03 threshold).
Failing example IDs: ex-0042, ex-0187, ex-0291
Dataset version: a3f7c9d

Plataformas como Galtea admiten canalizaciones de evaluación donde los criterios de calidad se derivan de especificaciones formales del producto, de modo que el control de evaluación aplica los mismos requisitos documentados en la especificación del producto en lugar de puntuaciones calibradas de forma improvisada.

Seguimiento de regresiones

Almacenar los resultados de las evaluaciones no es opcional. El almacenamiento mínimo viable para el seguimiento de regresiones debe incluir:

  1. Un registro estructurado de cada ejecución de evaluación: marca de tiempo, commit de Git de la aplicación, versión del conjunto de datos (hash del commit), modelo de evaluación y su instantánea, hash del prompt de evaluación, puntuaciones por categoría, recuento de ejemplos e IDs de los ejemplos fallidos. Todo ello almacenado en una base de datos o en un archivo JSON Lines de solo adición.
  2. Un panel que muestre la puntuación por categoría a lo largo del tiempo, con capacidad de filtrar por versión del conjunto de datos y versión del prompt de evaluación. La pregunta que esto responde es: "¿cuándo empezó a caer la fidelidad y qué despliegue coincidió con ello?". Un gráfico de puntuación que solo muestra la ejecución actual es una visualización de resultados, no un rastreador de regresiones.
  3. Alertas basadas en tendencias, no solo en umbrales. Una puntuación de fidelidad que cae 0,02 por semana durante cuatro semanas consecutivas es una regresión que un umbral estático fijado en 0,80 pasaría por alto por completo (la puntuación sigue siendo 0,84). Una alerta de tendencia configurada como "más de 0,015 por semana durante tres semanas consecutivas" la detectaría en la tercera semana. Las alertas de tendencia requieren los datos de series temporales que almacena el seguimiento de regresiones.

Errores comunes

Ejecutar la evaluación solo después de que algo se rompe. La evaluación retrospectiva te dice que algo salió mal, pero no qué cambió ni cuándo. La infraestructura de seguimiento de regresiones que responde a "¿cuándo empezó esto?" solo existe si has estado ejecutando evaluaciones de forma proactiva con cada cambio.

Configuraciones de evaluación no fijadas. Un modelo de evaluación que se actualiza entre ejecuciones, o un ajuste de temperatura que no está fijado explícitamente en 0, produce una varianza en las puntuaciones que no podrás distinguir de los cambios reales en la calidad. Fija todas las configuraciones.

Tratar el conjunto de datos de referencia como algo estático. El conjunto de datos debe crecer a medida que se descubren nuevos modos de fallo. Crea un proceso ligero para añadir ejemplos de fallos en producción al conjunto de referencia: un script que formatee el ejemplo correctamente, una plantilla de PR que incluya el resultado esperado y un paso de revisión de código que verifique la precisión de la verdad fundamental.

Establecer un único umbral binario. El umbral se desactiva tras la primera falsa alarma. Los umbrales por categoría con diferentes niveles de severidad son más difíciles de configurar y mucho más difíciles de desactivar, ya que generan menos falsas alarmas y bloquean las regresiones reales.

No versionar el prompt de evaluación. Un cambio en el prompt de evaluación altera el instrumento de medición. Las tendencias de puntuación a través de un cambio de prompt no versionado son solo ruido. Cuando cambies el prompt de evaluación, vuelve a ejecutar la línea base, marca el límite en el rastreador de regresiones e inicia una nueva línea de tendencia.

Registrando solo las puntuaciones actuales. Sin resultados históricos ni metadatos por ejecución, el seguimiento de regresión es imposible. Almacena todo desde la primera ejecución.