Асистент підтримки з подвійним двигуном RAG у Google Cloud: публічні документи, приватні посібники.
Створіть асистента підтримки, який одночасно запитує Vertex AI Search та Developer Knowledge API, а потім дозволяє Gemini узгодити рекомендації з власними правилами клієнта.
Кожен, хто працює у черзі підтримки відповідно до 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 та лише потім повідомляла інженера про будь-які конфлікти та пропонувала альтернативу, яка відповідає правилам.
Попередні вимоги
Переконайтеся, що у вас є це перед початком:
- Проєкт у Google Cloud із увімкненою оплатою.
- Інструмент gcloud CLI, встановлений та автентифікований на вашому комп’ютері.
- Python 3.10 або новіша версія.
Витрати та квоти
Цей посібник розроблений так, щоб усі операції залишалися в межах безкоштовних тарифів та квот для розробників кожного сервісу:
- Cloud Storage. Один невеликий файл runbook цілком вміщується в обмеження щодо безкоштовного сховища. Оплата виникає лише у разі перевищення цих обмежень або створення даних для вивантаження.
- 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 key.
- Бажано обмежити дію ключа лише Developer Knowledge API.
- Збережіть ключ у змінній середовища:
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. Вказується, що Сара Коннор є особою, яка мусить схвалювати будь-які винятки. Також згадується реальна база даних та з’єднувач VPC Acme.
Без обох двигунів звичайна модель просто порадила б підвищити значення min_instances. Молодший інженер підтримки може негайно це зробити, а в корпорації це може призвести до перевищення бюджетних лімітів, активації попереджень про оплату та ігнорування схвалення архітектури. Використання моделі в як загальнодоступному, так і в приватному контексті дозволяє отримати рішення, які є технічно обґрунтованими та водночас відповідають корпоративним правилам.
Підготовка до використання у продакшені
Перед тим, як це буде використовуватися для обробки реальних заявок, доцільно внести кілька змін:
- Зберігайте конфіденційну інформацію у менеджері секретів. Розміщуйте ключі API у Secret Manager та завантажуйте їх під час виконання програми, а не у змінних середовища у вигляді звичайного тексту.
roles/discoveryengine.viewer та roles/storage.objectViewer, а також обмежуйте API-ключі конкретними API та надійними IP-адресами мережі.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 знайдіть ключ, створений для Developer Knowledge API.
- Натисніть іконку смітника поруч з ним та підтвердіть дію.
Локальні файли
Видаліть скрипти та зразок посібника з вашого комп’ютера:
rm -f app.py app_test.py client-acme-runbook.txt