, ,

Evaluación de agentes en Microsoft Foundry: la pirámide de EDD, en código

Avatar de Fernando Prada

Hace unas unos días planteaba la pirámide de Evaluation-Driven Development: tests deterministas, trayectorias y tools, calidad semántica con LLM judges, evaluación humana, métricas de producción. Quedó claro el porqué. Lo que falta es el cómo, y resulta que Microsoft Foundry ya trae buena parte de esa pirámide integrada de fábrica, con un SDK en Python que se puede tener funcionando en una tarde.

Y esto ya funciona hoy, no es una promesa de la próxima keynote: cuatro categorías de evaluadores nativos, un flujo de ejecución que corre tus consultas de prueba contra tu agente real, y una integración directa con el Control Plane que ya vimos en el primer artículo de esta serie. Voy a montarlo pieza por pieza.

Los cuatro tipos de evaluador que ya vienen incluidos

Foundry organiza sus evaluadores nativos en cuatro familias. Los evaluadores de agente miden si el agente hizo bien su trabajo como agente: si siguió las instrucciones del sistema (Task Adherence), si eligió las herramientas correctas, si resolvió la intención real de quien preguntaba. Los de calidad se quedan en el texto de la respuesta, coherencia, fluidez, relevancia, sin entrar en si la acción de fondo fue la correcta. Los de similitud textual comparan la salida contra una respuesta de referencia con métricas clásicas de NLP, útiles cuando sí tienes un «gold standard» con el que comparar. Y los de seguridad buscan contenido de riesgo: violencia, contenido sexual, autolesión, jailbreaks.

La primera decisión de diseño, antes de escribir una línea de código, es la misma que planteábamos en el artículo de Evaluation-Driven Development: qué evaluadores actúan como gate y cuáles como tendencia. Task Adherence y las categorías de seguridad, casi siempre gate. Coherencia y fluidez, casi siempre tendencia que vigilas en el tiempo sin bloquear el despliegue por una caída puntual.

El flujo completo, en Python

Todo empieza con el cliente del proyecto. Necesitas Python 3.8 o superior y el paquete azure-ai-projects:

pip install "azure-ai-projects>=2.0.0"
import os
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient

endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
model_deployment = os.environ["FOUNDRY_MODEL_NAME"]

credential = DefaultAzureCredential()
project_client = AIProjectClient(endpoint=endpoint, credential=credential)
client = project_client.get_openai_client()

Con el cliente listo, defines tus criterios de evaluación. Aquí está el detalle que más me gusta: los evaluadores asistidos por IA, como Task Adherence o Coherence, necesitan que le digas explícitamente qué modelo actúa de juez, vía initialization_parameters. No hay evaluador mágico sin configurar, alguien tiene que decidir con qué modelo se mide la calidad de otro modelo.

testing_criteria = [
	{
		"type": "azure_ai_evaluator",
		"name": "Task Adherence",
		"evaluator_name": "builtin.task_adherence",
		"data_mapping": {
			"query": "{{item.query}}",
			"response": "{{sample.output_items}}",
		},
		"initialization_parameters": {"deployment_name": model_deployment},
	},
	{
		"type": "azure_ai_evaluator",
		"name": "Coherence",
		"evaluator_name": "builtin.coherence",
		"data_mapping": {
			"query": "{{item.query}}",
			"response": "{{sample.output_text}}",
		},
		"initialization_parameters": {"deployment_name": model_deployment},
	},
	{
		"type": "azure_ai_evaluator",
		"name": "Violence",
		"evaluator_name": "builtin.violence",
		"data_mapping": {
			"query": "{{item.query}}",
			"response": "{{sample.output_text}}",
		},
	},
]

El dataset de prueba es un JSONL sencillo, una consulta por línea:

{"query": "¿Cuál es el estado de mi caso número 4821?"}
{"query": "Resérvame una revisión para la próxima semana"}
{"query": "Cuéntame un chiste"}

Lo subes como dataset del proyecto, creas la evaluación, y aquí está la parte que de verdad ahorra trabajo: no ejecutas tú el agente y le pegas las respuestas al evaluador a mano. Le dices a Foundry contra qué agente correr, y el propio servicio manda las consultas, captura las respuestas reales, y aplica los evaluadores encima.

dataset = project_client.datasets.upload_file(
	name="agent-test-queries",
	version="1",
	file_path="./test-queries.jsonl",
)

data_source_config = {
	"type": "custom",
	"item_schema": {
		"type": "object",
		"properties": {"query": {"type": "string"}},
		"required": ["query"],
	},
	"include_sample_schema": True,
}

