Inicio / Artículos

Asistente de soporte RAG de doble motor en Google Cloud: Documentación pública, guías de uso privadas

Crear un asistente de soporte que consulte tanto Vertex AI Search como la API de conocimiento para desarrolladores, y que luego permita a Gemini armonizar los consejos con las reglas propias del cliente.

Editor: REACTAPP.TOP13 min de lectura2964 palabras

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

Cualquiera que trabaje en una cola de soporte bajo un SLA conoce el costo de cambiar de pestaña: una para la documentación pública de Google Cloud, otra para el manual del propio cliente, y un esfuerzo mental por combinar ambas. Pedirle a un asistente de IA genérico que acorte ese proceso suele dar como resultado una respuesta que parece correcta en teoría pero inadecuada para el cliente que tiene frente a usted.

La solución descrita aquí consiste en un sistema de recuperación reforzada con dos motores. Un ticket se envía simultáneamente a dos fuentes: la documentación pública de Google Cloud y un repositorio privado con los manuales del cliente, y un modelo Gemini sintetiza la información obtenida en una guía de resolución que respeta las reglas del cliente. Imagine encontrar una receta bien calificada en línea y que un nutricionista la modifique según su dieta antes de cocinarla.

La estructura utiliza cuatro componentes:

  • Dos vías de recuperación que funcionan paralelamente: una para consejos técnicos generales y otra para políticas internas estrictas.
  • Vertex AI Search como almacén privado, que indexa las reglas y restricciones empresariales directamente desde los archivos de procedimientos.
  • La API de conocimiento para desarrolladores como fuente pública, que proporciona recomendaciones fundamentadas y pasos para solucionar problemas extraídos de la documentación oficial.
  • Gemini como capa de razonamiento, que compara las soluciones públicas con las reglas privadas para que el resultado sea una guía que un ingeniero de soporte pueda seguir sin riesgos.

Por qué los consejos generales fallan

Tomemos un caso como este:

Acme informa de un retraso de aproximadamente cinco segundos al iniciar su frontend en Cloud Run. ¿Qué deberíamos hacer?

Un asistente que solo se base en documentos públicos casi con certeza dirá que se debe establecer min-instances en 1. Ese es el remedio estándar para los arranques lentos, y también representa un problema para este cliente por dos razones:

  • Límite presupuestario. Acme limita el servicio frontend a un máximo de una instancia, por lo que no es posible utilizar una instancia siempre activa.
  • Regla de proceso. La guía operativa de Acme prohíbe modificar min-instances sin la aprobación por escrito de su arquitecta principal, Sarah Connor.

Lo que se necesita es un sistema que obtenga las directrices públicas sobre la latencia de arranque en Cloud Run, verifique cada sugerencia frente a las restricciones privadas de Acme y solo entonces informe al ingeniero sobre cualquier conflicto y proponga una alternativa que cumpla con las normas.

Requisitos previos

Tenga listos estos elementos antes de comenzar:

  1. Un proyecto de Google Cloud con la facturación activada.
  2. La CLI gcloud, instalada y autenticada en su equipo.
  3. Python 3.10 o una versión más reciente.

Costos y cuotas

