Enterprise RAG Challenge 3 : enseignements tirés des soumissions publiques

Traduction automatique

Cet article a été traduit automatiquement depuis la version originale en anglais.

L’Enterprise RAG Challenge 3 (ERC3) demandait à des agents d’accomplir des tâches métier via l’API d’une entreprise simulée. Le classement figé des prix est particulièrement instructif, car de nombreux participants ont publié davantage qu’un score : architecture, combinaison de modèles, coût et notes sur les échecs.

J’ai étudié ces descriptions publiques pour répondre à une question plus précise : quels choix de conception revenaient dans les soumissions performantes, et lesquels sont utiles au-delà de ce benchmark ?

Cet article s’adresse aux ingénieurs qui construisent des agents appelant des APIs métier. Vous y verrez cinq conceptions tirées des comptes rendus publics, les problèmes que chacune résolvait (permissions, pagination, liens manquants, taille du contexte) et un ordre dans lequel les essayer dans votre propre agent.

Qu’est-ce que l’Enterprise RAG Challenge ?

L’Enterprise RAG Challenge 3 est un projet de recherche participatif à grande échelle qui évalue la façon dont des agents AI autonomes gèrent des tâches métier complexes. Contrairement aux benchmarks statiques, ERC3 s’exécute sur l’Agentic Enterprise Simulation (AGES), une simulation à événements discrets qui expose une API d’entreprise réaliste.

Ce que teste le benchmark

Dans AGES, les agents travaillent au sein d’une entreprise fictive qui comprend :

  • Des profils d’employés avec des compétences et des services précis
  • Des projets avec des affectations d’équipe et des relations clients
  • Un wiki d’entreprise avec des règles métier et des hiérarchies de permissions
  • Le suivi du temps et les opérations financières

Chaque tâche lance une simulation isolée. Le wiki de l’entreprise est partagé, mais les données opérationnelles varient d’une tâche à l’autre : un agent ne peut donc pas réussir la suite en mémorisant l’état d’une seule entreprise.

Lire les scores comme un instantané

ERC3 expose désormais un classement figé de la compétition et un benchmark public qui a continué de recevoir des exécutions après l’événement. Ces deux pages répondent à des questions différentes. Les chiffres ci-dessous décrivent le classement des prix à la date limite de la compétition, et non les meilleures sessions ultérieures :

IndicateurInstantané de la compétition
Soumissions aux prix38
Jeu de tâches103 tâches métier
Meilleur score des prix0.718
Date limite des prix9 décembre 2025, 13 h 40 CET

La page du benchmark en ligne peut afficher des scores plus élevés, car elle inclut les exécutions ultérieures. Le classement figé est donc la bonne source pour dire ce qui a gagné la compétition. Le classement Ultimate retient la meilleure exécution de chaque compte sans date limite de compétition ; ses scores ne sont donc pas comparables à ceux des prix.

Types de tâches

Les tâches couvrent plusieurs domaines de compétences :

  • Le raisonnement multi-sauts, par exemple faire correspondre les compétences des employés aux affectations de projets.
  • La validation des permissions, par exemple bloquer les modifications de salaire ou les accès aux données non autorisés.
  • Les requêtes ambiguës, y compris les demandes multilingues et reformulées.
  • Le respect strict du format de sortie, y compris les liens d’entités obligatoires dans les réponses.

Ce que suggèrent les soumissions

Les comptes rendus publics ne permettent pas de trancher nettement, par exemple en faveur de « multi-agent bat agent unique ». L’entrée classée quatrième aux prix, MMzXeM, était un agent unique avec des outils correspondant 1:1 aux endpoints de l’API, un outil ask_wiki et un system prompt de 320 tokens. En revanche, ces comptes rendus appuient quatre observations plus modestes :

  1. Les équipes ont isolé un composant lorsqu’il portait une exigence que les agents ratent sans cesse : une vérification de permission, la validation d’étapes, l’exécution de code ou les liens d’entités. Elles ne l’ont pas fait selon des « rôles d’agents » arbitraires.
  2. La validation s’est rapprochée des actions irréversibles. Plusieurs systèmes vérifiaient les permissions avant l’exécution, relisaient chaque étape ou contrôlaient la réponse finale.
  3. Les traces en échec ont guidé les changements. Le vainqueur a converti des exécutions échouées en révisions de prompt grâce à une boucle automatisée ; d’autres équipes ont documenté des correctifs d’outils et de prompts tout aussi concrets.
  4. La politique de contexte était un choix d’architecture. Les équipes ont essayé la distillation, le préchargement, la recherche et la compression de l’historique. Leurs propres rapports ne s’accordent pas sur l’utilité de la compression : il n’existe donc pas de recette universelle.

