Annoter les agents de navigation web : des traces WebArena à l'évaluation humaine
Comment utiliser l'affichage de traces d'agents web de Potato pour évaluer des agents de navigation web autonomes, avec des captures étape par étape, des superpositions SVG et des schémas d'annotation par étape.
Convertissez la trace de l'agent au format d'agent web de Potato, affichez-la avec le visualiseur web_agent_trace pour que chaque capture porte une superposition indiquant où l'agent a cliqué, et notez les étapes avec un schéma trajectory_eval.
Un agent de navigation web se juge sur les actions qu'il effectue sur une page, donc l'évaluation a besoin des deux moitiés de chaque étape, l'état de la page que l'agent a vu et l'action qu'il y a effectuée. Un journal d'actions seul ne suffit pas pour juger. « Clicked element product-101 » n'indique pas au relecteur s'il s'agissait de l'ordinateur portable le moins cher de la page, et une capture sans marqueur le laisse deviner lequel d'une douzaine de liens était la cible.
Ce guide met en place cette évaluation pour des traces WebArena. La même démarche fonctionne pour VisualWebArena, Mind2Web, les journaux d'Anthropic Computer Use et les enregistrements bruts de navigateur, puisqu'un seul convertisseur les couvre tous. Pour la référence complète de configuration, consultez la documentation source.
Ce qu'il vous faut
pip install potato-annotationIl vous faut aussi les traces elles-mêmes, qui pour WebArena sont des captures d'écran plus un journal d'actions JSON par épisode. VisualWebArena enregistre la même structure avec des informations supplémentaires d'ancrage visuel, et les enregistrements bruts de navigateur contiennent à la place des données de viewport et de trajet de la souris.
Ce que contient une trace WebArena brute
Un épisode WebArena contient la description de la tâche, la séquence d'actions et le chemin de chaque capture :
{
"trace_id": "wa_001",
"task": "Find the cheapest laptop on the electronics store and add it to the cart",
"website": "shopping",
"steps": [
{
"step": 0,
"url": "http://shop.example.com/",
"action_type": "click",
"action_target": "Electronics category link",
"element_id": "nav-electronics",
"coordinates": [245, 82],
"screenshot": "screenshots/wa_001_step_00.png"
},
{
"step": 1,
"url": "http://shop.example.com/electronics",
"action_type": "click",
"action_target": "Laptops subcategory",
"element_id": "cat-laptops",
"coordinates": [180, 310],
"screenshot": "screenshots/wa_001_step_01.png"
}
],
"success": true
}Convertir la trace
Le visualiseur de Potato lit un format d'étapes normalisé plutôt que le format propre à chaque framework, donc les épisodes bruts passent d'abord par le convertisseur :
python -m potato.trace_converter -i traces.json -f web_agent -o data/web_traces.jsonlCette commande écrit une instance par épisode, et chaque étape porte step_index, screenshot_url, action_type, l'element cible et sa boîte englobante, coordinates, un mouse_path facultatif, ainsi que le thought et l'observation de l'agent lorsque le journal source les a enregistrés. En remplaçant -f web_agent par --auto-detect, le convertisseur identifie lui-même le format, ce qui est l'option à retenir quand un répertoire contient des traces de plusieurs frameworks.
La configuration du projet
Le visualiseur est un type d'affichage et non un type d'annotation, donc il se place sous instance_display.fields et les schémas d'annotation restent séparés de lui. La taille des captures est le réglage à décider en premier. Avec 900 pixels de large, une page de bureau complète reste lisible sans repousser les contrôles d'annotation sous le bas de l'écran.
annotation_task_name: "WebArena Agent Evaluation"
task_dir: "."
data_files:
- "data/web_traces.jsonl"
item_properties:
id_key: id
text_key: task_description
output_annotation_dir: "output/"
export_annotation_format: "jsonl"
instance_display:
layout:
direction: vertical
gap: 16px
fields:
- key: task_description
type: text
label: "Task"
- key: steps
type: web_agent_trace
label: "Agent Browsing Trace"
display_options:
show_overlays: true
show_filmstrip: true
show_thought: true
show_observation: true
show_element_info: true
screenshot_max_width: 900
screenshot_max_height: 600
filmstrip_size: 80
annotation_schemes:
- annotation_type: radio
name: task_success
description: "Did the agent complete the task?"
labels:
- "Success"
- "Partial success"
- "Failure"
label_requirement:
required: true
- annotation_type: radio
name: task_efficiency
description: "Was the agent's path to the goal efficient?"
labels:
- "Optimal path"
- "Reasonable but not optimal"
- "Inefficient, unnecessary steps"
- "Completely wrong direction"
label_requirement:
required: true
- annotation_type: trajectory_eval
name: step_evaluation
description: "Judge each action against the task"
steps_key: steps
step_text_key: action_type
correctness_options:
- correct
- acceptable
- incorrect
- unnecessary
error_types:
- name: wrong_element_clicked
- name: wrong_page_navigated_to
- name: missed_better_option
- name: incorrect_form_input
- name: premature_completion
- name: unnecessary_navigation
- name: failed_to_scroll
- name: wrong_page_section
- name: other
severities:
- name: minor
weight: -1
- name: major
weight: -5
- name: critical
weight: -10
show_score: true
max_score: 100
user_config:
allow_all_users: trueUn seul schéma trajectory_eval couvre toute la passe par étape. Il affiche chaque étape sous forme de carte et recueille sur chacune la justesse, un type d'erreur, une gravité et une justification écrite, puis calcule un score de qualité de la trace à partir des poids de gravité. Avec les poids ci-dessus, une erreur majeure coûte cinq points à la trace et une erreur critique dix. Fixez-les délibérément, car ils décident du classement d'une trace comportant un échec grave par rapport à une trace comportant quatre petits.
Ce que voit l'annotateur