El tutorial está diseñado para permanecer dentro de los planes gratuitos y las cuotas de desarrollo de cada servicio:

  • Cloud Storage. Un archivo pequeño de runbook está dentro del límite de almacenamiento siempre gratuito. Solo se generan cargos si se excede ese límite o se produce tráfico de salida.
  • Vertex AI Search (Gemini Agent Search). Las cuentas nuevas reciben un plan de prueba gratuito. Una vez agotados los créditos de la prueba, las consultas se facturan por uso según el número de consultas realizadas (1,000 consultas como unidad), por lo que consulte la página de precios actual.
  • Developer Knowledge API. Es gratuito habilitarlo y usarlo dentro de los límites estándar para desarrolladores. Las asignaciones de recursos pueden consultarse en las páginas de cuotas de Google Cloud.
  • Gemini 3.5 Flash. Está disponible de forma gratuita dentro de los límites habituales para desarrolladores en Google AI Studio. La facturación pagada solo comienza si se realiza una actualización para obtener mayor concurrencia y límites de velocidad.
  • Preparación del entorno de Google Cloud

    Antes incluso del asistente en sí, es necesario configurar la infraestructura subyacente. Un script bash se encarga de tres tareas:

    • Habilitación de APIs. Activa Cloud Storage, Vertex AI Agent Builder (discoveryengine), Developer Knowledge y la Generative Language API, para que el orquestador pueda acceder a cada punto de conexión que necesite.
    • Creación de la identidad del servicio Discovery Engine. Se crea previamente el agente de servicio gestionado por Google (service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com), de modo que el indexador en segundo plano pueda recibir permisos IAM de inmediato, sin necesidad de hacer clic en la consola por primera vez.
    • Configuración del almacenamiento y el acceso. Se crea el bucket de Cloud Storage que albergará el manual del cliente y se otorgan los permisos roles/storage.objectViewer a dos entidades: el agente de búsqueda en segundo plano, que los necesita para indexar, y su propia cuenta, para que pueda inspeccionar el bucket en la consola.

    Pegue el script en una 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."
    

    Los valores que podría querer ajustar:

    • PROJECT_ID es el ID de su proyecto. Cuando el proyecto ya está configurado en gcloud, gcloud config get-value project lo devuelve automáticamente.
    • PROJECT_NUMBER es el número único del proyecto.
    • BUCKET_NAME da nombre al bucket para los runbooks y su valor por defecto es ${PROJECT_ID}-internal-kb.
    • USER_EMAIL es la dirección de la cuenta de Google Cloud activa que obtiene acceso de visualizador en la consola.

    Creación del motor de conocimiento privado

    La parte privada es un pequeño motor de búsqueda que alberga un ejemplo de runbook de cliente.

    Escribir y cargar el runbook

    Cree el archivo del runbook localmente y luego cópielo al 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 orden de carga utiliza el nombre del bucket del paso de configuración, ${PROJECT_ID}-internal-kb, a menos que lo hayan modificado. El manual de operaciones contiene intencionadamente toda la información relevante: el límite máximo de una instancia establecido por Acme, su regla de aprobación para el número mínimo de instancias, los nombres de sus conectores de base de datos y VPC, su política que exige el despliegue únicamente a través de GitHub Actions, y los contactos para escalación.

    Creación de un almacén de datos para búsquedas de agentes

    El almacén de datos es lo que permite buscar en el manual de operaciones. En la consola de Google Cloud:

    1. Abra Agent Builder (también denominado Vertex AI Search).
    2. En la navegación de la izquierda, elija Data Stores y luego Create data store.
    3. Como fuente, seleccione Cloud Storage.
  • Para la ruta de la carpeta, ingrese gs://PROJECT_ID-internal-kb/* reemplazando PROJECT_ID con su ID de proyecto real.
  • Elija Documentos no estructurados (PDF, HTML, TXT y similares) como tipo de datos y continúe.
  • Dé al almacén de datos el nombre acme-internal-kb.
  • Haga clic en Crear.
  • En la lista de Almacenes de datos, copie y guarde el ID del almacén de datos.
  • Creación de una aplicación de búsqueda para el servicio

    Las consultas desde el código pasan por una Aplicación de Búsqueda (un motor), que funciona como la capa de servicio sobre el almacén de datos:

    1. En Agent Builder, abra Aplicaciones.
    2. Haga clic en Crear aplicación.
    3. Bajo Búsqueda en la página de creación, haga clic en Crear.
    4. Nombré la aplicación acme-search-app.
    5. Para el nombre de la organización externa, ingrese Acme Corporation.
    6. Deje la ubicación en el valor predeterminado, global, y continúe.
    7. En el paso de Data Stores, marque acme-internal-kb.
    8. Haga clic en Create.
    9. Cuando termine, abra la aplicación desde la lista de Apps y copie su Engine ID (también llamado App ID) de la página de detalles. Se verá algo como acme-search-app_1234567890.

    Conectar el lado público: la API de conocimiento para desarrolladores

    Esta API proporciona acceso a la documentación oficial de Google Cloud y a las guías para desarrolladores. Los pasos siguientes crean una clave API, la restringen y confirman el acceso con una solicitud curl.

    Crear una clave API

    1. En la consola, vaya a Credentials.
    2. Elija Crear credenciales y luego Clave API.
    3. Es preferible restringir la clave únicamente al Developer Knowledge API.
    4. Almacene la clave en una variable de entorno:
    export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY
    

    Ponga su propia clave generada en lugar del marcador de posición.

    Pruebas con curl

    Una consulta rápida confirma que la clave funciona y que puede acceder a las documentaciones para desarrolladores 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?"}'
    

    Una respuesta estructurada en JSON que contiene una respuesta extraída de la documentación oficial indica que todo funciona correctamente.

    El script de orquestación

    A continuación se encuentra un script en Python que hace la misma pregunta a ambos motores y transfiere los resultados a Gemini para obtener la respuesta final.

    Instalación de bibliotecas

    pip install google-cloud-discoveryengine google-generativeai requests
    

    Creación del orquestador

    El siguiente comando escribe app.py. El script envía el texto del ticket recibido a ambas rutas de búsqueda, inserta los dos resultados en un modelo de prompt y pasa el prompt completado a 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
    

    El prompt es el que realiza el trabajo real. Indica al modelo que consulte la guía interna con las configuraciones y reglas específicas del cliente, y que compare cada recomendación genérica con dichas restricciones. Si una recomendación pública, como aumentar el número mínimo de instancias, viola un límite o regla de aprobación del cliente, el modelo debe indicarlo abiertamente y proponer una alternativa que cumpla con las normas. También debe mencionar los contactos reales del cliente, sus servicios, bases de datos y conectores de red al explicar los pasos.

    Ejecutar la prueba

    Establezca las variables de entorno e inicie el 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."
    

    Sustituya los valores reales:

    • PROJECT_ID es el ID de su proyecto.
    • ENGINE_ID es el ID del motor/aplicación de la aplicación Vertex AI Search.
    • DEVELOPERKNOWLEDGE_API_KEY es la clave que generó para la API de conocimiento de desarrolladores.
    • GEMINI_API_KEY es una clave de Google AI Studio. La documentación de Google explica cómo obtener una clave de la API Gemini.

    Así se verá el resultado

    Espere algo similar a esto:

    [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.
    …
    

    Qué muestra el resultado

    La respuesta combina las dos fuentes, y cada una aporta algo diferente:

    1. La documentación pública, a través de la API Developer Knowledge, proporciona las soluciones técnicas: activar cpu-boost, establecer la concurrencia en 80 o agregar una prueba de inicio /healthz.
    2. La base de conocimientos privada, mediante Agent Search, agrega lógica empresarial, restricciones de seguridad y detalles operativos. Indica que el consejo estándar (min_instances = 1) viola las reglas presupuestarias de Acme. Menciona a Sarah Connor como la persona que debe aprobar cualquier excepción. Hace referencia a la base de datos real de Acme y al conector VPC.

    Sin ambos motores, un modelo genérico simplemente recomendaría aumentar min_instances. Un ingeniero de soporte junior podría actuar de inmediato, y en una empresa eso podría superar los límites presupuestarios, activar alertas de facturación e ignorar la aprobación arquitectónica. Al basar el modelo tanto en contextos públicos como privados, se obtienen soluciones técnicamente sólidas que también cumplen con la política organizacional.

    Mejoras para su uso en producción

    Serían sensatas algunas modificaciones antes de que pueda utilizarse para casos reales:

    • Mantenga los datos confidenciales en un gestor de secretos. Coloque las claves API en el Secret Manager y cárguelas en tiempo de ejecución, no en variables de entorno en texto plano.
  • Utilice IAM con los menores privilegios posibles. Ejecute la carga de trabajo bajo una cuenta de servicio dedicada que solo contenga roles/discoveryengine.viewer y roles/storage.objectViewer, y limite las claves API a APIs específicas y direcciones de red de confianza.
  • Automatice la ingesta de datos. Conecte fuentes como Jira, Confluence, SharePoint o Google Drive al almacén de datos a través de los conectores de Vertex AI Search, eliminando así las subidas manuales.
  • Filtre por metadatos. Incluya los ID de cliente y las etiquetas del sistema en el filtro SearchRequest para que los resultados se limiten al entorno de un único cliente.
  • Almacene en caché búsquedas comunes. Mantenga las respuestas de documentación más frecuentes en Memorystore for Redis para reducir la latencia y el uso de cuotas.
  • Limpieza

    Para evitar cargos continuos, elimine lo que creó la guía.

    Búsqueda en la aplicación y el almacén de datos

    No se puede eliminar un almacén de datos mientras haya una aplicación asociada, por lo que primero debe eliminarse la aplicación. En la consola:

    1. Vaya a Agent Builder y luego a Apps.
    2. Encuentre acme-search-app, abra More actions (tres puntos) y elija Delete.
    3. Escriba el nombre de la aplicación en el cuadro de diálogo de confirmación y confírmelo.
    4. Abra Data Stores en el menú de navegación.
    5. Encuentre acme-internal-kb, abra More actions y elija Delete.
    6. Confírmelo.

    El bucket de almacenamiento

    Eliminar el contenido del bucket se hace con una sola orden:

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

    Claves de API

    1. Vaya a APIs y Servicios, y luego a Credenciales.
    2. Debajo de Claves de API, busque la clave creada para la API de Conocimientos para Desarrolladores.
    3. Haga clic en el ícono de papelera junto a ella y confírmelo.

    Archivos locales

    Elimine los scripts y el manual de ejemplo de su equipo:

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