Asystent wsparcia z podwójnym silnikiem RAG w Google Cloud: dokumenty publiczne, podręczniki prywatne
Stwórz asystenta wsparcia, który wysyła zapytania do Vertex AI Search oraz Developer Knowledge API, a następnie umożliwia Gemini dostosowanie porad do własnych zasad klienta.
Każdy, kto pracuje w kolejce obsługi klienta zgodnie z SLA, zna koszt przechodzenia między kartkami: jedna służy do przeglądania publicznych dokumentacji Google Cloud, druga – własnego podręcznika klienta, a trzeba jeszcze połączyć te informacje w umyśle. Prośba o pomoc do ogólnego asystenta AI często skutkuje odpowiedzią, która na papierze jest poprawna, ale nieodpowiednia dla klienta siedzącego przed tobą.
Rozwiązanie opisane tutaj polega na użyciu dwóch mechanizmów pozyskiwania informacji. Zgłoszenie jest wysyłane równolegle do dwóch źródeł – publicznych dokumentacji Google Cloud oraz prywatnego magazynu podręczników klienta – a model Gemini łączy uzyskane dane w przewodnik rozwiązań, który uwzględnia zasady klienta. Wyobraź sobie znalezienie dobrze ocenionego przepisu w internecie i poproszenie dietetyka o dostosowanie go do twojej diety przed gotowaniem.
Rozwiązanie to składa się z czterech elementów:
- Dwa niezależne ścieżki wyszukiwania działające równolegle – jedna przeznaczona na ogólne porady techniczne, a druga na ścisłe zasady wewnętrzne.
- Vertex AI Search jako prywatny magazyn, indeksujący reguły biznesowe i ograniczenia bezpośrednio z plików instrukcji.
- Developer Knowledge API jako publiczne źródło, dostarczające uzasadnione rekomendacje oraz kroki naprawcze z oficjalnej dokumentacji.
- Gemini jako warstwa analityczna, sprawdzająca publiczne rozwiązania pod kątem prywatnych zasad, tak aby wynik stanowił przewodnik, którego inżynier wsparcia może bezpiecznie się kierować.
Dlaczego ogólne porady zawodzą
Weźmy taką zgłoszenie:
Acme zgłasza opóźnienie około pięciu sekund przy uruchamianiu frontendu w Cloud Run. Co powinniśmy zrobić?
Asystent opierający się wyłącznie na dokumentacji publicznej prawie na pewno poradzi ustawienie min-instances na 1. To klasyczne rozwiązanie problemu z opóźnieniami przy uruchamianiu, ale w przypadku tego klienta stanowi również problem z dwóch powodów:
- Ograniczenie budżetowe. Acme ogranicza usługę frontend do maksymalnie jednej instancji, więc instancja działająca cały czas nie jest możliwa do zastosowania.
- Zasada procesowa. Przewodnik Acme zabrania zmiany wartości min-instances bez pisemnej zgody głównej architektki firmy, Sarah Connor.
Potrzebny jest system, który pobiera publiczne informacje na temat opóźnień przy uruchamianiu w Cloud Run, sprawdza każdą z rekomendacji pod kątem prywatnych ograniczeń Acme, a dopiero wtedy informuje inżyniera o ewentualnych konfliktach i proponuje alternatywę zgodną z regulacjami.
Wymagania wstępne
Miej to przygotowane przed rozpoczęciem:
- Projekt w Google Cloud z włączoną fakturacją.
- CLI gcloud, zainstalowane i uwierzytelnione na twoim komputerze.
- Python 3.10 lub nowsza wersja.
Koszty i kwoty
Tutorial ma na celu pozostanie w ramach darmowych pakietów oraz kwot przeznaczonych dla programistów dla każdej usługi:
- Cloud Storage.Jeden mały plik runbooka mieści się w pełni w limicie przechowywania dostępnym za darmo. Opłaty powstają tylko wtedy, gdy przekroczysz ten limit lub wygenerujesz ruch wyjściowy.
- Vertex AI Search (Gemini Agent Search).Nowe konta otrzymują darmowy okres próbny. Gdy skończą się kredyty na próbę, zapytania są fakturyzowane według modelu pay-as-you-go za każde 1000 zapytań, dlatego sprawdź aktualną stronę cenową.
- Developer Knowledge API.Jego włączenie i używanie jest bezpłatne w ramach standardowych ograniczeń dla programistów. Zasoby przydzielone na żądanie można monitorować na stronach z kwotami w Google Cloud.
Przygotowanie środowiska Google Cloud
Zanim pojawi się sam asystent, konieczne jest przygotowanie podstawowej infrastruktury. Jeden skrypt bash obsługuje trzy zadania:
- Włączanie API. Aktywuje Cloud Storage, Vertex AI Agent Builder (
discoveryengine), Developer Knowledge oraz Generative Language API, aby orkiestrator mógł uzyskać dostęp do wszystkich potrzebnych punktów końcowych. - Tworzenie tożsamości usługi Discovery Engine. Wstępnie tworzy agenta usług zarządzanego przez Google (
service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com), dzięki czemu indeksator w tle może od razu otrzymać uprawnienia IAM, bez konieczności klikania w konsoli po raz pierwszy. - Konfiguracja przechowywania i dostępu. Tworzy pojemnik Cloud Storage, który będzie przechowywał podręcznik klienta, oraz przyznaje rolę
roles/storage.objectViewerdwóm podmiotom: agentowi wyszukiwania w tle, który potrzebuje jej do indeksowania, oraz twojemu własnemu kontu, abyś mógł przeglądać zawartość pojemnika w konsoli.
Wklej skrypt do terminala:
# 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."
Wartości, które warto dostosować:
PROJECT_IDto identyfikator Twojego projektu. Gdy projekt jest już ustawiony w gcloud,gcloud config get-value projectzwraca go automatycznie.PROJECT_NUMBERto unikalny numer projektu.BUCKET_NAMEokreśla nazwę bucketu przeznaczonego na runbooki i ma wartość domyślną${PROJECT_ID}-internal-kb.USER_EMAILto adres aktywnego konta Google Cloud, które uzyskuje dostęp widza w konsoli.
Budowanie prywatnego silnika wiedzy
Część prywatna to mały silnik wyszukiwania przechowujący przykładowy runbook klienta.
Pisanie i przesyłanie runbooka
Stwórz plik runbooka lokalnie, a następnie skopiuj go do bucketu:
# 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}/
Komenda przesyłania używa nazwy bufora z kroku konfiguracji, ${PROJECT_ID}-internal-kb, chyba że została zmieniona. Podręcznik operacyjny zawiera celowo wszystkie istotne informacje: maksymalną liczbę jednej instancji ustaloną przez Acme, zasady zatwierdzania minimalnej liczby instancji, nazwy połączeń do bazy danych i VPC, politykę rozwoju tylko poprzez GitHub Actions oraz dane kontaktowe do eskalacji problemów.
Tworzenie magazynu danych do wyszukiwania agentów
To właśnie magazyn danych umożliwia wyszukiwanie informacji w podręczniku operacyjnym. W konsoli Google Cloud:
- Otwórz Agent Builder (oznaczony również jako Vertex AI Search).
- W menu po lewej stronie wybierz Data Stores, a następnie Create data store.
- Jako źródło wybierz Cloud Storage.
gs://PROJECT_ID-internal-kb/*, zastępując PROJECT_ID swoim rzeczywistym identyfikatorem projektu.acme-internal-kb.Tworzenie aplikacji wyszukiwania do obsługi
Zapytania z kodu przechodzą przez Aplikację wyszukiwania (silnik), która pełni rolę warstwy obsługi na szczycie magazynu danych:
- W Agent Builder otwórz zakładkę Aplikacje.
- Kliknij Stwórz aplikację.
- W sekcji Wyszukiwanie na stronie tworzenia kliknij Stworz.
- Nazwij aplikację
acme-search-app. - Dla nazwy zewnętrznej organizacji wpisz
Acme Corporation. - Zostaw lokalizację na wartości domyślnej, global, i kontynuuj.
- Na kroku Data Stores zaznacz
acme-internal-kb. - Kliknij Create.
- Gdy proces się zakończy, otwórz aplikację z listy Apps i skopiuj jej Engine ID (nazywany również App ID) z strony szczegółów. Wygląda mniej więcej tak:
acme-search-app_1234567890.
Łączenie strony publicznej: Developer Knowledge API
To API umożliwia dostęp do oficjalnej dokumentacji Google Cloud oraz przewodników dla programistów. Poniższe kroki polegają na utworzeniu klucza API, jego ograniczeniu oraz potwierdzeniu dostępu za pomocą jednego żądania curl.
Tworzenie klucza API
- W konsoli przejdź do Credentials.
- Wybierz Create credentials, a następnie API key.
- Najlepiej ograniczyć dostęp do klucza wyłącznie do Developer Knowledge API.
- Zachowaj klucz w zmiennej środowiskowej:
export DEVELOPERKNOWLEDGE_API_KEY="DEVELOPERKNOWLEDGE_API_KEY
Umieść własny wygenerowany klucz zamiast znacznika tymczasowego.
Testowanie za pomocą curl
Szybka zapytanie potwierdza, że klucz działa i że możesz uzyskać dostęp do dokumentacji deweloperskiej 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?"}'
Strukturalna odpowiedź w formacie JSON zawierająca rozwiązanie zaczerpnięte z oficjalnej dokumentacji oznacza, że wszystko działa poprawnie.
Skrypt orkiestracji
Następnie występuje skrypt w Pythonie, który zadaje obu silnikom to samo pytanie i przekazuje wyniki Gemini w celu uzyskania ostatecznej odpowiedzi.
Instalacja bibliotek
pip install google-cloud-discoveryengine google-generativeai requests
Tworzenie orkiestratora
Następujący polecenie zapisuje plik app.py. Skrypt wysyła tekst otrzymanego zgłoszenia do obu ścieżek wyszukiwania, umieszcza dwa wyniki w szablonie wiadomości i przekazuje wypełnioną wiadomość do 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
To właśnie prompt wykonywa główną pracę. Poleca modelowi przeczytanie prywatnego podręcznika z ustawieniami i zasadami specyficznymi dla danego klienta oraz porównanie każdej ogólnej rekomendacji z tymi ograniczeniami. Jeśli jakaś publiczna rekomendacja, np. zwiększenie liczby instancji, narusza limit lub zasadę zatwierdzenia określoną dla klienta, model musi to otwarcie wspomnieć i zaproponować alternatywę zgodną z regulacjami. Podczas wyjaśniania kroków musi również podać rzeczywiste dane kontaktowe klienta, usługi, bazy danych oraz połączenia sieciowe.
Rozpoczynanie testu
Ustaw zmienną środowiskową i uruchom skrypt:
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."
Podstaw rzeczywiste wartości:
PROJECT_IDto identyfikator twojego projektu.ENGINE_IDto identyfikator silnika/aplikacji w Vertex AI Search.DEVELOPERKNOWLEDGE_API_KEYto klucz utworzony dla API Developer Knowledge.GEMINI_API_KEYto klucz z Google AI Studio. Dokumentacja Google wyjaśnia, jak uzyskać klucz Gemini API.
Jak wygląda wynik
Oczekuj czegoś w tym rodzaju:
[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.
…
Co pokazuje wynik
Odpowiedź łączy oba źródła, z których każde wnosi coś innego:
- Dokumentacja publiczna, za pośrednictwem Developer Knowledge API, dostarcza rozwiązań technicznych: włączanie
cpu-boost, ustawianie wartości współbieżności na80lub dodawanie sondy startowej/healthz. - Prywatna baza wiedzy, za pośrednictwem Agent Search, dodaje logikę biznesową, ograniczenia bezpieczeństwa oraz szczegóły operacyjne. Wskazuje, że standardowa zalecenie (
min_instances = 1) narusza zasady budżetowe Acme. Wymienia Sarah Connor jako osobę, która musi zatwierdzić wszelkie wyjątki. Odnosi się do rzeczywistej bazy danych i łącznika VPC firmy Acme.
Bез obu silników model ogólny poleciłby jedynie zwiększenie wartości min_instances. Młodszy inżynier wsparcia mógłby natychmiast to zrealizować, a w przedsiębiorstwie mogłoby to doprowadzić do przekroczenia limitów budżetowych, uruchomienia alertów dotyczących fakturowania oraz pominięcia zatwierdzenia architektonicznego. Uzasadnienie modelu w kontekście zarówno publicznym, jak i prywatnym pozwala na znalezienie rozwiązań technicznie poprawnych, które jednocześnie są zgodne z polityką organizacji.
Zabezpieczanie go przed użyciem w produkcji
Zanim model będzie mógł obsługiwać rzeczywiste zgłoszenia, sensowne byłoby wprowadzenie kilku zmian:
- Zachowuj sekrety w menedżerze sekretów. Umieść klucze API w Secret Manager i ładuj je podczas wykonywania, a nie w zmiennych środowiskowych w formacie tekstowym.
roles/discoveryengine.viewer oraz roles/storage.objectViewer, a klucze API należy ograniczyć do określonych interfejsów i zaufanych adresów sieciowych.SearchRequest, aby wyniki dotyczyły wyłącznie środowiska danego klienta.Oczyszczanie
Aby uniknąć dalszych opłat, usuń to, co zostało stworzone w ramach tutorialu.
Szukanie w aplikacji i magazynie danych
Magazyn danych nie może zostać usunięty, dopóki jest przypisany do aplikacji, więc najpierw usuń aplikację. W konsoli:
- Przejdź do Agent Builder, a następnie do Apps.
- Znajdź
acme-search-app, otwórz More actions (trzy kropki) i wybierz Delete. - W oknie potwierdzenia wpisz nazwę aplikacji i potwierdź.
- Otwórz Data Stores w menu nawigacyjnym.
- Znajdź
acme-internal-kb, otwórz More actions i wybierz Delete. - Potwierdź.
Bucket przechowywania
Usunięcie zawartości bucketa odbywa się za pomocą jednego polecenia:
export BUCKET_NAME="${PROJECT_ID}-internal-kb"
gcloud storage rm - recursive gs://${BUCKET_NAME}/
Klucze API
- Przejdź do APIs & Services, a następnie do Credentials.
- W sekcji API Keys znajdź klucz stworzony dla Developer Knowledge API.
- Kliknij ikonę kosza obok niego i potwierdź.
Pliki lokalne
Usuń skrypty oraz przykładowy podręcznik z Twojego komputera:
rm -f app.py app_test.py client-acme-runbook.txt