1. परिचय
इस कोडलैब में, आपको किसी कॉफ़ी शॉप के लिए इंटरैक्टिव एआई बैरिस्टा एजेंट बनाने का तरीका बताया जाएगा. Google की ओपन-सोर्स एजेंट डेवलपमेंट किट (एडीके) और Gemini 3.5 Flash मॉडल का इस्तेमाल करके, आपको रिट्रीवल ऑगमेंटेड जनरेशन (आरएजी) को लागू करना होगा. इससे एजेंट के सुझाव, मॉक मेन्यू के डेटासेट पर आधारित होंगे. आखिर में, एजेंट को Streamlit यूज़र इंटरफ़ेस में रैप किया जाएगा और Cloud Run पर डिप्लॉय किया जाएगा.
आपको क्या करना होगा
- कॉफ़ी के आइटम, टैग, और एलर्जी पैदा करने वाले कॉम्पोनेंट की जानकारी देने वाला RAG डेटा सोर्स (
menu.json) बनाएं. - एडीके
LlmAgentका इस्तेमाल करके, एक एआई एजेंट बनाएं. साथ ही, मेन्यू का डेटा लोड करने के लिए, Python टूल को कनेक्ट करें. - एजेंट को Streamlit चैट ऐप्लिकेशन में रैप करें. यह ऐप्लिकेशन, बातचीत के इतिहास को मैनेज करता है.
- सोर्स-आधारित डिप्लॉयमेंट का इस्तेमाल करके, Streamlit ऐप्लिकेशन को Cloud Run पर डिप्लॉय करें.
- एलर्जन के बारे में जानकारी देने और आरएजी ग्राउंडिंग की जांच करें.

आपको किन चीज़ों की ज़रूरत होगी
- कोई वेब ब्राउज़र, जैसे कि Chrome.
- बिलिंग की सुविधा वाला Google क्लाउड प्रोजेक्ट.
- Python के बारे में बुनियादी जानकारी हो.
यह कोडलैब, सभी लेवल के डेवलपर के लिए है. इसमें शुरुआती डेवलपर भी शामिल हैं.
अनुमानित लागत: 1.00 डॉलर से कम.
2. शुरू करने से पहले
Google Cloud प्रोजेक्ट बनाना
- Google Cloud Console में जाकर, Google Cloud प्रोजेक्ट चुनें या बनाएं.
- पक्का करें कि आपके क्लाउड प्रोजेक्ट के लिए बिलिंग की सुविधा चालू हो.
Cloud Shell शुरू करना
- Google Cloud कंसोल में सबसे ऊपर मौजूद, Cloud Shell चालू करें पर क्लिक करें.

- पुष्टि करें:

