From 7cb07a405ca49315f7113464993243cfa4e72bd3 Mon Sep 17 00:00:00 2001 From: Murat Eken Date: Wed, 3 Jun 2026 13:38:35 +0200 Subject: [PATCH 1/3] updating code & solutions to be gemini-3.5-flash ready... --- .gitignore | 3 +++ hacks/adk-intro/README.md | 3 +++ hacks/bq-olh/README.md | 5 +++-- hacks/genai-fe/solutions.md | 2 +- hacks/genai-intro/artifacts/function/main.py | 4 ++-- hacks/genai-intro/solutions.md | 11 +++++++++++ hacks/httf-data/solutions.md | 8 ++++++++ 7 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index b7f8ec67..82543c90 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ node_modules/ .terraform* terraform.tfstate* terraform.tfvars* + +# AGY +.antigravitycli/ \ No newline at end of file diff --git a/hacks/adk-intro/README.md b/hacks/adk-intro/README.md index 3e5cc3b7..fe51f653 100644 --- a/hacks/adk-intro/README.md +++ b/hacks/adk-intro/README.md @@ -76,6 +76,8 @@ Once everything is set up, run `adk web` and make sure that the agent responds b ### Tips - Easiest option for ADK authentication is to use a properly configured `.env` file. +- Keep in mind that some models are only available in the `global` region. +- On Cloud Shell you might need to use the `--allow_origins="*"` parameter when launching `adk web` to prevent CORS errors. ## Challenge 2: Equipping the Scanner @@ -113,6 +115,7 @@ The provided code base already has a function that can look up the resources run ### Tips - You can verify the list by navigating to the *VM Instances* page in the Google Cloud Console or by using the `gcloud compute instances list` command in Cloud Shell. +- The `--reload_agents` parameter for `adk web` enables live reload of changed agent code, useful during development. ## Challenge 3: Sticky Notes diff --git a/hacks/bq-olh/README.md b/hacks/bq-olh/README.md index eb0b2ec7..e2f51711 100644 --- a/hacks/bq-olh/README.md +++ b/hacks/bq-olh/README.md @@ -13,8 +13,9 @@ We have selected Apache Iceberg as our foundational table format. It provides th Your team is tasked with building the bedrock of this platform on Google Cloud. You will begin by configuring the storage layer and conclude by demonstrating true multi-engine and AI interoperability. -![Architecture of the solution](./images/architecture.png) -*Architecture diagram for the challengesi in this gHack.* +| ![Architecture of the solution](./images/architecture.png) | +| :--: | +| *Architecture diagram for the challengesi in this gHack.* | ## Learning Objectives diff --git a/hacks/genai-fe/solutions.md b/hacks/genai-fe/solutions.md index 8356db2d..bf52fa42 100644 --- a/hacks/genai-fe/solutions.md +++ b/hacks/genai-fe/solutions.md @@ -192,7 +192,7 @@ And the correct response will be: *11/05/2024 15:42:06* (the exact wording might > [!WARNING] > The footage is from a European race, so the time format in the video is `dd/mm/yyyy` and not `mm/dd/yyyy`. -We've succesfully tested this with `gemini-2.0-flash-001` using the default settings (which is end of life by now), but participants are free to experiment with different available models and settings to get the correct answer. +We've succesfully tested this with `gemini-2.0-flash` using the default settings (which is end of life by now), but participants are free to experiment with different available models and settings to get the correct answer. ## Challenge 4: Telemetry to the rescue! diff --git a/hacks/genai-intro/artifacts/function/main.py b/hacks/genai-intro/artifacts/function/main.py index 9b5bce3d..007d5ff0 100644 --- a/hacks/genai-intro/artifacts/function/main.py +++ b/hacks/genai-intro/artifacts/function/main.py @@ -33,9 +33,9 @@ BQ_DATASET="articles" BQ_TABLE="summaries" -MODEL_NAME="gemini-2.5-flash" +MODEL_NAME="gemini-3.5-flash" -genai_client = genai.Client(vertexai=True, project=PROJECT_ID, location=REGION) +genai_client = genai.Client(vertexai=True, project=PROJECT_ID, location="global") def extract_text_from_document(src_bucket: str, file_name: str, dst_bucket: str) -> str: diff --git a/hacks/genai-intro/solutions.md b/hacks/genai-intro/solutions.md index a19384d4..51097da4 100644 --- a/hacks/genai-intro/solutions.md +++ b/hacks/genai-intro/solutions.md @@ -172,6 +172,17 @@ WITH CONNECTION `$REGION.conn-llm` OPTIONS (ENDPOINT = 'gemini-2.0-flash') > [!NOTE] > We're using `gemini-2.0-flash` here as an example, which is one of the latest GA models by the time of this writing. You could use any other model version that's not discontinued. +In case a model is not available in a specific region, but for example only has a global endpoint (gemini-3.5-flash, as per June 2026), you can indicate it in the endpoint definition. + +```sql +CREATE OR REPLACE MODEL + articles.llm REMOTE +WITH CONNECTION `$REGION.conn-llm` +OPTIONS ( + ENDPOINT = 'projects/$PROJECT_ID/locations/global/publishers/google/models/gemini-3.5-flash' +) +``` + Finally, we can use the linked model to make predictions. ```sql diff --git a/hacks/httf-data/solutions.md b/hacks/httf-data/solutions.md index ccc0ad2f..c12ecafe 100644 --- a/hacks/httf-data/solutions.md +++ b/hacks/httf-data/solutions.md @@ -272,6 +272,14 @@ OPTIONS (ENDPOINT = 'gemini-2.0-flash'); EOF ``` +In case a model is not available in a specific region, but for example only has a global endpoint (gemini-3.5-flash, as per June 2026), you can indicate it in the endpoint definition. + +```sql +CREATE OR REPLACE MODEL $BQ_DATASET.text_generation +REMOTE WITH CONNECTION \`us.$CONN_ID\` +OPTIONS (ENDPOINT = 'projects/$GOOGLE_CLOUD_PROJECT/locations/global/publishers/google/models/gemini-3.5-flash'); +``` + Generating the product description will involve designing a prompt, which is an art by itself. It's sufficient to ensure that the prompt is common sense and includes the columns mentioned in the instructions. ```shell From f5af5046b917d9a9c1d51c916cb7491cb6249bac Mon Sep 17 00:00:00 2001 From: Murat Eken Date: Wed, 3 Jun 2026 20:30:03 +0200 Subject: [PATCH 2/3] updated for ADK 2... --- README.md | 2 +- hacks/adk-intro/README.md | 25 ++++++++++----------- hacks/adk-intro/solutions.md | 43 ++++++++++++++++++++---------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 32d0ed90..6fdb7801 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Teams are not left on their own to solve the challenges. Coaches work with each - [Easy Ads: From Concept to Creation with GenMedia](./hacks/genmedia-on-gcp/README.md) > In this hack, we'll step into the role of a creative director at a cutting-edge ad agency. Our mission is to create a compelling 20-30 second video advertisement for a revolutionary new product using Google Cloud's generative AI tools. - [Introduction to Agents with ADK](./hacks/adk-intro/README.md) - > This is an introduction to Agentic AI using Agent Development Kit (ADK) framework. We'll be introducing various ADK concepts step-by-step, starting with a single agent and progressively building a tool-using, collaborative multi-agent system. + > **UPDATED FOR ADK 2** This is an introduction to Agentic AI using Agent Development Kit (ADK) framework. We'll be introducing various ADK concepts step-by-step, starting with a single agent and progressively building a tool-using, collaborative multi-agent system. - [Modernizing Classic Data Warehousing with BigQuery](./hacks/bq-dwh/README.md) > In this hack we'll implement a classic data warehouse using modern tools, such as Cloud Storage, BigQuery, Dataform and Looker Studio. We'll start with a modified version of the well known AdventureWorks OLTP database, and we'll implement a dimensional model to report on business questions using a BI visualization tool. - [Open Lakehouse with Apache Iceberg](./hacks/bq-olh/README.md) diff --git a/hacks/adk-intro/README.md b/hacks/adk-intro/README.md index fe51f653..98d2eba2 100644 --- a/hacks/adk-intro/README.md +++ b/hacks/adk-intro/README.md @@ -2,7 +2,7 @@ ## Introduction -Welcome to DevCore Inc.! We are a fast-moving tech company where innovation thrives. Our developers have the freedom to spin up Cloud resources on demand, fostering rapid prototyping and experimentation. However, this freedom has a downside: *zombie resources*. Developers frequently create servers for tests but forget to stop or delete them afterwards. +Welcome to Cymbal Inc.! We are a fast-moving tech company where innovation thrives. Our developers have the freedom to spin up Cloud resources on demand, fostering rapid prototyping and experimentation. However, this freedom has a downside: *zombie resources*. Developers frequently create servers for tests but forget to stop or delete them afterwards. This results in hundreds of idle resources running 24/7, costing the company tens of thousands of dollars every month for zero value. The manual cleanup process is tedious, error-prone, and can't keep up. We need an automated, intelligent system to solve this problem. @@ -76,7 +76,6 @@ Once everything is set up, run `adk web` and make sure that the agent responds b ### Tips - Easiest option for ADK authentication is to use a properly configured `.env` file. -- Keep in mind that some models are only available in the `global` region. - On Cloud Shell you might need to use the `--allow_origins="*"` parameter when launching `adk web` to prevent CORS errors. ## Challenge 2: Equipping the Scanner @@ -110,7 +109,7 @@ The provided code base already has a function that can look up the resources run ### Learning Resources -- [Tools in ADK](https://google.github.io/adk-docs/tools/) +- [Tools in ADK](https://adk.dev/tools-custom/) ### Tips @@ -137,9 +136,9 @@ Modify the `resource_scanner_agent` to save the list of all virtual machines in ### Learning Resources -- [Session state in ADK](https://google.github.io/adk-docs/sessions/state/) -- [Updating state](https://google.github.io/adk-docs/sessions/state/#how-state-is-updated-recommended-methods) -- [Output schemas](https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key) +- [Session state in ADK](https://adk.dev/sessions/state/) +- [Updating state](https://adk.dev/sessions/state/#how-state-is-updated-recommended-methods) +- [Output schemas](https://adk.dev/agents/llm-agents/#data-handling) ### Tips @@ -151,16 +150,16 @@ Modify the `resource_scanner_agent` to save the list of all virtual machines in Breaking down complex problems into smaller, manageable sub-problems is a well-established strategy in software development. Multi-agent systems apply this principle to AI, allowing specialized agents to handle specific aspects of a larger task. -In this challenge we'll introduce the concept of *sub-agents* and *workflow agents* which are specialized agents that control the execution flow of its sub-agents. +In this challenge we'll introduce the concept of *sub-agents* and *Workflows* which are specialized constructs that control the execution flow of its sub-agents by organizing them in a graph-like structure. > [!NOTE] -> Workflow agents (sequential, parallel, loop) can be useful for orchestration in many cases as they're reliable, well structured and predictable. However, it's also possible to use LLM based Agents for orchestration if more flexibility is needed. In that case you'll be defining the order and conditions for running the sub-agents in the agent's instructions (the prompt). +> Workflows can be useful for orchestration in many cases as they're reliable, well structured and predictable. However, it's also possible to use LLM based Agents for orchestration if more flexibility is needed. In that case you'll be defining the order and conditions for running the sub-agents in the agent's instructions (the prompt). ### Description Create two new agents, `resource_monitor_agent` which should filter the list of resources found by the `resource_scanner_agent`, and store that into the session store as `idle_resources` using the appropriate schema. This agent should return back only the instances that are idle. -Then create a new sequential agent `orchestrator_agent` that calls the `resource_scanner_agent` and the `resource_monitor_agent` in sequence. Once you have created the new agents, update the `root_agent` to be the `orchestrator_agent`. +Then create a new **Workflow** `orchestrator_agent` that calls the `resource_scanner_agent` and the `resource_monitor_agent` in sequence. Once you have created the new agents, update the `root_agent` to be the `orchestrator_agent`. > [!NOTE] > This is a very basic scenario where we're looking up basic stats and letting the Agent to decide what's idle. The power of the LLM based agents is however that they can also detect more advanced patterns based on more complex data (which is beyond the scope of this challenge) @@ -173,12 +172,12 @@ Then create a new sequential agent `orchestrator_agent` that calls the `resource ### Learning Resources -- [Multi-Agent Systems in ADK](https://google.github.io/adk-docs/agents/multi-agents/) -- If idle resource list is not generated correctly, make your instructions more specific +- [Graph based agent workflows](https://adk.dev/graphs/) ### Tips - You can use `adk web` UI to view the agents involved. +- If idle resource list is not generated correctly, make your instructions more specific ## Challenge 5: MCP: Universal Tooling @@ -207,7 +206,7 @@ Then add the `resource_labeler_agent` to the `orchestrator_agent` sequence. ### Learning Resources -- [MCP Tools in ADK](https://google.github.io/adk-docs/tools/mcp-tools/) +- [MCP Tools in ADK](https://adk.dev/tools-custom/mcp-tools) ### Tips @@ -242,7 +241,7 @@ Create a new agent `resource_cleaner_agent` that uses A2A protocol to connect to ### Learning Resources -- [Using A2A Agents in ADK](https://google.github.io/adk-docs/a2a/quickstart-consuming/) +- [Using A2A Agents in ADK](https://adk.dev/a2a/quickstart-consuming/) ### Tips diff --git a/hacks/adk-intro/solutions.md b/hacks/adk-intro/solutions.md index 49fee53e..4ee39a88 100644 --- a/hacks/adk-intro/solutions.md +++ b/hacks/adk-intro/solutions.md @@ -38,6 +38,9 @@ Since we're using Cloud Source Repositories, the authentication is done automati If they get the message `warning: You appear to have cloned an empty repository`, they were too quick. The repository is initialized asynchronously at project startup and takes a minute or so. In that case they should retry (after deleting the empty repository, the `ghacks-adk-intro` directory). +> [!NOTE] +> We have had issues where the initialization of the project was not successful when using QL. So if no Git repository is created after a few minutes restart the lab so the project gets re-created and re-initialized. + Once the repository is cloned, although it's not a hard requirement, the best practice is to start with a virtual environment. There are multiple tools to create virtual environments and install packages but we'll stick to the defaults. ```shell @@ -93,6 +96,9 @@ Now we can run the `adk web` command and preview it by clicking the web preview If you get authentication errors, make sure that the environment variables as defined above (in the `.env` file) are set and have been sourced. +> [!NOTE] +> At the time of this writing the latest Gemini model is only available in the `global` region, so we have hardcoded the location to be `global` for the model. The location that's configured through the `.env` file is ignored (although it would be used as the deployment location if the agent was deployed to the Agent Runtime). + ## Challenge 2: Equipping the Scanner ### Notes & Guidance @@ -150,7 +156,7 @@ Again the new driver should follow the same steps for the first challenge to clo ```python # keep other imports -from google.adk.agents import SequentialAgent +from google.adk import Workflow # keep resource_scanner_agent as is resource_monitor_agent = Agent( @@ -166,9 +172,12 @@ resource_monitor_agent = Agent( output_schema=schemas.VMStatsList, ) -orchestrator_agent = SequentialAgent( +orchestrator_agent = Workflow( name="orchestrator_agent", - sub_agents=[resource_scanner_agent, resource_monitor_agent] + edges=[ + ("START", resource_scanner_agent), + (resource_scanner_agent, resource_monitor_agent), + ] ) root_agent = orchestrator_agent @@ -195,12 +204,12 @@ The following snippet indicates what needs to be changed. ```python # keep other imports -from google.adk.tools.mcp_tool import MCPToolset +from google.adk.tools.mcp_tool import McpToolset from google.adk.tools.mcp_tool import StreamableHTTPConnectionParams # keep resource_scanner_agent and idle_checker_agent as is -mcp_tool_set = MCPToolset( +mcp_tool_set = McpToolset( connection_params=StreamableHTTPConnectionParams( url="http://localhost:8888/" ) @@ -217,12 +226,11 @@ resource_labeler_agent = Agent( tools=[mcp_tool_set, tools.get_current_date, tools.add_days_to_date] ) -orchestrator_agent = SequentialAgent( +orchestrator_agent = Workflow( name="orchestrator_agent", - sub_agents=[ - resource_scanner_agent, - resource_monitor_agent, - resource_labeler_agent + edges=[ + ("START", resource_scanner_agent), + (resource_scanner_agent, resource_monitor_agent, resource_labeler_agent), ] ) ``` @@ -244,7 +252,7 @@ ADK provides many different classes and methods for handling the authentication ```python MCP_SERVER_CLOUD_RUN_URL="..." # typically https://mcp-server-$PROJECT_NUMBER.$REGION.run.app -mcp_tool_set = MCPToolset( +mcp_tool_set = McpToolset( connection_params=StreamableHTTPConnectionParams( url=f"{MCP_SERVER_CLOUD_RUN_URL}/", headers={"Authorization": f"Bearer {tools.get_bearer_token(MCP_SERVER_CLOUD_RUN_URL)}"}, @@ -297,14 +305,11 @@ resource_cleaner_agent = RemoteA2aAgent( f"http://localhost:8080/a2a/resource_cleaner_agent{AGENT_CARD_WELL_KNOWN_PATH}" ) ) - -orchestrator_agent = SequentialAgent( +orchestrator_agent = Workflow( name="orchestrator_agent", - sub_agents=[ - resource_scanner_agent, - resource_monitor_agent, - resource_labeler_agent, - resource_cleaner_agent + edges=[ + ("START", resource_scanner_agent), + (resource_scanner_agent, resource_monitor_agent, resource_labeler_agent, resource_cleaner_agent), ] ) ``` @@ -324,7 +329,7 @@ resource_cleaner_agent = RemoteA2aAgent( name="resource_cleaner_agent", description="This agent stops idle instances that have been scheduled for cleanup", agent_card=( - f"http://localhost:8080/a2a/resource_cleaner_agent{AGENT_CARD_WELL_KNOWN_PATH}" + f"https://{A2A_SERVER_CLOUD_RUN_URL}/a2a/resource_cleaner_agent{AGENT_CARD_WELL_KNOWN_PATH}" ), httpx_client=httpx_client ) From 0e69687ace33825451bb3109b0c201c49b7253b2 Mon Sep 17 00:00:00 2001 From: Murat Eken Date: Thu, 4 Jun 2026 09:53:09 +0200 Subject: [PATCH 3/3] improving wording for adk-intro challenges/solutions... --- hacks/adk-intro/README.md | 10 +++++----- hacks/adk-intro/solutions.md | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/hacks/adk-intro/README.md b/hacks/adk-intro/README.md index 98d2eba2..a32d7e05 100644 --- a/hacks/adk-intro/README.md +++ b/hacks/adk-intro/README.md @@ -56,7 +56,7 @@ We're taking baby steps, let's get started with our development environment. Thi ### Description -We've already prepared a code base for you and put it in a Git repository (your coach will provide you the link). Clone that on Cloud Shell, create a virtual environment and install the requirements. +We've already prepared a code base for you and put it in a Git repository (your coach will provide you the link). Clone that on Cloud Shell, create a virtual environment, activate it and install the requirements from the `requirements.txt` file in that environment. Once everything is set up, run `adk web` and make sure that the agent responds back. @@ -157,9 +157,9 @@ In this challenge we'll introduce the concept of *sub-agents* and *Workflows* wh ### Description -Create two new agents, `resource_monitor_agent` which should filter the list of resources found by the `resource_scanner_agent`, and store that into the session store as `idle_resources` using the appropriate schema. This agent should return back only the instances that are idle. +Create a new agent, `resource_monitor_agent` which should filter the list of resources found by the `resource_scanner_agent`, and store that into the session store as `idle_resources` using the appropriate schema. This agent should return back only the instances that are idle. -Then create a new **Workflow** `orchestrator_agent` that calls the `resource_scanner_agent` and the `resource_monitor_agent` in sequence. Once you have created the new agents, update the `root_agent` to be the `orchestrator_agent`. +Then create a new *Workflow* `orchestrator_agent` that calls the `resource_scanner_agent` and the `resource_monitor_agent` in sequence. Once you have created the new agents, update the `root_agent` to be the `orchestrator_agent`. > [!NOTE] > This is a very basic scenario where we're looking up basic stats and letting the Agent to decide what's idle. The power of the LLM based agents is however that they can also detect more advanced patterns based on more complex data (which is beyond the scope of this challenge) @@ -177,7 +177,7 @@ Then create a new **Workflow** `orchestrator_agent` that calls the `resource_sca ### Tips - You can use `adk web` UI to view the agents involved. -- If idle resource list is not generated correctly, make your instructions more specific +- If idle resource list is not generated correctly, make your instructions more specific. ## Challenge 5: MCP: Universal Tooling @@ -187,7 +187,7 @@ We have built and referenced our own tool in the second challenge, but what abou There's a plethora of various MCP Tool providers (for example see this [list](https://mcpservers.org/)), which can run locally as well as remotely. For this challenge we'll use a sample tool that we have developed for this hack using [FastMCP](https://gofastmcp.com/getting-started/welcome) library and running remotely on [Cloud Run](https://cloud.google.com/run/docs/host-mcp-servers). -In this challenge we'll make sure that idle resources are tagged so that we can give the developers time to verify if we can stop them. In order to do that we'll use an MCP tool that adds a new label with a termination date in the future to the idle resource. +Goal of this challenge is to make sure that idle resources are tagged so that we can give the developers time to verify if we can stop them. In order to do that we'll use an MCP tool that adds a new label with a termination date in the future, to the idle resource. ### Description diff --git a/hacks/adk-intro/solutions.md b/hacks/adk-intro/solutions.md index 4ee39a88..f96f4587 100644 --- a/hacks/adk-intro/solutions.md +++ b/hacks/adk-intro/solutions.md @@ -97,7 +97,7 @@ Now we can run the `adk web` command and preview it by clicking the web preview If you get authentication errors, make sure that the environment variables as defined above (in the `.env` file) are set and have been sourced. > [!NOTE] -> At the time of this writing the latest Gemini model is only available in the `global` region, so we have hardcoded the location to be `global` for the model. The location that's configured through the `.env` file is ignored (although it would be used as the deployment location if the agent was deployed to the Agent Runtime). +> At the time of this writing the latest Gemini model is only available in the `global` region, so we have hardcoded the location to be `global` for the model (see the `settings.py` for the implementation details). The location that's configured through the `.env` file is ignored (although it would be used as the deployment location if the agent was deployed to the Agent Runtime). ## Challenge 2: Equipping the Scanner @@ -241,29 +241,30 @@ The proxy solves the authenticaton part of this simple tool. It's also possible import google.auth.transport.requests import google.oauth2.id_token -def get_bearer_token(audience: str) -> str: +def get_auth_headers(readonly_context=None) -> dict[str, str]: + """Returns http headers for authenticating against MCP toolset servers.""" request = google.auth.transport.requests.Request() - token = google.oauth2.id_token.fetch_id_token(request, audience) - return token + token = google.oauth2.id_token.fetch_id_token(request, MCP_SERVER_CLOUD_RUN_URL) + return {"Authorization": f"Bearer {token}"} ``` -ADK provides many different classes and methods for handling the authentication configuration, but we'll stick to the simple method of providing the bearer token in the header of the request. +ADK provides many different classes and methods for handling the authentication configuration, but we'll stick to the simple method of providing the bearer token in the header of the request. Note that the parameter `header_provider` is passed the function reference for `get_auth_headers` as a callable. ```python MCP_SERVER_CLOUD_RUN_URL="..." # typically https://mcp-server-$PROJECT_NUMBER.$REGION.run.app mcp_tool_set = McpToolset( connection_params=StreamableHTTPConnectionParams( - url=f"{MCP_SERVER_CLOUD_RUN_URL}/", - headers={"Authorization": f"Bearer {tools.get_bearer_token(MCP_SERVER_CLOUD_RUN_URL)}"}, - ) + url=f"{MCP_SERVER_CLOUD_RUN_URL}/" + ), + header_provider=get_auth_headers ) ``` > [!NOTE] > At the time of this writing using the `auth_scheme` and `auth_credentials` for bearer tokens doesn't work well with MCP servers, as those credentials are not utilized for listing the tools, tracked [here](https://github.com/google/adk-python/issues/2168). -As our tool is simple, this approach works fine, but in real world, you might need to use OAuth flows, API keys etc. And there will be cases where the currently authenticated user's credentials need to be forwarded to remote agents/tools so that they can perform actions on behalf of the user (see the official [docs](https://google.github.io/adk-docs/safety/#identity-and-authorization) for more details). +As our tool is simple, this approach works fine, but in real world, you might need to use OAuth flows, API keys etc. And there will be cases where the currently authenticated user's credentials need to be forwarded to remote agents/tools so that they can perform actions on behalf of the user (see the official [docs](https://adk.dev/safety/#identity-and-authorization) for more details). In order to verify if the labels have been set correctly, you can either navigate to the relevant section on Google Cloud Console or run the following command: