Pruebas de agentes basadas en aserciones
Compruebe las llamadas a herramientas, los pasos intermedios y la estructura de la salida final.
Pruebas de agentes basadas en aserciones es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
Más allá de la coincidencia exacta de cadenas
Dado que las salidas de los LLM no son deterministas, probarlas con assert response == 'exact text' resulta frágil. En su lugar, escriba aserciones que comprueben la estructura y la intención de la respuesta sin depender de una redacción exacta.
Comprobación de que se realizaron llamadas a herramientas
En los agentes que realizan llamadas a funciones, la aserción más fiable consiste en verificar que el agente eligió llamar a la herramienta correcta. Esta comprobación es estructural: no depende de la redacción exacta del proceso de razonamiento del LLM.
import json
from unittest.mock import patch, MagicMock
@patch('myagent.client.chat.completions.create')
def test_agent_calls_search_tool(mock_create):
# Mock: agent decides to call search_web
tool_call = MagicMock()
tool_call.function.name = 'search_web'
tool_call.function.arguments = json.dumps({'query': 'Python tutorials'})
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
)
response = mock_create() # simulating the agent call
tc = response.choices[0].message.tool_calls
assert tc is not None
assert len(tc) > 0
assert tc[0].function.name == 'search_web'
# --- demo: give unittest.mock.patch a real dotted path to patch ---
import sys
import types
_myagent = types.ModuleType('myagent')
_myagent.client = types.SimpleNamespace(
chat=types.SimpleNamespace(completions=types.SimpleNamespace(create=lambda *a, **k: None))
)
sys.modules['myagent'] = _myagent
test_agent_calls_search_tool()
print('test_agent_calls_search_tool: PASS')
Comprobación del nombre correcto de la herramienta
Además de comprobar que existen llamadas a herramientas, verifique que el nombre específico de la herramienta coincide con lo esperado. Esto detecta los casos en los que el agente elige la herramienta equivocada para una consulta determinada.
import json
from unittest.mock import MagicMock
def extract_tool_calls(response) -> list:
message = response.choices[0].message
if not message.tool_calls:
return []
return [
{
'name': tc.function.name,
'args': json.loads(tc.function.arguments)
}
for tc in message.tool_calls
]
# In a test:
# calls = extract_tool_calls(mock_response)
# assert calls[0]['name'] == 'get_weather'
# assert calls[0]['args']['city'] == 'Paris'
print('Tool name and argument assertions are the most reliable agent tests')Comprobación de los argumentos de la herramienta
Después de verificar el nombre de la herramienta, compruebe que los argumentos son correctos. El agente no solo debe elegir la herramienta adecuada, sino también completarla con los parámetros correctos de la solicitud del usuario.
import json
from unittest.mock import patch, MagicMock
@patch('myagent.client.chat.completions.create')
def test_weather_tool_gets_correct_city(mock_create):
tool_call = MagicMock()
tool_call.function.name = 'get_weather'
tool_call.function.arguments = json.dumps({'city': 'Tokyo', 'unit': 'celsius'})
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
)
response = mock_create()
args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
assert args['city'] == 'Tokyo'
assert args.get('unit') in ['celsius', 'fahrenheit', None] # flexible
# --- demo: give unittest.mock.patch a real dotted path to patch ---
import sys
import types
_myagent = types.ModuleType('myagent')
_myagent.client = types.SimpleNamespace(
chat=types.SimpleNamespace(completions=types.SimpleNamespace(create=lambda *a, **k: None))
)
sys.modules['myagent'] = _myagent
test_weather_tool_gets_correct_city()
print('test_weather_tool_gets_correct_city: PASS')
Validación de salidas mediante JSON Schema
Cuando su agente devuelve JSON estructurado, valide la salida con un JSON Schema para asegurarse de que todos los campos obligatorios están presentes y tienen los tipos correctos. La biblioteca jsonschema facilita esta tarea.
# pip install jsonschema
import jsonschema
AGENT_RESPONSE_SCHEMA = {
'type': 'object',
'required': ['answer', 'sources', 'confidence'],
'properties': {
'answer': {'type': 'string', 'minLength': 1},
'sources': {
'type': 'array',
'items': {'type': 'string', 'format': 'uri'}
},
'confidence': {'type': 'number', 'minimum': 0, 'maximum': 1}
}
}
def test_agent_output_schema(agent_output: dict):
try:
jsonschema.validate(instance=agent_output, schema=AGENT_RESPONSE_SCHEMA)
print('Schema validation passed')
except jsonschema.ValidationError as e:
raise AssertionError(f'Invalid agent output: {e.message}')Aserciones sobre la presencia de palabras clave
En las respuestas de texto cuya redacción exacta puede variar, compruebe que los conceptos o las palabras clave aparecen en la salida. Es una comprobación flexible pero significativa: la respuesta del agente debe mencionar al menos los términos relevantes.
def assert_keywords_present(text: str, keywords: list, require_all: bool = True):
lower_text = text.lower()
found = [kw.lower() in lower_text for kw in keywords]
if require_all:
missing = [kw for kw, f in zip(keywords, found) if not f]
assert not missing, f'Missing keywords: {missing}'
else:
assert any(found), f'None of {keywords} found in: {text[:100]}'
# Tests
response = 'The capital city of France is Paris, located in western Europe.'
assert_keywords_present(response, ['paris', 'france', 'capital'])
print('All keywords present!') # passes
assert_keywords_present(response, ['spain', 'france'], require_all=False)
print('At least one keyword present!') # passesComprobación del formato de la respuesta: tipos
Las aserciones de tipos son rápidas y fiables. Verifique que el agente devuelve un dict y no None, que los campos de tipo lista son listas y que los campos numéricos están dentro de rangos válidos.
def test_agent_returns_valid_structure(agent_result):
# Type checks
assert isinstance(agent_result, dict), 'Result must be a dict'
assert isinstance(agent_result.get('answer'), str), 'answer must be a string'
assert isinstance(agent_result.get('steps'), list), 'steps must be a list'
# Non-empty checks
assert len(agent_result['answer']) > 0, 'answer must not be empty'
assert len(agent_result['steps']) >= 1, 'must have at least one step'
# Range checks
confidence = agent_result.get('confidence', 0)
assert 0.0 <= confidence <= 1.0, 'confidence must be 0-1'
print('Structural assertions are fast and reliable')Comprobación de finish_reason
El campo finish_reason indica por qué el modelo dejó de generar contenido. Comprobarlo ayuda a detectar problemas: 'stop' significa que la respuesta terminó correctamente, 'tool_calls' significa que el agente quiere llamar a una herramienta y 'length' significa que la respuesta se truncó.
from unittest.mock import MagicMock
def test_agent_stops_cleanly(mock_response):
finish_reason = mock_response.choices[0].finish_reason
assert finish_reason in ('stop', 'tool_calls'), \
f'Unexpected finish_reason: {finish_reason}'
def test_no_truncation(mock_response):
finish_reason = mock_response.choices[0].finish_reason
assert finish_reason != 'length', \
'Response was truncated — increase max_tokens'
# Example mock for a clean stop
mock = MagicMock()
mock.choices = [MagicMock(finish_reason='stop')]
test_agent_stops_cleanly(mock)
print('finish_reason: stop — clean termination')Comprobación del número de pasos de un bucle
Un agente que se ejecuta en un bucle debe completarse en un número razonable de pasos. Compruebe que el agente termina dentro de un número máximo de iteraciones; esto detecta bucles infinitos que la protección max_iterations debe evitar.
def test_agent_completes_in_bounded_steps(mock_agent):
result = mock_agent.run('Search for the weather in Paris')
# Agent should complete within 5 steps
assert result['steps_taken'] <= 5, \
f'Agent took too many steps: {result["steps_taken"]}'
# Agent should produce a final answer, not exit on timeout
assert result['status'] == 'completed', \
f'Agent did not complete: {result["status"]}'
assert result['answer'] is not None
print('Bounding step count prevents runaway agents from passing tests')Parametrización de pruebas para múltiples entradas
@pytest.mark.parametrize de pytest permite ejecutar la misma prueba con muchas entradas diferentes. Es ideal para comprobar que su agente dirige distintos tipos de consultas a las herramientas correctas.
import pytest
from unittest.mock import patch, MagicMock
import json
@pytest.mark.parametrize('query,expected_tool', [
('What is the weather in Tokyo?', 'get_weather'),
('Calculate 15% of 200', 'calculator'),
('Search for Python books', 'web_search'),
('What time is it in Berlin?', 'get_time'),
])
@patch('myagent.client.chat.completions.create')
def test_agent_tool_routing(mock_create, query, expected_tool):
tool_call = MagicMock()
tool_call.function.name = expected_tool
tool_call.function.arguments = json.dumps({'input': query})
mock_create.return_value = MagicMock(
choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))]
)
response = mock_create()
actual = response.choices[0].message.tool_calls[0].function.name
assert actual == expected_toolEscritura de funciones auxiliares de aserción personalizadas
A medida que crezca su suite de pruebas de agentes, extraiga los patrones de aserción comunes a funciones auxiliares. Así las pruebas serán más cortas, legibles y fáciles de mantener cuando cambie el formato de respuesta del agente.
import json
def assert_tool_called(response, tool_name: str, required_args: dict = None):
message = response.choices[0].message
assert message.tool_calls, 'Expected tool call but got plain text'
names = [tc.function.name for tc in message.tool_calls]
assert tool_name in names, f'Expected {tool_name}, got {names}'
if required_args:
for tc in message.tool_calls:
if tc.function.name == tool_name:
args = json.loads(tc.function.arguments)
for key, val in required_args.items():
assert args.get(key) == val, \
f'Arg {key}: expected {val}, got {args.get(key)}'
# Clean test using the helper:
# assert_tool_called(response, 'get_weather', {'city': 'Paris'})
# --- demo ---
from unittest.mock import MagicMock
tool_call = MagicMock()
tool_call.function.name = 'get_weather'
tool_call.function.arguments = json.dumps({'city': 'Paris'})
response = MagicMock(choices=[MagicMock(message=MagicMock(tool_calls=[tool_call]))])
assert_tool_called(response, 'get_weather', {'city': 'Paris'})
print('assert_tool_called passed: agent called get_weather with city=Paris')
Comprobación de conocimientos: pruebas de agentes basadas en aserciones
Compruebe sus conocimientos sobre las estrategias de aserción para las pruebas de agentes.
Recapitulación: pruebas de agentes basadas en aserciones
Ahora dispone de un conjunto completo de herramientas de aserción para las pruebas de agentes:
- Compruebe que
tool_callsno está vacío cuando el agente debe usar una herramienta - Compruebe el nombre correcto de la herramienta con
tc.function.name == 'expected_tool' - Valide los argumentos de la herramienta analizando
tc.function.argumentscomo JSON - Use
jsonschema.validate()para validar salidas estructuradas - Use comprobaciones de presencia de palabras clave para realizar aserciones flexibles sobre texto
- Compruebe
finish_reasony el número de pasos en los agentes que usan bucles - Use
@pytest.mark.parametrizepara varios escenarios de entrada
Preguntas frecuentes
¿La lección «Pruebas de agentes basadas en aserciones» es gratis?
Sí — el texto completo de «Pruebas de agentes basadas en aserciones» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Pruebas de agentes basadas en aserciones»?
Compruebe las llamadas a herramientas, los pasos intermedios y la estructura de la salida final. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Pruebas de agentes basadas en aserciones»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Por qué probar agentes es diferente
- Simulación de llamadas a LLM en pruebas
- Pruebas de agentes basadas en aserciones
- Pruebas de integración para pipelines de agentes