Accueil / Articles

Assistant de support RAG à double moteur sur Google Cloud : documents publics, manuels privés

Créer un assistant de support qui interroge à la fois Vertex AI Search et l’API Developer Knowledge, puis permet à Gemini de concilier les conseils avec les règles propres du client.

Éditeur : REACTAPP.TOP13 min de lecture2964 mots

Sources: How I built a dual-engine AI Support Assistant with Gemini Agent Search and Google Developer Knowledge API

Tout ceux qui travaillent dans une file d’attente de support conformément à un SLA connaissent le coût lié au changement de fenêtres : une pour la documentation publique de Google Cloud, une autre pour le manuel d’opérations du client, ainsi qu’une synthèse mentale des deux. Demander à un assistant IA générique de simplifier ce processus donne souvent une réponse qui semble correcte sur le papier mais qui est inadaptée au client qui se trouve en face de vous.

La solution décrite ici repose sur une architecture à deux moteurs d’extraction de données. Une demande est envoyée en parallèle vers deux sources : la documentation publique de Google Cloud et un stock privé des manuels d’opérations du client. Un modèle Gemini réconcilie ensuite les informations obtenues pour produire un guide de résolution respectant les règles du client. Imaginez trouver une recette bien notée en ligne et faire modifier ses ingrédients par un nutritionniste en fonction de votre régime alimentaire avant de cuisiner.

Cette solution comprend quatre composants :

  • Deux voies de recherche fonctionnant en parallèle : l’une pour les conseils techniques généraux et l’autre pour les politiques internes strictes.
  • Vertex AI Search en tant que stockage privé, indexant les règles et contraintes métier directement à partir des fichiers de procédures.
  • L’API Developer Knowledge en tant que source publique, fournissant des recommandations fondées et des étapes de dépannage tirées de la documentation officielle.
  • Gemini en tant que couche de raisonnement, comparant les solutions publiques aux règles privées afin que le résultat soit un guide que l’ingénieur support puisse suivre en toute sécurité.

Pourquoi les conseils généraux échouent

Prenons un ticket comme celui-ci :

Acme signale un délai d’environ cinq secondes lors du démarrage de leur interface Cloud Run. Que devrions-nous faire ?

Un assistant se basant uniquement sur les documents publics dira presque certainement de mettre min-instances à 1. C’est la solution classique pour les démarrages lents, mais elle pose également problème à ce client pour deux raisons :

  • Un plafond budgétaire. Acme limite le service frontend à au plus une seule instance, ce qui rend impossible l’utilisation d’une instance toujours active.
  • Une règle interne. Le guide de fonctionnement d’Acme interdit de modifier min-instances sans l’accord écrit de son architecte en chef, Sarah Connor.

Il faut donc un système capable de récupérer les directives publiques concernant la latence de démarrage de Cloud Run, de vérifier chaque suggestion par rapport aux contraintes privées d’Acme, et de signaler ensuite au développeur tout conflit en proposant une alternative conforme.

Prérequis

Ayez ces éléments prêts avant de commencer :

  1. Un projet Google Cloud avec la facturation activée.
  2. L’outil CLI gcloud, installé et authentifié sur votre machine.
  3. Python 3.10 ou une version plus récente.

Costs et quotas

