1. 簡介
在本程式碼研究室中,您將為咖啡廳建構互動式 AI Barista 代理。您將使用 Google 的開放原始碼 Agent Development Kit (ADK) 和 Gemini 3.5 Flash 模型,實作檢索增強生成 (RAG),根據模擬菜單資料集提供建議。最後,您會將代理程式包裝在 Streamlit 使用者介面中,並部署至 Cloud Run。
學習內容
- 建立 RAG 資料來源 (
menu.json),內含咖啡品項、標記和過敏原。 - 使用 ADK
LlmAgent建構 AI 代理,並連結 Python 工具來載入菜單資料。 - 將代理程式包裝在 Streamlit 對話應用程式中,管理對話記錄。
- 使用以來源為準的部署方式,將 Streamlit 應用程式部署至 Cloud Run。
- 測試 RAG 基礎和過敏原意識。

軟硬體需求
- 網路瀏覽器,例如 Chrome。
- 已啟用計費功能的 Google Cloud 專案。
- 對 Python 有基本的瞭解。
本程式碼研究室適合各種程度的開發人員 (包括初學者)。
預估費用:不到 $1.00 美元。
2. 事前準備
建立 Google Cloud 專案
- 在 Google Cloud 控制台中,選取或建立 Google Cloud 專案。
- 確認 Cloud 專案已啟用計費功能。
啟動 Cloud Shell
- 按一下 Google Cloud 控制台頂端的「啟用 Cloud Shell」。

- 驗證:

gcloud auth list
- 確認已設定有效專案:
gcloud config get project
如果顯示的專案 ID 不正確或未設定,請執行:
gcloud config set project <YOUR_PROJECT_ID>
啟用 API
執行下列指令,啟用所有必要的 API:
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. 建立模擬選單資料來源
為避免 AI Barista 產生不存在的項目,您將建立本機菜單資料集。代理會在執行階段透過自訂工具讀取這個檔案。
- 在 Cloud Shell 編輯器中建立並開啟
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 與即時資料庫
為什麼我們使用簡單的本機 menu.json 檔案,而不是即時資料庫?
如果是快速教學課程或原型,本機 JSON 檔案可省去初始資料庫設定時間和複雜度。不過,在實際的企業正式環境應用程式中,您會將代理程式連線至 Cloud Firestore、AlloyDB 或 Cloud SQL 等代管資料庫。
咖啡店經理使用即時資料庫,即可動態新增季節性商品、更新價格或調整過敏原標記,不必重建容器映像檔或重新部署應用程式程式碼。稍後在程式碼研究室中,我們會使用即時資料庫做為選用步驟。
5. 建構 ADK 代理
現在,您要安裝必要套件,並建構核心 ADK 代理程式邏輯。您將定義 get_menu() 工具,並傳遞至 LlmAgent。
- 在 Cloud Shell 編輯器中建立並開啟
requirements.txt:
cloudshell edit requirements.txt
- 將下列依附元件貼到編輯器中,然後儲存檔案:
google-adk==2.2.0
streamlit==1.58.0
- 在 Cloud Shell 編輯器中建立並開啟
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 編輯器中建立並開啟
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}")
💬 討論:模型取捨與擷取權杖效率
為什麼要呼叫函式工具來擷取菜單,而不是直接將整個菜單文字貼到代理的系統指令中?
代幣經濟!在提示中放入 8 個項目很便宜,但如果咖啡店擴展到 500 個項目,包括自訂食材,該怎麼辦?直接將大型資料集貼到系統提示中,會增加提示詞元數,導致每次查詢的交易費用提高,API 回應延遲時間也變長。
使用 ADK 工具時,代理程式只會在需要時動態要求讀取菜單。LLM 只會收到相關的菜單資料做為背景資訊,盡量縮小提示符記大小。
💬 討論:記憶體狀態和生產商店
使用者關閉瀏覽器分頁時,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 Platform 使用者角色 (
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 安全性
我們使用 gcloud run deploy –source 部署至 Cloud Run,未建立 Dockerfile 或 Procfile。Cloud Run 如何編譯及執行我們的 Python 應用程式?
Cloud Run 會在幕後使用 Buildpacks 分析存放區,偵測到 requirements.txt 和 Python 原始碼檔案後,引擎會自動編譯並封裝 Python 執行階段容器。
編寫自訂 Dockerfile 可讓您完全控管容器的系統套件和基礎層。Procfile 是較簡單的啟動指令宣告方式,不必完整設定容器。但如要快速部署,從來源 (--source) 部署的效率非常高。
為什麼我們需要建立自訂服務帳戶 barista-agent-sa,而不是直接使用預設的 Compute Engine 服務帳戶?
安全第一!根據預設,預設的 Compute Engine 服務帳戶具備極為廣泛的編輯者權限。在預設服務帳戶下執行 Cloud Run 容器,表示如果應用程式有安全性錯誤,攻擊者可能會讀取、寫入或刪除 Google Cloud 專案中的其他資源。
建立專屬服務帳戶並只指派 roles/aiplatform.user 角色,可確保應用程式只具備呼叫 Gemini 的必要存取權,符合最小權限原則。
7. 測試 RAG 行為
在網路瀏覽器中開啟 Cloud Run 服務網址,並向 AI Barista 提問,測試其基礎和安全限制。
- 菜單內要求:詢問:「推薦一些濃郁溫暖的飲品。」預期:代理程式推薦義式濃縮咖啡。
- 菜單外陷阱:問:「你們有抹茶星冰樂嗎?」預期:代理會禮貌地拒絕,並說明菜單上沒有這項產品。
- 過敏原相關要求:問:「我對乳糖不耐症,有什麼可以點?」預期:代理程式只會推薦不含乳製品的菜單項目 (例如燕麥奶拿鐵、義式濃縮咖啡、冰滴咖啡)。但「不」建議卡布奇諾或可頌。

8. 選用:使用 Vector Search,以 Firestore 為代理建立基準
在實際工作環境中,將菜單項目儲存在本機 menu.json 檔案中並非理想做法,因為菜單的任何變更都需要重建容器映像檔,並重新部署 Cloud Run 服務。
如要讓應用程式動態調整大小,您可以將菜單資料遷移至 Cloud Firestore,並使用 Vector Search 根據語意相似度,只擷取最相關的菜單項目。

1. 啟用 Firestore API 並初始化資料庫
執行下列指令,啟用 Firestore API 並以原生模式建立名為 coffee-menu 的 Firestore 資料庫:
gcloud services enable firestore.googleapis.com
gcloud firestore databases create --database="coffee-menu" --location=$REGION
注意:啟用 API 後,可能需要 1 到 2 分鐘才會全面生效。如果資料庫建立指令提示您輸入 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 使用者」 (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. 更新代碼
現在,請更新程式碼,從 Firestore 擷取菜單,而不是從 menu.json 讀取。
- 在 Cloud Shell 編輯器中開啟
requirements.txt:
cloudshell edit requirements.txt
- 在檔案結尾附加 Firestore 和 GenAI 用戶端程式庫,然後儲存檔案:
google-cloud-firestore==2.27.0
google-genai==2.11.0
- 在 Cloud Shell 編輯器中開啟
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 編輯器中開啟
app.py:
cloudshell edit app.py
- 在
app.py中找出# [START load_menu]區塊,然後將其完全替換為下列 Firestore 載入邏輯 (從# [START load_menu]到# [END load_menu]):
# [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 中新增菜單項目,並確認代理程式是否會推薦該項目。
- 在 Cloud Shell 中執行下列指令,使用 Python 將新文件寫入 Firestore 的
menu集合:
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 應用程式,清除對話工作階段並載入新的資料庫狀態。
- 請注意:
- 側邊欄選單會自動顯示「抹茶拿鐵」。
- 向聊天機器人提問:「你們有抹茶飲品嗎?」
- 服務專員應會成功推薦新產品「抹茶拿鐵」,並顯示你剛新增的說明和價格。這表示代理程式直接在即時 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 的 ADK 和 Cloud Run,建構及部署檢索增強生成 (RAG) AI Barista 代理。
目前所學內容
- 在 Python 中建構簡單的 RAG 工具。
- 使用 ADK
LlmAgent和InMemoryRunner。 - 在 Streamlit 中建立有狀態的即時通訊體驗。
- 使用以來源為基礎的建構作業,將 Streamlit 部署至 Cloud Run。