Cinq approches instructives

Ce ne sont pas les cinq premières dans l’ordre du classement. Je les ai choisies parce que leurs descriptions publiques exposent cinq façons distinctes de construire le système : la révision automatisée du prompt, les étapes spécialisées, la validation à chaque étape, les guards sur la réponse et l’isolation plan-execute.

ÉquipeContexte du classementScore publié
VZS9FLPrix, 1er0.718
LcnxuyPrix, 8e0.505
NLN7DwPrix, 2e0.621
J8GvbiPrix, 16e0.437
5qsp7i (key_concept_parallel)Ultimate, 3e0.670

1. Prompt engineering évolutionnaire (équipe VZS9FL / @aostrikov)

L’approche ayant obtenu le meilleur score a automatisé le prompt engineering grâce à une boucle d’auto-amélioration.

Versions du prompt de v1 à v80, et un tour de boucle : l’agent principal journalise les tâches échouées, l’analyseur propose des causes, l’agent de versions en garde certaines et écrit la version suivanteVersions du prompt de v1 à v80, et un tour de boucle : l’agent principal journalise les tâches échouées, l’analyseur propose des causes, l’agent de versions en garde certaines et écrit la version suivante

Au lieu d’ajuster à la main le prompt de production, l’équipe a construit une boucle de trois agents qui transformait les traces en échec en révisions candidates.

Pipeline à trois agents :

AgentRôle
Agent principalExécute le benchmark, journalise toutes les actions et tous les échecs
Agent analyseurExamine les tâches échouées, formule des hypothèses sur les causes profondes
Agent versionneurGénère une nouvelle version du prompt qui intègre les enseignements

Le prompt de production était la 80e version générée automatiquement. L’équipe décrit la boucle comme l’analyse des tâches échouées, la proposition de causes et le choix des suggestions à intégrer. Le classement établit le score final et le nombre d’itérations. Il n’isole pas la part du gain qui vient de l’automatisation plutôt que des modèles, des outils ou des retours accumulés du benchmark.

Stack : claude-opus-4.5 avec le SDK Python d’Anthropic et le tool use natif.


2. Pipeline séquentiel multi-agent (équipe Lcnxuy / @andrey_aiweapps)

Cette soumission a construit un workflow séquentiel dans lequel des composants spécialisés se chargeaient des contrôles de sécurité, de l’extraction du contexte, de l’exécution et de la mise en forme des liens d’entités.

Quatre étapes, du contrôle de sécurité à l’outil de réponse, les cinq phases d’exécution, et deux réponses : l’une sans ses liens, l’autre avec des liens construits dans l’outil de réponseQuatre étapes, du contrôle de sécurité à l’outil de réponse, les cinq phases d’exécution, et deux réponses : l’une sans ses liens, l’autre avec des liens construits dans l’outil de réponse

Les composants documentés :

  1. Security Gate Agent : vérification préalable à l’exécution, qui valide les permissions d’après les règles du wiki avant le lancement de la boucle principale.
  2. Context Extraction Agent : extrait les règles essentielles de system prompts de plus de 500 lignes et précharge les données de l’utilisateur, du projet et du client.
  3. Execution Agent : planification de type ReAct en 5 phases internes (Identité → Détection des menaces → Collecte d’informations → Validation des accès → Exécution).
  4. LinkGeneratorAgent : intégré à l’outil de réponse, il analyse le contexte pour inclure les liens d’entités requis.

