Agentic Data Science: an event-driven GCP pipeline with an ADK analyst on top
Drop a CSV in a bucket, it lands in BigQuery via Cloud Functions Gen 2 and Eventarc, and a Google ADK agent analyzes it. All in Terraform and GitHub Actions.
Agentic Data Science: an event-driven GCP pipeline with an ADK analyst on top
Repo: JeanFraga/Agentic-Data-Science
Goal
Two things in one project:
- An event-driven ingestion platform on GCP. Dropping a CSV into a bucket loads it into BigQuery automatically, and every API, bucket, dataset, service account and function is defined in Terraform and deployed by GitHub Actions.
- A conversational analyst built with Google’s Agent Development Kit (ADK) that answers questions about the loaded data.
The Titanic dataset is the worked example.
Architecture
flowchart LR
GH[Push to main] --> GA[GitHub Actions<br/>init / validate / plan / apply]
GA --> TF[Terraform]
TF --> ST[(GCS state bucket<br/>versioned)]
TF --> TB[(GCS landing bucket)]
TF --> CF[Cloud Function Gen 2]
TF --> SA[Service accounts + IAM]
GA --> LD[load script]
LD -->|titanic.csv| TB
TB -->|object.finalized<br/>via Eventarc| CF
CF -->|load job| BQT[(BigQuery<br/>test_dataset.titanic)]
U[User - adk web] --> RA[Root agent<br/>Gemini 2.0 Flash]
RA --> BA[BigQuery sub-agent] --> BQT
RA --> AA[Analytics sub-agent<br/>code executor]
Ingestion
- A script creates the dataset if it’s missing and uploads the CSV to the landing bucket.
- The
object.finalizedevent reaches a Cloud Functions Gen 2 function through Eventarc. The function runs Python 3.11 with retries on and its own service account. - The function ignores unexpected files and decodes defensively: UTF-8, then latin-1, then cp1252.
- It normalizes column names and loads the table with
WRITE_TRUNCATE, typing every column as STRING. Raw-first: typing happens downstream.
The agent
- A root agent on Gemini 2.0 Flash routes to two sub-agents, each wrapped as an
AgentTool:- a BigQuery agent with query, schema-lookup and count tools
- an analytics agent that uses ADK’s built-in code executor for pandas-style analysis
- A
before_agent_callbackinjects the table schema into the root agent’s instructions, so the model writes SQL against real column names. - The agent runs locally through
adk web.
Terraform and CI
- Terraform state lives in a versioned, public-access-blocked GCS bucket.
- Five service accounts are defined in code, each with its own role set (
for_each) and dataset-level grants. GitHub Actions, the function and three agent identities each get their own account, and the agent account can impersonate the others. - On pull requests, CI runs
init,validateandplan. Onmainit applies, then runs the load script as an end-to-end smoke test.
Problems I hit (and fixed)
- Duplicate IAM definitions. A second permissions file granted the function
bigquery.adminand conflicted with my cleanup. I consolidated to one file and cut the function down todataEditor,userandobjectViewer. Gen 2 then neededrun.invokerandeventarc.eventReceiver, and the GCS service agent neededpubsub.publisher. - Moving from Gen 1 to Gen 2. I moved from
google_cloudfunctions_functiontogoogle_cloudfunctions2_function, rewrote the handler for CloudEvents, and added Cloud Run, Eventarc and Artifact Registry permissions. - IAM as code. Service accounts I had created by hand in the console moved into Terraform, so the whole project is reproducible.
- ADK 1.0 API changes. After upgrading, Pydantic errors appeared. The fix was renaming
system_instructiontoinstructionand giving every agent aname. - Templating for publishing. Before making the repo public I replaced the hard-coded project ID with a placeholder throughout the docs.
What I’d do next
- Switch CI from a service-account key to Workload Identity Federation. I did this in my next project.
- Add a typed Silver table so the agent doesn’t have to cast STRING columns.
- Make the agent’s SQL tool read-only.
- Have the agent actually use the dedicated BigQuery ML and Vertex AI identities I provisioned, then deploy it instead of running it locally.
What I learned
Event-driven ingestion on GCP is mostly IAM. Eventarc, Cloud Run (under Gen 2 functions), Pub/Sub and Cloud Storage each need specific grants, and Terraform is what made those grants understandable and repeatable.
This post is licensed under CC BY 4.0 by the author.