Gouverner les données dark non structurées avec Gemini et Knowledge Catalog

1. Introduction

Plus de 80% des données d'entreprise sont stockées dans des formats non structurés (manuels d'ingénierie au format PDF, procédures opérationnelles normalisées, certificats de conformité et schémas d'équipement) dans Cloud Storage. Étant donné que les catalogues traditionnels n'indexent que les schémas de tables relationnelles, ces documents restent des données obscures : invisibles pour la recherche d'entreprise, déconnectés des tables d'inventaire structurées et non vérifiés pour la conformité réglementaire.

Lorsqu'un auditeur ou un agent d'IA pose la question "Quels SKU matériels nécessitent la certification de sécurité UL-153 et quels sont leurs niveaux de risque thermique ?", un catalogue qui n'indexe que les tables SQL ne peut pas y répondre. Les faits sont enfermés dans des PDF non structurés dans Cloud Storage.

Solution architecturale : métadonnées multimodales en tant que code

Dans cet atelier de programmation, vous allez créer un pipeline Metadata-as-Code de bout en bout dans Google Cloud qui transforme les manuels PDF non structurés dans Cloud Storage en composants Knowledge Catalog avec validation du schéma à l'aide de Gemini Enterprise Agent Platform (SDK Google GenAI, google-genai) :

Flux de gouvernance des données obscures non structurées

  1. Enregistrement d'ensembles de fichiers (EntryGroup et Entry) : regroupe les PDF non structurés dans Cloud Storage en entrées d'ensembles de fichiers logiques régies dans Knowledge Catalog.
  2. Gemini Multimodal Extraction (schéma Pydantic) : appelle Gemini Enterprise Agent Platform sur les octets PDF bruts avec une validation response_schema déterministe.
  3. Liaison d'aspect du plan de contrôle : associe les charges utiles JSON validées pour les opérations et la conformité en tant qu'instances Aspect fortement typées dans l'entrée de catalogue.
  4. Découverte unifiée du catalogue : permet de découvrir les ensembles de fichiers PDF non structurés et leurs métadonnées extraites via SearchEntriesRequest.

Concepts clés : primitives du plan de contrôle Knowledge Catalog

Imaginons que votre équipe stocke des centaines de manuels de produits au format PDF dans Cloud Storage. Pour retrouver et consulter facilement ces fichiers dans Knowledge Catalog, imaginez les cinq composants de base comme des manuels papier rangés dans un classeur de bureau :

  • EntryGroup (armoire verrouillée pour une équipe) : armoire avec une serrure. Seuls les membres de l'équipe disposant de l'autorisation roles/dataplex.entryCreator peuvent placer des dossiers à l'intérieur. Vous devez vider tous les dossiers du meuble avant de pouvoir le retirer.
  • EntryType (libellé de catégorie de dossier) : libellé qui indique aux utilisateurs le type de composant que représente un dossier (par exemple, "Collection de manuels au format PDF" au lieu de "Table de base de données").
  • Entry (dossier de fichiers avec un lien de stockage) : dossier de catalogue pour vos manuels au format PDF. Au lieu de contenir des copies papier volumineuses, le dossier contient une fiche d'emplacement indiquant le bucket Cloud Storage (gs://...) exact où se trouvent les fichiers PDF.
  • AspectType (formulaire de checklist vide) : formulaire vide réutilisable avec des cases préremplies (par exemple, une checklist de sécurité demandant operational_hazard_level sous forme de texte et confidence_score sous forme de nombre compris entre 0.0 et 1.0). Vous créez ce formulaire vide une seule fois et le réutilisez dans des milliers de dossiers.
  • Aspect (Liste de contrôle remplie associée à un dossier) : une fois que Gemini Enterprise Agent Platform a lu le manuel au format PDF et rempli les champs vides, la liste de contrôle remplie est associée au dossier Entry. Comme chaque checklist possède sa propre clé (aspect_keys), l'équipe d'ingénieurs et l'équipe juridique peuvent chacune joindre leur propre checklist au même dossier sans écraser le travail de l'autre.

Les cinq ressources régionales se trouvent sous projects/{project}/locations/{location}/ :

Chemin primitif et relatif

Rôle dans Knowledge Catalog

Règle de suppression et IAM

EntryGroup
.../entryGroups/{id}

Conteneur logique qui regroupe les entrées et contrôle l'accès des équipes (roles/dataplex.entryCreator).

Doit contenir zéro ressource enfant Entry avant la suppression.

EntryType
.../entryTypes/{id}

Définition de catégorie pour une classe d'assets (entry.entry_type, comme les ensembles de fichiers PDF non structurés).

Ne supprimez-le qu'une fois que toutes les entrées utilisant ce type ont été supprimées.

Entry (ensemble de fichiers)
.../entries/{entry_id}