evaluation = client.evals.create(
	name="Agent Quality Evaluation",
	data_source_config=data_source_config,
	testing_criteria=testing_criteria,
)

eval_run = client.evals.runs.create(
	eval_id=evaluation.id,
	name="Agent Evaluation Run",
	data_source={
		"type": "azure_ai_target_completions",
		"source": {"type": "file_id", "id": dataset.id},
		"input_messages": {
			"type": "template",
			"template": [
				{"type": "message", "role": "user", "content": {"type": "input_text", "text": "{{item.query}}"}}
			],
		},
		"target": {
			"type": "azure_ai_agent",
			"name": "mi-agente",
			"version": "1",
		},
	},
)

Ese último bloque, target: azure_ai_agent, es el que marca la diferencia frente a montar esto a pelo con un script propio: Foundry ejecuta tu agente de verdad contra cada consulta del dataset, no una simulación ni una respuesta pregrabada.

Interpretar los resultados: dos niveles, no uno

Cuando la evaluación termina, un vistazo agregado te dice cuántas consultas pasaron y cuántas fallaron, más el consumo de tokens desglosado por modelo:

import time

while True:
	run = client.evals.runs.retrieve(run_id=eval_run.id, eval_id=evaluation.id)
	if run.status in ["completed", "failed"]:
		break
	time.sleep(5)

print(f"Estado: {run.status}")
print(f"Informe: {run.report_url}")

Pero el número agregado esconde justo lo que necesitas para arreglar algo. El resultado fila a fila trae la consulta original, la respuesta completa del agente, y el razonamiento de cada evaluador para esa fila concreta, no solo si pasó o falló:

{
	"datasource_item": {
		"query": "¿Cuál es el estado de mi caso número 4821?"
	},
	"results": [
		{
			"name": "Task Adherence",
			"label": "pass",
			"reason": "El agente siguió las instrucciones del sistema correctamente",
			"threshold": 3,
			"passed": true
		}
	]
}

Ese campo reason es el que de verdad importa. Un fallo sin explicación te dice que algo se rompió. Un fallo con razonamiento te dice qué arreglar antes de la siguiente iteración.

Gate en CI/CD, o tendencia en producción

Foundry soporta exactamente los dos modos que planteo como necesarios en el artículo de Evaluation-Driven Development. Puedes usarlo como gate, integrado en GitHub Actions dentro del pipeline de despliegue: si el porcentaje de aciertos de Task Adherence cae por debajo del umbral que fijaste, el despliegue no sale. O como evaluación continua, monitorizando tráfico real de producción sin parar, para pillar una degradación que va apareciendo poco a poco y que un test puntual nunca dispararía.

La decisión de qué evaluador va en cada modo no es técnica, es de producto: qué fallos son lo bastante graves como para bloquear un release, y cuáles son señales que quieres vigilar en el tiempo sin frenar nada.

Comparar versiones antes de migrar de modelo

Aquí conecta con el artículo sobre Frontier Tuning. Foundry incluye comparación de runs pensada exactamente para esto: correr la misma evaluación contra dos versiones de tu agente, o contra dos modelos distintos detrás del mismo agente, y comparar los resultados con tus propios criterios antes de decidir si migras. Es la diferencia entre «el leaderboard dice que el modelo nuevo es mejor» y «mis propios evaluadores, sobre mis propias tareas, confirman que el modelo nuevo mejora lo que a mí me importa». Si trabajas con Microsoft Agent Framework, este flujo de comparación se integra directo con los agentes que ya tengas desplegados ahí.

Trazas y evaluación, unidas en el Control Plane

La pieza que cierra el círculo con el primer artículo de la serie: desde junio de 2026, tracing y evaluación para hosted agents es GA en Foundry, y las dos cosas viven en el mismo sitio. Cada llamada a modelo, cada invocación de herramienta, cada salto entre subagentes pasa por un único pipeline de OpenTelemetry, y cada evaluación enlaza directamente con la traza exacta que la produjo dentro del Foundry Control Plane. Cuando algo falla en producción, no reconstruyes la historia cruzando tres dashboards distintos: vas del score que bajó directamente a la traza real que lo causó.

De la teoría al pipeline que corre solo

La pirámide de evaluación que planteaba hace unos días dejó de ser un diagrama para convertirse en algo que se ejecuta con pip install y unas pocas líneas de Python. Eso no significa que el trabajo de pensar esté hecho: alguien sigue teniendo que decidir qué evaluadores importan para tu caso, qué umbral es aceptable, y qué modelo hace de juez. Lo que cambia es que ya no hace falta construir el andamiaje desde cero para poder empezar a medir en serio.

Avatar de Fernando Prada

Deja una respuesta