Главная / Статьи

Вспомогательный ассистент с двумя двигателями 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 служит частным хранилищем, индексирующим бизнес-правила и ограничения непосредственно из файлов руководства.
  • API Developer Knowledge выступает в качестве публичного источника, возвращая обоснованные рекомендации и шаги по устранению неполадок из официальной документации.
  • 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. Один небольшой файл руководства полностью входит в лимит хранилища, предоставляемый бесплатно. Оплата возникает только в случае превышения этого лимита или при передаче данных наружу.
  • 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-ключ.
    3. Желательно ограничить использование ключа исключительно API Developer Knowledge.
    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. Указано также, что Сара Коннор является лицом, которое должно утверждать любые исключения. В документации упоминается реальная база данных Acme и соединитель VPC.

    Без обоих двигателей стандартная модель просто порекомендовала бы увеличить значение min_instances. Младший инженер поддержки может сразу приступить к выполнению этой рекомендации, что в корпоративной среде может привести к нарушению бюджетных ограничений, активации сигналов об оплате и пропуску этапа утверждения архитектуры. Использование модели в контексте как публичных, так и приватных данных позволяет находить решения, которые являются технически обоснованными и соответствуют корпоративной политике.

    Усиление модели для производственного использования

    Перед тем, как начать использовать её для обработки реальных заявок, целесообразно внести несколько изменений:

    • Храните секреты в менеджере секретов. Размещайте ключи API в Secret Manager и загружайте их во время выполнения программы, а не в переменных окружения в простом текстовом формате.
  • Используйте принцип минимальных привилегий в IAM. Запускайте рабочую нагрузку под специальным учетной записью сервиса, имеющей только роли roles/discoveryengine.viewer и roles/storage.objectViewer, а также ограничьте API-ключи конкретными API и доверенными сетевыми адресами.
  • Автоматизируйте загрузку данных. Подключите источники, такие как Jira, Confluence, SharePoint или Google Drive, к хранилищу данных с помощью коннекторов Vertex AI Search, чтобы исключить ручную загрузку.
  • Фильтруйте по метаданным. Передавайте идентификаторы клиентов и системные теги в фильтр 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 найдите ключ, созданный для API Developer Knowledge.
    3. Нажмите иконку мусорной корзины рядом с ним и подтвердите действие.

    Локальные файлы

    Удалите скрипты и примерный руководство по использованию с вашего компьютера:

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