Selon le compte rendu de l’équipe, le LinkGeneratorAgent servait à éviter les échecs par liens manquants : des réponses dont le texte était correct, mais sans les liens d’entités requis. C’est la partie la plus transposable. En l’intégrant à l’outil de réponse, on transforme une exigence du benchmark (les liens d’entités obligatoires) en propriété de l’interface, au lieu d’en faire une instruction de plus que le modèle d’exécution peut oublier.

Stack : les frameworks atomic-agents et instructor avec gpt-5.1-codex-max, gpt-4.1 et claude-sonnet-4.5.


3. Raisonnement guidé par schéma avec validation des étapes (équipe NLN7Dw / Ilia Ris)

Le raisonnement guidé par schéma (SGR) oblige le modèle à remplir, à chaque étape, un schéma de sortie fixe, par exemple son raisonnement, un plan et le prochain tool call, afin que le programme puisse lire et vérifier chaque champ. L’article sur le SGR de ce blog montre comment imposer un tel schéma avec vLLM. Cette équipe a associé le SGR à une inférence rapide et à un validateur sur chaque étape proposée. Cette conception rend la révision peu coûteuse : on rejette une étape défectueuse avant qu’elle ne devienne un tool call, puis on demande au flux principal de la retravailler avec les commentaires du validateur.

Le modèle lit le plan, les enregistrements et l’historique compressé, écrit une étape sous forme de schéma, et le StepValidator renvoie une étape défectueuse avant tout tool callLe modèle lit le plan, les enregistrements et l’historique compressé, écrit une étape sous forme de schéma, et le StepValidator renvoie une étape défectueuse avant tout tool call

Composants clés :

ComposantFonction
StepValidatorInspecte chaque étape proposée. En cas d’anomalie, la renvoie pour révision avec des commentaires.
Gestion du contextePlan complet du tour précédent, plus un historique compressé pour les tours plus anciens
Enrichissement dynamiqueRécupère automatiquement profil utilisateur, projets et clients ; un LLM filtre pour n’injecter que l’utile à la tâche
Wrappers d’auto-paginationTous les endpoints de liste renvoient automatiquement des résultats complets

L’équipe a indiqué avoir exécuté gpt-oss-120b sur Cerebras à environ 3 000 tokens par seconde au maximum. Elle a associé la validation à une inférence à haut débit, ce qui a peut-être réduit le coût de latence. Le résultat public n’isole pas cet effet.

Stack : un agent NextStep modifié, l’agent d’exemple SGR publié pour ERC3.


4. Système d’enrichissement et de guards (équipe J8Gvbi / @mishka)

Cette soumission a ajouté à une base SGR des indications non bloquantes et un système de guards à plusieurs niveaux. Au fur et à mesure que les réponses de l’API revenaient, des enrichers les inspectaient et ajoutaient des consignes opérationnelles au contexte suivant. Les guards contrôlaient la réponse de l’agent avant son envoi.

Deux enrichers transforment les réponses d’API en indications, puis trois modes de guard : le blocage strict rejette, le blocage souple accepte une réponse répétée, l’indication souple accepte avec des conseilsDeux enrichers transforment les réponses d’API en indications, puis trois modes de guard : le blocage strict rejette, le blocage souple accepte une réponse répétée, l’indication souple accepte avec des conseils

Plus de 20 enrichers inspectaient les réponses de l’API et injectaient des indications contextuelles :

RoleEnricher: "You are LEAD of this project, proceed with update."
PaginationHintEnricher: "next_offset=5 means MORE results! MUST paginate."

Système de guards à trois modes :

ModeComportement
Blocage strictUne réponse que les données de l’API montrent impossible, par exemple qui nomme un employé extérieur à l’équipe du projet, est rejetée
Blocage soupleUne réponse risquée est rejetée une fois avec un avertissement, puis acceptée si l’agent la soumet de nouveau après cet avertissement
Indication soupleUne consigne est ajoutée sans blocage