La capture de l'étape courante occupe le panneau principal, avec des superpositions SVG dessinées par-dessus. Chaque superposition marque une partie différente de l'étape, et leurs couleurs sont fixes :
| Superposition | Marque |
|---|---|
| Cercle rouge avec réticule | Les coordonnées du clic |
| Surbrillance jaune | Le champ dans lequel l'agent a tapé |
| Flèche verte | La direction et l'amplitude d'un défilement |
| Rectangle bleu en pointillés | La boîte englobante de l'élément cible |
| Ligne courbe orange | Le trajet de la souris jusqu'à la cible |
Une pellicule de miniatures court en bas de l'écran, et cliquer sur l'une d'elles mène à l'étape correspondante. Sur les traces de plus d'une dizaine d'étapes, la pellicule fait la différence entre un relecteur qui voit la forme de l'exécution et un relecteur qui fait défiler à l'aveugle. Le panneau de détails de l'étape présente les informations sur l'élément, le raisonnement de l'agent et l'observation qu'il a enregistrée, et chacun peut être désactivé dans display_options quand le journal source n'y contient rien d'utile.
Une rangée de cases à cocher sous le visualiseur active et désactive les superpositions :
| Contrôle | Superposition |
|---|---|
| Clicks | Le réticule rouge du clic |
| Bounding Boxes | Le rectangle bleu en pointillés |
| Mouse Path | La ligne courbe orange |
| Scroll | La flèche verte de défilement |
Désactiver des superpositions compte sur les pages denses, où un marqueur de clic peut recouvrir le texte qui vous dirait si le clic était le bon. Notez que la surbrillance jaune de saisie n'a pas de contrôle propre. Quatre des cinq superpositions se désactivent individuellement, et la surbrillance du champ saisi est toujours dessinée. Ce visualiseur n'a pas de raccourcis clavier, donc la navigation entre étapes passe par la pellicule et les contrôles d'étape.
La passe d'annotation
Un relecteur traite une trace en cinq temps :
- Lire la description de la tâche. Tout ce qui suit est un jugement par rapport à elle.
- Parcourir la pellicule. Saisir la forme de l'exécution avant de noter quoi que ce soit, pour ne pas marquer une étape précoce comme correcte sur la foi d'éléments qui n'arrivent que plus tard.
- Passer les étapes une à une. Pour chacune, lire l'état de la page, regarder la superposition pour voir sur quoi l'agent a agi, et noter l'action. Si elle est incorrecte, choisir le type d'erreur et une gravité, et indiquer dans la justification ce que l'agent aurait dû faire à la place.
- Noter la trace. Juger la réussite globale et l'efficacité une fois toutes les étapes vues.
- Soumettre et passer à la suivante.
Les quatre options de justesse répartissent les étapes selon la ligne qui compte pour les données d'entraînement. Les actions correctes progressent vers l'objectif de la manière attendue. Les actions acceptables progressent par un chemin plus lent, par exemple passer par une page de catégorie au lieu d'utiliser le champ de recherche. Les actions incorrectes sont des erreurs, comme un mauvais élément, une page sans rapport ou un texte erroné dans un formulaire. Les actions inutiles n'aident ni ne nuisent, comme un clic suivi immédiatement d'un retour en arrière, et il vaut la peine de les séparer des erreurs, car un agent qui erre pose un autre problème qu'un agent qui se trompe.
La taxonomie des erreurs
Les types d'erreur sont ce qui transforme une pile de traces ratées en quelque chose sur quoi un développeur d'agents peut agir, donc les catégories doivent correspondre à des causes distinctes plutôt qu'à des degrés d'erreur :
| Type d'erreur | Ce qu'il signifie | Exemple |
|---|---|---|
| Mauvais élément cliqué | L'agent a agi sur le mauvais élément d'interface | A cliqué sur « Tablets » au lieu de « Laptops » |
| Mauvaise page atteinte | L'agent s'est retrouvé à un endroit sans rapport | Est allé sur « About Us » au lieu de la liste de produits |
| Meilleure option manquée | Une meilleure action était disponible | A parcouru les catégories alors que le champ de recherche était sous ses yeux |
| Saisie de formulaire incorrecte | L'agent a saisi le mauvais texte | A cherché « labtop » au lieu de « laptop » |
| Fin prématurée | L'agent a déclaré la réussite trop tôt | A ajouté le mauvais article au panier et s'est arrêté |
| Navigation inutile | L'étape ne contribue pas à l'objectif | A visité la page d'accueil entre deux pages de catégorie |
| Défilement manquant | La cible était sous le viewport | L'élément n'a jamais été visible et l'agent n'a jamais fait défiler |
| Mauvaise section de la page | Bonne page, mauvaise zone | A cliqué sur l'en-tête au lieu du contenu principal |
Traiter les traces difficiles
Sur une trace de plus de quinze étapes, noter chaque étape avec la même profondeur dépense l'essentiel de l'effort sur les étapes qui allaient manifestement bien. Parcourez la pellicule à la recherche des trois motifs qui signalent un problème, puis concentrez l'effort à ces endroits. Ces motifs sont une URL qui change de façon inattendue, une étape où l'agent revient en arrière, et une suite de captures presque identiques, signe habituel d'un agent coincé dans une boucle.
Sur une trace ratée, l'annotation qu'il faut réussir est la première étape incorrecte. Tout ce qui suit se déroule dans un état que l'agent n'aurait jamais dû atteindre, donc les erreurs ultérieures décrivent les conséquences plutôt que la cause.
Certaines actions ne peuvent pas être jugées sur la seule capture, en général quand plusieurs éléments se chevauchent au point du clic. Les informations sur l'élément dans le panneau de l'étape donnent la balise, le texte et la boîte englobante de la cible, ce qui suffit normalement à établir ce qui a réellement été cliqué ; show_element_info garde ce panneau visible.
VisualWebArena
Les traces VisualWebArena passent par le même convertisseur, qui reconnaît les deux formats grâce aux champs action_type et element de leurs étapes d'action. Les informations d'ancrage que portent ces traces apparaissent sous forme de superposition de boîte englobante, donc une passe centrée sur l'ancrage dépend de ce que les annotateurs gardent à l'écran et non d'une modification de la configuration. Laisser Bounding Boxes cochée et décocher Clicks donne la vue qui montre si l'agent a visé le bon élément et pas seulement le bon pixel.
Lire les résultats
Les erreurs des agents web ont tendance à se regrouper à certains points d'une trace, et le taux d'erreur par position d'étape est le moyen le plus rapide de voir où. Chaque annotation trajectory_eval stocke une liste d'enregistrements par étape, chacun avec son step_index et sa correctness :
import json
annotations = []
with open("output/exports/jsonl/annotations.jsonl") as f:
for line in f:
annotations.append(json.loads(line))
step_counts = {}
for ann in annotations:
evaluation = ann["labels"].get("step_evaluation", {})
for step in evaluation.get("steps", []):
pos = step["step_index"]
counts = step_counts.setdefault(pos, {})
label = step.get("correctness", "unrated")
counts[label] = counts.get(label, 0) + 1
print("Error rate by step position:")
for pos in sorted(step_counts):
counts = step_counts[pos]
total = sum(counts.values())
errors = counts.get("incorrect", 0) + counts.get("unnecessary", 0)
print(f" Step {pos}: {errors / total:.1%} error rate ({total} observations)")La distribution des types d'erreur répond à l'autre question, celle de savoir en quoi l'agent se trompe plutôt que quand :
error_counts = {}
for ann in annotations:
evaluation = ann["labels"].get("step_evaluation", {})
for step in evaluation.get("steps", []):
error = step.get("error_type")
if error:
error_counts[error] = error_counts.get(error, 0) + 1
for error, count in sorted(error_counts.items(), key=lambda x: -x[1]):
print(f" {error}: {count}")Passer à l'échelle
Pour un résultat que vous comptez publier, un seul relecteur par trace ne suffit pas, car les jugements par étape portent exactement le genre d'ambiguïté que les statistiques d'accord servent à mesurer. Fixez l'objectif de couverture et l'ordre d'attribution au niveau supérieur de la configuration :
num_annotators_per_item: 3
assignment_strategy: randomComparez ensuite les annotateurs sur l'étiquette au niveau de la trace, celle où un désaccord est sans ambiguïté. Exportez d'abord en Parquet, pour que la comparaison porte sur des colonnes et non sur du JSON analysé :
python -m potato.export -c config.yaml -f parquet -o ./parquet_export/from sklearn.metrics import cohen_kappa_score
import pandas as pd
df = pd.read_parquet("parquet_export/annotations.parquet")
success = df[df["schema_name"] == "task_success"]
pivot = success.pivot(index="instance_id", columns="annotator", values="value")
annotators = pivot.columns.tolist()
for i in range(len(annotators)):
for j in range(i + 1, len(annotators)):
pair = pivot[[annotators[i], annotators[j]]].dropna()
kappa = cohen_kappa_score(pair[annotators[i]], pair[annotators[j]])
print(f"Kappa ({annotators[i]} vs {annotators[j]}): {kappa:.3f}")Sur des évaluations de plusieurs centaines de traces, Solo Mode place un modèle dans la même file que le relecteur humain. Le modèle propose des étiquettes et l'humain les corrige jusqu'à ce qu'ils s'accordent assez souvent pour que le modèle termine le reste :
solo_mode:
enabled: true
labeling_models:
- endpoint_type: "anthropic"
model: "claude-sonnet-4-20250514"
api_key: "${ANTHROPIC_API_KEY}"
thresholds:
end_human_annotation_agreement: 0.90
minimum_validation_sample: 50Le réglage auquel il faut réfléchir est le seuil d'accord, pas le choix du modèle. À 0,90, l'humain continue d'annoter jusqu'à ce que neuf étiquettes du modèle sur dix correspondent aux siennes, mesuré sur au moins cinquante comparaisons, et l'abaisser échange de la qualité d'étiquetage contre des heures de relecture d'une manière difficile à détecter après coup.
Pour aller plus loin
- Annotation agentique, pour la référence complète du convertisseur et de l'affichage
- Annotation d'agents web, pour les options d'affichage du visualiseur
- Évaluer des agents d'IA, pour l'évaluation d'agents au-delà du navigateur
- Export Parquet, pour les colonnes que lit l'analyse ci-dessus