gcloud auth list
- पुष्टि करें कि आपका चालू प्रोजेक्ट सेट है:
gcloud config get project
अगर दिखाया गया प्रोजेक्ट आईडी सही नहीं है या कोई भी आईडी सेट नहीं है, तो यह कमांड चलाएं:
gcloud config set project <YOUR_PROJECT_ID>
एपीआई चालू करें
सभी ज़रूरी एपीआई चालू करने के लिए, यह निर्देश चलाएं:
gcloud services enable \
run.googleapis.com \
aiplatform.googleapis.com \
cloudbuild.googleapis.com
3. अपना प्रोजेक्ट सेट अप करना
इस चरण में, आपको अपने प्रोजेक्ट के एनवायरमेंट वैरिएबल को शुरू करना होगा. साथ ही, अपने प्रोजेक्ट के लिए वर्किंग डायरेक्ट्री बनानी होगी.
- अपने चालू Cloud Shell सेशन में, प्रोजेक्ट के एनवायरमेंट वैरिएबल को इस तरह से शुरू करें:
export PROJECT_ID=$(gcloud config get-value project)
ध्यान दें: सबसे नज़दीकी इलाके का इस्तेमाल करें
अपने सबसे नज़दीकी क्षेत्र का पता लगाएं और नीचे दिए गए कमांड में insert-region-here की जगह पर उसे डालें:
export REGION=[insert-region-here]
coffee-barista-agentनाम की नई प्रोजेक्ट डायरेक्ट्री बनाएं और उसमें बदलाव करें:
mkdir coffee-barista-agent && cd coffee-barista-agent
4. मेन्यू के मॉक डेटा सोर्स को बनाना
एआई बरिस्ता को सही जानकारी देने और उसे ऐसे आइटम के बारे में जानकारी देने से रोकने के लिए जो मौजूद नहीं हैं, आपको स्थानीय मेन्यू का डेटासेट बनाना होगा. एजेंट, इस फ़ाइल को कस्टम टूल की मदद से रनटाइम में पढ़ेगा.
- Cloud Shell Editor में
menu.jsonबनाएं और उसे खोलें:
cloudshell edit menu.json
- यहां दिए गए JSON कॉन्टेंट को एडिटर में चिपकाएं और फ़ाइल सेव करें:
[
{
"name": "Espresso Solo",
"description": "A single shot of rich, bold espresso.",
"price": 2.50,
"tags": ["strong", "hot", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Oat Milk Honey Latte",
"description": "Creamy steamed oat milk with espresso and a touch of honey.",
"price": 5.00,
"tags": ["sweet", "hot", "dairy-free"],
"allergens": []
},
{
"name": "Cold Brew Coffee",
"description": "Smooth, slow-steeped cold brew served over ice.",
"price": 4.00,
"tags": ["strong", "cold", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Seasonal Pumpkin Latte",
"description": "Spiced pumpkin sauce, espresso, and steamed milk, topped with whipped cream.",
"price": 5.50,
"tags": ["sweet", "hot", "seasonal"],
"allergens": ["dairy"]
},
{
"name": "Classic Croissant",
"description": "Flaky, buttery traditional French pastry.",
"price": 3.50,
"tags": ["bakery", "savory"],
"allergens": ["wheat", "dairy"]
},
{
"name": "Vegan Blueberry Muffin",
"description": "Soft, sweet muffin packed with real blueberries, entirely plant-based.",
"price": 3.75,
"tags": ["bakery", "sweet", "dairy-free", "vegan"],
"allergens": ["wheat"]
},
{
"name": "Nitro Cold Brew",
"description": "Cold brew infused with nitrogen for a super smooth, creamy head.",
"price": 4.50,
"tags": ["strong", "cold", "dairy-free", "sugar-free"],
"allergens": []
},
{
"name": "Iced Caramel Macchiato",
"description": "Chilled milk and vanilla syrup marked with espresso and caramel drizzle.",
"price": 5.25,
"tags": ["sweet", "cold"],
"allergens": ["dairy"]
}
]
- पुष्टि करें कि JSON फ़ाइल को सही तरीके से फ़ॉर्मैट किया गया हो:
cat menu.json | python3 -m json.tool > /dev/null && echo "Valid JSON!"
💬 चर्चा: लोकल JSON बनाम लाइव डेटाबेस
हम लाइव डेटाबेस के बजाय, सामान्य local_menu.json फ़ाइल का इस्तेमाल क्यों कर रहे हैं?
क्विक ट्यूटोरियल या प्रोटोटाइप के लिए, लोकल JSON फ़ाइल से डेटाबेस को सेटअप करने में लगने वाला शुरुआती समय और जटिलता कम हो जाती है. हालांकि, एंटरप्राइज़ के प्रोडक्शन ऐप्लिकेशन में, एजेंट को Cloud Firestore, AlloyDB या Cloud SQL जैसे मैनेज किए गए डेटाबेस से कनेक्ट किया जाता है.
लाइव डेटाबेस का इस्तेमाल करके, कॉफ़ी शॉप के मैनेजर सीज़नल आइटम जोड़ सकते हैं, कीमतें अपडेट कर सकते हैं या एलर्जी वाले टैग को डाइनैमिक तरीके से अडजस्ट कर सकते हैं. इसके लिए, उन्हें कंटेनर इमेज को फिर से बनाने या ऐप्लिकेशन कोड को फिर से डिप्लॉय करने की ज़रूरत नहीं होती. हम कोडलैब में बाद में, लाइव डेटाबेस का इस्तेमाल करेंगे. हालांकि, यह एक ज़रूरी चरण नहीं है.
5. ADK एजेंट बनाना
अब ज़रूरी पैकेज इंस्टॉल करें और ADK एजेंट का मुख्य लॉजिक बनाएं. आपको एक get_menu() टूल तय करना होगा और उसे LlmAgent को पास करना होगा.
- Cloud Shell Editor में
requirements.txtबनाएं और उसे खोलें:
cloudshell edit requirements.txt
- नीचे दी गई डिपेंडेंसी को एडिटर में चिपकाएं और फ़ाइल सेव करें:
google-adk==2.2.0
streamlit==1.58.0
- Cloud Shell Editor में
agent.pyबनाएं और उसे खोलें:
cloudshell edit agent.py
agent.pyमें यह कोड चिपकाएं:
# agent.py
import json
from google.adk.agents import LlmAgent
# [START get_menu]
def get_menu() -> str:
"""Retrieves the coffee shop menu from menu.json.
Returns:
str: A JSON string representing the list of menu items.
"""
try:
with open("menu.json", "r") as f:
menu_data = json.load(f)
return json.dumps(menu_data)
except Exception as e:
return json.dumps({"error": f"Could not retrieve menu: {str(e)}"})
# [END get_menu]
# Create the barista agent
barista_agent = LlmAgent(
name="barista_agent",
model="gemini-3.5-flash",
instruction="""You are a friendly barista at ☕ Coffee Shop.
Your job is to recommend drinks and pastries to customers based on their preferences.
Rules you MUST follow:
1. You must recommend items ONLY from the menu returned by get_menu().
2. Do NOT recommend or suggest any item that is not present in the menu.
3. If a user's preference is vague or unclear, ask exactly ONE friendly clarifying question to narrow down what they want (e.g., cold or hot, sweet or strong, coffee or pastry).
4. Be warm and welcoming, but remain professional.
5. Ground your recommendations in the actual tags, descriptions, and allergens listed in the menu (e.g., if a user is dairy-free, recommend ONLY items tagged 'dairy-free' or with no dairy allergens).
""",
tools=[get_menu]
)
from google.adk.apps import App
# Define the App object
app = App(
name="coffee_barista_app",
root_agent=barista_agent
)
- Cloud Shell Editor में
app.pyबनाएं और उसे खोलें:
cloudshell edit app.py
app.pyमें यह कोड चिपकाएं:
# app.py
import streamlit as st
import json
# Set page config for a premium look
st.set_page_config(
page_title="☕ Coffee Shop - Barista Bot",
page_icon="☕",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS to make the header sticky (adapts to light/dark themes)
st.markdown("""
<style>
div[data-testid="element-container"]:has(.header-container),
div.element-container:has(.header-container) {
position: sticky;
top: 2.875rem;
z-index: 999;
background-color: transparent;
padding-bottom: 10px;
}
</style>
""", unsafe_allow_html=True)
# App Header (using inline styles for the permanent coffee theme look)
st.markdown("""
<div class="header-container" style="text-align: center; padding: 20px; background: linear-gradient(135deg, #8B5E3C, #6F4E37); color: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h1 style="margin: 0; font-size: 2.5rem; font-weight: 700; color: white;">☕ ☕ Coffee Shop</h1>
<p style="margin: 5px 0 0 0; font-size: 1.1rem; opacity: 0.9; color: white;">Your friendly AI Barista is ready to help you find the perfect drink or pastry!</p>
</div>
""", unsafe_allow_html=True)
# Load Menu for the sidebar
# [START load_menu]
try:
with open("menu.json", "r") as f:
menu_items = json.load(f)
except Exception as e:
st.error(f"Error loading menu: {e}")
menu_items = []
# [END load_menu]
# Sidebar Menu & Configuration
with st.sidebar:
st.markdown("## ☕ Coffee Shop Menu")
st.markdown("Explore our offerings and ask the barista for recommendations.")
st.markdown("---")
for item in menu_items:
with st.container(border=True):
st.markdown(f"**{item['name']}** • **${item['price']:.2f}**")
st.caption(item['description'])
# Tags & Allergens as native badges
tags = " ".join([f"`{t}`" for t in item.get("tags", [])])
if tags:
st.markdown(tags)
allergens = ", ".join(item.get("allergens", []))
if allergens:
st.markdown(f"⚠️ *Allergens: {allergens}*")
# Chat Interface
if "session_id" not in st.session_state:
import uuid
st.session_state.session_id = str(uuid.uuid4())
if "runner" not in st.session_state:
from google.adk.runners import InMemoryRunner
from agent import app
st.session_state.runner = InMemoryRunner(app=app)
if "messages" not in st.session_state:
st.session_state.messages = [
{"role": "assistant", "content": "Welcome to ☕ Coffee Shop! What can I get started for you today?"}
]
# Display existing messages
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# User Input
if prompt := st.chat_input("Ask for recommendations (e.g., 'What dairy-free pastries do you have?')"):
# Display user message
with st.chat_message("user"):
st.markdown(prompt)
st.session_state.messages.append({"role": "user", "content": prompt})
# Generate response
with st.chat_message("assistant"):
try:
import asyncio
# Run the ADK runner asynchronously using asyncio.run
async def fetch_response():
return await st.session_state.runner.run_debug(
prompt,
session_id=st.session_state.session_id
)
res_events = asyncio.run(fetch_response())
response_text = "".join([
part.text
for event in res_events
if event.content and event.content.parts
for part in event.content.parts
if part.text
])
st.markdown(response_text)
st.session_state.messages.append({"role": "assistant", "content": response_text})
except Exception as e:
st.error(f"Apologies, I ran into an error: {e}")
💬 चर्चा: मॉडल के ट्रेडऑफ़ और टोकन को वापस पाने की क्षमता
पूरे मेन्यू के टेक्स्ट को एजेंट के सिस्टम निर्देशों में चिपकाने के बजाय, मेन्यू को वापस पाने के लिए फ़ंक्शन टूल को कॉल क्यों किया जाता है?
टोकन इकॉनमी! प्रॉम्प्ट में आठ आइटम डालने पर कम खर्च आता है. हालांकि, अगर कॉफ़ी शॉप में कस्टम सामग्री के साथ 500 आइटम शामिल किए जाते हैं, तो क्या होगा? बड़े डेटासेट को सीधे तौर पर सिस्टम प्रॉम्प्ट में चिपकाने से, आपके प्रॉम्प्ट टोकन की संख्या बढ़ जाती है. इससे हर क्वेरी पर लेन-देन की लागत बढ़ जाती है और एपीआई के जवाब में देरी होती है.
एडीके टूल का इस्तेमाल करके, एजेंट सिर्फ़ ज़रूरत पड़ने पर मेन्यू को पढ़ने का अनुरोध करता है. एलएलएम को सिर्फ़ काम का मेन्यू डेटा, कॉन्टेक्स्ट के तौर पर मिलता है. इससे प्रॉम्प्ट टोकन का साइज़ कम हो जाता है.
💬 चर्चा: मेमोरी स्टेट और प्रोडक्शन स्टोर
क्या Streamlit के st.session_state में सेव किया गया चैट का इतिहास, उपयोगकर्ता के ब्राउज़र टैब बंद करने पर भी बना रहता है?
नहीं, ऐसा नहीं है. st.session_state पूरी तरह से मेमोरी में होता है और चालू ब्राउज़र कनेक्शन के लिए यूनीक होता है. अगर कोई व्यक्ति पेज को रीफ़्रेश करता है या टैब बंद करता है, तो बारिस्ता के साथ हुई उसकी बातचीत का इतिहास मिट जाता है.
प्रोडक्शन ऐप्लिकेशन के लिए, ADK रनर को Cloud Firestore या Redis जैसे परसिस्टेंट स्टोरेज बैकएंड से कनेक्ट किया जाता है. ADK में, पहले से मौजूद सेवा के ऐब्स्ट्रैक्शन (जैसे कि SessionService) होते हैं. इनकी मदद से, पेज को फिर से लोड करने और अलग-अलग डिवाइसों पर, चैट के इतिहास को सेव करना और उसे फिर से शुरू करना आसान हो जाता है.
6. एजेंट को Cloud Run पर डिप्लॉय करना
Cloud Run के बिल्ट-इन बिल्डपैक का इस्तेमाल करके, Streamlit ऐप्लिकेशन को सीधे सोर्स से डिप्लॉय किया जाएगा. कम से कम विशेषाधिकार के सिद्धांत का पालन करने के लिए, Compute Engine के डिफ़ॉल्ट सेवा खाते का इस्तेमाल करने के बजाय, कस्टम सेवा खाते का इस्तेमाल करके इसे बनाया और डिप्लॉय किया जाएगा.
- कोई सेवा खाता बनाएं:
gcloud iam service-accounts create barista-agent-sa \
--description="Service account for Coffee Barista ADK agent on Cloud Run" \
--display-name="Barista Agent Service Account"
- नए सेवा खाते को Gemini Enterprise Agent प्लैटफ़ॉर्म के उपयोगकर्ता की भूमिका (
roles/aiplatform.user) असाइन करें:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
gcloud run deployका इस्तेमाल करके सेवा को डिप्लॉय करें. साथ ही,--service-accountफ़्लैग के ज़रिए नए सेवा खाते का ईमेल पता पास करें:
gcloud run deploy coffee-barista \
--source . \
--region $REGION \
--allow-unauthenticated \
--labels dev-tutorial=codelab-streamlit-rag-adk \
--command "/cnb/lifecycle/launcher" \
--args "sh,-c,python3 -m streamlit run app.py --server.port=\$PORT --server.address=0.0.0.0 --server.enableCORS=false --server.enableXsrfProtection=false" \
--service-account "barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global
- डिप्लॉय करने के बाद, कमांड आउटपुट में सेवा का यूआरएल ढूंढें.
💬 चर्चा: कंटेनर और सोर्स को डिप्लॉय करना, और IAM सुरक्षा
हमने Dockerfile या Procfile बनाए बिना, gcloud run deploy –source का इस्तेमाल करके Cloud Run पर डिप्लॉय किया. Cloud Run को हमारे Python ऐप्लिकेशन को कंपाइल और एक्ज़ीक्यूट करने का तरीका कैसे पता चला?
Cloud Run, आपकी रिपॉज़िटरी का विश्लेषण करने के लिए, पर्दे के पीछे Buildpacks का इस्तेमाल करता है. requirements.txt और Python सोर्स फ़ाइलों का पता चलने पर, इंजन अपने-आप Python रनटाइम कंटेनर को कंपाइल और पैकेज करता है.
कस्टम Dockerfile लिखने से, आपको अपने कंटेनर के सिस्टम पैकेज और बेस लेयर पर पूरा कंट्रोल मिलता है. Procfile, कंटेनर को पूरी तरह से कॉन्फ़िगर किए बिना, स्टार्टअप कमांड का एलान करने का एक आसान तरीका है. हालांकि, तुरंत डिप्लॉय करने के लिए, सोर्स (--source) से डिप्लॉय करना बहुत बेहतर होता है.
हमने डिफ़ॉल्ट Compute Engine सेवा खाते का इस्तेमाल करने के बजाय, कस्टम सेवा खाता barista-agent-sa क्यों बनाया?
सुरक्षा सबसे पहले! Compute Engine के डिफ़ॉल्ट सेवा खाते के पास, डिफ़ॉल्ट रूप से एडिटर की बहुत सारी अनुमतियां होती हैं. डिफ़ॉल्ट सेवा खाते के तहत Cloud Run कंटेनर चलाने का मतलब है कि अगर हमारे ऐप्लिकेशन में सुरक्षा से जुड़ी कोई गड़बड़ी है, तो हमलावर हमारे Google Cloud प्रोजेक्ट में मौजूद अन्य संसाधनों को पढ़ सकता है, उनमें बदलाव कर सकता है या उन्हें मिटा सकता है.
हम एक खास सेवा खाता बनाते हैं और उसे सिर्फ़ roles/aiplatform.user की भूमिका असाइन करते हैं. इससे हम कम से कम विशेषाधिकार के सिद्धांत का पालन करते हैं: ऐप्लिकेशन के पास Gemini को कॉल करने के लिए ज़रूरी ऐक्सेस होता है और इससे ज़्यादा कुछ नहीं.
7. आरएजी के काम करने के तरीके की जांच करना
वेब ब्राउज़र में Cloud Run सेवा का यूआरएल खोलें. इसके बाद, एआई बरिस्ता से सवाल पूछकर, यह जांच करें कि वह भरोसेमंद स्रोतों से जानकारी ले रहा है या नहीं और सुरक्षा से जुड़ी पाबंदियों का पालन कर रहा है या नहीं.
- मेन्यू में मौजूद आइटम के बारे में अनुरोध: पूछें: "कोई ऐसी चीज़ सुझाओ जो स्ट्रॉन्ग और गर्म हो."जवाब: एजेंट, एस्प्रेसो का सुझाव देता है.
- मेन्यू में शामिल न होने के बावजूद आइटम के बारे में पूछना: सवाल: "क्या आपके पास माचा फ़्रापुचिनो है?"जवाब: एजेंट विनम्रता से मना करता है और बताता है कि यह मेन्यू में शामिल नहीं है.
- एलर्जन के बारे में जानकारी देने वाला अनुरोध: पूछें: "मुझे लैक्टोज़ से एलर्जी है. मेरे लिए कौन-कौनसे विकल्प उपलब्ध हैं?"जवाब: एजेंट सिर्फ़ डेयरी प्रॉडक्ट के बिना बने मेन्यू आइटम (जैसे, ओट मिल्क लैट्टे, एस्प्रेसो, कोल्ड ब्रू) के बारे में सुझाव देता है. यह कैपुचिनो या क्रोइसेंट का सुझाव नहीं देता है.

8. ज़रूरी नहीं: वेक्टर सर्च का इस्तेमाल करके, अपने एजेंट को Firestore से जोड़ना
प्रोडक्शन के दौरान, मेन्यू आइटम को लोकल menu.json फ़ाइल में सेव करना सही नहीं है. ऐसा इसलिए, क्योंकि मेन्यू में किसी भी तरह का बदलाव करने के लिए, कंटेनर इमेज को फिर से बनाना और Cloud Run सेवा को फिर से डिप्लॉय करना पड़ता है.
ऐप्लिकेशन को डाइनैमिक और स्केलेबल बनाने के लिए, मेन्यू के डेटा को Cloud Firestore पर माइग्रेट किया जा सकता है. साथ ही, वेक्टर सर्च का इस्तेमाल करके, सिमैंटिक समानता के आधार पर सिर्फ़ सबसे काम के मेन्यू आइटम वापस पाए जा सकते हैं.

1. Firestore API चालू करना और डेटाबेस को शुरू करना
Firestore API चालू करने और नेटिव मोड में coffee-menu नाम का Firestore डेटाबेस बनाने के लिए, यहां दी गई कमांड चलाएं:
gcloud services enable firestore.googleapis.com
gcloud firestore databases create --database="coffee-menu" --location=$REGION
ध्यान दें: एपीआई चालू होने में एक से दो मिनट लग सकते हैं. अगर डेटाबेस बनाने के लिए इस्तेमाल की गई कमांड से आपको API [firestore.googleapis.com] not enabled on project... Would you like to enable and retry? का मैसेज मिलता है, तो आगे बढ़ने के लिए Y टाइप करें. इसके अलावा, एक मिनट इंतज़ार करें और कमांड को फिर से चलाएं.
2. मेन्यू के डेटा के साथ Firestore को सीड करना
अपनी menu.json फ़ाइल में मौजूद मेन्यू आइटम से Firestore डेटाबेस को तुरंत सीड करने के लिए, Cloud Shell में Python स्क्रिप्ट को स्थानीय तौर पर चलाया जा सकता है.
- सीडिंग स्क्रिप्ट चलाने के लिए, Cloud Shell में Firestore और GenAI की क्लाइंट लाइब्रेरी को स्थानीय तौर पर इंस्टॉल करें:
pip3 install google-cloud-firestore==2.27.0 google-genai==2.11.0
- सीडिंग स्क्रिप्ट
seed.pyबनाएं:
cloudshell edit seed.py
seed.pyमें यह कोड चिपकाएं:
# seed.py
import json
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
db = firestore.Client(database="coffee-menu")
client = genai.Client(
vertexai=True,
project=os.environ.get("PROJECT_ID"),
location=os.environ.get("REGION", "us-central1")
)
with open("menu.json", "r") as f:
menu_items = json.load(f)
for item in menu_items:
# Use the name as the document ID
doc_id = item["name"].lower().replace(" ", "-")
# Generate text embedding using Vertex AI text-embedding-004 model
text_to_embed = f"{item['name']}: {item['description']}"
response = client.models.embed_content(
model="text-embedding-004",
contents=text_to_embed,
)
embedding = response.embeddings[0].values
# Add embedding vector to the menu item data
item["embedding"] = Vector(embedding)
db.collection("menu").document(doc_id).set(item)
print("Firestore menu collection seeded with vector embeddings successfully!")
- स्क्रिप्ट चलाएं:
python3 seed.py
3. Firestore वेक्टर इंडेक्स बनाना
अपने मेन्यू आइटम पर वेक्टर सर्च करने के लिए, आपको अपने Firestore डेटाबेस में मौजूद embedding फ़ील्ड पर कंपोज़िट वेक्टर इंडेक्स बनाना होगा.
Cloud Shell टर्मिनल में यह कमांड चलाएं:
gcloud firestore indexes composite create \
--collection-group=menu \
--query-scope=COLLECTION \
--database="coffee-menu" \
--field-config=field-path=embedding,vector-config='{"dimension":"768", "flat": "{}"}'
ध्यान दें: Firestore इंडेक्स बनाने की प्रोसेस बैकग्राउंड में चलती है. इसे पूरा होने में कुछ मिनट लग सकते हैं. इंडेक्स बनने के दौरान, कोडलैब के अगले चरणों को पूरा किया जा सकता है.
4. सेवा खाते को Firestore का ऐक्सेस देना
Cloud Run सेवा को Firestore से क्वेरी करने की अनुमति देने के लिए, आपको उसके सेवा खाते को Cloud Datastore User (roles/datastore.user) की भूमिका देनी होगी:
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/datastore.user"
ध्यान दें: हम नेटिव मोड में Cloud Firestore का इस्तेमाल कर रहे हैं. हालांकि, Google Cloud, ऐक्सेस कंट्रोल को मैनेज करने के लिए, Cloud Datastore की यूनिफ़ाइड IAM भूमिकाओं (roles/datastore.viewer या roles/datastore.user) का इस्तेमाल करता है.
5. कोड अपडेट करना
अब अपने कोड को अपडेट करें, ताकि मेन्यू को menu.json से पढ़ने के बजाय Firestore से वापस पाया जा सके.
- Cloud Shell Editor में
requirements.txtखोलें:
cloudshell edit requirements.txt
- फ़ाइल के आखिर में Firestore और GenAI की क्लाइंट लाइब्रेरी जोड़ें और इसे सेव करें:
google-cloud-firestore==2.27.0
google-genai==2.11.0
- Cloud Shell Editor में
agent.pyखोलें:
cloudshell edit agent.py
agent.pyमें# [START get_menu]ब्लॉक ढूंढें और उसे पूरी तरह से (# [START get_menu]से# [END get_menu]तक) Firestore के इस कोड से बदलें:
# [START get_menu]
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector
def get_menu(query: str) -> str:
"""Retrieves coffee shop menu items matching the user's query.
Args:
query: The search query or preference to find matching menu items.
Returns:
str: A JSON string representing the list of top matching menu items.
"""
try:
# Initialize clients
db = firestore.Client(database="coffee-menu")
client = genai.Client()
# Generate embedding for the search query
response = client.models.embed_content(
model="text-embedding-004",
contents=query,
)
query_vector = response.embeddings[0].values
# Search the Firestore database using Vector Search
results = db.collection("menu").find_nearest(
vector_field="embedding",
query_vector=Vector(query_vector),
distance_measure=DistanceMeasure.COSINE,
limit=3,
).stream()
menu_data = []
for doc in results:
item = doc.to_dict()
# Remove embedding field to save tokens
item.pop("embedding", None)
menu_data.append(item)
return json.dumps(menu_data)
except Exception as e:
return json.dumps({"error": f"Could not retrieve menu: {str(e)}"})
# [END get_menu]
- Cloud Shell Editor में
app.pyखोलें:
cloudshell edit app.py
app.pyमें# [START load_menu]ब्लॉक ढूंढें और उसे पूरी तरह से (# [START load_menu]से# [END load_menu]तक) Firestore के इस लोडिंग लॉजिक से बदलें:
# [START load_menu]
from google.cloud import firestore
try:
db = firestore.Client(database="coffee-menu")
docs = db.collection("menu").stream()
menu_items = []
for doc in docs:
item = doc.to_dict()
item.pop("embedding", None)
menu_items.append(item)
except Exception as e:
st.error(f"Error loading menu from Firestore: {e}")
menu_items = []
# [END load_menu]
6. Cloud Run पर फिर से डिप्लॉय करना
अपडेट किए गए ऐप्लिकेशन को डिप्लॉय करें:
gcloud run deploy coffee-barista \
--source . \
--region $REGION \
--allow-unauthenticated \
--command "/cnb/lifecycle/launcher" \
--args "sh,-c,python3 -m streamlit run app.py --server.port=\$PORT --server.address=0.0.0.0 --server.enableCORS=false --server.enableXsrfProtection=false" \
--service-account "barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
--set-env-vars GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=$PROJECT_ID,GOOGLE_CLOUD_LOCATION=global
7. Firestore इंटिग्रेशन की पुष्टि करना
Firestore से एजेंट के कनेक्शन की जांच करने के लिए, सीधे Firestore में एक नया मेन्यू आइटम जोड़ें. इसके बाद, पुष्टि करें कि एजेंट ने इसकी सलाह दी है.
- Python का इस्तेमाल करके, Firestore में मौजूद
menuकलेक्शन में नया दस्तावेज़ लिखने के लिए, Cloud Shell में यहां दिया गया निर्देश चलाएं:
python3 -c "
import os
from google import genai
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
db = firestore.Client(database='coffee-menu')
client = genai.Client(
vertexai=True,
project=os.environ.get('PROJECT_ID'),
location=os.environ.get('REGION', 'us-central1')
)
name = 'Matcha Green Tea Latte'
desc = 'Creamy steamed milk infused with premium Japanese matcha powder.'
res = client.models.embed_content(
model='text-embedding-004',
contents=f'{name}: {desc}'
)
embedding = res.embeddings[0].values
db.collection('menu').document('matcha-latte').set({
'name': name,
'description': desc,
'price': 5.50,
'tags': ['sweet', 'hot', 'dairy-free'],
'allergens': [],
'embedding': Vector(embedding)
})
print('Successfully added Matcha Latte with vector embeddings!')
"
- चैट सेशन को मिटाने और डेटाबेस की नई स्थिति को लोड करने के लिए, ब्राउज़र में Streamlit ऐप्लिकेशन को रीफ़्रेश करें.
- ध्यान दें कि:
- Matcha Green Tea Latte, साइडबार मेन्यू में अपने-आप दिखने लगता है.
- चैटबॉट से पूछें: "क्या आपके पास माचा ड्रिंक है?"
- एजेंट को, माचा ग्रीन टी लैट्टे का सुझाव देना चाहिए. साथ ही, उसमें वही ब्यौरा और कीमत होनी चाहिए जो आपने अभी जोड़ी है. इससे पुष्टि होती है कि एजेंट, सीधे आपके लाइव Firestore डेटाबेस में क्वेरी कर रहा है!
9. व्यवस्थित करें
अपने Google Cloud बिलिंग खाते से शुल्क लिए जाने से रोकने के लिए, डिप्लॉय की गई Cloud Run सेवा और कस्टम सेवा खाता मिटाएं.
Cloud Run सेवा मिटाएं:
gcloud run services delete coffee-barista --region $REGION --quiet
कस्टम सेवा खाता मिटाने के लिए:
gcloud iam service-accounts delete barista-agent-sa@$PROJECT_ID.iam.gserviceaccount.com --quiet
(ज़रूरी नहीं) अगर आपने Firestore डेटाबेस बनाया है, तो उसे मिटाएं:
gcloud firestore databases delete --database="coffee-menu" --quiet
ज़रूरी नहीं: पूरे प्रोजेक्ट को मिटाएं. ⚠️यह तरीका सिर्फ़ तब अपनाएं, जब आपने इस लैब के लिए कोई अलग प्रोजेक्ट बनाया हो
gcloud projects delete $PROJECT_ID
10. बधाई हो
बधाई हो! आपने Google के एडीके और Cloud Run का इस्तेमाल करके, Retrieval-Augmented Generation (आरएजी) एआई बरिस्ता एजेंट बनाया और उसे डिप्लॉय किया हो.
आपने क्या सीखा
- Python में आसान RAG टूल बनाना.
- ADK
LlmAgentऔरInMemoryRunnerका इस्तेमाल करना. - Streamlit में स्टेटफ़ुल चैट की सुविधा बनाना.
- सोर्स पर आधारित बिल्ड का इस्तेमाल करके, Streamlit को Cloud Run पर डिप्लॉय करना.