Головна / Статті

Асистент підтримки з подвійним двигуном RAG у Google Cloud: публічні документи, приватні посібники.

Створіть асистента підтримки, який одночасно запитує Vertex AI Search та Developer Knowledge API, а потім дозволяє Gemini узгодити рекомендації з власними правилами клієнта.

Видавець: REACTAPP.TOP13 хв читання2964 слів

Джерела: How I built a dual-engine AI Support Assistant with Gemini Agent Search and Google Developer Knowledge API

Кожен, хто працює у черзі підтримки відповідно до SLA, знає, наскільки складно перемикатися між вкладками: одна для публічної документації Google Cloud, інша — для власного посібника клієнта, а ще потрібно поєднати цю інформацію в голові. Запит у загального штучного інтелект-асистента щодо прискорення цього процесу часто призводить до відповіді, яка на папері здається правильною, але є неправильною для клієнта, який сидить перед вами.

Рішення, описане тут, — це схема з двома двигунами та доповненням інформації. Заява надсилається одночасно до двох джерел: публічних документацій Google Cloud та приватного сховища посібників клієнта, а модель Gemini узгоджує отриману інформацію, створюючи посібник з рішеннями, який враховує правила клієнта. Уявіть собі пошук добре оціненого рецепта в Інтернеті та подальшу корекцію його фахівцем з харчування під ваш режим харчування перед тим, як почати готувати.

Для реалізації використовуються чотири компоненти:

  • Два шляхи пошуку, які працюють паралельно: один для загальних технічних порад, інший — для суворих внутрішніх правил.
  • Vertex AI Search як приватний сховище, яке індексує бізнес-правила та обмеження безпосередньо з файлів посібників.
  • Developer Knowledge API як публічне джерело, яке надає обґрунтовані рекомендації та кроки для усунення проблем з офіційної документації.
  • Gemini як шар обробки інформації, який перевіряє публічні рішення за приватними правилами, щоб результат був посібником, яким інженер з підтримки може безпечно керуватися.

Чому загальні поради не завжди ефективні

Візьмемо такий заявку:

Acme повідомляє про затримку при запуску переднього кінця на Cloud Run у розмірі приблизно п’яти секунд. Що нам робити?

Асистент, який спирається лише на публічні документації, майже напевно порадить встановити min-instances на значення 1. Це стандартне рішення проблеми холодних запусків, але для цього клієнта воно також є проблемою з двох причин:

  • Ліміт бюджету. Acme обмежує кількість інстансів фронтенд-сервісу максимум однією, тому інстанція, яка завжди працює, тут не підходить.
  • Правило процесу. Інструкції Acme забороняють змінювати значення min-instances без письмового схвалення їхньої головної архітекторки Сари Коннер.

Потрібна система, яка б отримувала публічні рекомендації щодо часу запуску в Cloud Run, перевіряла кожну з них на відповідність приватним обмеженням Acme та лише потім повідомляла інженера про будь-які конфлікти та пропонувала альтернативу, яка відповідає правилам.

Попередні вимоги

Переконайтеся, що у вас є це перед початком:

  1. Проєкт у Google Cloud із увімкненою оплатою.
  2. Інструмент gcloud CLI, встановлений та автентифікований на вашому комп’ютері.
  3. Python 3.10 або новіша версія.

Витрати та квоти

