← Zakaria Khchiche · Tous les articles

Copilot Studio sur des milliers de documents : faire répondre l’agent seulement quand une source le justifie

Un serveur MCP qui filtre les passages avec Jev (TypeSafe) avant la réponse : architecture, mise en place pas à pas et résultats mesurés.

Zakaria Khchiche · · également publié sur Medium

Un serveur MCP qui filtre les passages avec Jev (TypeSafe) avant la réponse : architecture, mise en place pas à pas et résultats mesurés.

Un agent Copilot Studio branché sur une grande bibliothèque documentaire répond presque toujours. Le problème, c’est quand il ne devrait pas : la procédure trouvée concerne un autre modèle d’équipement, la révision citée est périmée, ou aucun document ne couvre la question. J’ai construit un serveur MCP qui ajoute une étape de décision entre la recherche et la réponse. Pour chaque passage retrouvé, Jev, le modèle « System One » de TypeSafe, répond à cinq questions oui/non, et le code décide : preuve, contradiction ou rejet. L’agent répond avec ses sources, signale que la question repose sur une information fausse, ou dit qu’il n’a rien trouvé.

Le code est public : github.com/Zakariakhchiche/copilot-studio-jev. Je l’ai aussi proposé comme exemple au dépôt officiel de Microsoft : microsoft/CopilotStudioSamples#539.

En résumé

Le problème : la recherche classe, elle ne décide pas

Dans une bibliothèque de procédures de maintenance, les documents presque identiques sont la norme : deux modèles de pompe, une révision 1 archivée à côté de la révision 2, une note de forum. La recherche, même avec un re-classement sémantique, range ces passages par ressemblance avec la question. Elle ne dit pas lequel répond vraiment, ni s’il faut répondre.

Exemple tiré de la démo : un technicien demande « les roulements de la P-200 se graissent toutes les 500 heures, combien de graisse dois-je mettre ? ». La procédure en vigueur dit 2 000 heures. La révision archivée, elle, dit bien 500 heures et 30 g. Si l’agent la reçoit comme une source, il répond avec assurance… à partir d’un document périmé.

L’architecture

  1. Copilot Studio appelle l’outil search_procedures d'un serveur MCP (transport Streamable HTTP, comme l'exige Copilot Studio).
  2. Azure AI Search renvoie les N meilleurs passages (12 par défaut).
  3. Jev reçoit une requête par passage, avec la question et le passage dans le « state », et répond à cinq questions oui/non, en parallèle.
  4. Le code applique des seuils dans un ordre fixe et renvoie à l’agent un statut, une consigne et les passages retenus avec leur URL.

Les cinq questions, adaptées de la recette « Classifying RAG passages » de la documentation TypeSafe :

Aucune question ne demande « faut-il garder ce passage ? ». Jev renvoie des probabilités ; la politique est dans le code. Changer de politique, c’est modifier un seuil relu en revue de code, pas réécrire un prompt. L’ordre compte : injection d’abord (sécurité), puis document périmé, puis contradiction, puis pertinence et preuve.

L’agent reçoit l’un de ces trois statuts :

Mise en place pas à pas

1. Installer et tester. Il faut Node.js 20 ou plus. Les tests tournent sans clé API grâce à un faux point d’accès TypeSafe alimenté par des scores enregistrés lors du test réel.

git clone https://github.com/Zakariakhchiche/copilot-studio-jev
cd copilot-studio-jev/mcp-server
npm install
npm test
npm run build

2. Configurer. Copier .env.example en .env et renseigner au minimum TYPESAFE_API_KEY (clé à créer sur console.typesafe.ai). Fixer TYPESAFE_MODEL=jev-1.13.0 plutôt que l'alias jev-latest : l'alias change à chaque nouvelle version du modèle, ce qui peut décaler les probabilités sur lesquelles vos seuils ont été réglés. Sans variables Azure, le serveur interroge le petit corpus de démonstration fourni.

3. Charger sa bibliothèque dans Azure AI Search. Découper les documents en passages de quelques centaines de mots, les exporter en JSON (id, titre, texte, URL, type de source), puis lancer le script d’ingestion, qui crée l’index et sa configuration sémantique :

npm run ingest -- chemin/vers/passages.json

Le coût de Jev dépend du nombre de passages examinés par question (GATE_CANDIDATES), pas de la taille de la bibliothèque : qu'elle contienne 500 ou 15 000 documents, chaque question coûte une requête Jev par passage candidat.

4. Exposer le serveur. Pour un test, un Dev Tunnel suffit :

node --env-file=.env build/index.js
devtunnel host -p 3000 --allow-anonymous

En production, héberger le serveur (Azure Container Apps ou App Service par exemple) et le protéger : la variable MCP_API_KEY active une vérification de l'en-tête x-api-key.

