Un agent saute ses outils quand il repond depuis sa memoire parametrique au lieu d'appeler l'outil de recherche ou la base de connaissances que vous lui avez donne. L'appel saute est plus rapide, ne renvoie aucune erreur et s'affiche en vert sur votre tableau de latence. Il est aussi faux, parce que le modele a devine au lieu de s'ancrer. La solution est deterministe : journalisez chaque trace d'appel d'outil, verifiez que l'outil a vraiment ete appele pour les requetes qui exigent des donnees fraiches, et faites echouer l'evaluation quand aucun appel n'a eu lieu.
Pourquoi les tableaux de latence cachent les appels sautes
Un appel d'outil ajoute un aller-retour. Appeler POST /api/v1/google, attendre les resultats, puis les renvoyer au modele coute quelques centaines de millisecondes et un credit. Quand l'agent saute ca et repond directement depuis ses poids, la requete est plus rapide et moins chere. Votre p50 baisse. Le taux d'erreur reste a zero, parce que deviner n'est pas une erreur que le runtime peut voir.
Le tableau recompense donc exactement l'echec qui vous importe. Une requete factuelle qui aurait du declencher une recherche mais ne l'a pas fait parait identique a un tour de conversation qui n'avait legitimement besoin d'aucun outil. La latence, le taux d'erreur et le nombre de tokens ne les distinguent pas. Le signal dont vous avez besoin, c'est de savoir si l'outil s'est declenche, et ca vit dans la trace, pas dans les metriques.
Validez l'usage des outils de facon deterministe, pas avec un juge LLM
Pour la question "l'agent a-t-il appele l'outil", une verification deterministe l'emporte sur un juge LLM. Le juge est un autre modele qui peut halluciner, coute des tokens et est lent. Mais vous avez deja la verite terrain : la trace d'appel. Soit un appel a votre outil de recherche est dans la trace, soit il n'y est pas. Verifiez ca.
La verification la plus forte a deux parties. D'abord, verifiez que l'outil a ete appele. Ensuite, verifiez que la reponse finale reference vraiment quelque chose que l'outil a renvoye, pour que l'agent ne puisse pas appeler l'outil puis l'ignorer. Avec l'endpoint Google de Scavio, vous recevez des resultats organic avec de vraies URLs, donc vous pouvez verifier que la reponse en cite une.
import requests
API_KEY = "sk-your-key"
def search(query):
r = requests.post(
"https://api.scavio.dev/api/v1/google",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"query": query, "light_request": False},
)
r.raise_for_status()
return r.json()
def assert_grounded(query, agent_answer, tool_calls):
# 1) deterministe : l'outil de recherche a vraiment ete appele
assert any(c["name"] == "search" for c in tool_calls), \
f"agent skipped search for fact query: {query!r}"
# 2) deterministe : la reponse cite une URL que l'outil a renvoyee
results = search(query)
returned_urls = [item["link"] for item in results.get("organic", [])]
assert any(url in agent_answer for url in returned_urls), \
"answer does not reference any retrieved source"
# cas d'evaluation exemple
assert_grounded(
query="latest stable python release",
agent_answer="Python 3.13 is current. Source: https://www.python.org/downloads/",
tool_calls=[{"name": "search", "args": {"query": "latest stable python release"}}],
)Aucun modele dans la boucle. La verification passe ou echoue sur des faits que vous pouvez voir.
Forcez l'ancrage pour les requetes factuelles
Detecter un appel saute apres coup, c'est bien pour les evaluations. L'empecher en production, c'est mieux. Pour les requetes dont vous savez qu'elles exigent des donnees fraiches, forcez l'appel de recherche au lieu de le laisser au jugement du modele.
La plupart des frameworks d'agents exposent tool_choice. Mettez-le a required (ou nommez votre outil de recherche explicitement) pour le chemin des requetes factuelles, afin que le modele doive appeler POST /api/v1/google et repondre depuis de vrais resultats organiques avant de pouvoir repondre. Vous ne demandez pas a l'agent de decider si l'ancrage compte. Vous decidez pour lui sur les cas ou vous savez deja que la reponse ne peut pas venir des donnees d'entrainement.
Cela echange un peu de latence et un credit par appel contre une reponse vraiment a jour. Sur un chemin factuel, c'est l'echange que vous voulez a chaque fois.
La limite honnete
La validation deterministe des appels d'outils l'emporte sur un juge LLM pour "l'a-t-il appele", mais elle ne vous dit pas quelles requetes avaient besoin d'un outil au depart. Quelqu'un doit etiqueter ca. Votre jeu d'evaluation a besoin de cas marques comme exigeant un outil, pour que l'assertion sache quand un appel manquant est un bug et quand aucun outil n'etait correctement requis.
Cet etiquetage, c'est le vrai travail. Construisez un jeu de requetes factuelles ou la bonne reponse depend de donnees actuelles, marquez chacune comme outil-requis, et lancez l'assertion en deux parties contre chacune. La verification deterministe est bon marche et exacte une fois que vous avez les etiquettes. Obtenir les etiquettes, c'est la partie qu'aucune automatisation ne fait pour vous.