Dual-Engine RAG Support Assistant auf Google Cloud: Öffentliche Dokumente, private Runbooks
Erstelle einen Support-Assistenten, der sowohl Vertex AI Search als auch die Developer Knowledge API abfragt und anschließend es Gemini ermöglicht, Ratschläge mit den eigenen Regeln des Kunden abzugleichen.
Jeder, der in einer Support-Queue nach einem SLA arbeitet, kennt den Aufwand beim Wechseln zwischen verschiedenen Tabellen: einer für die öffentlichen Dokumentationen von Google Cloud, einer weiteren für das eigene Runbook des Kunden sowie die notwendige mentale Verknüpfung beider Quellen. Wenn man einen allgemeinen KI-Assistenten bittet, diesen Prozess abzukürzen, erhält man oft eine Antwort, die auf dem Papier richtig ist, aber für den Kunden vor einem falsch ist.
Die hier beschriebene Lösung basiert auf einer dualen, durch KI unterstützten Sucharchitektur. Ein Ticket wird parallel an zwei Quellen gesendet – an die öffentlichen Google Cloud-Dokumente sowie an einen privaten Speicher mit den Kunden-Runbooks – und ein Gemini-Modell vereinigt die Ergebnisse zu einem Lösungsführer, der die Regeln des Kunden berücksichtigt. Stellen Sie sich vor, Sie finden ein gut bewertetes Rezept im Internet und lassen es vor dem Kochen von einem Ernährungswissenschaftler an Ihre Ernährungsgewohnheiten anpassen.
Die Implementierung setzt vier Komponenten voraus:
- Zwei parallele Abfragemöglichkeiten: eine für allgemeine technische Beratung und eine für strenge interne Richtlinien.
- Vertex AI Search als privater Speicher, der Geschäftsregeln und Einschränkungen direkt aus Runbook-Dateien indiziert.
- Die Developer Knowledge API als öffentliche Quelle, die auf offizieller Dokumentation basierende Empfehlungen sowie Lösungsschritte zur Fehlerbehebung liefert.
- Gemini als Überlegungsschicht, die öffentliche Lösungen anhand privater Regeln prüft, sodass das Ergebnis ein Leitfaden ist, dem ein Support-Engineer sicher folgen kann.
Warum allgemeine Ratschläge fehlschlagen
Nehmen wir ein Ticket wie dieses:
Acme berichtet von etwa fünf Sekunden Verzögerung beim Start ihrer Cloud Run-Frontend-Anwendung. Was sollen wir tun?
Ein Assistent, der sich nur auf öffentliche Dokumente stützt, wird fast sicher vorschlagen, min-instances auf 1 zu setzen. Das ist die übliche Lösung für Cold Starts – doch auch bei diesem Kunden stellt dies ein Problem dar, und zwar aus zwei Gründen:
- Budgetobergrenze. Acme beschränkt den Frontend-Service auf höchstens eine Instanz, weshalb eine ständig verfügbare Instanz nicht geeignet ist.
- Prozessregel. Acmes Handbuch verbietet es,
min-instanceszu ändern, ohne die schriftliche Genehmigung der leitenden Architektin, Sarah Connor.
Es wird ein System benötigt, das die öffentlichen Richtlinien zur Startverzögerung bei Cloud Run abruft, jede Empfehlung mit Acmes internen Einschränkungen abgleicht und erst dann dem Ingenieur über mögliche Konflikte informiert sowie eine konforme Alternative vorschlägt.
Voraussetzungen
Haben Sie diese vor dem Start bereit:
- Ein Google Cloud-Projekt mit aktivierter Abrechnung.
- Die gcloud CLI, installiert und auf Ihrem Rechner authentifiziert.
- Python 3.10 oder neuer.
Kosten und Quoten
Dieses Tutorial soll sich innerhalb der kostenlosen Tarife sowie der Entwicklerquoten für jeden Dienst bewegen:
- Cloud Storage. Eine kleine Runbook-Datei liegt voll im Rahmen der kostenlosen Speicherkapazität. Gebühren entstehen nur, wenn Sie diesen Rahmen überschreiten oder Daten aus dem Speicher abrufen.
- Vertex AI Search (Gemini Agent Search). Neue Konten erhalten einen kostenlosen Testaccount. Sobald die Testkredite aufgebraucht sind, werden Abfragen nach dem Pay-as-you-go-Prinzip pro 1.000 Abfragen abgerechnet – prüfen Sie daher die aktuelle Preisliste.
- Developer Knowledge API. Ihre Aktivierung und Nutzung sind kostenlos, solange Sie sich innerhalb der üblichen Entwicklerlimits bewegen. Anfragen nach zusätzlichen Ressourcen können auf den Quotenseiten von Google Cloud überwacht werden.
Vorbereitung der Google Cloud-Umgebung
Bevor der Assistent selbst eingesetzt werden kann, muss die zugrundeliegende Infrastruktur eingerichtet werden. Ein Bash-Skript kümmert sich um drei Aufgaben:
- Aktivierung von APIs. Es schaltet Cloud Storage, Vertex AI Agent Builder (
discoveryengine), Developer Knowledge sowie die Generative Language API ein, damit der Orchestrierer auf alle benötigten Endpunkte zugreifen kann. - Erstellung der Service-Identität für den Discovery Engine. Es erstellt im Voraus den von Google verwalteten Service-Agenten (
service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com), sodass dem Hintergrund-Indexierer sofort IAM-Rechte gewährt werden können, ohne dass zuerst auf der Konsole geklickt werden muss. - Einrichtung von Speicher und Zugriff. Es erstellt den Cloud Storage-Container, in dem das Kunden-Runbook gespeichert wird, und gewährt
roles/storage.objectVieweran zwei Principale: dem Hintergrund-Suchagenten, der ihn zur Indexierung benötigt, sowie Ihrem eigenen Konto, damit Sie den Container in der Konsole überprüfen können.
Kopieren Sie das Skript in ein 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."
Die Werte, die Sie anpassen möchten:
PROJECT_IDist Ihre Projekt-ID. Wenn das Projekt bereits in gcloud festgelegt wurde, gibtgcloud config get-value projectdiesen Wert automatisch zurück.PROJECT_NUMBERist die eindeutige Nummer des Projekts.BUCKET_NAMEgibt dem Bucket für Runbooks einen Namen und standardmäßig${PROJECT_ID}-internal-kb.USER_EMAIList die Adresse des aktiven Google Cloud-Kontos, das im Konsolenzugang als Betrachter fungiert.
Aufbau des privaten Wissensengines
Die private Seite ist ein kleiner Suchmaschinen-System, das ein Beispiel-Kundenrunbook enthält.
Schreiben und Hochladen des Runbooks
Erstellen Sie die Runbook-Datei lokal und kopieren Sie sie anschließend in den 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}/
Der Upload-Befehl verwendet den Bucket-Namen aus dem Einrichtungsschritt, ${PROJECT_ID}-internal-kb, es sei denn, Sie haben ihn geändert. Das Runbook enthält absichtlich alle wichtigen Informationen: Acmes Obergrenze von einer Instanz, seine Regeln für die Mindestanzahl an Instanzen, die Namen seiner Datenbanken und VPC-Connector, seine Richtlinie, dass Deployments nur über GitHub Actions erfolgen dürfen, sowie die Kontakte für Eskalationen.
Erstellung eines Agent Search-Datenspeichers
Der Datenspeicher macht das Runbook suchbar. Im Google Cloud Console:
- Öffnen Sie Agent Builder (auch als Vertex AI Search bezeichnet).
- Wählen Sie im linken Navigationsmenü Data Stores aus und anschließend Create data store.
- Als Quelle wählen Sie Cloud Storage.
gs://PROJECT_ID-internal-kb/* ein und ersetzen Sie PROJECT_ID durch Ihre tatsächliche Projekt-ID.acme-internal-kb.Eine Suchanwendung zum Bereitstellen erstellen
Anfragen aus Code werden über eine Suchanwendung (einen Motor) weitergeleitet, die als Bereitstellungsstufe über dem Datenspeicher fungiert:
- Offnen Sie in Agent Builder den Bereich Anwendungen.
- Klicken Sie auf Anwendung erstellen.
- Unter Suchen auf der Erstellungsseite klicken Sie auf Erstellen.
- Nennen Sie die Anwendung
acme-search-app.
Acme Corporation ein.acme-internal-kb ein Häkchen.acme-search-app_1234567890.Vernetzung der öffentlichen Seite: Die Developer Knowledge API
Diese API bietet zugriff auf offizielle Google Cloud-Dokumentationen und Entwicklerleitfäden. Die untenstehenden Schritte erstellen eine API-Schlüssel, beschränken deren Zugriff und bestätigen den Zugang mit einer curl-Anfrage.
Erstellung einer API-Schlüssel
- Gehen Sie in der Konsole zu Credentials.
- wählen Sie Create credentials, anschließend API key.
- Es ist vorzuziehen, den Schlüssel ausschließlich auf die Developer Knowledge API zu beschränken.
- Speichern Sie den Schlüssel in einer Umgebungsvariable:
export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY
Setzen Sie Ihren selbst erstellten Schlüssel anstelle des Platzhalters ein.
Testen mit curl
Eine schnelle Anfrage bestätigt, dass der Schlüssel funktioniert und dass Sie auf Googles Entwicklerdokumentation zugreifen können:
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?"}'
Eine strukturierte JSON-Antwort, die eine aus der offiziellen Dokumentation entnommene Antwort enthält, bedeutet, dass alles funktioniert.
Das Orchestrierungsskript
Danach folgt ein Python-Skript, das beiden Engines dieselbe Frage stellt und die Ergebnisse an Gemini weiterleitet, um die endgültige Antwort zu erhalten.
Installation von Bibliotheken
pip install google-cloud-discoveryengine google-generativeai requests
Erstellung des Orchestrators
Der folgende Befehl schreibt app.py hin. Das Skript sendet den eingehenden Ticket-Text an beide Suchpfade, fügt die beiden Ergebnisse in ein Prompt-Muster ein und übermittelt das ausgefüllte Prompt an 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
Die Anweisung erledigt die eigentliche Arbeit. Sie weist das Modell an, das private Handbuch mit kundenbezogenen Einstellungen und Regeln zu lesen sowie jede allgemeine Empfehlung mit diesen Einschränkungen abzugleichen. Wenn eine öffentliche Empfehlung, wie beispielsweise die Erhöhung der Mindestinstanzenanzahl, eine Kundengrenze oder eine Genehmigungsregel verletzt, muss das Modell dies offen benennen und einen konformen Ersatzvorschlag machen. Zudem muss es bei der Erklärung der Schritte die tatsächlichen Kontakte, Dienste, Datenbanken und Netzwerkverbindungen des Kunden nennen.
Testausführung
Stellen Sie die Umgebungsvariablen ein und starten Sie das Skript:
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."
Setzen Sie die tatsächlichen Werte ein:
PROJECT_IDist Ihre Projekt-ID.ENGINE_IDist die Engine-/App-ID der Vertex AI Search-App.DEVELOPERKNOWLEDGE_API_KEYist der Schlüssel, den Sie für die Developer Knowledge API erstellt haben.GEMINI_API_KEYist ein Schlüssel von Google AI Studio. In Googles Dokumentation wird erklärt, wie man einen Gemini API-Schlüssel erhält.
Wie das Ausgabeergebnis aussieht
Erwarten Sie etwas in dieser Art:
[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.
…
Was das Ergebnis zeigt
Die Antwort kombiniert die beiden Quellen, wobei jede etwas anderes beiträgt:
- Öffentliche Dokumentation über die Developer Knowledge API liefert die technischen Lösungen: Aktivierung von
cpu-boost, Einstellung der Konkurrenz auf80oder Hinzufügen einer/healthz-Startüberprüfung. - Die private Wissensdatenbank über Agent Search fügt Geschäftslogik, Sicherheitsbeschränkungen sowie operative Details hinzu. Sie weist darauf hin, dass der Standardrat (
min_instances = 1) Acmes Budgetregeln verletzt. Sie nennt Sarah Connor als die Person, die jede Ausnahme genehmigen muss. Zudem wird auf Acmes tatsächliche Datenbank sowie den VPC-Connector verwiesen.
Ohne beide Engines würde ein generisches Modell lediglich empfehlen, min_instances zu erhöhen. Ein junger Support-Ingenieur könnte sofort darauf reagieren, was in einem Unternehmen zu Überschreitungen der Budgetgrenzen, Auslösung von Rechnungswarnungen und Umgehung der architektonischen Freigabe führen könnte. Die Grundlage des Modells in sowohl öffentlichen als auch privaten Kontexten ermöglicht Lösungen, die technisch fundiert sind und gleichzeitig den organisatorischen Richtlinien entsprechen.
Absicherung für die Produktion
Vor der Verwendung bei echten Anfragen wären einige Änderungen sinnvoll:
- Halten Sie Geheimnisse in einem Secrets Manager auf. Legen Sie API-Schlüssel im Secret Manager ab und laden Sie sie zur Laufzeit, nicht in Textdateien als Umgebungsvariablen.
roles/discoveryengine.viewer und roles/storage.objectViewer enthält, und beschränken Sie API-Schlüssel auf bestimmte APIs sowie vertrauenswürdige Netzwerkadressen.SearchRequest-Filter, damit die Ergebnisse auf das Umfeld eines bestimmten Kunden eingegrenzt werden.Aufbereitung
Um anhaltende Gebühren zu vermeiden, entfernen Sie die vom Tutorial erstellten Elemente.
Suche in App und Datenspeicher
Ein Datenspeicher kann nicht gelöscht werden, solange eine App daran gebunden ist, daher muss zuerst die App gelöscht werden. In der Konsole:
- Gehen Sie zu Agent Builder, dann zu Apps.
- Finden Sie
acme-search-app, klicken Sie auf Mehr Aktionen (drei Punkte) und wählen Sie Löschen. - Geben Sie den Namen der App im Bestätigungsdialog ein und bestätigen Sie die Löschung.
- Offnen Sie Data Stores im Navigationsmenü.
- Finden Sie
acme-internal-kb, klicken Sie auf Mehr Aktionen und wählen Sie Löschen. - Bestätigen Sie die Löschung.
Der Speicherkorb
Um den Inhalt des Korbs zu entfernen, reicht ein Befehl:
export BUCKET_NAME="${PROJECT_ID}-internal-kb"
gcloud storage rm - recursive gs://${BUCKET_NAME}/
API-Schlüssel
- Gehen Sie zu APIs & Services, dann zu Credentials.
- Unter API Keys finden Sie den Schlüssel, der für die Developer Knowledge API erstellt wurde.
- Klicken Sie auf das Mülleimer-Symbol daneben und bestätigen Sie die Aktion.
Lokale Dateien
Löschen Sie die Skripte sowie das Beispiel-Runbook von Ihrem Rechner:
rm -f app.py app_test.py client-acme-runbook.txt