Enregistrement de catalogue associant un URI Cloud Storage physique (entry_source.resource) à un nom logique (gcs:{bucket}).

Se trouve dans un EntryGroup et contient les enregistrements Aspect associés.

AspectType
.../aspectTypes/{id}

Modèle de schéma réutilisable définissant les record_fields typés (string, enum, double) et les numéros de champ commençant par 1 (index).

Créé une fois par région et partagé entre les entrées.

Aspect
Entry.aspects[key]

Métadonnées JSON validées correspondant à un AspectType, associées à un Entry via UpdateEntryRequest.

Mise à jour à l'aide de update_mask et aspect_keys afin que les différents pipelines ne s'écrasent pas les uns les autres.

Ce que vous allez faire

À l'aide des scripts associés dans codelabs/metadata-as-code-lab, vous allez exécuter et inspecter un pipeline en sept étapes :

  1. schemas.py : schémas Pydantic BaseModel doubles (DarkDataExtractedMetadata et DocumentGovernanceMetadata) associés un à un aux modèles Knowledge Catalog AspectType.
  2. step2_provision.py : provisionnement idempotent des plans EntryGroup, EntryType et AspectType personnalisés.
  3. step3_register_fileset.py : importation de LUM-LIG-DES-8G8J_manual.pdf dans Cloud Storage et enregistrement en tant que Fileset régi Entry.
  4. step4_extract_metadata.py : découverte dynamique du modèle Gemini phare et extraction multimodale de PDF à l'aide de google-genai.
  5. step5_attach_aspects.py, step6_search_catalog.py et step7_verify_assertions.py : pièce jointe sur l'aspect chirurgical, recherche de catalogue avec interruption exponentielle et assertions de plan de contrôle de bout en bout.

Conditions préalables et ce dont vous avez besoin

  • Un projet Google Cloud avec la facturation activée et Google Cloud Shell (préinstallé avec gcloud, git, python3 et pip).
  • Autorisations IAM pour gérer les ressources du catalogue de connaissances (roles/dataplex.admin), provisionner les buckets et les objets Cloud Storage (roles/storage.admin) et appeler Gemini Enterprise Agent Platform (roles/aiplatform.user).
  • Un navigateur Web tel que Google Chrome.

2. Préparation

Cloner le dépôt associé dans Cloud Shell

Ouvrez la console Google Cloud, lancez Cloud Shell, puis effectuez un checkout sparse de codelabs/metadata-as-code-lab, qui inclut l'exemple de manuel d'ingénierie au format PDF (LUM-LIG-DES-8G8J_manual.pdf) et tous les scripts Python :

git clone --branch main --depth 1 --filter=blob:none --sparse \
  https://github.com/GoogleCloudPlatform/devrel-demos.git
cd devrel-demos
git sparse-checkout set codelabs/metadata-as-code-lab
cd codelabs/metadata-as-code-lab
ls -la

Configurer les variables d'environnement

Définissez votre ID de projet, l'emplacement du plan de contrôle régional et le bucket Cloud Storage cible dans Cloud Shell :

export PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project)}"
gcloud config set project $PROJECT_ID

export REGION="us-central1"
export BUCKET_NAME="${PROJECT_ID}-dark-data-manuals"
export GEMINI_LOCATION="global"

echo "Configured Project ID        : $PROJECT_ID"
echo "Knowledge Catalog Region     : $REGION"
echo "Gemini Endpoint Location     : $GEMINI_LOCATION"
echo "Target Cloud Storage Bucket  : $BUCKET_NAME"

Activer les API et installer les dépendances

Activez les API Google Cloud requises et installez les dépendances Python (google-genai, google-cloud-dataplex, pydantic et pandas) à partir de requirements.txt :

gcloud services enable \
  dataplex.googleapis.com \
  storage.googleapis.com \
  aiplatform.googleapis.com \
  --quiet
python3 -m pip install --upgrade --quiet --user --break-system-packages -r requirements.txt

3. Définir des schémas Pydantic et des modèles d'aspects

schemas.py définit deux contrats complémentaires qui associent la validation Pydantic côté client aux modèles Knowledge Catalog AspectType côté serveur :

  1. DarkDataExtractedMetadata : attributs opérationnels extraits du PDF (titre du document, ontologie du domaine, niveau de risque pour la sécurité, provenance de l'auteur/de la version, résumé et score de confiance de l'extraction).
  2. DocumentGovernanceMetadata : codes de conformité réglementaire (UL-153, FCC Part 15, CE-LVD, RoHS), responsable de la gestion, règle de conservation et chaîne d'identifiant de table de référence (lakehouse_cross_ref_table) pour associer le manuel PDF aux tables d'inventaire en aval.

Inspecter schemas.py

