Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ node_modules/
.terraform*
terraform.tfstate*
terraform.tfvars*

# AGY
.antigravitycli/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
32 changes: 17 additions & 15 deletions hacks/adk-intro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand All @@ -76,6 +76,7 @@ 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.
- 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

Expand Down Expand Up @@ -108,11 +109,12 @@ 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

- 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

Expand All @@ -134,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

Expand All @@ -148,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.
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 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)
Expand All @@ -170,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

Expand All @@ -185,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

Expand All @@ -204,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

Expand Down Expand Up @@ -239,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

Expand Down
60 changes: 33 additions & 27 deletions hacks/adk-intro/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 (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

### Notes & Guidance
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -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/"
)
Expand All @@ -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),
]
)
```
Expand All @@ -233,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(
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:

Expand Down Expand Up @@ -297,14 +306,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),
]
)
```
Expand All @@ -324,7 +330,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
)
Expand Down
5 changes: 3 additions & 2 deletions hacks/bq-olh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion hacks/genai-fe/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
4 changes: 2 additions & 2 deletions hacks/genai-intro/artifacts/function/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 11 additions & 0 deletions hacks/genai-intro/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions hacks/httf-data/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading