Tester les agents par assertions
Vérifiez les appels aux outils, les étapes intermédiaires et la structure de la sortie finale.
Tester les agents par assertions est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Aller au-delà de la correspondance exacte des chaînes
Comme les sorties des LLM sont non déterministes, les tester avec assert response == 'exact text' est fragile. Écrivez plutôt des assertions qui vérifient la structure et l'intention de la réponse sans dépendre de sa formulation exacte.
Vérifier que des appels d'outils ont été effectués
Pour les agents qui appellent des fonctions, l'assertion la plus fiable consiste à vérifier que l'agent a choisi d'appeler le bon outil. Cette vérification est structurelle : elle ne dépend pas de la formulation exacte du raisonnement du 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')
Vérifier le nom correct de l'outil
Au-delà de la simple vérification de l'existence d'appels d'outils, vérifiez que le nom précis de l'outil correspond à ce qui est attendu. Cela détecte les cas où l'agent choisit le mauvais outil pour une requête donnée.
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')Vérifier les arguments des outils
Après avoir vérifié le nom de l'outil, vérifiez que ses arguments sont corrects. L'agent doit non seulement choisir le bon outil, mais aussi le renseigner avec les bons paramètres issus de la demande de l'utilisateur.
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')
Validation des sorties par schéma JSON
Lorsque votre agent renvoie du JSON structuré, validez la sortie par rapport à un schéma JSON afin de vérifier que tous les champs requis sont présents et possèdent les bons types. La bibliothèque jsonschema facilite cette tâche.
# 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}')Assertions de présence de mots-clés
Pour les réponses textuelles dont la formulation exacte varie, vérifiez que les concepts ou mots importants apparaissent dans la sortie. Cette méthode est souple tout en restant pertinente : la réponse de l'agent doit au moins mentionner les termes concernés.
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!') # passesVérifier le format de la réponse : vérifications de type
Les vérifications de type sont rapides et fiables. Vérifiez que l'agent renvoie un dictionnaire plutôt qu'une valeur nulle, que les champs de type liste sont bien des listes et que les champs numériques se trouvent dans des intervalles valides.
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')Vérifier la raison de fin
Le champ finish_reason vous indique pourquoi le modèle a cessé de générer du contenu. Le vérifier aide à détecter les problèmes : 'stop' signifie que la réponse est terminée normalement, 'tool_calls' signifie que l'agent veut appeler un outil, et 'length' indique une troncature.
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')Vérifier le nombre d'étapes d'une boucle
Un agent qui s'exécute en boucle doit terminer en un nombre raisonnable d'étapes. Vérifiez que l'agent termine avant d'atteindre un nombre maximal d'itérations — cela détecte les boucles infinies que votre garde-fou est censé empêcher.
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')Paramétrer les tests pour plusieurs entrées
@pytest.mark.parametrize de pytest vous permet d'exécuter le même test avec de nombreuses entrées différentes. C'est idéal pour vérifier que votre agent achemine différents types de requêtes vers les bons outils.
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_toolÉcrire des fonctions d'assertion personnalisées
À mesure que votre suite de tests d'agents s'agrandit, regroupez les schémas d'assertion courants dans des fonctions utilitaires. Les tests seront ainsi plus courts, plus lisibles et plus faciles à maintenir lorsque le format des réponses de l'agent changera.
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')
Vérification des connaissances : tests d'agents fondés sur des assertions
Testez votre compréhension des stratégies d'assertion pour les tests d'agents.
Récapitulatif : tests d'agents fondés sur des assertions
Vous disposez maintenant d'une boîte à outils complète d'assertions pour les tests d'agents :
- Vérifiez que
tool_callsn'est pas vide lorsque l'agent doit utiliser un outil - Vérifiez le nom correct de l'outil avec
tc.function.name == 'expected_tool' - Validez les arguments de l'outil en analysant
tc.function.argumentscomme du JSON - Utilisez
jsonschema.validate()pour valider les sorties structurées - Utilisez des vérifications de présence de mots-clés pour des assertions textuelles souples
- Vérifiez
finish_reasonet le nombre d'étapes pour les agents en boucle - Utilisez
@pytest.mark.parametrizepour plusieurs scénarios d'entrée
Questions Fréquemment Posées
La leçon « Tester les agents par assertions » est-elle gratuite ?
Oui — le texte complet de « Tester les agents par assertions » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Tester les agents par assertions » ?
Vérifiez les appels aux outils, les étapes intermédiaires et la structure de la sortie finale. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.
Combien de temps prend la leçon « Tester les agents par assertions » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Pourquoi tester les agents est différent
- Simuler les appels aux LLM dans les tests
- Tester les agents par assertions
- Tests d’intégration pour les pipelines d’agents