Skip to content

Log full agent invocation trace with intermediate tool calls - #30

Open
mborne wants to merge 3 commits into
masterfrom
28-write-trace
Open

Log full agent invocation trace with intermediate tool calls#30
mborne wants to merge 3 commits into
masterfrom
28-write-trace

Conversation

@mborne

@mborne mborne commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces conversation tracing for agent-based tests, allowing each test's agent interactions to be recorded and saved under reports/<model>/<test>.txt. This makes it easier to debug and understand model behavior during test runs. The implementation includes new helper functions for safe file naming and trace writing, updates to the test agent fixture, and comprehensive tests for the new functionality.

Agent conversation tracing and reporting:

  • Added a RecordingAgent proxy and updated the mcp_agent fixture to record all agent invocations during a test, writing the conversation trace to reports/<model>/<test>.txt on teardown, regardless of test success or failure. This includes storing the test status for trace annotation. (conftest.py, conftest.pyL100-R147)
  • Implemented the write_agent_trace helper function to serialize agent conversations, ensure safe file paths, and create report files only when there is content to write. (helpers.py, helpers.pyR11-R61)
  • Added the slugify helper to generate safe directory and file names from model and test names, removing provider prefixes and illegal characters. (helpers.py, helpers.pyR11-R61)

Documentation updates:

  • Updated CODING.md and README.md to document the new tracing feature, including usage instructions and the purpose of the generated reports. (CODING.md, [1]; README.md, [2]

Testing improvements:

  • Added unit tests for slugify and write_agent_trace to ensure correct file naming and trace writing, including edge cases where no results are present. (test_helpers.py, test_helpers.pyR436-R474)

These changes provide a robust and testable mechanism for inspecting agent/model behavior during test runs, improving debuggability and transparency.

(closes #28)

…calls in reports/{model_name}/{test_name}.txt (refs #28)
@mborne

mborne commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Here is an example of the reports/claude-haiku-4-5/test_count_batiment_30m_angouleme.txt generated file (ping @esgn ) :

# model  : anthropic:claude-haiku-4-5
# test   : test_count_batiment_30m_angouleme
# status : passed
================================ Human Message =================================

Combien y a t il de batiments de plus de 30 mètres sur la commune d'Angoulême? 
================================== Ai Message ==================================

[{'text': "Je vais vous aider à trouver le nombre de bâtiments de plus de 30 mètres sur la commune d'Angoulême. Commençons par géocoder la commune.", 'type': 'text'}, {'id': 'toolu_01Pz4E1BVib7TtfgWdBxfuXe', 'caller': {'type': 'direct'}, 'input': {'text': 'Angoulême'}, 'name': 'geocode', 'type': 'tool_use'}]
Tool Calls:
  geocode (toolu_01Pz4E1BVib7TtfgWdBxfuXe)
 Call ID: toolu_01Pz4E1BVib7TtfgWdBxfuXe
  Args:
    text: Angoulême
================================= Tool Message =================================
Name: geocode

[{'type': 'text', 'text': '{"results":[{"lon":0.151689,"lat":45.647622,"fulltext":"Angoulême, 16000","kind":"administratif","zipcode":"16000"},{"lon":0.148008,"lat":45.644105,"fulltext":"Angoulême, 16000 Angoulême","kind":"municipality","city":"Angoulême","zipcode":"16000"},{"lon":0.150299,"lat":45.645185,"fulltext":"Angoulême, 16000 Angoulême","kind":"lieu-dit habité","city":"Angoulême","zipcode":"16000"}]}', 'id': 'lc_618e8938-dd39-43d9-9f3f-2ce7426e5d47'}]
================================== Ai Message ==================================

[{'text': "Maintenant, je vais récupérer les informations administratives d'Angoulême pour obtenir son code INSEE et son emprise géographique.", 'type': 'text'}, {'id': 'toolu_01CuDoqoCt6XHjeqgd7x3PBt', 'caller': {'type': 'direct'}, 'input': {'lon': 0.151689, 'lat': 45.647622}, 'name': 'adminexpress', 'type': 'tool_use'}]
Tool Calls:
  adminexpress (toolu_01CuDoqoCt6XHjeqgd7x3PBt)
 Call ID: toolu_01CuDoqoCt6XHjeqgd7x3PBt
  Args:
    lon: 0.151689
    lat: 45.647622
================================= Tool Message =================================
Name: adminexpress

[{'type': 'text', 'text': '{"results":[{"cleabs":"ARR_DEP_0000000000000161","nom_officiel":"Angoulême","nom_officiel_en_majuscules":"ANGOULEME","numero_de_l_arrondissement":"1","code_insee":"161","code_insee_du_departement":"16","code_insee_de_la_region":"75","type":"arrondissement","id":"arrondissement.144","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:arrondissement","feature_id":"arrondissement.144"}},{"cleabs":"CANTON__0000002490608439","nom_officiel":"Angoulême-1","nom_officiel_en_majuscules":"ANGOULEME-1","numero_du_canton":"01","code_insee":"1601","codes_insee_des_arrondissements":"161","code_insee_du_departement":"16","code_insee_de_la_region":"75","composition_du_canton":"Fraction d\'une commune et de commune(s) entière(s)","type":"canton","id":"canton.1902","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:canton","feature_id":"canton.1902"}},{"cleabs":"COLLTERR000000000000016D","nom_officiel":"Charente","nom_officiel_en_majuscules":"CHARENTE","code_insee":"16D","code_insee_de_la_region":"75","code_siren":"221600018","type":"collectivite_territoriale","id":"collectivite_territoriale.95","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:collectivite_territoriale","feature_id":"collectivite_territoriale.95"}},{"cleabs":"COMMUNE_0000000000016015","nom_officiel":"Angoulême","nom_officiel_en_majuscules":"ANGOULEME","statut":"Préfecture","code_insee":"16015","population":41908,"date_du_recensement":"2023-01-01Z","organisme_recenseur":"INSEE","code_insee_du_canton":"1698","code_insee_de_l_arrondissement":"161","code_insee_du_departement":"16","code_insee_de_la_region":"75","code_siren":"211600150","codes_siren_des_epci":"200071827","code_postal":"16000","superficie_cadastrale":2190,"type":"commune","id":"commune.5277","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:commune","feature_id":"commune.5277"}},{"cleabs":"DEPARTEM0000000000000016","nom_officiel":"Charente","nom_officiel_en_majuscules":"CHARENTE","code_insee":"16","code_insee_de_la_region":"75","code_siren":"171600018","type":"departement","id":"departement.50","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:departement","feature_id":"departement.50"}},{"cleabs":"EPCI____0000000200071827","nom_officiel":"CA du Grand Angoulême","nom_officiel_en_majuscules":"CA DU GRAND ANGOULEME","nature":"Communauté d\'agglomération","codes_insee_des_communes_membres":"16015/16019/16026/16055/16061/16078/16101/16113/16119/16120/16138/16146/16154/16166/16168/16187/16199/16210/16232/16236/16244/16263/16271/16287/16291/16341/16348/16358/16368/16370/16374/16382/16385/16388/16415/16418/16420/16422","codes_insee_des_departements_membres":"16","code_siren":"200071827","type":"epci","id":"epci.646","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:epci","feature_id":"epci.646"}},{"cleabs":"REGION__0000000000000075","nom_officiel":"Nouvelle-Aquitaine","nom_officiel_en_majuscules":"NOUVELLE-AQUITAINE","code_insee":"75","code_siren":"173300005","type":"region","id":"region.11","feature_ref":{"typename":"ADMINEXPRESS-COG.LATEST:region","feature_id":"region.11"}}]}', 'id': 'lc_b1d64fdd-c7b5-427e-bb33-a81ca14e6ef7'}]
================================== Ai Message ==================================

[{'text': "Parfait ! J'ai trouvé que le code INSEE d'Angoulême est 16015. Maintenant, je vais rechercher les données de bâtiments pour pouvoir interroger ceux de plus de 30 mètres.", 'type': 'text'}, {'id': 'toolu_0142KxQGzS3KBM2GJeHvXJKb', 'caller': {'type': 'direct'}, 'input': {'query': 'bâtiment hauteur', 'max_results': 10}, 'name': 'gpf_wfs_search_types', 'type': 'tool_use'}]
Tool Calls:
  gpf_wfs_search_types (toolu_0142KxQGzS3KBM2GJeHvXJKb)
 Call ID: toolu_0142KxQGzS3KBM2GJeHvXJKb
  Args:
    query: bâtiment hauteur
    max_results: 10
================================= Tool Message =================================
Name: gpf_wfs_search_types

[{'type': 'text', 'text': '{"results":[{"id":"BDTOPO_V3:batiment","title":"Bâtiment","description":"Construction au-dessus du sol qui est utilisée pour abriter des humains, des animaux, des objets, pour la production de biens économiques ou pour la prestation de services et qui se réfère à toute structure construite ou érigée de façon permanente sur son site.","score":210.23723478521248},{"id":"CADASTRALPARCELS.PARCELLAIRE_EXPRESS:batiment","title":"PCI Vecteur : Bâtiments","description":"Bâtiments","score":74.22237981346841},{"id":"BDTOPO_V3:reservoir","title":"Réservoir","description":"Réservoir (eau, matières industrielles,…)","score":33.71685332457126},{"id":"BDTOPO_V3:erp","title":"ERP","description":"Les établissements recevant du public (ERP) sont des bâtiments dans lesquels des personnes extérieures sont admises.","score":23.454052072920405},{"id":"BDTOPO_V3:construction_ponctuelle","title":"Construction ponctuelle","description":"Construction de faible emprise, caractérisée par sa hauteur (plus haute que large).","score":18.31707933354171},{"id":"BDTOPO_V3:plan_d_eau","title":"Plan d\'eau","description":"Une ou plusieurs étendue(s) d’eau continentale de surface, d’origine naturelle ou anthropique, possédant un toponyme ou identifiée(s) par un code hydrographique.","score":8.508830263308472},{"id":"BDTOPO_V3:pylone","title":"Pylône","description":"Support en charpente métallique ou en béton, d’une ligne électrique aérienne.","score":7.795365708958033},{"id":"BDTOPO_V3:haie","title":"Haie","description":"Axe d\'une haie.\\r\\nLa Haie est définie comme une clôture naturelle composée d’arbres, d’arbustes, d’épines ou de branchages et servant à délimiter ou à protéger un champ.","score":7.449432709556386},{"id":"BDTOPO_V3:troncon_de_route","title":"Tronçon de route","description":"","score":5.072623806839074}]}', 'id': 'lc_c29229de-9ad8-46da-8231-5d8e8fbb91bb'}]
================================== Ai Message ==================================

[{'text': 'Excellent ! Je vais utiliser le type BDTOPO_V3:batiment qui contient les bâtiments. Maintenant, je vais décrire ce type pour voir quelles propriétés sont disponibles, notamment pour la hauteur.', 'type': 'text'}, {'id': 'toolu_01JCXXymGGwWpBAAjEQ7eEcY', 'caller': {'type': 'direct'}, 'input': {'typename': 'BDTOPO_V3:batiment'}, 'name': 'gpf_wfs_describe_type', 'type': 'tool_use'}]
Tool Calls:
  gpf_wfs_describe_type (toolu_01JCXXymGGwWpBAAjEQ7eEcY)
 Call ID: toolu_01JCXXymGGwWpBAAjEQ7eEcY
  Args:
    typename: BDTOPO_V3:batiment
================================= Tool Message =================================
Name: gpf_wfs_describe_type

[{'type': 'text', 'text': '{"id":"BDTOPO_V3:batiment","namespace":"BDTOPO_V3","name":"batiment","title":"Bâtiment","description":"Construction au-dessus du sol qui est utilisée pour abriter des humains, des animaux, des objets, pour la production de biens économiques ou pour la prestation de services et qui se réfère à toute structure construite ou érigée de façon permanente sur son site.","properties":[{"name":"cleabs","type":"string","title":"Cleabs","description":"Identifiant unique de l\'objet."},{"name":"nature","type":"string","title":"Nature","description":"Attribut permettant de distinguer différents types de bâtiments selon leur architecture.","enum":["Arc de triomphe","Arène ou théâtre antique","Chapelle","Château","Eglise","Fort, blockhaus, casemate","Indifférenciée","Industriel, agricole ou commercial","Monument","Moulin à vent","Serre","Silo","Tour, donjon","Tribune"]},{"name":"usage_1","type":"string","title":"Usage 1","description":"Usage du bâtiment ou d\'une partie du bâtiment.","enum":["Agricole","Annexe","Commercial et services","Indifférencié","Industriel","Religieux","Résidentiel","Sportif"]},{"name":"usage_2","type":"string","title":"Usage 2","description":"Autre usage d\'un bâtiment de fonction mixte.","enum":["Agricole","Annexe","Commercial et services","Indifférencié","Industriel","Religieux","Résidentiel","Sportif"]},{"name":"construction_legere","type":"boolean","title":"Construction légère","description":"Indique qu\'il s\'agit d\'une structure légère, non attachée au sol par l\'intermédiaire de fondations, ou d\'un bâtiment ou partie de bâtiment ouvert sur au moins un côté."},{"name":"etat_de_l_objet","type":"string","title":"Etat de l\'objet bati","description":"État ou stade de vie d\'un objet qui, pour le thème bâti, peut être en projet, en construction, en service ou en ruine.","enum":["En construction","En projet","En ruine","En service"]},{"name":"date_creation","type":"string","title":"Date de création","description":"Date et heure à laquelle l\'objet a été saisi pour la première fois dans la base de données de production de l\'IGN."},{"name":"date_modification","type":"string","title":"Date de modification","description":"Date et heure à laquelle l\'objet a été modifié pour la dernière fois dans la base de données de production."},{"name":"date_d_apparition","type":"string","title":"Date d\'apparition","description":"Date de création, de construction ou d\'apparition de l\'objet, ou date la plus ancienne à laquelle on peut attester de sa présence sur le terrain."},{"name":"date_de_confirmation","type":"string","title":"Date de confirmation","description":"Date la plus récente à laquelle on peut attester de la présence de l\'objet sur le terrain ou par des données externes de référence."},{"name":"sources","type":"string","title":"Sources","description":"Sources attestant l\'existence de l\'objet, éventuellement dans le cadre d\'un partenariat : organisme, administration, fichier de référence."},{"name":"identifiants_sources","type":"string","title":"Identifiants sources","description":"Identifiants de l\'objet dans les répertoires des organismes consultés pour leur inventaire."},{"name":"methode_d_acquisition_planimetrique","type":"string","title":"Méthode d\'acquisition planimétrique","description":"Méthode principale ayant permis d\'acquérir la géométrie planimétrique de l\'objet.","enum":["BDCarto","BDNyme","BDParcellaire","BDParcellaire recalée","BDTopo","Calcul d\'enveloppe urbaine","Calculé","Fichier numérique décimétrique","Fichier numérique métrique","Fichier numérique non métrique","Géocodage","Géoroute","Image satellite","Image satellite HR","Inconnue","Levé GPS","Levé non GPS","Lidar","Lidar HD","Orthophotographie","Photogrammétrie","Plan métrique papier","Plan non métrique papier","Processus IA métrique","Processus IA non métrique","Scan25"]},{"name":"methode_d_acquisition_altimetrique","type":"string","title":"Méthode d\'acquisition altimétrique","description":"Méthode principale ayant permis d\'acquérir l\'altimétrie (Z)  de l\'objet.","enum":["BDAlti","BDTopo","Corrélation","Fichier numérique","Fichier numérique décimétrique","Interpolation bâti BDTopo","Levé GPS","Lidar","Lidar HD","Pas de Z","Photogrammétrie","Photogrammétrie longue focale","Plan coté","Radar","Z corrigé"]},{"name":"precision_planimetrique","type":"float","title":"Précision planimétrique","description":"Précision planimétrique  (en mètres) de la géométrie décrivant l\'objet"},{"name":"precision_altimetrique","type":"float","title":"Précision altimétrique","description":"Précision altimétrique  (en mètres) de la géométrie décrivant l\'objet."},{"name":"nombre_de_logements","type":"integer","title":"Nombre de logements","description":"Nombre de logement dans le bâtiment."},{"name":"nombre_d_etages","type":"integer","title":"Nombre d\'étages","description":"Nombre total d\'étages du bâtiment, rez-de-chaussée compris. Les sous-sol ne sont pas comptés."},{"name":"materiaux_des_murs","type":"string","title":"Matériaux des murs","description":"Code indiquant les matériaux des murs du bâtiment, issu des informations contenues dans les fichiers fonciers."},{"name":"materiaux_de_la_toiture","type":"string","title":"Matériaux de la toiture","description":"Code indiquant les matériaux de la toiture du bâtiment, issu des informations contenues dans les fichiers fonciers."},{"name":"hauteur","type":"float","title":"Hauteur","description":"Hauteur du bâtiment mesurée entre le sol et le point haut de la gouttière (altitude maximum de la polyligne décrivant le bâtiment), exprimée en mètres."},{"name":"altitude_minimale_sol","type":"float","title":"Altitude minimale sol","description":"Altitude représentative au pied du bâtiment, du côté bas de la pente, exprimée en mètres."},{"name":"altitude_minimale_toit","type":"float","title":"Altitude minimale toit","description":"Altitude du toit du bâtiment au niveau de l\'arête décrivant son contour, exprimée en mètres."},{"name":"altitude_maximale_toit","type":"float","title":"Altitude maximale toit","description":"Altitude qui correspond à la hauteur maximale du toit, c’est-à-dire au faîte du toit, exprimée en mètres."},{"name":"altitude_maximale_sol","type":"float","title":"Altitude maximale sol","description":"Altitude qui correspond à l’altitude maximale située au pied d’un bâtiment, exprimée en mètres."},{"name":"origine_du_batiment","type":"string","title":"Origine du bâtiment","description":"Précise l\'origine de la géométrie du Bâtiment ou Réservoir.","enum":["Autre","Cadastre","Imagerie aérienne","Lidar HD"]},{"name":"appariement_fichiers_fonciers","type":"string","title":"Appariement fichiers fonciers","description":"Indicateur relatif à la fiabilité de l\'appariement avec les fichiers fonciers.\\r\\n\\r\\nCe chiffre exprime un taux de concordance entre la BD TOPO® et les fichiers fonciers basé sur plusieurs critères dont les principaux sont la surface, l\'adresse, la nature et la hauteur."},{"name":"identifiants_rnb","type":"string","title":"Identifiants RNB","description":"Concaténation des identifiants du Référentiel National des Bâtiments  Référentiel National des Bâtiments (<a href=\\"https://rnb.beta.gouv.fr/definition\\">RNB</a>) associés au bâtiment BD TOPO®."},{"name":"geometrie","type":"multipolygon","defaultCrs":"EPSG:4326"}]}', 'id': 'lc_1217fad1-beca-4a55-935f-37d15b9e6a18'}]
================================== Ai Message ==================================

[{'text': 'Parfait ! Je vois qu\'il y a une propriété "hauteur" qui est exactement ce qu\'il me faut. Maintenant je vais interroger les bâtiments d\'Angoulême avec une hauteur supérieure à 30 mètres.', 'type': 'text'}, {'id': 'toolu_01L1eSygvnZtAesGQKWDV8mg', 'caller': {'type': 'direct'}, 'input': {'typename': 'BDTOPO_V3:batiment', 'select': ['hauteur', 'nature', 'usage_1'], 'where': [{'property': 'hauteur', 'operator': 'gt', 'value': '30'}], 'spatial_operator': 'intersects_feature', 'intersects_feature_typename': 'ADMINEXPRESS-COG.LATEST:commune', 'intersects_feature_id': 'commune.5277', 'result_type': 'hits'}, 'name': 'gpf_wfs_get_features', 'type': 'tool_use'}]
Tool Calls:
  gpf_wfs_get_features (toolu_01L1eSygvnZtAesGQKWDV8mg)
 Call ID: toolu_01L1eSygvnZtAesGQKWDV8mg
  Args:
    typename: BDTOPO_V3:batiment
    select: ['hauteur', 'nature', 'usage_1']
    where: [{'property': 'hauteur', 'operator': 'gt', 'value': '30'}]
    spatial_operator: intersects_feature
    intersects_feature_typename: ADMINEXPRESS-COG.LATEST:commune
    intersects_feature_id: commune.5277
    result_type: hits
================================= Tool Message =================================
Name: gpf_wfs_get_features

[{'type': 'text', 'text': '{"result_type":"hits","totalFeatures":19}', 'id': 'lc_348eb960-5b58-4093-8529-d61d96aa4ac1'}]
================================== Ai Message ==================================

Parfait ! J'ai la réponse à votre question.

**Il y a 19 bâtiments de plus de 30 mètres sur la commune d'Angoulême.**

Ces bâtiments sont identifiés dans la base de données BDTOPO de l'IGN (Institut Géographique National) et leur hauteur est mesurée entre le sol et le point haut de la gouttière.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds per-test conversation tracing for the MCP agent used in the test suite, writing serialized agent interactions to reports/<model>/<test>.txt to improve debuggability (including tool-call visibility via LangChain message rendering).

Changes:

  • Add slugify() and write_agent_trace() helpers to safely generate trace paths and write conversation logs.
  • Wrap the session-scoped agent with a per-test RecordingAgent and write traces during fixture teardown, annotated with test outcome.
  • Document the new reports/ traces and add unit tests for the helper behavior.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
helpers.py Adds filename sanitization and trace serialization/writing helper.
conftest.py Updates MCP agent fixture to record per-test invocations and emit trace files on teardown.
test_helpers.py Adds unit tests for slugify() and write_agent_trace().
README.md Documents that tests produce per-test conversation trace files.
CODING.md Adds usage notes and an example for inspecting trace output.
.gitignore Ensures generated reports/<model>/... traces are not versioned.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread conftest.py
Comment thread helpers.py Outdated
Comment thread README.md Outdated
mborne and others added 2 commits July 27, 2026 23:47
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@mborne

mborne commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@tgerbeau you may use the following command to test :

uv run scripts/run_tests.py .\config\models-anthropic.yaml --model=claude-haiku-4-5

...and then have a look to reports/claude-haiku-4-5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trace full MCP execution path and intermediate tool calls

3 participants