Tout d'abord, DarkDataExtractedMetadata utilise les contraintes Pydantic Field (ge=0.0, le=1.0, c'est-à-dire supérieur/inférieur ou égal à, sur confidence_score) pour contraindre la sortie JSON de Gemini Enterprise Agent Platform :

class DarkDataExtractedMetadata(BaseModel):
    document_title: str = Field(
        description="Official document title or product model name extracted from the PDF manual."
    )
    # ... domain_ontology, operational_hazard_level, author_provenance ...
    version_provenance: str = Field(
        description="Document version, revision code, or publication standard identifier."
    )
    # ... document_summary ...
    confidence_score: float = Field(
        ge=0.0,
        le=1.0,
        description="AI extraction confidence metric between 0.0 and 1.0.",
    )

Ensuite, extracted_aspect_template reflète ces noms de champs exacts et mappe les types str et float de Python aux types string et double record_fields de Knowledge Catalog. Notez que le contrat protobuf AspectType.MetadataTemplate exige que chaque champ de record_fields déclare un index unique, strictement positif et basé sur 1 (de 1 à 7). Si vous omettez ou dupliquez index, create_aspect_type échouera avec 400 INVALID_ARGUMENT :

extracted_aspect_template = {
    "name": "DarkDataExtractedMetadata",
    "type": "record",
    "record_fields": [
        {
            "name": "document_title",
            "type": "string",
            "index": 1,
            "annotations": {"description": "Official document title or product model name."},
        },
        # ... fields 2 through 6 (string) ...
        {
            "name": "confidence_score",
            "type": "double",
            "index": 7,
            # ... annotations ...
        },
    ],
}

Valider le module de schéma

Exécutez schemas.py pour vérifier la configuration de votre environnement et compiler les deux schémas :

python3 schemas.py
Configured Project ID         : your-project-id
Knowledge Catalog Region      : us-central1
Gemini Endpoint Location      : global
Governed Fileset Storage URI  :
  gs://your-project-id-dark-data-manuals/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf
Target Fileset Entry ID       : sop-manuals-fileset-lum8g8j
✓ Defined DarkDataExtractedMetadata and DocumentGovernanceMetadata
  Pydantic schemas.
✓ Prepared Knowledge Catalog AspectType metadata templates.

4. Provisionner des espaces de noms et des types d'aspects personnalisés

Avant d'enregistrer des entrées d'ensemble de fichiers ou d'associer des métadonnées, step2_provision.py provisionne trois ressources de plan de contrôle dans Knowledge Catalog :

  • EntryGroup : dark-data-fileset-group
  • EntryType : unstructured-pdf-fileset
  • AspectTypePlans : dark-data-extracted-metadata et document-governance-aspect

Provisionnement idempotent dans step2_provision.py

Chaque appel de création dans step2_provision.py encapsule l'opération de longue durée (op.result()) dans un bloc try / except AlreadyExists afin que le script puisse être réexécuté en toute sécurité sans échouer sur les ressources existantes :

# Provision AspectType 1: dark-data-extracted-metadata
try:
    at1_op = catalog_client.create_aspect_type(
        parent=parent_location,
        aspect_type_id=EXTRACTED_ASPECT_TYPE_ID,
        aspect_type=AspectType(
            name=extracted_aspect_type_name,
            display_name="Dark Data Multimodal Extracted Metadata",
            # ... description ...
            metadata_template=extracted_aspect_template,
        ),
    )
    if hasattr(at1_op, "result"):
        at1_op.result()
    print(f"Created AspectType: {extracted_aspect_type_name}")
except AlreadyExists:
    print(f"AspectType already exists; retrieving live resource: {extracted_aspect_type_name}")

live_extracted_aspect_type = catalog_client.get_aspect_type(name=extracted_aspect_type_name)

Une fois metadata_template=extracted_aspect_template enregistré, Knowledge Catalog applique le schéma à la limite de l'API. Tout appel update_entry() ultérieur qui transmet une chaîne à confidence_score (double) est refusé.

Exécuter step2_provision.py

Exécutez le script de provisionnement et vérifiez que les types d'aspect personnalisés sont enregistrés dans votre région :

python3 step2_provision.py
gcloud dataplex aspect-types list --location=$REGION
Created EntryGroup:
  projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group
Created EntryType :
  projects/your-project-id/locations/us-central1/entryTypes/
  unstructured-pdf-fileset
Created AspectType:
  projects/your-project-id/locations/us-central1/aspectTypes/
  dark-data-extracted-metadata
Created AspectType:
  projects/your-project-id/locations/us-central1/aspectTypes/
  document-governance-aspect
✓ All Knowledge Catalog namespaces and AspectType blueprints are active.

5. Enregistrer des entrées d'ensembles de fichiers non structurés