Wiki RAG hybride : l’équipe interrogeait une copie locale du wiki de l’entreprise de trois façons : des regex pour les motifs structurés comme salary|privacy, des embeddings de sentence-transformer pour les correspondances sémantiques et le recoupement de tokens comme recours par mots-clés. Mon interprétation de ces trois voies : les regex trouvent les noms de champs et les termes exacts, et les embeddings trouvent les règles formulées autrement que la question.

Stack : qwen/qwen3-235b-a22b-2507 sur le framework SGR de LangChain.


5. REPL plan-execute (équipe 5qsp7i / key_concept_parallel)

Cette architecture séparait la planification et l’écriture du code dans des appels de modèle distincts. Le Python généré s’exécute dans un REPL (read-eval-print loop) : une session Python qui conserve ses variables d’une étape à l’autre. Elle figure dans le classement Ultimate, plus large, et non dans le top cinq figé des prix. Sa description publique reste utile, car elle montre une autre forme de décomposition : une isolation par phase d’exécution plutôt que par rôle métier.

Planificateur, modèle de code, REPL et modèle de décision avec leurs issues, et une trace en quatre étapes où une replanification réécrit les étapes 3 et 4 mais garde les variables REPL s1 et s2Planificateur, modèle de code, REPL et modèle de décision avec leurs issues, et une trace en quatre étapes où une replanification réécrit les étapes 3 et 4 mais garde les variables REPL s1 et s2

Des modèles différents se partageaient les tâches : l’un planifiait, l’autre écrivait le Python, et un modèle de décision distinct choisissait quoi faire après chaque étape.

Configuration multi-modèle :

ÉtapeModèle
Planificationopenai/gpt-5.1
Génération de codedeepseek/deepseek-v3.2
Décision après l’étapeopenai/gpt-4.1
Réponse finaleopenai/gpt-4.1

Le REPL de fin d’étape :

  1. Le planificateur crée une étape de haut niveau.
  2. Le modèle de génération de code, dans un contexte de modèle neuf, écrit pour elle un script Python.
  3. Le script s’exécute dans un REPL propre à la tâche, dont les variables persistent d’une étape à l’autre.
  4. Le modèle de décision examine le résultat et choisit l’une de quatre issues : continuer avec l’étape suivante, terminer parce que la tâche est faite, abandonner parce qu’elle a échoué, ou replanifier.

Le chemin de replanification est l’idée réutilisable. Quand le résultat d’une étape montre que le plan ne convient plus, le modèle de décision peut conserver le travail terminé et réécrire seulement les étapes restantes du plan. Dans le diagramme, « terminé » et « échoué » partagent une même sortie vers la réponse finale.


Les motifs récurrents dans les soumissions

Les implémentations différaient, mais plusieurs préoccupations d’ingénierie revenaient sans cesse dans les descriptions publiques.

La gestion du contexte était explicite

Aucune équipe ne pouvait donner au modèle toutes les règles, tous les enregistrements et toutes les étapes précédentes sans faire un choix de politique. Les systèmes différaient par l’endroit où ils filtraient l’information : avant l’exécution, à la recherche ou dans l’historique. Le diagramme découpe le contexte du modèle en quatre parties et montre quelle politique contrôle chacune, quand elle s’applique et quelles équipes l’ont utilisée.

Le contexte découpé en règles du wiki, enregistrements, résultats de recherche et historique des tours, chacun avec sa politique, le moment où elle s’applique et les équipes qui l’ont utiliséeLe contexte découpé en règles du wiki, enregistrements, résultats de recherche et historique des tours, chacun avec sa politique, le moment où elle s’applique et les équipes qui l’ont utilisée

StratégieApprocheIdéale pour
Distillation des règlesPrétraiter les règles du wiki en instructions compactes en préservant les contraintesPrompts légers, démarrage rapide
Préchargement agressifCharger les données utilisateur/projet/client avant l’exécutionMinimiser les tool calls
RAG hybrideFlux de recherche regex + sémantique + mots-clésBesoins de recherche complexes
Compression de l’historiqueGarder les tours récents en entier, compresser l’historique plus ancienConversations longues

