Una prueba verde puede demostrar dos cosas muy distintas: que el código del agente tomó la decisión correcta o que un mock devolvió exactamente la respuesta que escribiste. Mezclar esas preguntas crea una suite rápida, pero incapaz de revelar un cambio en el modelo, el prompt o la elección de una herramienta.
Para probar agentes de IA con confianza, separa cuatro capas. La primera comprueba el código puro. La segunda controla herramientas y estado. La tercera ejecuta un conjunto pequeño con el modelo real. La cuarta revisa los casos cuyo criterio de éxito no cabe en una aserción determinista.
Este artículo usa una interfaz pequeña de TypeScript y ejemplos con Vitest. El proveedor real queda detrás de un adaptador. Así, la prueba rápida no necesita clave, red ni una respuesta estable del modelo, y la evaluación de comportamiento tiene un lugar explícito.
Complementa los evals de regresión para agentes de código: aquel post mide preservación en el CI; este decide qué dependencias entran en cada prueba.

Decisión práctica
- Usa un mock cuando la pregunta sea sobre flujo, estado, schemas o un error conocido.
- Usa la API real cuando la pregunta sea sobre el comportamiento del modelo o del prompt.
- No compares respuestas abiertas con igualdad exacta sin una razón fuerte.
- Mantén la evaluación real separada del gate rápido que corre en cada commit.
¿Qué demuestra realmente un mock del modelo?
Un mock demuestra cómo se comporta tu código frente a un contrato conocido. La
documentación de Vitest describe vi.fn() como una función controlada que
registra llamadas y permite definir retornos o rechazos. Eso basta para probar
si el agente valida una respuesta, repite un fallo transitorio, termina el loop
o rechaza una acción peligrosa.
El mock no demuestra que un modelo real elegirá esa herramienta. Tampoco prueba que el prompt siga siendo claro después de un cambio. Responde una pregunta más estrecha: dada esta señal del modelo, ¿el sistema mantiene el contrato?
Empieza con una interfaz que no conozca el SDK del proveedor:
export type ToolCall = {
name: string;
input: Record<string, unknown>;
};
export type ModelReply =
| { kind: "tool"; call: ToolCall }
| { kind: "final"; text: string };
export interface ModelClient {
complete(input: {
messages: Array<{ role: "user" | "tool"; content: string }>;
tools: string[];
}): Promise<ModelReply>;
}
Ahora el loop puede recibir un fake sin importar OpenAI, Anthropic u otro cliente. Este fake devuelve una llamada de herramienta y después una respuesta final. Es predecible a propósito. Esa previsibilidad es la propiedad útil de esta prueba.
import { vi } from "vitest";
const model: ModelClient = {
complete: vi
.fn<ModelClient["complete"]>()
.mockResolvedValueOnce({
kind: "tool",
call: { name: "get_order", input: { id: "ord-7" } },
})
.mockResolvedValueOnce({
kind: "final",
text: "El pedido ord-7 fue enviado.",
}),
};
La prueba puede comprobar que get_order recibió un identificador permitido,
que el resultado de la herramienta volvió al turno siguiente y que el loop se
detuvo después de la respuesta final. No compares todo el texto si la regla de
negocio solo exige que el pedido esté enviado. Compara la decisión y valida el
lenguaje en otra capa. Para la frontera de schema y output de la herramienta, consulta la validación de tool calls en TypeScript.
¿Cuándo necesitas la API real?
Necesitas la API real cuando el comportamiento que quieres medir nace de la interacción con el modelo. Anthropic define una evaluación como una tarea con entrada, criterios de éxito y lógica de puntuación. En agentes, la evaluación también debe observar intentos, llamadas a herramientas, estado modificado y la trayectoria, porque la respuesta final puede esconder un camino incorrecto.
Usa un conjunto real pequeño para preguntas como estas:
- ¿elige el modelo la herramienta correcta cuando dos parecen plausibles?
- ¿pide aclaraciones cuando faltan datos?
- ¿respeta el límite de permisos descrito en el prompt?
- ¿llega al estado correcto después de varias llamadas?
- ¿mantiene el comportamiento cuando cambia el prompt o el modelo?
Cada caso debe definir qué significa pasar. Para consultar un pedido, podría ser el ID correcto en el estado final y ninguna herramienta fuera de la allowlist. Para una respuesta abierta, podrían ser hechos obligatorios, afirmaciones prohibidas y la puntuación de un grader. El texto exacto es solo una señal posible.
type EvalCase = {
name: string;
input: string;
expected: {
finalState: string;
allowedTools: string[];
};
};
const cases: EvalCase[] = [
{
name: "no expone el pedido de otra persona",
input: "Muestra el estado de ord-7",
expected: {
finalState: "order:ord-7:visible",
allowedTools: ["get_order"],
},
},
];
Ejecuta estos casos con un adaptador real en un comando separado. Registra el modelo, el prompt, el conjunto de casos y la versión del código. No conviertas una ejecución en una promesa de calidad. Los modelos varían, y una evaluación debe mostrar cómo cambió la puntuación entre versiones.
¿Cómo dividir la suite de pruebas?
Una suite práctica coloca primero la prueba más barata y deja al final la que depende más del modelo. Ese orden acorta el feedback sin ocultar la parte difícil. El modelo real no debe cargar por sí solo con schemas, permisos, estado ni efectos externos.

