Evaluaciones para GenAI y agentes: pruebas de un solo turno vs. pruebas de varios turnos en CI
Compare las evaluaciones de un solo turno basadas en trazas con las evaluaciones de varios turnos impulsadas por simuladores, abarcando estándares de referencia, jueces basados en LLM, métricas de agentes, puertas de control de integración y siete prácticas de trabajo.
“Funciona con mi prompt” no es un estándar de ingeniería. Una prueba unitaria convencional da como resultado un éxito o un fracaso de manera determinista, pero los sistemas generativos ofrecen respuestas abiertas y probabilísticas. Un pipeline RAG puede estar en su mayor parte correcto, y un agente puede completar una tarea de forma que, en teoría, se considere un éxito, pero que sea inseguro en la práctica. Por eso los evaluaciones, a diferencia de la intuición o de un canal de chat lleno de comentarios como “me parece bien”, son ahora la base de la ingeniería seria de IA generativa y sistemas agentes. A continuación se explica qué se debe medir, cómo difieren las evaluaciones de un solo turno y de varios turnos, los compromisos de cada una, y los patrones en Python que se adaptan a un pipeline CI.
Por qué las evaluaciones no son opcionales
Tres características de las aplicaciones de LLM las hacen necesarias:
- Indeterminismo. Los mismos prompts pueden producir resultados diferentes en distintas ejecuciones, versiones del modelo o valores de temperatura.
- Fallo acumulativo en los agentes. Mientras que un chatbot simple solo tiene una oportunidad de cometer un error, un agente combina entre 10 y 50 llamadas al modelo, llamadas a herramientas y decisiones. Una tasa de error modesta en cada paso se convierte en un fallo frecuente de principio a fin.
- Regresiones silenciosas. Un pequeño cambio en el prompt, una actualización del modelo o un reindexado del almacén de vectores puede degradar la calidad sin que se genere ninguna excepción.
La evaluación convierte la percepción de “que está mejor” en un número que se puede seguir a lo largo del tiempo, utilizar para decidir si se implementa algo y comparar entre versiones.
Dos formas de evaluación
Casi todos los frameworks, como DeepEval, Ragas, LangSmith, Braintrust y OpenAI Evals, siguen uno de estos dos patrones.
Un solo turno, basado en rastros
El proceso es sencillo: tomar cada caso de prueba, ejecutar la aplicación una vez y calificar el rastro resultante.
Open a test run with trace-level metrics
for each golden:
yield golden
call app with golden.input
capture trace
build a test case from the trace
score test case with metrics
upload test run (traces + scores)
Un golden es una muestra etiquetada: la entrada, la respuesta que debe generar y, con frecuencia, el contexto o las llamadas a herramientas que debe incluir. El trace registra toda la historia de una ejecución, incluyendo qué fragmentos se recuperaron, qué herramientas se utilizaron, el razonamiento intermedio, la latencia y el recuento de tokens, y no solo el texto final. Evaluar ese registro en lugar de solo la respuesta permite identificar la etapa en la que ocurre el fallo: ¿la recuperación de información, la generación o una llamada a herramienta en algún momento del proceso?
Múltiples turnos, basado en simuladores
Este es el patrón más difícil y realista para agentes y asistentes de chat.
def simulate(conversational goldens)
for each golden:
loop until expected outcome OR max turns:
simulator model generates a user turn
ask app for next reply
capture assistant turn
build a ConversationalTestCase from the full transcript
evaluate(test cases, metrics) # metrics run concurrently
-> pass/fail + reason per metric
Aquí no existe una pareja fija de entrada y salida. Un modelo simulador actúa como usuario con un objetivo, como cancelar una suscripción o obtener un reembolso por un pedido específico, y adapta cada mensaje a las respuestas del asistente, de manera similar a como lo haría una persona. El sistema se evalúa en función de la trayectoria completa: ¿se mantuvo enfocado en la tarea? ¿utilizó las herramientas adecuadamente a lo largo de las interacciones? ¿logró alcanzar su objetivo al final? ¿inventó detalles sobre las políticas ya en la tercera interacción?
Los agentes, los asistentes de múltiples pasos y cualquier sistema con memoria o estado necesitan este tipo de enfoque. Una evaluación de una sola interacción no puede detectar si un agente se contradice en la cuarta interacción o utiliza una misma herramienta cinco veces seguidas.
Elegir métricas
Las métricas se dividen en cuatro grupos, y es sensato combinarlas, ya que ninguna por sí sola ofrece una visión completa.
Un modelo mental útil: las métricas de un solo turno proporcionan una imagen instantánea, mientras que las métricas basadas en agencia evalúan una trayectoria.
El LLM como juez
La mayoría de las métricas actuales que no dependen de referencias funcionan mediante el enfoque de “LLM como juez”. Un modelo potente, a menudo diferente y más capaz que el modelo que se está probando, evalúa la salida según un criterio establecido y explica su puntuación.
from dataclasses import dataclass
JUDGE_PROMPT = """
You are evaluating an AI assistant's response for FAITHFULNESS.
A response is faithful if every factual claim it makes is supported
by the provided context. It is unfaithful if it adds unsupported
claims, contradicts the context, or fabricates details.
Context:
{context}
Response:
{response}
Return JSON: {{"score": <0.0-1.0>, "reason": "<short explanation>"}}
"""
@dataclass
class TestCase:
input: str
actual_output: str
retrieval_context: list[str]
def faithfulness_metric(test_case: TestCase, judge_llm) -> dict:
prompt = JUDGE_PROMPT.format(
context="\n".join(test_case.retrieval_context),
response=test_case.actual_output,
)
result = judge_llm.generate(prompt, response_format="json")
return {"score": result["score"], "reason": result["reason"], "passed": result["score"] >= 0.7}
Los jueces presentan ciertas desventajas:
- Sesgo de preferencia personal. Los modelos evalúan sus propios resultados o aquellos elaborados en su propio estilo de manera más favorable. Utilice un juez de otra familia de modelos siempre que sea posible.
- Sensibilidad a los prompts. Pequeños cambios en la redacción de las instrucciones afectan las calificaciones. Mantenga los prompts para los jueces bajo control de versiones, al igual que el código.
- Costo y latencia. La utilización de jueces también implica gastos económicos. Cuando el presupuesto es limitado, pruebe con tráfico real de producción y no evalúe todo el contenido.
- Sesgo por extensión y confianza. Los jueces tienden a dar respuestas más largas y seguras, independientemente de si son correctas o no. Calibre periódicamente sus evaluaciones con un pequeño conjunto etiquetado por humanos.
El uso en un solo turno en la práctica
Un conjunto ideal puede ser tan sencillo como una lista de diccionarios que contengan una entrada y un resultado esperado.
goldens = [
{
"input": "What's our refund policy for digital products?",
"expected_output": "Digital products are refundable within 14 days if unused.",
"expected_tools": [],
},
{
"input": "Cancel order #48213",
"expected_output": None, # open-ended, judged not matched
"expected_tools": ["lookup_order", "cancel_order"],
},
]
El ejecutor sigue el bucle descrito anteriormente: inicia una ejecución de prueba, recorre cada caso de prueba, llama a la aplicación, captura el seguimiento, calcula la puntuación y la sube.
from deepeval import evaluate
from deepeval.metrics import FaithfulnessMetric, AnswerRelevancyMetric
from deepeval.test_case import LLMTestCase
def run_eval_suite(goldens, app_callback, metrics):
test_cases = []
for golden in goldens:
trace = app_callback(golden["input"]) # your traced LLM/RAG app
test_case = LLMTestCase(
input=golden["input"],
actual_output=trace.final_output,
retrieval_context=trace.retrieved_chunks,
expected_output=golden.get("expected_output"),
)
test_cases.append(test_case)
results = evaluate(test_cases, metrics) # runs metrics concurrently
return results
metrics = [FaithfulnessMetric(threshold=0.7), AnswerRelevancyMetric(threshold=0.7)]
results = run_eval_suite(goldens, my_rag_app, metrics)
Una afirmación convierte las puntuaciones en una barrera de control.
def assert_eval_gate(results, min_pass_rate=0.9):
passed = sum(1 for r in results if r.success)
pass_rate = passed / len(results)
assert pass_rate >= min_pass_rate, f"Eval pass rate {pass_rate:.2%}
below gate {min_pass_rate:.0%}"
Conéctelo al pipeline de PR de la misma manera en que se conecta pytest. Si un cambio en las instrucciones o en la recuperación de información hace que el nivel de fidelidad baje por debajo del umbral, la fusión debería bloquearse y nada debería enviarse sin que se note.
Múltiples turnos en la práctica
En lugar de una entrada fija, el simulador recibe un perfil de usuario y un objetivo, y es él quien dirige la conversación.
class ConversationSimulator:
def __init__(self, simulator_model, max_turns=8):
self.simulator_model = simulator_model
self.max_turns = max_turns
def simulate(self, golden, chatbot_callback):
transcript = []
user_turn = self.simulator_model.generate_opening(golden["scenario"])
for _ in range(self.max_turns):
transcript.append({"role": "user", "content": user_turn})
assistant_turn = chatbot_callback(transcript)
transcript.append({"role": "assistant", "content": assistant_turn})
if self.simulator_model.is_goal_met(golden["expected_outcome"], transcript):
break
user_turn = self.simulator_model.generate_next_turn(transcript, golden)
return transcript
Luego, la conversación resultante se califica con métricas de agencia.
from deepeval.test_case import ConversationalTestCase, Turn
from deepeval.metrics import (
TaskCompletionMetric,
ToolCorrectnessMetric,
ConversationCompletenessMetric,
)
def build_conversational_test_case(transcript, tools_called):
turns = [Turn(role=t["role"], content=t["content"]) for t in transcript]
return ConversationalTestCase(turns=turns, tools_called=tools_called)
metrics = [
TaskCompletionMetric(threshold=0.8), # did it accomplish the user's goal?
ToolCorrectnessMetric(threshold=1.0), # right tools, right args, right order
ConversationCompletenessMetric(threshold=0.7), # did it address everything raised?
]
test_case = build_conversational_test_case(transcript, tools_called=agent_trace.tool_calls)
result = evaluate([test_case], metrics)
Las verificaciones típicas a nivel de agente incluyen:
- Corrección en las llamadas a herramientas: usar la herramienta adecuada, con los argumentos correctos y en el orden apropiado; no basta con que “se haya llamado a alguna herramienta”.
- Eficiencia de los pasos y detección de bucles: llamadas repetidas a una misma herramienta sin progreso alguno.
- Adherencia al rol y a las políticas: mantenerse dentro de la personalidad y los límites definidos durante toda la conversación, no solo en el primer turno.
- Recuperación de errores: intentar nuevamente de manera sensata tras un fallo en la llamada a una herramienta, en lugar de generar un resultado inventado.
- Transmisión del contexto: recordar lo que dijo el usuario tres turnos antes.
Evaluación de los compromisos
| Un solo turno | Varios turnos | |
|---|---|---|
| Costo | Una llamada por mensaje, económico y rápido |
Dado que los resultados de múltiples turnos son ruidosos, hay que interpretarlos estadísticamente. Ejecute el conjunto de pruebas N veces y analice las distribuciones en lugar de centrarse en una sola puntuación.
Prácticas que funcionan
- Comience con un conjunto pequeño y limpio de 30 a 100 ejemplos. Un conjunto ruidoso de 1,000 es peor que uno ordenado de 50, ya que las etiquetas incorrectas contaminan la información útil.
- Evalúe por separado la recuperación y la generación en RAG. La fidelidad y el recuerdo contextual responden a preguntas diferentes, y combinarlos oculta al verdadero culpable.
- Juzgue con un modelo más robusto e independiente, y verifique ocasionalmente sus puntuaciones en comparación con evaluadores humanos. Incluso 20 a 30 muestras al mes pueden revelar desviaciones en el juicio.
Conclusión
Las evaluaciones para la IA generativa comenzaron como algo secundario, una celda de cuaderno en la que alguien realizaba pruebas antes de una demostración. Los sistemas agentes han superado ese enfoque. Una vez que el software puede actuar varias veces, llamar a herramientas y mantener su estado de una interacción a otra, la evaluación ya no es un proceso de control de calidad. Se convierte en el principal indicador del comportamiento real del sistema.
Las evaluaciones de una sola interacción basadas en seguimiento ofrecen pruebas rápidas y económicas de la calidad de los resultados. Las evaluaciones de múltiples interacciones impulsadas por simuladores proporcionan la única lectura fiable sobre si un agente se comporta como se pretende a lo largo de toda la interacción, y para la mayoría de los agentes en producción esa lectura es lo más importante. Construya ambos tipos de evaluaciones y automatícelas ambas. No confíe ciegamente en ninguno de ellos, y mantenga a un humano para que revise los resultados.
Lecturas relacionadas
- AutoSaddler: Permitir que las huellas de ejecución reescriban la harness de un agente de IA — AutoSaddler diagnostica ejecuciones fallidas de los agentes, corrige su harness, verifica que las soluciones sean generalizables y mantiene un historial de evolución. Se explica la diferencia entre parches de dirección y parches de capacidad.
- Dónde gastan tokens las herramientas de coding agencial en la práctica — Las ventanas de contexto, los bucles de herramientas y las reintentos aumentan el consumo; presupuestos y caché que mantienen las demostraciones asequibles.