Compromis : NLN7Dw a compressé les anciens tours, tandis que f1Uixf a rapporté que la compression de l’historique avait nui à ses expériences et a gardé toute la conversation. Il faut traiter la compression comme un choix mesuré, pas comme un réglage par défaut. Les entrées solides différaient aussi par la taille du prompt. Kc7F2N (3e aux prix) gardait ses propres instructions minimales, s’appuyait sur des règles de wiki distillées et construisait ses outils de recherche sur des embeddings. MMzXeM (4e aux prix) limitait le prompt principal à 320 tokens et plaçait tout le wiki derrière son outil ask_wiki, qui comptait sur le context caching pour rester peu coûteux.


Des contrôles avant, pendant et après la boucle principale

Plusieurs équipes ont placé des vérifications avant, pendant ou après la boucle principale. Ces vérifications nécessitent des données différentes à chaque étape. Une implémentation commune peut les effectuer si elle reçoit les bonnes entrées à chaque étape. Un critique appelé uniquement pour la réponse finale ne peut pas empêcher un tool call non autorisé survenu plus tôt. Le diagramme situe chaque vérification sur une frise allant de la tâche à l’évaluateur et montre ce qu’elle voit et le défaut qu’elle empêche.

Une chronologie de la tâche au grader avec le contrôle de sécurité avant la boucle, un contrôle d’étape dans la boucle et le guard à trois modes après, chacun avec ce qu’il voit et ce qu’il arrêteUne chronologie de la tâche au grader avec le contrôle de sécurité avant la boucle, un contrôle d’étape dans la boucle et le guard à trois modes après, chacun avec ce qu’il voit et ce qu’il arrête

Type de guardrailQuandExemple
Contrôles pré-exécutionAvant le début de la boucleLe Security Gate Agent valide les permissions d’après les règles du wiki
Validateurs dans la bouclePendant le raisonnementLe StepValidator vérifie chaque action proposée et déclenche une révision si elle est défectueuse
Guards post-exécutionAvant la soumission finaleLe système de guards à trois modes compare la réponse de l’agent aux données de l’API et à la politique

Wrappers d’outils

Plusieurs équipes ont modifié la couche d’outils que voit le modèle, au lieu d’ajouter des instructions sur l’API brute :

  • Auto-pagination : NLN7Dw a retiré la pagination de chaque outil. Une seule fonction parcourt toutes les pages et renvoie la liste complète.
  • Normalisation floue : les parseurs d’outils de J8Gvbi associent le vocabulaire humain aux noms de l’API, de sorte que « Willingness to travel » devient le champ will_travel.
  • Nettoyage des schémas : f1Uixf a retiré les champs inutilisés des tool schemas et enrichi les descriptions des paramètres. NLN7Dw a reconstruit Req_LogTimeEntry, le seul outil du SDK dont les champs arrivaient dans un autre ordre, parce que cet ordre troublait le modèle.
  • Outils de raisonnement : xoDvsa (7e aux prix, 0.515) a ajouté des outils plan, think et critic à un simple agent ReAct. L’outil critic renvoie un structured output avec des champs de raisonnement dédiés.

Un wrapper masque la pagination au modèle : l’agent demande une liste une seule fois, et list_all suit next_offset jusqu’à la valeur -1, la valeur de fin utilisée dans l’exemple officiel ERC3. Le code ci-dessous illustre l’étape 1 de l’ordre d’adoption à la fin de cet article ; ce n’est le code d’aucune équipe. respond construit les liens vers les entités à partir des IDs que l’agent a récupérés et rejette un ID qu’il n’a jamais vu. Chaque lien vient donc du code, et un ID inventé est rejeté avant que l’évaluateur le voie.

# Stand-in for the benchmark API: 12 projects, 5 per page.
PROJECTS = [{"id": f"proj_{i}", "name": f"Project {i}"} for i in range(12)]

def call_api(endpoint: str, offset: int = 0, limit: int = 5) -> dict:
    page = PROJECTS[offset : offset + limit]
    more = offset + limit < len(PROJECTS)
    return {"items": page, "next_offset": offset + limit if more else -1}