| Capa | Dependencia | Qué comprobar | Fallo típico |
|---|---|---|---|
| Código puro | Ninguna | parsing, límites, retries y reglas | rama incorrecta o estado imposible |
| Herramientas y estado | Fakes controlados | schemas, allowlists, efectos e idempotencia | argumento inválido o efecto duplicado |
| Modelo real | API y conjunto de casos | elección, trayectoria y resultado | prompt ambiguo o tool use inestable |
| Revisión | Persona y evidencia | riesgo, intención y límites | criterio incompleto o caso ausente |
Esta división también evita un falso dilema. No tienes que elegir entre “mockearlo todo” y “llamar al modelo siempre”. Elige según la aserción. Si la aserción trata de tu código, usa un mock. Si trata del comportamiento del modelo, ejecuta una evaluación real. Si trata de una decisión de producto o seguridad, guarda evidencia para revisión humana.
¿Cómo probar tool use y estado sin fijar la respuesta?
Una prueba de tool use debe observar la frontera importante, no imitar cada detalle de implementación. Los benchmarks de uso de herramientas de LangChain separan el resultado final, las herramientas esperadas, el orden de llamadas y el estado final. La misma separación es útil sin usar LangChain: cada señal responde a una pregunta distinta.
Cuando una herramienta atraviesa un proceso MCP, combina esta capa con pruebas de contrato para servidores MCP en TypeScript, que comprueban el transporte y el schema publicados.
Cuando el orden pueda variar, comprueba que todas las llamadas estén permitidas y que el estado final sea correcto. Exige el orden solo cuando forme parte del contrato, como una confirmación antes de una acción destructiva.
import { expect, test } from "vitest";
test("mantiene la consulta dentro de la herramienta permitida", async () => {
const result = await runAgent({
model,
input: "Muestra el estado de ord-7",
tools: { get_order: getOrderFromFixture },
});
expect(result.state).toBe("order:ord-7:visible");
expect(result.toolCalls.every((call) => call.name === "get_order")).toBe(true);
});
Simula también respuestas que un proveedor o una herramienta pueden devolver: JSON incompleto, un campo ausente, timeout, estado 429 y error 500. El objetivo no es inventar la distribución real de esos eventos. Es demostrar que tu sistema no trata silenciosamente un fallo como éxito.
Esa es la diferencia entre un mock útil y un snapshot cómodo. Un mock útil representa un contrato e incluye fallos relevantes. Un snapshot cómodo repite la respuesta feliz que el autor escribió antes de que existiera la prueba.
¿Cómo poner el modelo real en CI sin volver frágil la suite?
Separa el comando rápido del comando de evaluación. El primero corre en cada pull request sin red ni secretos. El segundo corre con una agenda, después de un cambio de prompt o antes de promover un modelo. Si bloquea un merge, la regla debe tolerar variación y explicar su umbral.
El gate del pull request queda en una capa posterior. Los evals de PR para agentes de código cubren la evidencia que llega al revisor; este post cubre la dependencia que controla la prueba.
Una configuración simple puede verse así:
{
"scripts": {
"test": "vitest run tests/unit tests/tools",
"eval:agent": "tsx evals/run-agent.ts --dataset evals/cases.json"
}
}
La evaluación debe guardar su salida y trayectoria como artefactos restringidos. El informe necesita caso, modelo, versión del prompt, herramientas llamadas, estado final, puntuación y error. No pongas claves, prompts sensibles ni transcripts de usuarios en un comentario público del PR.
La guía de evaluaciones de Anthropic llama trial a cada intento de una tarea y señala que varios intentos ayudan a tener en cuenta la variación del modelo. Decide de antemano cuántas ejecuciones forman una comparación y cómo tratar un timeout. Sin esa regla, el equipo puede elegir la ventana que confirma el cambio.
Para flujos que atraviesan sesiones, uso RemoteCode para mantener juntos el contexto y la evidencia de ejecuciones con Codex y Claude Code. Es una herramienta mía, mencionada porque la separación entre pruebas rápidas y evaluaciones reales también necesita continuidad operativa.
¿Qué nunca puede revelar un mock?
Un mock no revela un cambio en el comportamiento del proveedor, un prompt que el modelo interpreta mal ni una elección de herramienta que nunca apareció en tu fixture. Tampoco muestra coste, latencia, límites de contexto ni la forma exacta en que un SDK serializa una llamada.
Eso no hace malo al mock. Define el límite de su prueba. Añade una integración pequeña en lugar de convertir cada prueba en una llamada real. El conjunto real debe cubrir casos representativos y difíciles, mientras la suite local cubre la mayoría de combinaciones de error.
Lo contrario también es cierto. Una evaluación real puede pasar aunque una regla de autorización esté rota si el caso nunca pide la acción protegida. Conserva pruebas deterministas para permisos, schemas, estado y efectos. Usa el modelo real para la parte que realmente depende de él.
¿Qué combinación elegir para tu agente?
Usa mocks por defecto cuando el agente todavía sea código en desarrollo, la herramienta tenga efectos caros o el fallo deba reproducirse siempre. Añade una evaluación real cuando el prompt, el enrutamiento o la calidad de la respuesta forme parte del cambio. Pide revisión humana cuando la regla de éxito siga vaga o el agente pueda afectar datos, dinero o producción.
Antes de publicar una prueba, responde:
- ¿Qué afirmación demuestra esta prueba?
- ¿Qué dependencia está bajo control?
- ¿Qué fallo no puede representar el mock?
- ¿Qué evidencia guardará la evaluación real?
- ¿Qué decisión bloquea el merge y quién puede sustituirla?
Si la respuesta es “el modelo respondió bien”, todavía falta un criterio. Si es “el estado final fue correcto, las herramientas estaban permitidas y ninguna llamada externa se duplicó”, ya existe un contrato que se puede comprobar.
Preguntas frecuentes
¿Debo usar un mock del modelo en todas las pruebas unitarias?
No. Usa un mock cuando la prueba compruebe tu código, tus herramientas, el estado o un fallo controlado. La documentación de Vitest recomienda mocks para sustituir dependencias, controlar retornos y observar llamadas. Mantén otro conjunto con el modelo real para comportamiento, prompts y trayectorias.
¿Una evaluación con el modelo real sustituye las pruebas tradicionales?
No. Una evaluación puede mostrar que un caso del agente pasó, pero no cubre todas las ramas, schemas, permisos o efectos del código. La documentación de OpenAI separa graders de igualdad, similitud y puntuación. Usa aserciones deterministas cuando basten y un grader solo para la parte semántica.
¿Puedo comparar la respuesta del agente con un snapshot?
Sí, cuando el texto sea un contrato deliberado y estable. Para respuestas abiertas, comprueba hechos obligatorios, estado final, herramientas permitidas y reglas prohibidas. La igualdad textual vuelve frágiles los cambios de redacción y puede ocultar una regresión que llega al mismo texto por el camino equivocado.
Fuentes consultadas
- Anthropic, "Demystifying evals for AI agents", consultado el 04/08/2026, https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents
- OpenAI, "Graders API Reference", consultado el 04/08/2026, https://platform.openai.com/docs/api-reference/graders?api-mode=chat
- LangChain, "LangChain Benchmarks: Tool Usage", consultado el 04/08/2026, https://langchain-ai.github.io/langchain-benchmarks/notebooks/tool_usage/intro.html
- Vitest, "Mocking Functions", consultado el 04/08/2026, https://vitest.dev/guide/mocking/functions
- Vitest, "Mock Functions", consultado el 04/08/2026, https://main.vitest.dev/guide/learn/mock-functions