Cet tutoriel vise à rester dans les forfaits gratuits et les quotas de développement prévus pour chaque service :

  • Cloud Storage. Un petit fichier de guide reste bien dans la limite de stockage toujours gratuite. Des frais s’appliquent uniquement si vous dépassez cette limite ou générez des transferts de données vers l’extérieur.
  • Vertex AI Search (Gemini Agent Search). Les nouveaux comptes bénéficient d’un forfait d’essai gratuit. Une fois les crédits d’essai épuisés, les requêtes sont facturées au forfait selon le nombre de requêtes effectuées (1 000 requêtes par période) ; veuillez donc consulter la page des tarifs en vigueur.
  • Developer Knowledge API. Son activation et son utilisation sont gratuites dans les limites standard prévues pour les développeurs. Les allocations demandées peuvent être suivies sur les pages de quotas de Google Cloud.
  • Gemini 3.5 Flash. Disponible gratuitement dans les limites habituelles pour les développeurs sur Google AI Studio. La facturation payante n’est activée que si vous passez à une version offrant plus de concurrence et des limites de débit plus élevées.
  • Préparation de l’environnement Google Cloud

    Avant même l’assistant lui-même, il faut mettre en place l’infrastructure sous-jacente. Un script bash gère trois tâches :

    • Activation des API. Il active Cloud Storage, Vertex AI Agent Builder (discoveryengine), Developer Knowledge ainsi que l’API de langage génératif, afin que l’orchestrateur puisse accéder à tous les points de terminaison nécessaires.
    • Création de l’identité du service Discovery Engine. Il crée à l’avance l’agent de service géré par Google (service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com), afin que l’indexeur en arrière-plan puisse recevoir immédiatement des permissions IAM, sans avoir besoin de cliquer dans la console pour la première fois.
    • Mise en place du stockage et de l’accès. Il crée le bucket Cloud Storage qui conservera le manuel d’utilisation du client et accorde roles/storage.objectViewer à deux entités : l’agent de recherche en arrière-plan, qui en a besoin pour indexer, et votre propre compte, vous permettant ainsi d’examiner le bucket dans la console.

    Copiez le script dans un terminal :

    # 1. Initialize variables from active gcloud config
    export PROJECT_ID=$(gcloud config get-value project)
    export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
    export BUCKET_NAME="${PROJECT_ID}-internal-kb"
    export USER_EMAIL=$(gcloud config get-value account)
    
    # 2. Enable all required GCP APIs
    gcloud services enable \
      storage-api.googleapis.com \
      discoveryengine.googleapis.com \
      developerknowledge.googleapis.com \
      generativelanguage.googleapis.com \
      --project=${PROJECT_ID}
    
    # 3. Provision the Discovery Engine Service Identity programmatically
    # (This creates the background service account without requiring a manual Console click)
    gcloud beta services identity create \
      --service=discoveryengine.googleapis.com \
      --project=${PROJECT_ID}
    
    # 4. Create the GCS bucket
    gcloud storage buckets create gs://${BUCKET_NAME} --project=${PROJECT_ID} --location=us-central1
    
    # 5. Grant Storage Object Viewer to the Agent Search Service Agent (for background indexing)
    gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
      --member="serviceAccount:@gcp-sa-discoveryengine.iam.gserviceaccount.com">service-${PROJECT_NUMBER}@gcp-sa-discoveryengine.iam.gserviceaccount.com" \
      --role="roles/storage.objectViewer"
    
    # 6. Grant Storage Object Viewer to your active Console user (for frontend validation)
    gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
      --member="user:${USER_EMAIL}" \
    
      --role="roles/storage.objectViewer"
    
    echo "Setup complete! All APIs enabled and permissions configured successfully."
    

    Les valeurs que vous pourriez vouloir ajuster :

    • PROJECT_ID est l’ID de votre projet. Lorsque le projet est déjà défini dans gcloud, gcloud config get-value project le renvoie automatiquement.
    • PROJECT_NUMBER est le numéro unique du projet.
    • BUCKET_NAME donne un nom au bucket destiné aux runbooks et vaut par défaut ${PROJECT_ID}-internal-kb.
    • USER_EMAIL est l’adresse du compte Google Cloud actif qui obtient un accès en tant qu’utilisateur dans la console.

    Création du moteur de connaissances privé

    La partie privée est un petit moteur de recherche contenant un exemple de runbook client.

    Rédaction et téléchargement du runbook

    Créez le fichier du runbook localement, puis copiez-le dans le bucket :

    # Create the client runbook file with .txt extension
    cat << 'EOF' > client-acme-runbook.txt
    # Client Acme Corporation - Cloud Run Deployment Runbook
    * **Client ID:** ACME-404
    * **SLA Tier:** Platinum (4-hour resolution target)
    * **Infrastructure Specifications:**
      * Frontend Service Name: `acme-frontend-prod`
      * Regional Deployment: `us-central1`
      * Max Instances: `1` (Hard cap imposed due to strict budget constraints)
      * CPU Allocation: `CPU allocated only during request processing`
      * Backend Database: Cloud SQL PostgreSQL (`acme-db-prod`)
      *   Networking: Serverless VPC (Virtual Private Cloud) Access connector (`acme-vpc-connector`)
    * **Deployment Constraints:**
      * Deployed strictly via GitHub Actions workflow (`.github/workflows/deploy.yml`). Manual console updates are forbidden.
      * Direct access to the production DB requires using the Bastion Host `acme-bastion-prod`.
    * **Escalation Path:**
      * Primary Contact: Sarah Connor (Lead Architect, sarah@acme-corp.com)
      * Secondary Contact: PagerDuty escalation policy `acme-prod-critical`
    * **Common Issues & Custom Rules:**
      * **High Cold Starts:** High cold-start latency due to CPU throttling during scale-up. Do not modify the `min-instances` count without approval from Sarah Connor, as it breaches billing thresholds.
      * **Database Connection Timeouts:** If timeouts occur, verify that the Serverless VPC Access connector is in a `READY` state before attempting database restarts.
    EOF
    
    # Upload the file to your bucket
    gcloud storage cp client-acme-runbook.txt gs://${BUCKET_NAME}/
    

    La commande de téléversement utilise le nom du bucket défini lors de l’étape d’installation, ${PROJECT_ID}-internal-kb, à moins que vous ne l’ayez modifié. Le guide de déploiement contient délibérément toutes les informations essentielles : la limite maximale d’une seule instance imposée par Acme, sa règle d’approbation concernant le nombre minimal d’instances, les noms de ses connexions de base de données et de VPC, sa politique exigeant un déploiement uniquement via GitHub Actions, ainsi que les contacts pour l’escalade.

    Création d’un stockage de données pour la recherche des agents

    C’est le stockage de données qui permet de rechercher dans le guide de déploiement. Dans la console Google Cloud :

    1. Ouvrez Agent Builder (également indiqué sous le nom de Vertex AI Search).
    2. Dans la navigation de gauche, sélectionnez Data Stores, puis Create data store.
    3. En tant que source, choisissez Cloud Storage.
  • Pour le chemin du dossier, saisissez gs://PROJECT_ID-internal-kb/*, en remplaçant PROJECT_ID par votre véritable ID de projet.
  • Sélectionnez Documents non structurés (PDF, HTML, TXT et similaires) comme type de données et continuez.
  • Donnez au stockage de données le nom acme-internal-kb.
  • Cliquez sur Créer.
  • Dans la liste des Stockages de données, copiez et conservez l’ID du stockage de données.
  • Création d’une application de recherche pour le service

    Les requêtes provenant du code passent par une application de recherche (un moteur), qui agit comme couche de service au-dessus du stockage de données :

    1. Dans Agent Builder, ouvrez Applications.
    2. Cliquez sur Créer une application.
    3. Sous Recherche sur la page de création, cliquez sur Créer.
    4. Nommez l’application acme-search-app.
    5. Pour le nom de l’organisation externe, saisissez Acme Corporation.
    6. Laissez la localisation à la valeur par défaut, global, et continuez.
    7. À l’étape Data Stores, cochez acme-internal-kb.
    8. Cliquez sur Create.
    9. Lorsque c’est terminé, ouvrez l’application depuis la liste Apps et copiez son Engine ID (également appelé App ID) sur la page des détails. Il ressemble à acme-search-app_1234567890.

    Connexion du côté public : l’API Developer Knowledge

    Cette API offre un accès direct à la documentation officielle de Google Cloud ainsi qu’aux guides pour développeurs. Les étapes suivantes permettent de créer une clé API, de la restreindre et de confirmer l’accès avec une seule requête curl.

    Création d’une clé API

    1. Dans la console, allez dans Credentials.
    2. Sélectionnez Créer des identifiants, puis Clé API.
    3. Il est préférable de limiter l’accès à cette clé uniquement à la API Developer Knowledge.
    4. Stockez la clé dans une variable d’environnement :
    export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY
    

    Remplacez le texte de remplacement par votre propre clé générée.

    Test avec curl

    Une requête rapide confirme que la clé fonctionne et que vous pouvez accéder aux documents de développement de Google :

    curl -X POST "https://developerknowledge.googleapis.com/v1:answerQuery?key=$DEVELOPERKNOWLEDGE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"query": "How do I reduce Cloud Run cold starts?"}'
    

    Une réponse JSON structurée contenant une réponse tirée de la documentation officielle indique que tout fonctionne correctement.

    Le script d’orchestration

    Ensuite vient un script Python qui pose la même question aux deux moteurs et transmet les résultats à Gemini pour obtenir la réponse finale.

    Installation des bibliothèques

    pip install google-cloud-discoveryengine google-generativeai requests
    

    Création de l’orchestrateur

    La commande suivante écrit app.py. Le script envoie le texte du ticket reçu vers les deux chemins de recherche, insère les deux résultats dans un modèle de prompt et transmet le prompt rempli à Gemini :

    cat << 'EOF' > app.py
    import os
    import sys
    import requests
    import google.generativeai as genai
    from google.cloud import discoveryengine_v1beta as discoveryengine
    # Configuration - Replace with your details
    PROJECT_ID = os.environ.get("PROJECT_ID")
    ENGINE_ID = os.environ.get("ENGINE_ID")
    LOCATION = "global"
    MODEL_NAME = os.environ.get("GEMINI_MODEL", "gemini-3.5-flash")
    # Initialize Gemini Client (Uses GEMINI_API_KEY env variable)
    genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
    model = genai.GenerativeModel(MODEL_NAME)
    
    # 1. Helper to query the Private KB (Agent Search)
    def query_private_kb(query_text: str):
    client = discoveryengine.SearchServiceClient()
    serving_config = f"projects/{PROJECT_ID}/locations/{LOCATION}/collections/default_collection/engines/{ENGINE_ID}/servingConfigs/default_search"
    request = discoveryengine.SearchRequest(
    serving_config=serving_config,
    query=query_text,
    page_size=3,
    )
    response = client.search(request)
    results = []
    for result in response.results:
    doc_data = result.document.derived_struct_data
    if "snippets" in doc_data:
    for s in doc_data["snippets"]:
    results.append(s.get("snippet", ""))
    return "\n".join(results)
    
    # 2. Helper to query the Public KB (Developer Knowledge API)
    def query_public_gcp_kb(query_text: str):
    api_key = os.environ.get("DEVELOPERKNOWLEDGE_API_KEY")
    url = f"https://developerknowledge.googleapis.com/v1:answerQuery?key={api_key}"
    headers = {"Content-Type": "application/json"}
    payload = {"query": query_text}
    try:
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()
    data = response.json()
    return data.get("answer", {}).get("answerText", "No answer found.")
    except Exception as e:
    return f"Error querying Developer Knowledge API: {e}"
    
    # 3. Main Orchestration
    def run_support_assistant(ticket_query: str):
    print(f"\n[Ticket Query]: {ticket_query}")
    # Dynamically search private client specs using the ticket query
    print("Searching private database…")
    private_context = query_private_kb(ticket_query)
    # Dynamically search public GCP advice using the ticket query
    print("Searching public Google Cloud docs…")
    public_context = query_public_gcp_kb(ticket_query)
    # Format system prompt template
    prompt = f"""
    You are a Senior Cloud Support Assistant. Your task is to resolve the support ticket.
    CRITICAL INSTRUCTIONS:
    1. You MUST read the [Private Runbook Context] for specific client configurations, limits, and rules.
    2. You MUST cross-reference all generic GCP recommendations against the client's private constraints. If a generic recommendation (e.g., increasing min-instances) violates a client limit or rule (e.g., budget caps or requiring specific manager approvals), you MUST call this out explicitly and provide the compliant fallback option.
    3. Refer to specific client contacts (e.g., Sarah Connor), services, databases, and network connectors mentioned in the runbook when explaining the steps.
    [Private Runbook Context]:
    {private_context}
    [Official GCP Guidance]:
    {public_context}
    [Support Ticket Query]:
    {ticket_query}
    Provide a precise troubleshooting guide for the support engineer.
    """
    response = model.generate_content(prompt)
    print("\n[AI Assistant Resolution Guide]:")
    print(response.text)
    if __name__ == "__main__":
    if not all([PROJECT_ID, ENGINE_ID, os.environ.get("DEVELOPERKNOWLEDGE_API_KEY"), os.environ.get("GEMINI_API_KEY")]):
    print("Please set your environment variables: PROJECT_ID, ENGINE_ID, DEVELOPERKNOWLEDGE_API_KEY, and GEMINI_API_KEY.")
    else:
    # Allow passing the ticket query as a command line argument
    default_query = "Client Acme is complaining about 5-second cold start latency on their frontend service. How can we fix it?"
    query = sys.argv[1] if len(sys.argv) > 1 else default_query
    run_support_assistant(ticket_query=query)
    EOF
    

    C’est l’instruction qui effectue le travail réel. Elle demande au modèle de consulter le manuel interne contenant les paramètres et règles spécifiques au client, puis de comparer chaque recommandation générale à ces contraintes. Si une recommandation publique, comme augmenter le nombre minimal d’instances, enfreint une limite ou une règle d’approbation du client, le modèle doit l’indiquer clairement et proposer une solution alternative conforme. Il doit également mentionner les contacts réels du client, ses services, ses bases de données et ses connexions réseau lorsqu’il explique les étapes.

    Exécution du test

    Definissez les variables d’environnement et lancez le script :

    export PROJECT_ID="PROJECT_ID"
    export ENGINE_ID="ENGINE_ID"
    export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY"
    export GEMINI_API_KEY="GEMINI_API_KEY"
    # Run with the default query
    python3 app.py
    # Or pass a custom query as a command line argument
    python3 app.py "Client Acme is complaining about database connection timeouts on their Cloud SQL backend."
    

    Remplacez par des valeurs réelles :

    • PROJECT_ID est l’ID de votre projet.
    • ENGINE_ID est l’ID du moteur/application de l’application Vertex AI Search.
    • DEVELOPERKNOWLEDGE_API_KEY est la clé que vous avez générée pour l’API Developer Knowledge.
    • GEMINI_API_KEY est une clé provenant de Google AI Studio. La documentation de Google explique comment obtenir une clé Gemini API.

    À quoi ressemble la sortie

    Vous devriez obtenir quelque chose de ce genre :

    [AI Assistant Resolution Guide]:
    ### **Acme Support Ticket: Resolution & Troubleshooting Guide**
    **Ticket ID:** #ACM-98431
    **Service:** `acme-frontend` (Cloud Run)
    **Issue:** 5-second cold start latency
    **Assigned To:** Cloud Support Engineering Team
     - -
    ### **CRITICAL COMPLIANCE WARNING (Read Before Acting)**
    According to Acme's operational rules:
    * **The Conflict:** Standard GCP documentation suggests setting `min_instances` (e.g., to 1 or more) to eliminate cold starts. However, **Acme operates under a strict budget cap policy that prohibits idle/always-on instances** for the frontend service without explicit business justification.
    * **The Rule:** You **MUST NOT** increase `min_instances` on `acme-frontend` without prior written approval from **Sarah Connor** (Acme's Technical Lead & Budget Approver).
    * **Compliant Fallback:** If Sarah Connor does not approve the budget increase for `min_instances`, you must resolve the latency strictly through **code-level optimizations, CPU adjustments, startup probes, and connection tuning** as detailed below.
     - -
    ### **Step-by-Step Troubleshooting & Resolution Guide**
    #### **Phase 1: Triage and Governance Check**
    1. **Contact Sarah Connor:** Reach out to Sarah Connor to request a budget exception for setting `min_instances = 1`.
    2. **Fallback implementation:** If denied, proceed strictly with the budget-compliant fallback steps below to optimize the cold start duration within the 0-instance scaling limit.
    #### **Phase 2: Network & Connection Optimization**
    1. **Inspect VPC Connector Latency:** `acme-frontend` routes traffic to the database via the **`acme-vpc-connector`** (Serverless VPC Access). Ensure that the connector throughput is not bottlenecked.
    2. **Database Connection Lazy-Loading:** The frontend connects to **`acme-production-db`** (Cloud SQL). Do not open the connection pool until the first actual handler request arrives to minimize container boot time.
    …
    

    Ce que montre le résultat

    La réponse combine les deux sources, chacune apportant quelque chose de différent :

    1. La documentation publique, via l’API Developer Knowledge, fournit les correctifs techniques : activer cpu-boost, définir la concurrence à 80, ou ajouter une vérification de démarrage /healthz.
    2. La base de connaissances privée, via Agent Search, ajoute de la logique métier, des contraintes de sécurité et des détails opérationnels. Elle indique que le conseil standard (min_instances = 1) contrevient aux règles budgétaires d’Acme. Elle mentionne Sarah Connor comme la personne qui doit approuver toute exception. Elle fait référence à la base de données réelle d’Acme ainsi qu’à son connecteur VPC.

    Sans ces deux moteurs, un modèle générique se contenterait de recommander d’augmenter la valeur de min_instances. Un ingénieur support junior pourrait agir immédiatement, ce qui, dans une entreprise, pourrait dépasser les limites budgétaires, déclencher des alertes de facturation et faire l’économie de l’approbation architecturale. En ancrant le modèle dans un contexte à la fois public et privé, on obtient des solutions techniquement solides tout en respectant les politiques de l’organisation.

    Rendre le modèle plus robuste pour la production

    Quelques modifications seraient nécessaires avant qu’il ne puisse traiter des tickets réels :

    • Gérer les secrets dans un gestionnaire de secrets. Stockez les clés API dans le Secret Manager et chargez-les en temps de exécution, plutôt que dans des variables d’environnement au format texte brut.
  • Utilisez des principes de moindre privilège pour IAM. Exécutez la charge de travail sous un compte de service dédié ne possédant que les rôles roles/discoveryengine.viewer et roles/storage.objectViewer, et restreignez les clés API aux APIs spécifiques ainsi qu’aux adresses réseau fiables.
  • Automatisez l’ingestion des données. Liez des sources telles que Jira, Confluence, SharePoint ou Google Drive au stockage de données via des connecteurs Vertex AI Search, afin d’éliminer les téléchargements manuels.
  • Filtrez par métadonnées. Transmettez les IDs des clients et les étiquettes système dans le filtre SearchRequest pour limiter les résultats au seul environnement d’un client.
  • Mémorisez les recherches fréquentes. Conservez dans Memorystore for Redis les réponses à des questions de documentation fréquentes afin de réduire la latence et l’utilisation des quotas.
  • Nettoyage

    Afin d’éviter des frais continus, supprimez ce que le tutoriel a créé.

    Recherche dans l’application et le stockage de données

    Un stockage de données ne peut pas être supprimé tant qu’une application y est associée, il faut donc d’abord supprimer l’application. Dans la console :

    1. Allez dans Agent Builder, puis dans Apps.
    2. Localisez acme-search-app, ouvrez More actions (trois points) et sélectionnez Delete.
    3. Saisissez le nom de l’application dans la boîte de dialogue de confirmation et confirmez.
    4. Ouvrez Data Stores dans le menu de navigation.
    5. Localisez acme-internal-kb, ouvrez More actions, puis sélectionnez Delete.
    6. Confirmez.

    Le bucket de stockage

    Supprimer le contenu du bucket se fait avec une seule commande :

    export BUCKET_NAME="${PROJECT_ID}-internal-kb"
    gcloud storage rm - recursive gs://${BUCKET_NAME}/
    

    Clés API

    1. Allez dans APIs & Services, puis dans Credentials.
    2. Sous API Keys, trouvez la clé créée pour l’API Developer Knowledge.
    3. Cliquez sur l’icône poubelle à côté et confirmez.

    Fichiers locaux

    Supprimez les scripts ainsi que le guide d’exécution de exemple de votre ordinateur :

    rm -f app.py app_test.py client-acme-runbook.txt