Au lieu d'encombrer le catalogue avec des millions de pointeurs de fichiers isolés, Knowledge Catalog regroupe les documents associés dans Cloud Storage (gs://YOUR_BUCKET/sop-pdfs/*.pdf) en entrées d'ensemble de fichiers logiques, soit par le biais d'analyses de découverte automatisées (DataScanServiceClient), soit par un enregistrement programmatique direct (CatalogServiceClient.create_entry).

Comment step3_register_fileset.py mappe le stockage au catalogue

step3_register_fileset.py importe LUM-LIG-DES-8G8J_manual.pdf dans gs://${BUCKET_NAME}/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf et enregistre le Entry régi :

fully_qualified_fileset_uri = f"gcs:{BUCKET_NAME}"
fileset_entry_spec = Entry(
    name=fileset_entry_name,
    entry_type=entry_type_resource_name,
    fully_qualified_name=fully_qualified_fileset_uri,
    entry_source=EntrySource(
        resource=VIRTUAL_STORAGE_URI,
        system="Cloud Storage",
        platform="Google Cloud",
        display_name="LUM-LIG-DES-8G8J Unstructured SOP & Technical Manual Fileset",
        # ... description ...
        location=REGION,
    ),
)

create_entry_op = catalog_client.create_entry(
    parent=entry_group_resource_name,
    entry_id=FILESET_ENTRY_ID,
    entry=fileset_entry_spec,
)

Notez les rôles distincts des deux champs URI sur Entry :

  • fully_qualified_name=fully_qualified_fileset_uri (f"gcs:{BUCKET_NAME}") : utilise le schéma FQN gcs:{bucket_name} canonique requis par Knowledge Catalog pour corréler l'entrée de catalogue logique avec le bucket Cloud Storage sous-jacent.
  • entry_source.resource=VIRTUAL_STORAGE_URI : stocke l'URI de l'objet gs:// physique inspecté par les workers d'extraction en aval.

Exécuter step3_register_fileset.py

python3 step3_register_fileset.py
Created Cloud Storage bucket:
  gs://your-project-id-dark-data-manuals in us-central1
Uploaded unstructured PDF manual to:
  gs://your-project-id-dark-data-manuals/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf
Registered governed Fileset Entry:
  projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group/entries/sop-manuals-fileset-lum8g8j
  -> Fully Qualified Name : gcs:your-project-id-dark-data-manuals
  -> Physical Resource URI:
  gs://your-project-id-dark-data-manuals/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf
  -> Source System        : Cloud Storage

6. Extraire les métadonnées d'un document avec Gemini

step4_extract_metadata.py lit les octets PDF bruts et appelle Gemini Enterprise Agent Platform (genai_client.models.generate_content) à l'aide de deux modèles clés :

1. Découverte dynamique des modèles phares

Au lieu de coder en dur des chaînes de version de modèle fragiles, le script interroge genai_client.models.list() et trie les candidats actifs par (is_ga_channel, semantic_version_tuple) pour choisir le modèle flash en disponibilité générale (DG) le plus récent :

genai_client = genai.Client(
    vertexai=True,
    project=PROJECT_ID,
    location=GEMINI_LOCATION,
)

for model_obj in genai_client.models.list():
    raw_id = (model_obj.name or "").split("/")[-1]
    # Filter to flagship Gemini Flash models (version >= 3.0) and prioritize GA over preview
    ...
    discovered_candidates.append((is_ga_channel, ver_tuple, raw_id))

discovered_candidates.sort(key=lambda item: (item[0], item[1]), reverse=True)

2. Extraction multimodale avec contraintes de schéma (Part.from_bytes + response_schema)

Le passage de response_mime_type="application/json" et response_schema=DarkDataExtractedMetadata (suivi de DocumentGovernanceMetadata) contraint le décodage des jetons directement au schéma JSON Pydantic :

with open(LOCAL_PDF_PATH, "rb") as pdf_file:
    raw_pdf_bytes = pdf_file.read()

extraction_response = genai_client.models.generate_content(
    model=candidate_model,
    contents=[
        types.Part.from_bytes(data=raw_pdf_bytes, mime_type="application/pdf"),
        extraction_prompt,
    ],
    config=types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema=DarkDataExtractedMetadata,
        temperature=0.1,
    ),
)

extracted_dark_data = json.loads(extraction_response.text)
validated_extraction = DarkDataExtractedMetadata(**extracted_dark_data)

Exécuter step4_extract_metadata.py

python3 step4_extract_metadata.py
Dynamically discovered active GA model currency :
  gemini-3.8-flash (version (3, 8))
Loaded unstructured PDF manual at: LUM-LIG-DES-8G8J_manual.pdf
Read unstructured PDF document (1184 bytes).
  Executing multimodal extraction with gemini-3.8-flash...
