Demandez à une équipe IA d’où provient un document d’entraînement donné, et la réponse est souvent le nom d’un jeu de données, pas une source. Les régulateurs, les clients et les tribunaux veulent désormais la seconde réponse : quelle page, récupérée quand, selon quelles conditions, et ce qui lui a été fait avant l’entraînement. Pour les équipes qui construisent des modèles à partir de données web, la provenance est passée du statut de bonne pratique à celui de chose qu’il faut pouvoir démontrer.
La difficulté tient au moment où l’information est captée. L’essentiel de ce dont un enregistrement de provenance a besoin n’existe qu’au moment de la collecte. Ce guide explique pourquoi la question est devenue pressante, quel enregistrement conserver pour chaque document, et comment le faire traverser l’extraction, le filtrage et la déduplication sans en perdre le fil.
Pourquoi la provenance est devenue une exigence
Trois évolutions ont transformé la provenance d’un sujet de recherche en une tâche de conformité.
L’AI Act européen demande un résumé public. L’article 53(1)(d) impose aux fournisseurs de modèles d’IA à usage général de publier « un résumé suffisamment détaillé du contenu utilisé pour l’entraînement », selon un modèle fourni par le Bureau de l’IA. Les obligations s’appliquent à partir du 2 août 2025, et les modèles déjà sur le marché avant cette date ont jusqu’au 2 août 2027. La Commission a présenté le modèle en juillet 2025 et précise que son utilisation est obligatoire.
Le modèle demande des précisions. Pour les données scrapées, il demande les noms des crawlers utilisés, la période de collecte, et une liste des noms de domaine les plus pertinents : le top 10 % de tous les domaines par taille du contenu scrapé, ou pour les petites et moyennes entreprises le top 5 % ou 1 000 domaines, selon le chiffre le plus bas. Il demande également aux fournisseurs de décrire comment ils ont respecté les opt-outs de fouille de textes et de données « avant et pendant la collecte des données ». L’article 53(1)(c) exige séparément une politique de droit d’auteur qui identifie et respecte ces réserves.
Le commun se réduit, et les enregistrements étaient médiocres. La Data Provenance Initiative a audité plus de 1 800 jeux de données textuelles en 2023 et a trouvé des taux d’omission de licence supérieurs à 70 % et des taux d’erreur supérieurs à 50 % sur les sites d’hébergement les plus utilisés. Un audit de suivi portant sur 14 000 domaines web a montré qu’en une seule année, environ 5 % de tous les tokens de C4, et 28 % de ses sources les plus activement maintenues, étaient devenus entièrement restreints par robots.txt. Un jeu de données acceptable au moment du crawl peut ne plus l’être aujourd’hui, et vous ne pouvez montrer la différence que si vous avez enregistré l’état à ce moment-là.
Que conserver pour chaque document
Un enregistrement de provenance est écrit une fois, par le crawler, au moment de la récupération. Tout ce qui suit s’y ajoute plutôt que de le remplacer.
| Champ | Pourquoi c’est important |
|---|---|
| URL source | La base de la liste de domaines et de toute demande de retrait |
| Heure de récupération (UTC) | Prouve la période de collecte et l’état de l’opt-out à cette date |
| Statut de la réponse | Distingue le contenu réel des pages de blocage et des erreurs |
| Hash du corps brut | Relie chaque copie ultérieure exactement à ce qui a été reçu |
| Point de vue (vantage point) | La même URL peut servir un contenu différent selon le pays et le réseau |
| Décision robots.txt | Montre que le crawler a respecté les règles en vigueur au moment de la récupération |
| Réserve TDM observée | L’opt-out auquel les règles européennes sur le droit d’auteur et le modèle s’intéressent |
| Licence ou conditions déclarées | Ce que la page indiquait sur la réutilisation, le cas échéant |
| Nom et version du crawler | Le modèle demande les noms des crawlers ; les versions expliquent les changements de comportement |
| Enregistrements parents | Relie une copie nettoyée ou dédupliquée à sa source |
Deux champs sont souvent oubliés. Le premier est le point de vue : une page observée depuis un pays ou un réseau peut différer de la même URL vue ailleurs, ce qui est l’argument de notre article sur l’enregistrement de l’endroit d’où les données web ont été observées. Le second est le corps brut lui-même. Un hash prouve quels octets vous avez reçus, mais conserver la réponse, par exemple en archivant les pages dans des fichiers WARC, permet de le montrer.
Un enregistrement de provenance en code
L’exemple ci-dessous écrit un enregistrement par document récupéré, ne conserve que les documents qui étaient autorisés et non réservés au moment de la récupération, et relie chaque copie extraite à sa source par hash. Il produit également le classement de domaines que demande le modèle de résumé. Il fonctionne sur Python 3.9 et versions ultérieures, avec uniquement la bibliothèque standard.
from __future__ import annotations
import hashlib
import json
from collections import Counter
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from urllib.parse import urlsplit
def sha256(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
@dataclass
class Provenance:
"""One record per fetched document, written at collection time."""
url: str
fetched_at: str # ISO 8601, UTC
status: int
content_sha256: str # hash of the raw response body
vantage: str # where it was observed from, e.g. "residential:de"
robots_allowed: bool # robots.txt decision for our crawler, at fetch time
tdm_reserved: bool # machine-readable TDM opt-out seen, at fetch time
license: str | None # licence or terms as stated on the page, if any
collector: str # crawler name and version
parents: list[str] = field(default_factory=list) # hashes this record derives from
step: str = "fetch" # fetch, extract, filter, dedup ...
def fetched(url, body, status, vantage, robots_allowed, tdm_reserved, license=None,
collector="corpus-crawler/1.4"):
return Provenance(
url=url,
fetched_at=datetime.now(timezone.utc).isoformat(timespec="seconds"),
status=status,
content_sha256=sha256(body),
vantage=vantage,
robots_allowed=robots_allowed,
tdm_reserved=tdm_reserved,
license=license,
collector=collector,
)
def derived(parent: Provenance, new_body: bytes, step: str) -> Provenance:
"""A transformed copy keeps the source fields and points back at its parent."""
rec = Provenance(**{**asdict(parent), "parents": [parent.content_sha256], "step": step})
rec.content_sha256 = sha256(new_body)
return rec
def eligible(rec: Provenance) -> bool:
"""Only documents that were allowed and not reserved at fetch time go further."""
return rec.status == 200 and rec.robots_allowed and not rec.tdm_reserved
def domain_summary(records, top_share=0.1):
"""Domains by document count, and the top share of them, for a training-data summary."""
counts = Counter(urlsplit(r.url).hostname for r in records)
ranked = [d for d, _ in counts.most_common()]
keep = max(1, round(len(ranked) * top_share))
return counts, ranked[:keep]
if __name__ == "__main__":
raw = [
fetched("https://news.example/a", b"<html>a</html>", 200, "residential:de", True, False),
fetched("https://news.example/b", b"<html>b</html>", 200, "residential:de", True, False),
fetched("https://shop.example/p/1", b"<html>p1</html>", 200, "residential:fr", True, True),
fetched("https://blog.example/x", b"<html>x</html>", 403, "residential:us", True, False),
fetched("https://forum.example/t/9", b"<html>t9</html>", 200, "residential:us", False, False),
]
kept = [r for r in raw if eligible(r)]
texts = [derived(r, r.url.encode() + b" text", "extract") for r in kept]
with open("manifest.jsonl", "w") as f:
for rec in raw + texts:
f.write(json.dumps(asdict(rec)) + "\n")
counts, top = domain_summary(raw)
print(f"fetched {len(raw)}, eligible {len(kept)}, extracted {len(texts)}")
print("domains:", dict(counts))
print("top domains:", top)
child = texts[0]
print("lineage ok:", child.parents == [kept[0].content_sha256], child.step, child.url)
Sortie :
fetched 5, eligible 2, extracted 2
domains: {'news.example': 2, 'shop.example': 1, 'blog.example': 1, 'forum.example': 1}
top domains: ['news.example']
lineage ok: True extract https://news.example/a
La page réservée, la page bloquée et la page interdite par robots.txt figurent toutes encore dans le manifeste. C’est important : l’enregistrement de ce que vous avez choisi de ne pas utiliser fait partie de la preuve. Le modèle classe les domaines par taille du contenu scrapé, donc en production, comptez des octets ou des tokens plutôt que des documents.
Faire traverser la provenance au pipeline
Un enregistrement écrit au moment de la récupération n’est utile que si chaque étape ultérieure conserve le lien.
- Extraction. Le texte sur lequel vous entraînez n’est pas le HTML que vous avez récupéré. Donnez au texte extrait son propre hash et faites-le pointer vers l’enregistrement brut, comme le fait
derived()ci-dessus. - Filtrage. Consignez la raison pour laquelle un document a été écarté, pas seulement le fait qu’il l’a été. « Sous le seuil de qualité » et « réserve TDM » sont des réponses différentes à un régulateur.
- Déduplication. Lorsque des quasi-doublons sont fusionnés, conservez la liste de chaque source que représente la copie survivante. Sinon, une demande de retrait pour une URL ne peut pas retrouver le document dans lequel elle a fini.
- Mélange et échantillonnage. Consignez quelle version du manifeste chaque exécution d’entraînement a utilisée, afin que le résumé décrive les données qu’un modèle a réellement vues.
- Suppression. Conservez une liste de retrait indexée par URL et par hash. Lorsqu’un site réserve ses droits par la suite ou qu’un ayant droit demande un retrait, les mêmes clés permettent de retrouver le document dans tous les jeux de données dérivés.
L’état de l’opt-out mérite une attention particulière. Un fichier robots.txt ou une réserve de fouille de textes et de données peut changer après votre crawl, dans un sens ou dans l’autre. Consignez la décision que vous avez prise au moment de la récupération, et revérifiez avant chaque nouvelle collecte plutôt que de supposer que la réponse de l’année dernière tient toujours.
Des standards à emprunter
Vous n’avez pas besoin d’inventer un schéma à partir de rien.
- Datasheets for Datasets (Gebru et al., Communications of the ACM, 2021) propose que chaque jeu de données documente sa motivation, sa composition, son processus de collecte et ses usages recommandés. C’est la bonne forme pour le résumé au niveau du jeu de données.
- Les Data Provenance Standards de la Data & Trusted AI Alliance définissent 22 champs de métadonnées en trois groupes (source, provenance et usage), incluant la période de génération des données, la méthode, la licence d’utilisation et l’endroit où le consentement est documenté. La version 1.0.0 a été publiée en juillet 2024.
- C2PA Content Credentials attache un manifeste de provenance signé à un contenu multimédia individuel. Ils sont surtout utiles pour les images, l’audio et la vidéo, où le contenu lui-même peut porter son enregistrement.
Où la provenance échoue habituellement
- Elle est ajoutée après coup. Un crawl sans enregistrements par document ne peut pas être audité plus tard, aussi bonne soit la fiche du jeu de données.
- La licence est recopiée de la fiche du jeu de données. L’audit mentionné plus haut a montré que c’est là que se concentrent les erreurs. Enregistrez ce que disait la source, et traitez les étiquettes agrégées comme des affirmations à vérifier.
- Les pages de blocage sont comptées comme du contenu. Une réponse 200 peut tout de même être une page de défi. Les codes de statut et les vérifications de contenu doivent figurer dans l’enregistrement.
- Le point de vue est inconnu. Collecter chaque marché depuis une connexion résidentielle locale n’aide la provenance que si l’enregistrement indique quel pays et quel réseau chaque récupération a utilisés.
En résumé
La provenance est peu coûteuse à écrire au moment de la collecte et presque impossible à récupérer après coup. Écrivez un enregistrement par document au moment de la récupération, faites traverser le hash à chaque transformation, conservez ce que vous avez exclu autant que ce que vous avez gardé, et le résumé demandé par l’AI Act devient une requête plutôt qu’une enquête.
Sources et références
- Règlement (UE) 2024/1689 (AI Act), Articles 53, 111 et 113 : eur-lex.europa.eu
- Commission européenne, notice explicative et modèle pour le résumé public du contenu d’entraînement (juillet 2025) : digital-strategy.ec.europa.eu
- Directive (UE) 2019/790, Article 4(3) : eur-lex.europa.eu
- Longpre et al., The Data Provenance Initiative (2023) : arxiv.org/abs/2310.16787
- Longpre et al., Consent in Crisis: The Rapid Decline of the AI Data Commons (2024) : arxiv.org/abs/2407.14933
- Gebru et al., Datasheets for Datasets : doi.org/10.1145/3458723
- Data & Trusted AI Alliance, Data Provenance Standards v1.0.0 : dtaalliance.org
- C2PA Specification : spec.c2pa.org