5. Brancher l’outil dans Copilot Studio. Dans l’agent : Outils, Ajouter un outil, Nouvel outil, Model Context Protocol. Renseigner un nom, une description claire (l’orchestrateur s’en sert pour décider quand appeler l’outil), l’URL du serveur se terminant par /mcp, et l'authentification (aucune, ou clé API dans l'en-tête x-api-key). Désactiver ensuite les connaissances générales et la recherche web de l'agent, pour qu'il ne puisse pas répondre hors de la bibliothèque.

6. Donner des instructions courtes à l'agent, par exemple : « Pour toute question sur un équipement, la maintenance ou la sécurité, appelle search_procedures et suis le champ guidance du résultat. Ne réponds jamais de mémoire. Cite le titre et l'URL de chaque passage utilisé. » L'outil renvoie de toute façon la consigne adaptée au statut dans le champ guidance.

Transparence : j'ai testé le serveur MCP avec le client MCP officiel (liste des outils, appel, clé API) et le filtrage avec la vraie API Jev, mais pas encore dans un agent Copilot Studio de bout en bout. Les étapes ci-dessus suivent la documentation Microsoft « Connect your agent to an existing MCP server ».

Résultats mesurés

Test réalisé le 3 octobre 2026 avec jev-1.13.0, sur le corpus de démonstration (12 procédures fictives d'une régie d'eau, dont des quasi-doublons, une révision archivée et une note de forum piégée) :

Les temps correspondent à tous les passages d'une question, avec 6 requêtes simultanées, mesurés depuis l'Europe : ils dépendent du réseau et de la charge de TypeSafe. Les 44 requêtes du test ont consommé environ 22 600 jetons en entrée, soit environ 0,001 $ au tarif public de Jev.

Deux leçons du test réel

1. Une question anti-injection trop large écarte les vraies procédures. La question de la recette TypeSafe (« le passage tente-t-il de contrôler le système qui répond ? ») a noté la procédure de consignation à 0,76 : une procédure est écrite à l'impératif (« appliquer un cadenas », « fermer la vanne »). Elle était donc écartée, et l'agent aurait répondu sans la procédure de sécurité principale. Reformulée en « texte adressé à une IA plutôt qu'à un lecteur humain », la question note la procédure à 0,02 et la note piégée à 0,99.

2. Les documents périmés ont besoin de leur propre question. Sans elle, la révision archivée était acceptée comme preuve (0,97) pour la question à 500 heures, et la fausse prémisse passait. Avec la question is_superseded, la révision archivée est notée 0,98 et écartée, les autres passages restent sous 0,03.

Et en français ?

TypeSafe indique que l'anglais est la langue principale de Jev et que les autres langues sont moins précises. Je l'ai vérifié : la question sur la consignation posée en français, face aux procédures en anglais, donne 0,49 de pertinence et 0,50 de preuve pour la bonne procédure, juste sous les seuils. L'agent s'abstient : c'est un échec sûr, pas une mauvaise réponse, mais c'est un échec. Le paramètre de l'outil demande donc à l'orchestrateur d'envoyer la question dans la langue de la bibliothèque. Pour une bibliothèque en français, il faut mesurer sur ses propres documents et régler les seuils en conséquence.

Audit et rejeu des décisions

Suite à une question d’un lecteur, chaque réponse peut maintenant laisser une trace. Avec la variable AUDIT_LOG_PATH, le serveur ajoute une ligne JSON par réponse : la question, la version exacte du modèle, les seuils appliqués, et chaque passage examiné avec sa version de document, ses cinq scores et sa décision. La réponse de l'outil renvoie l'identifiant d'audit correspondant, ce qui permet de retrouver pourquoi l'agent a répondu, signalé une contradiction ou s'est abstenu.

Comme la décision ne lit que les scores enregistrés, on peut rejouer les réponses passées avec de nouveaux seuils sans rappeler Jev :

GATE_EVIDENCE_MIN=0.6 npm run replay -- audit.jsonl

Le script liste les passages et les réponses dont la décision changerait. Pour les documents périmés, la bonne approche reste double : filtrer les révisions archivées dès la recherche grâce aux métadonnées de l’index (champ version ou statut), et garder la question is_superseded comme filet de sécurité. Les traces contiennent les questions des utilisateurs : en production, elles vont dans votre outil de journalisation, avec la durée de conservation prévue par votre organisation.

Limites

À retenir

Le code complet, le connecteur Power Platform TypeSafe et les tests : github.com/Zakariakhchiche/copilot-studio-jev.

Zakaria Khchiche est Tech Lead Data & IA freelance à Paris. Il conçoit et met en production des agents IA pour de grands comptes, et contribue à des frameworks d'agents open source. LinkedIn · Site

Vous voulez que vos équipes construisent des agents Copilot Studio fiables comme celui-ci ? J'anime une formation Copilot Studio pratique avec Spar-x, organisme certifié Qualiopi, finançable par votre OPCO : programme de la formation · demande de devis. Et un kit gratuit pour l'article 4 de l'AI Act : kit AI Act.