Цей посібник розроблений так, щоб усі операції залишалися в межах безкоштовних тарифів та квот для розробників кожного сервісу:

  • Cloud Storage. Один невеликий файл runbook цілком вміщується в обмеження щодо безкоштовного сховища. Оплата виникає лише у разі перевищення цих обмежень або створення даних для вивантаження.
  • Vertex AI Search (Gemini Agent Search). Нові облікові записи отримують безкоштовний пробний тариф. Після вичерпання кредитів на пробний період запити оплачуються за принципом pay-as-you-go за кожні 1 000 запитів, тому перевірте актуальну сторінку з цінами.
  • Developer Knowledge API. Його можна безкоштовно увімкнути та використовувати в межах стандартних обмежень для розробників. Запити на виділення ресурсів можна переглядати на сторінках квот у Google Cloud.
  • Gemini 3.5 Flash. Безкоштовно в межах звичайних обмежень для розробників у Google AI Studio. Оплата починається лише у разі оновлення до версії з вищими обмеженнями на одночасність та швидкість обробки.
  • Підготовка середовища Google Cloud

    Перш ніж запустити самого асистента, необхідно налаштувати базову інфраструктуру. Один скрипт bash виконує три завдання:

    • Увімкнення API. Він увімкнує Cloud Storage, Vertex AI Agent Builder (discoveryengine), Developer Knowledge та Generative Language API, щоб оркестратор міг отримати доступ до кожного необхідного кінцевого пункту.
    • Створення ідентичності сервісу Discovery Engine. Він заздалегідь створює агента сервісу, яким керує Google (service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com), щоб індексатор у фоновому режимі міг одразу отримати дозволи IAM, без необхідності натискання кнопок у консолі під час першого використання.
    • Налаштування зберігання та доступу. Він створює контейнер Cloud Storage, у якому буде зберігатися посібник клієнта, та надає роль roles/storage.objectViewer двом особам: агенту пошуку у фоновому режимі, який потребує її для індексації, та вашому власному обліковому запису, щоб ви могли переглядати контейнер у консолі.

    Вставте скрипт у термінал:

    # 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."
    

    Значення, які ви можете захотіти налаштувати:

    • PROJECT_ID — це ідентифікатор вашого проєкту. Якщо проєкт вже налаштований у gcloud, команда gcloud config get-value project автоматично повертає його.
    • PROJECT_NUMBER — це унікальний номер проєкту.
    • BUCKET_NAME — це назва бакета для посібників; за замовчуванням вона дорівнює ${PROJECT_ID}-internal-kb.
    • USER_EMAIL — це адреса активного облікового запису Google Cloud, який отримує права перегляду в консолі.

    Створення приватного двигуна знань

    Приватна частина — це невеликий пошуковий двигун, який містить зразок посібника клієнта.

    Написання та завантаження посібника

    Створіть файл посібника локально, а потім скопіюйте його до бакета:

    # 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}/
    

    Команда завантаження використовує назву бакета з кроку налаштування, ${PROJECT_ID}-internal-kb, якщо ви її не змінили. Керівник виконання навмисно містить лише важливу інформацію: жорсткий ліміт Acme у одну інстанцію, правила схвалення щодо мінімальної кількості інстанцій, назви баз даних та з’єднувачів VPC, політику розгортання лише через GitHub Actions та контакти для ескалації проблем.

    Створення сховища даних для пошуку агентів

    Саме сховище даних робить керівник виконання здатним до пошуку. У консолі Google Cloud:

    1. Відкрийте Agent Builder (також позначений як Vertex AI Search).
    2. У лівій навігації виберіть Data Stores, потім Create data store.
    3. Як джерело виберіть Cloud Storage.
  • Для шляху до папки введіть gs://PROJECT_ID-internal-kb/*, замінивши PROJECT_ID на ваш справжній ідентифікатор проєкту.
  • Виберіть Неструктуровані документи (PDF, HTML, TXT та подібні) як тип даних та продовжуйте.
  • Назвіть сховище даних acme-internal-kb.
  • Натисніть Створити.
  • У списку Сховища даних скопіюйте та збережіть ID сховища даних.
  • Створення застосунку пошуку для обслуговування

    Запити з коду проходять через застосунок пошуку (двигун), який виступає як шар обслуговування над сховищем даних:

    1. У Agent Builder відкрийте розділ Застосунки.
    2. Натисніть Створити застосунок.
    3. У розділі Пошук на сторінці створення натисніть Створити.
    4. Назвіть застосунок acme-search-app.
  • Для назви зовнішньої організації введіть Acme Corporation.
  • Залиште параметр місцезнаходження на значенні за замовчуванням — global, і продовжуйте.
  • На кроці Data Stores позначте acme-internal-kb.
  • Натисніть Create.
  • Коли процес завершиться, відкрийте додаток зі списку Apps та скопіюйте його Engine ID (також називається App ID) зі сторінки деталей. Він виглядає приблизно так: acme-search-app_1234567890.
  • Підключення публічної частини: Developer Knowledge API

    Цей API забезпечує доступ до офіційної документації Google Cloud та посібників для розробників. Наведені нижче кроки дозволяють створити ключ API, обмежити його функції та підтвердити доступ за допомогою одного запиту curl.

    Створення ключа API

    1. У консолі перейдіть до розділу Credentials.
    2. Виберіть Створити облікові дані, а потім API key.
    3. Бажано обмежити дію ключа лише Developer Knowledge API.
    4. Збережіть ключ у змінній середовища:
    export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY
    

    Поставте свій власний створений ключ замість місця для заповнення.

    Тестування за допомогою curl

    Швидкий запит підтверджує, що ключ працює та що ви можете отримати доступ до документації розробників 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?"}'
    

    Структурована відповідь у форматі JSON, яка містить відповідь, отриману з офіційної документації, свідчить про те, що все працює.

    Скрипт оркестрації

    Далі йде скрипт на Python, який ставить обом двигунам одне й те саме запитання та передає результати Gemini для отримання остаточної відповіді.

    Встановлення бібліотек

    pip install google-cloud-discoveryengine google-generativeai requests
    

    Створення оркестратора

    Наступна команда записує файл app.py. Скрипт надсилає текст отриманого замовлення до обох шляхів пошуку, вставляє два результати у шаблон запиту та передає заповнений запит до 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
    

    Саме запит виконує основну роботу. Він просить модель ознайомитися з приватним посібником щодо налаштувань та правил, специфічних для клієнта, та порівняти кожну універсальну рекомендацію з цими обмеженнями. Якщо публічна рекомендація, наприклад щодо збільшення кількості екземплярів, порушує ліміт чи правило схвалення клієнта, модель повинна відкрито про це сказати та запропонувати альтернативний варіант, який відповідає правилам. Крім того, при поясненні кроків вона мусить назвати фактичних контактів, послуги, бази даних та мережеві з’єднання клієнта.

    Запуск тесту

    Встановіть змінні середовища та запустіть скрипт:

    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."
    

    Підставте реальні значення:

    • PROJECT_ID — це ідентифікатор вашого проєкту.
    • ENGINE_ID — це ідентифікатор двигуна/додатку Vertex AI Search.
    • DEVELOPERKNOWLEDGE_API_KEY — це ключ, який ви створили для API Developer Knowledge.
    • GEMINI_API_KEY — це ключ від Google AI Studio. У документації Google пояснюється, як отримати ключ Gemini API.

    Як виглядає результат

    Очікуйте щось на кшталт цього:

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

    Що показує результат

    Відповідь поєднує два джерела, і кожне з них вносить щось окреме:

    1. Публічна документація через API Developer Knowledge надає технічні рішення: увімкнення cpu-boost, встановлення значення конкурентності на 80 або додавання запиту на старт /healthz.
    2. Приватна база знань через Agent Search додає бізнес-логіку, обмеження щодо безпеки та деталі операційного керування. У ній зазначається, що стандартна рекомендація (min_instances = 1) порушує правила бюджету Acme. Вказується, що Сара Коннор є особою, яка мусить схвалювати будь-які винятки. Також згадується реальна база даних та з’єднувач VPC Acme.

    Без обох двигунів звичайна модель просто порадила б підвищити значення min_instances. Молодший інженер підтримки може негайно це зробити, а в корпорації це може призвести до перевищення бюджетних лімітів, активації попереджень про оплату та ігнорування схвалення архітектури. Використання моделі в як загальнодоступному, так і в приватному контексті дозволяє отримати рішення, які є технічно обґрунтованими та водночас відповідають корпоративним правилам.

    Підготовка до використання у продакшені

    Перед тим, як це буде використовуватися для обробки реальних заявок, доцільно внести кілька змін:

    • Зберігайте конфіденційну інформацію у менеджері секретів. Розміщуйте ключі API у Secret Manager та завантажуйте їх під час виконання програми, а не у змінних середовища у вигляді звичайного тексту.
  • Використовуйте принцип мінімальних привілеїв IAM. Запускайте робоче навантаження під спеціалізованим обліковим записом сервісу, який містить лише roles/discoveryengine.viewer та roles/storage.objectViewer, а також обмежуйте API-ключі конкретними API та надійними IP-адресами мережі.
  • Автоматизуйте процес отримання даних. Під’єднуйте джерела, такі як Jira, Confluence, SharePoint чи Google Drive, до сховища даних за допомогою конекторів Vertex AI Search, щоб усунути необхідність вручну завантажувати дані.
  • Фільтруйте за метаданими. Включайте ID клієнта та системні теги у фільтр SearchRequest, щоб результати стосувалися лише середовища одного клієнта.
  • Зберігайте у кеші поширені запити. Зберігайте часто використовувані відповіді з документації у Memorystore для Redis, щоб зменшити затримки та споживання квот.
  • Очищення

    Щоб уникнути постійних оплат, видаліть те, що було створено під час навчального курсу.

    Пошук у додатку та сховищі даних

    Сховище даних не можна видалити, поки до нього приєднаний додаток, тому спочатку видаліть додаток. У консолі:

    1. Перейдіть до Agent Builder, потім до Apps.
    2. Знайдіть acme-search-app, відкрийте More actions (три крапки) та виберіть Delete.
    3. Введіть назву додатку у діалоговому вікні підтвердження та підтвердіть дію.
    4. У меню навігації відкрийте Data Stores.
    5. Знайдіть acme-internal-kb, відкрийте More actions та виберіть Delete.
    6. Підтвердіть дію.

    Бакет зберігання

    Щоб видалити вміст бакета, достатньо одного команди:

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

    Ключі API

    1. Перейдіть до розділу APIs & Services, потім до Credentials.
    2. У розділі API Keys знайдіть ключ, створений для Developer Knowledge API.
    3. Натисніть іконку смітника поруч з ним та підтвердіть дію.

    Локальні файли

    Видаліть скрипти та зразок посібника з вашого комп’ютера:

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