Вспомогательный ассистент с двумя двигателями RAG в Google Cloud: общедоступные документы, приватные руководства
Создать вспомогательного ассистента, который запрашивает данные из Vertex AI Search и Developer Knowledge API, а затем позволяет Gemini согласовывать рекомендации с собственными правилами клиента.
Каждый, кто работает в очереди поддержки согласно 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 и только затем сообщала бы инженеру о возможных конфликтах и предлагала бы альтернативу, соответствующую этим ограничениям.
Предварительные требования
Перед началом подготовьте следующее:
- Проект в Google Cloud с включенной оплатой.
- Инструмент gcloud CLI, установленный и авторизованный на вашем компьютере.
- Python 3.10 или новее.
Стоимость и квоты
В данном учебнике предполагается использование бесплатных тарифов и квот разработчиков для каждого сервиса:
- Cloud Storage. Один небольшой файл руководства полностью входит в лимит хранилища, предоставляемый бесплатно. Оплата возникает только в случае превышения этого лимита или при передаче данных наружу.
- Vertex AI Search (Gemini Agent Search). Новые аккаунты получают бесплатный пробный период. После истечения кредитов на пробную версию запросы оплачиваются по модели pay-as-you-go за каждые 1 000 запросов, поэтому обратитесь к странице с текущими тарифами.
- Developer Knowledge API. Его можно включить и использовать бесплатно в рамках стандартных лимитов для разработчиков. Запросы на выделение ресурсов можно отслеживать на страницах квот в Google Cloud.
Подготовка среды 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:
- Откройте Agent Builder (также обозначенный как Vertex AI Search).
- В левой навигации выберите Data Stores, затем Create data store.
- В качестве источника укажите Cloud Storage.
gs://PROJECT_ID-internal-kb/*, заменив PROJECT_ID на ваш реальный идентификатор проекта.acme-internal-kb.Создание приложения поиска для обслуживания
Запросы из кода проходят через приложение поиска (движок), которое выступает в качестве слоя обслуживания поверх хранилища данных:
- В Agent Builder откройте раздел Приложения.
- Нажмите Создать приложение.
- На странице создания в разделе Поиск нажмите Создать.
- Назовите приложение
acme-search-app.
Acme Corporation.acme-internal-kb.acme-search-app_1234567890.Подключение публичной части: Developer Knowledge API
Этот API обеспечивает доступ к официальной документации Google Cloud и руководствам для разработчиков. Приведенные ниже шаги позволяют создать ключ API, ограничить его права и подтвердить доступ с помощью одного запроса curl.
Создание ключа API
- В консоли перейдите в раздел Credentials.
- Выберите Создать учетные данные, затем API-ключ.
- Желательно ограничить использование ключа исключительно API Developer Knowledge.
- Храните ключ в переменной окружения:
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.
…
Что показывает результат
Ответ объединяет информацию из двух источников, причем каждый из них вносит свой вклад:
- Публичная документация через API Developer Knowledge предоставляет технические решения: включение опции
cpu-boost, установка значения конкурентности в80или добавление проверки запуска/healthz. - Частная база знаний через Agent Search включает бизнес-логику, ограничения по безопасности и операционные детали. В ней указано, что стандартная рекомендация (
min_instances = 1) нарушает правила бюджетирования Acme. Указано также, что Сара Коннор является лицом, которое должно утверждать любые исключения. В документации упоминается реальная база данных Acme и соединитель VPC.
Без обоих двигателей стандартная модель просто порекомендовала бы увеличить значение min_instances. Младший инженер поддержки может сразу приступить к выполнению этой рекомендации, что в корпоративной среде может привести к нарушению бюджетных ограничений, активации сигналов об оплате и пропуску этапа утверждения архитектуры. Использование модели в контексте как публичных, так и приватных данных позволяет находить решения, которые являются технически обоснованными и соответствуют корпоративной политике.
Усиление модели для производственного использования
Перед тем, как начать использовать её для обработки реальных заявок, целесообразно внести несколько изменений:
- Храните секреты в менеджере секретов. Размещайте ключи API в Secret Manager и загружайте их во время выполнения программы, а не в переменных окружения в простом текстовом формате.
roles/discoveryengine.viewer и roles/storage.objectViewer, а также ограничьте API-ключи конкретными API и доверенными сетевыми адресами.SearchRequest, чтобы результаты отображались только для среды определенного клиента.Очистка
Чтобы избежать постоянных платежей, удаляйте то, что было создано в рамках обучающего курса.
Поиск приложения и хранилища данных
Хранилище данных нельзя удалить, пока к нему привязано приложение, поэтому сначала удалите приложение. В консоли:
- Перейдите в Agent Builder, затем в Apps.
- Найдите
acme-search-app, откройте опцию More actions (три точки) и выберите Delete. - В диалоговом окне подтверждения введите имя приложения и подтвердите действие.
- Откройте раздел Data Stores в меню навигации.
- Найдите
acme-internal-kb, откройте опцию More actions и выберите Delete. - Подтвердите действие.
Бакет хранения
Чтобы удалить содержимое бакета, достаточно одной команды:
export BUCKET_NAME="${PROJECT_ID}-internal-kb"
gcloud storage rm - recursive gs://${BUCKET_NAME}/
Ключи API
- Перейдите в раздел APIs & Services, затем в Credentials.
- В разделе API Keys найдите ключ, созданный для API Developer Knowledge.
- Нажмите иконку мусорной корзины рядом с ним и подтвердите действие.
Локальные файлы
Удалите скрипты и примерный руководство по использованию с вашего компьютера:
rm -f app.py app_test.py client-acme-runbook.txt