✓ Multimodal extraction completed using model: gemini-3.8-flash
✓ Governance classification completed using model: gemini-3.8-flash

--- Extracted Multimodal Operational Metadata ---
Extracted Attribute    Multimodal Value
document_title    LUM-LIG-DES-8G8J Contemporary Linen Desk Lamp
domain_ontology    Electrical Hardware & Lighting Systems
operational_hazard_level    MEDIUM
author_provenance    Global Hardware Safety & Lighting Systems Engin
version_provenance    Rev 4.2
document_summary    This document outlines standard operating proce
confidence_score    0.99

--- Extracted Governance & Inventory Cross-Reference Metadata ---
Governance Attribute    Classification Value
compliance_classifications    UL-153 Portable Luminaires, FCC Part 15, CE-LVD
stewardship_owner    Global Hardware Safety & Lighting Systems Engin
retention_policy    10-Year Active Product Lifecycle Archival
lakehouse_cross_ref_table    lakehouse.your-project-id.retail_hardware
governance_status    VERIFIED_PRODUCTION

7. Associer des aspects validés à des entrées de catalogue

step5_attach_aspects.py lie les charges utiles JSON validées (extracted_metadata.json et governance_metadata.json) à l'entrée sop-manuals-fileset-lum8g8j via catalog_client.update_entry().

Mutation de l'aspect chirurgical dans step5_attach_aspects.py

extracted_aspect_key = f"{PROJECT_ID}.{REGION}.{EXTRACTED_ASPECT_TYPE_ID}"
governance_aspect_key = f"{PROJECT_ID}.{REGION}.{GOVERNANCE_ASPECT_TYPE_ID}"

updated_entry_payload = Entry(
    name=fileset_entry_name,
    entry_type=entry_type_resource_name,
    fully_qualified_name=fully_qualified_fileset_uri,
    aspects={
        extracted_aspect_key: Aspect(
            aspect_type=extracted_aspect_type_name,
            data=extracted_data,
        ),
        governance_aspect_key: Aspect(
            aspect_type=governance_aspect_type_name,
            data=governance_data,
        ),
    },
)

update_op = catalog_client.update_entry(
    request=dataplex_v1.UpdateEntryRequest(
        entry=updated_entry_payload,
        update_mask={"paths": ["aspects"]},
        aspect_keys=[extracted_aspect_key, governance_aspect_key],
    )
)

Si vous transmettez les deux  scopes update_mask={"paths": ["aspects"]} et aspect_keys=[extracted_aspect_key, governance_aspect_key], la mutation sera strictement limitée à ces deux clés d'aspect, ce qui laissera entry_source et tous les aspects appartenant à d'autres équipes intacts.

Exécuter step5_attach_aspects.py

python3 step5_attach_aspects.py
Successfully bound 2 Aspect(s) to Entry:
  projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group/entries/sop-manuals-fileset-lum8g8j

=== Live Attached Aspect: document-governance-aspect ===
Catalog Field    Authoritative Stored Value
compliance_classifications    UL-153 Portable Luminaires, FCC Part 15, CE-LVD
stewardship_owner    Global Hardware Safety & Lighting Systems Engin
retention_policy    10-Year Active Product Lifecycle Archival
lakehouse_cross_ref_table    lakehouse.your-project-id.retail_hardware
governance_status    VERIFIED_PRODUCTION

=== Live Attached Aspect: dark-data-extracted-metadata ===
Catalog Field    Authoritative Stored Value
document_title    LUM-LIG-DES-8G8J Contemporary Linen Desk Lamp
domain_ontology    Electrical Hardware & Lighting Systems
operational_hazard_level    MEDIUM
author_provenance    Global Hardware Safety & Lighting Systems Engin
version_provenance    Rev 4.2
document_summary    This document outlines standard operating proce
confidence_score    0.99