def list_all(endpoint: str) -> list[dict]:
    """Follow next_offset until the API returns the terminal value -1."""
    items, offset = [], 0
    while offset != -1:
        page = call_api(endpoint, offset=offset)
        items.extend(page["items"])
        offset = page["next_offset"]
    return items

def respond(text: str, entity_ids: list[str], fetched: list[dict]) -> dict:
    """Build required links from IDs; refuse IDs the agent never fetched."""
    known = {row["id"] for row in fetched}
    unknown = [i for i in entity_ids if i not in known]
    if unknown:
        raise ValueError(f"unknown entity ids: {unknown}")
    return {"message": text, "links": [{"id": i} for i in entity_ids]}

projects = list_all("/projects/list")
assert len(projects) == 12  # three pages, one list
print(respond("You lead two projects.", ["proj_3", "proj_11"], projects))

L’agent ne voit jamais de limite de page, et proj_11, issu de la troisième page, est un lien valide.


Modes d’échec et corrections structurelles rapportées par les équipes

Les comptes rendus mentionnent à répétition des échecs où l’agent appelle l’API ou applique une politique de l’entreprise. Les correctifs les plus réutilisables ont déplacé l’exigence dans le code ou dans une étape de validation dédiée :

Mode d’échecDescriptionCorrection architecturale
Contournement de permissionExécuter des actions restreintes sans vérifier les permissions de l’utilisateurSecurity Gate Agent pré-exécution ; séquence obligatoire Identité → Permissions → Exécution
Liens d’entités manquantsRéponse textuelle correcte, mais liens de référence requis absentsLinkGeneratorAgent intégré à l’outil de réponse
Pagination inachevéeNe traiter que la première page des résultats d’une listeWrappers d’auto-pagination pour tous les endpoints de liste
Boucles de tool callsAppels répétés avec de légères variationsLimites de tours ; tool schemas plus clairs ; choix du modèle testé sur le workflow réel
Surcharge du contexteRemplir le contexte de sections de wiki sans rapportDistillation des règles ; filtrage dynamique du contexte

Deux rapports ajoutent des détails sur la ligne des boucles. f1Uixf a observé des boucles de tool calls avec GPT-5, mais pas avec le modèle Qwen3 plus petit. NLN7Dw a fixé sa limite de tours trop bas, et 5 tâches sur 103 ne se sont pas terminées : une limite de tours demande donc de la marge.


Un ordre d’adoption pratique

ERC3 est une seule entreprise simulée, pas une étude d’ablation générale sur les agents. Utilisez-le comme source d’hypothèses de conception, puis testez ces hypothèses sur vos propres traces. Un ordre d’adoption raisonnable est le suivant :

  1. Rendez d’abord l’exactitude de l’API déterministe. Paginez automatiquement les endpoints de liste, normalisez les champs flous, validez les schémas et générez les liens requis dans l’outil de réponse.
  2. Ajoutez des contrôles là où une erreur coûte cher. Vérifiez l’identité et la permission avant toute modification ; ne validez une étape avant exécution que si l’appel de modèle supplémentaire détecte des échecs qui valent son coût.
  3. Écrivez une politique de contexte. Décidez ce qui est préchargé, récupéré, compressé ou conservé tel quel. Mesurez la politique par tranche de tâches plutôt qu’au seul nombre de tokens.
  4. Transformez les traces en échec en cas de régression. Classez l’échec, changez un seul mécanisme et relancez la tranche concernée. N’automatisez la révision du prompt qu’une fois cette boucle fiable.
  5. Décomposez quand la responsabilité devient plus claire. Un composant séparé se justifie quand il peut porter une contrainte, utiliser un autre modèle ou outil, ou être testé indépendamment, et non simplement parce que « multi-agent » paraît plus capable.

La figure ci-dessous esquisse à quoi ressemble chaque échec dans une trace et pointe vers la correction à essayer en premier. Les numéros correspondent aux étapes ci-dessus.

Sept échecs esquissés tels qu’ils apparaissent dans une trace, chacun avec la correction à essayer en premier et son numéro d’étapeSept échecs esquissés tels qu’ils apparaissent dans une trace, chacun avec la correction à essayer en premier et son numéro d’étape

Références