Validation à double canal

  • Piste 1 (validation par CLI) : inspectez l'entrée avec tous les aspects associés via gcloud :
    gcloud dataplex entries describe sop-manuals-fileset-lum8g8j \
      --entry-group=dark-data-fileset-group \
      --location=$REGION \
      --view=ALL
    
  • Parcours 2 (Inspection de l'UI de la console) :
    1. Ouvrez la Search Console Knowledge Catalog, recherchez LUM-LIG-DES-8G8J Unstructured SOP & Technical Manual Fileset (sop-manuals-fileset-lum8g8j), puis sélectionnez l'entrée correspondante dans les résultats.
    2. Sous Tags et aspects facultatifs, vérifiez que les aspects Métadonnées extraites multimodales des données obscures et Aspect de référence croisée pour la gouvernance et l'inventaire des documents affichent le niveau de risque extrait (MEDIUM), les codes de conformité (UL-153, FCC Part 15, CE-LVD, RoHS) et la chaîne de table de référence croisée (lakehouse_cross_ref_table).

8. Interroger les données obscures régies dans le catalogue

Une fois les aspects personnalisés associés, les manuels PDF non structurés dans Cloud Storage deviennent détectables grâce à la recherche Knowledge Catalog (catalog_client.search_entries()).

Sondage search_entries() dans step6_search_catalog.py

step6_search_catalog.py interroge search_entries() avec un intervalle exponentiel entre les tentatives et lit les clés d'aspect canoniques via .endswith() :

search_query = f"entrygroup={entry_group_resource_name} name:{FILESET_ENTRY_ID}"

while elapsed_seconds < max_wait_seconds:
    catalog_search_results = list(
        catalog_client.search_entries(
            request=dataplex_v1.SearchEntriesRequest(
                name=parent_location,
                query=search_query,
            )
        )
    )
    if catalog_search_results:
        break
    backoff_seconds = min(5 * (2 ** attempt), 30)
    time.sleep(backoff_seconds)
    ...

for k, asp in detailed_entry.aspects.items():
    if k.endswith(f".{REGION}.{EXTRACTED_ASPECT_TYPE_ID}"):
        print(f"    Domain Ontology     : {asp.data.get('domain_ontology')}")
        print(f"    Hazard Level        : {asp.data.get('operational_hazard_level')}")
    elif k.endswith(f".{REGION}.{GOVERNANCE_ASPECT_TYPE_ID}"):
        print(f"    Compliance Codes    : {asp.data.get('compliance_classifications')}")
        print(f"    Lakehouse Join Table: {asp.data.get('lakehouse_cross_ref_table')}")

Exécuter step6_search_catalog.py

python3 step6_search_catalog.py
Executing Knowledge Catalog search query:
  'entrygroup=projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group name:sop-manuals-fileset-lum8g8j'...

✓ Discovered 1 governed asset(s) in Knowledge Catalog:

[1] Entry Resource Name :
    projects/your-project-id/locations/us-central1/entryGroups/
    dark-data-fileset-group/entries/sop-manuals-fileset-lum8g8j
    Fully Qualified Name: gcs:your-project-id-dark-data-manuals
    Entry Type :
    projects/your-project-id/locations/us-central1/entryTypes/
    unstructured-pdf-fileset
    Physical Storage URI:
    gs://your-project-id-dark-data-manuals/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf
    Domain Ontology     : Electrical Hardware & Lighting Systems
    Hazard Level        : MEDIUM
    Compliance Codes    : UL-153 Portable Luminaires, FCC Part 15, CE-LVD, RoHS
    Lakehouse Join Table:
    lakehouse.your-project-id.retail_hardware.luminaire_sku_inventory_iceberg

Validation à double canal

  • Piste 1 (recherche CLI) : interrogez l'index de recherche directement via gcloud :
    gcloud dataplex entries search "name:sop-manuals-fileset-lum8g8j" --project=$PROJECT_ID
    
  • Piste 2 (recherche en langage naturel dans l'UI de la console) :
    1. Ouvrez la Search Console Knowledge Catalog et recherchez LUM-LIG-DES-8G8J ou UL-153.
    2. Observez comment Knowledge Catalog affiche l'entrée d'ensemble de fichiers PDF Cloud Storage contrôlée et ses aspects de métadonnées associés.

9. Valider les assertions de gouvernance de bout en bout

step7_verify_assertions.py exécute des assertions déterministes sur les réponses du plan de contrôle Knowledge Catalog en direct pour vérifier que les EntryGroup, EntryType, les deux plans personnalisés AspectType, les charges utiles d'aspect liées EntryView.ALL (limites confidence_score et chaîne lakehouse_cross_ref_table) et la découverte de l'index de recherche réussissent tous :

verify_eg = catalog_client.get_entry_group(name=entry_group_resource_name)
assert verify_eg.name == entry_group_resource_name, "EntryGroup resource name mismatch."

verify_entry = catalog_client.get_entry(
    request=dataplex_v1.GetEntryRequest(
        name=fileset_entry_name,
        view=dataplex_v1.EntryView.ALL,
    )
)
bound_extracted = next(
    (
        asp for k, asp in verify_entry.aspects.items()
        if k.endswith(f".{REGION}.{EXTRACTED_ASPECT_TYPE_ID}")
    ),
    None,
)
bound_governance = next(
    (
        asp for k, asp in verify_entry.aspects.items()
        if k.endswith(f".{REGION}.{GOVERNANCE_ASPECT_TYPE_ID}")
    ),
    None,
)

assert bound_extracted is not None, f"Aspect '{EXTRACTED_ASPECT_TYPE_ID}' not found on live Entry."
assert bound_governance is not None, (
    f"Aspect '{GOVERNANCE_ASPECT_TYPE_ID}' not found on live Entry."
)
assert (
    bound_extracted.data.get("document_title") == expected_extraction["document_title"]
), "Extracted title mismatch."
assert (
    0.0 <= float(bound_extracted.data.get("confidence_score", -1.0)) <= 1.0
), "Invalid confidence score in catalog."
assert (
    "lakehouse" in str(bound_governance.data.get("lakehouse_cross_ref_table", "")).lower()
), "Missing Lakehouse cross-reference."
...
assert len(search_results) >= 1, "Expected at least 1 search result in Knowledge Catalog."

Exécuter step7_verify_assertions.py

python3 step7_verify_assertions.py && echo "Verification exit code: $?"
Running substantive end-to-end verification assertions...
✓ All 5 substantive verification assertions passed
  against live Google Cloud control plane state!
Verification exit code: 0

10. Concevoir un pipeline basé sur des événements

L'exécution interactive de scripts Python dans Cloud Shell est idéale pour créer et tester vos contrats de gouvernance. Toutefois, les environnements d'entreprise reçoivent quotidiennement des milliers de manuels d'ingénierie, de certificats de conformité des fournisseurs et de documents de procédures opérationnelles (SOP) nouveaux ou révisés provenant des portails ERP, PLM (Product Lifecycle Management) et des fournisseurs. L'exécution manuelle de scripts de traitement par lot crée rapidement des angles morts de gouvernance entre l'importation de documents et l'indexation du catalogue.

En production, les équipes de plate-forme opérationnalisent le code exact que vous avez exécuté aux étapes 4 et 5 en tant que pipeline sans intervention et basé sur les événements :

Pipeline de données obscures basé sur des événements

  1. Ingestion continue de documents (Cloud Storage → Eventarc) : chaque fois qu'un nouveau manuel au format PDF est importé ou qu'une nouvelle révision remplace un objet existant dans Cloud Storage, le bucket émet un CloudEvent google.cloud.storage.object.v1.finalized contenant l'identifiant du bucket, le chemin d'accès à l'objet et le numéro de génération de l'objet.
  2. Extraction multimodale sans état (Eventarc →Cloud Run →Gemini Enterprise Agent Platform) : Eventarc achemine le CloudEvent vers un worker Cloud Run sans serveur qui regroupe schemas.py, step4_extract_metadata.py et step5_attach_aspects.py. Le worker transmet les octets PDF à Gemini Enterprise Agent Platform avec response_schema appliqué, ce qui garantit que chaque attribut extrait est conforme à DarkDataExtractedMetadata et DocumentGovernanceMetadata.
  3. Mise à jour du plan de contrôle chirurgical (Cloud Run →Knowledge Catalog) : le worker résout le fichier parent Entry et exécute catalog_client.update_entry() avec update_mask={"paths": ["aspects"]} et aspect_keys explicite. Même si des dizaines de documents finissent d'être traités simultanément, chaque mutation ne met à jour que les clés d'aspect désignées.
  4. Mise en quarantaine des responsables basée sur le degré de confiance : si l'extraction d'un ancien PDF analysé génère un confidence_score inférieur à votre seuil opérationnel (par exemple, < 0.85), le responsable définit governance_status = "PENDING_STEWARD_REVIEW" sur l'aspect associé afin que les responsables des données puissent interroger Knowledge Catalog pour trouver les composants à faible degré de confiance nécessitant un examen humain.

Comment les entreprises appliquent ce modèle en production

Cas d'utilisation Enterprise

Fonctionnement pratique des métadonnées en tant que code

Impact commercial et opérationnel

Audits réglementaires automatisés et préparation aux rappels
(fabrication, matériel et énergie)

Les systèmes de conformité associent les aspects de sécurité des PDF non structurés (compliance_classifications, operational_hazard_level) aux tableaux structurés d'inventaire des SKU (lakehouse_cross_ref_table) dans BigQuery et Lakehouse pour Apache Iceberg.

Lorsqu'une norme telle que UL-153 ou CE-LVD est mise à jour, les auditeurs identifient en quelques secondes tous les SKU matériels et les entrepôts concernés, au lieu d'ouvrir manuellement des milliers de dossiers PDF.

Agents d'IA ancrés et préfiltrage RAG
(assistants Enterprise Knowledge)

Avant qu'un agent de génération augmentée par récupération (RAG) sur Gemini Enterprise Agent Platform ne réponde à la question d'un ingénieur sur le terrain, il filtre les entrées du Knowledge Catalog par governance_status = "VERIFIED_PRODUCTION" et version_provenance.

Empêche les assistants IA d'halluciner ou de citer des brouillons d'ingénierie obsolètes, des procédures opérationnelles standard expirées ou des manuels tiers non vérifiés.

Enrichissement simultané par plusieurs équipes
(maillage de données décentralisé)

Plusieurs pipelines indépendants enrichissent le même ensemble de fichiers Entry : l'ingénieur en IA met à jour dark-data-extracted-metadata, le service juridique ajoute des règles de conservation via document-governance-aspect et le service de sécurité ajoute des aspects de sensibilité DLP.

Étant donné que les modifications des champs UpdateEntryRequest sont effectuées via aspect_keys, chaque équipe de domaine déploie et met à jour son pipeline de métadonnées de manière indépendante, sans écraser les données de gouvernance des autres équipes.

Évolution du schéma CI/CD via GitOps
(ingénierie de plate-forme et de données)

Les équipes chargées des plates-formes stockent schemas.py aux côtés des définitions Terraform google_dataplex_aspect_type dans Git. Les requêtes d'extraction valident que tout nouveau champ Pydantic possède un index basé sur 1 correspondant dans le modèle AspectType avant le déploiement.

Élimine la dérive de schéma entre le code d'extraction d'application et le plan de contrôle Knowledge Catalog dans les projets dev, staging et prod.

11. Nettoyer votre environnement

Dans Knowledge Catalog, les conteneurs parents et les modèles de schéma ne peuvent pas être supprimés tant que des entrées enfants les référencent. cleanup.py supprime les ressources dans l'ordre inverse strict des dépendances (objets de bucket Cloud Storage → fichiers JSON temporaires → AspectTypes → EntryType → EntryGroup → Entry) :

# 1. Delete Fileset Entry first
catalog_client.delete_entry(name=target_entry_path)

# 2. Delete EntryGroup container
del_eg_op = catalog_client.delete_entry_group(name=target_eg_path)
if hasattr(del_eg_op, "result"):
    del_eg_op.result()

# 3. Delete EntryType and Custom AspectTypes
del_et_op = catalog_client.delete_entry_type(name=target_et_path)
if hasattr(del_et_op, "result"):
    del_et_op.result()
for aspect_id_val in (EXTRACTED_ASPECT_TYPE_ID, GOVERNANCE_ASPECT_TYPE_ID):
    target_at_path = f"{active_parent}/aspectTypes/{aspect_id_val}"
    del_at_op = catalog_client.delete_aspect_type(name=target_at_path)
    if hasattr(del_at_op, "result"):
        del_at_op.result()

Exécuter cleanup.py

python3 cleanup.py
Starting reverse-dependency resource cleanup in Knowledge Catalog...
Deleted Fileset Entry:
  projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group/entries/sop-manuals-fileset-lum8g8j
Deleted EntryGroup   :
  projects/your-project-id/locations/us-central1/entryGroups/
  dark-data-fileset-group
Deleted EntryType    :
  projects/your-project-id/locations/us-central1/entryTypes/
  unstructured-pdf-fileset
Deleted AspectType   :
  projects/your-project-id/locations/us-central1/aspectTypes/
  dark-data-extracted-metadata
Deleted AspectType   :
  projects/your-project-id/locations/us-central1/aspectTypes/
  document-governance-aspect
Deleted Cloud Storage object :
  gs://your-project-id-dark-data-manuals/sop-pdfs/LUM-LIG-DES-8G8J_manual.pdf
Deleted Cloud Storage bucket : gs://your-project-id-dark-data-manuals
Removed local temporary file : extracted_metadata.json
Removed local temporary file : governance_metadata.json
✓ Standalone teardown complete. Environment cleanly reset.

Supprimer le répertoire du dépôt cloné

Une fois que cleanup.py a fini de supprimer les ressources cloud, revenez à votre répertoire d'accueil et supprimez le dossier du dépôt devrel-demos cloné de Cloud Shell :

cd ~
rm -rf ~/devrel-demos

12. Félicitations !

Vous avez créé et validé un pipeline de gouvernance Metadata-as-Code de bout en bout qui transforme les PDF non structurés dans Cloud Storage en éléments Knowledge Catalog détectables et conformes au schéma à l'aide de Gemini Enterprise Agent Platform.

Étape suivante

  • Automatisez avec Terraform : gérez les ressources google_dataplex_aspect_type et google_dataplex_entry_group de manière déclarative dans les pipelines CI/CD.
  • Déployer des workers Eventarc et Cloud Run : conteneurisez step4_extract_metadata.py et step5_attach_aspects.py dans un service Cloud Run axé sur les événements.
  • Joindre l'inventaire en aval dans BigQuery : exportez les métadonnées de gouvernance extraites (lakehouse_cross_ref_table) pour joindre les attributs de conformité des documents non structurés aux tables d'inventaire des SKU structurées.

Documents de référence