Crafting a Lagos Tourism Agent
with Gemma 4 & Genkit
Move from static prompts to autonomous local execution! Build an Agentic RAG pipeline in TypeScript that knows when to query a local vector database for Ponta da Piedade history and when to check live Atlantic weather before recommending a kayak tour.
🤖 100% Local LLM
Powered by Gemma 4 (gemma4:e2b) via Ollama with native function calling & zero cloud API keys.
🧭 Hyper-Local Vector RAG
21 curated Markdown chunks on Ponta da Piedade grottos, maritime history & safety rules in a local vector store.
🛠️ Autonomous Tools
TypeScript tools calling live Open-Meteo Marine APIs + local tour safety filters orchestrated by Genkit.
Why Static Prompts Fail in the Real World
Imagine a visitor at Praia da Batata asking your local LLM two questions in one breath:
1️⃣ Hyper-Local Knowledge Blindness
"Why was the 1913 Ponta da Piedade lighthouse delayed for 30 years, and what are the fishermen names of the sea caves?"
- Base LLMs guess or omit the Ermida de Nossa Senhora da Piedade chapel dispute.
- They cannot know private event data like the DevFest Lagos 2026 passphrase (CARAVELA-GEMMA-2026).
2️⃣ Real-Time World Blindness (Safety Risk!)
"Can we take a kayak cave tour around Ponta da Piedade right now?"
- A static LLM has zero access to live Atlantic wave height or afternoon Nortada wind gusts.
- Guessing "Sure, go kayak!" during a 2.3m swell or 35 km/h offshore wind is dangerous.
Installing Node.js (v20+ LTS) & Workshop Repo
Google Genkit and our TypeScript runner (tsx) require Node.js v20 or newer (v20 LTS or v22 LTS recommended).
🍎 macOS / 🐧 Linux (nvm)
Install via nvm (Node Version Manager) or Homebrew:
🪟 Windows (winget / fnm)
Install via PowerShell winget or download from nodejs.org:
📦 Install Workshop Packages
Verify your Node version (≥ 20) and install the repo dependencies:
Installing Ollama & Pulling gemma4:e2b
Ollama runs local LLMs as an HTTP server on http://127.0.0.1:11434. We use Gemma 4 (gemma4:e2b) for fast local inference & native tool calling.
1️⃣ Install Ollama (ollama.com)
- 🐧 Linux: curl -fsSL https://ollama.com/install.sh | sh
- 🍎 macOS: brew install --cask ollama (or download `.dmg`)
- 🪟 Windows: winget install Ollama.Ollama
2️⃣ Pull gemma4:e2b & Verify
- Pull the model once—then the entire workshop runs 100% offline!
- Run npm run verify to confirm Ollama & Genkit are connected.
5 Self-Contained Steps — Ready to Run!
Every step is in its own folder with completed TypeScript code. Inspect the code, run npm run step X, or test directly in the Genkit UI!
Basic Prompt
step-01-basic-prompt/
Single-shot generation & Zod output with gemma4:e2b. Exposes static LLM limits.
Vector RAG
step-02-vector-rag/
Embeds 21 Lagos Markdown chunks & grounds answers via ai.retrieve() + genkit start UI.
Custom Tools
step-03-custom-tools/
Equips Gemma with live Open-Meteo marine weather & tour safety filter tools.
Agentic RAG
step-04-agentic-rag/
Turns vector search into searchLagosKnowledge tool for autonomous multi-tool planning.
Production Agent
step-05-production-agent/
Multi-turn memory, strict Zod response contract, safety guardrails & interactive CLI.
Configuring Genkit + Local gemma4:e2b
🔌 Why Genkit + Ollama?
- Unified TypeScript SDK: Same ai.defineFlow, ai.defineTool, and ai.retrieve APIs for local or cloud models.
- Critical Flag: Set supports: { tools: true } on gemma4:e2b so Genkit unlocks tool calling!
- Local Vector Store: @genkit-ai/dev-local-vectorstore persists embeddings in __db_lagos_tourism.json.
Step 1: Static Prompts & Structured Zod Output
🎯 What Step 1 Demonstrates
- Wraps ai.generate() inside an observable ai.defineFlow().
- Uses output: { schema: BasicPromptOutputSchema } so gemma4:e2b returns strongly-typed JSON.
🛑 The Baseline Failure
- Cannot explain the 1883–1913 Ermida chapel dispute at Ponta da Piedade.
- Does not know the secret passphrase (CARAVELA-GEMMA-2026).
- Cannot check live Atlantic swell before kayaking.
Step 2: Grounding with Local Vector RAG
📚 Hyper-Local Lagos Knowledge Base
- 01-ponta-da-piedade-history-grottos.md — 1913 Lighthouse delay, A Catedral, A Cozinha, Submarino.
- 02-kayak-maritime-safety-rules.md — Official wind ($\le 20\text{ km/h}$) & wave ($\le 1.2\text{m}$) limits.
- 05-devfest-lagos-2026-insider-guide.md — Canary passphrase CARAVELA-GEMMA-2026.
Test RAG & Model Directly with genkit start
Don't just test inside the terminal app! Launch the Genkit Developer UI (http://localhost:4000) scoped to Step 2 to test each primitive directly:
🤖 Test ollama/gemma4:e2b
Select the local model in the UI Playground and prompt it directly without RAG to see raw model behavior & token latency.
🧭 Test lagos_tourism
Query the vector store directly without calling the LLM! Type "1913 lighthouse" or "DevFest passphrase" and inspect the top-k chunks.
🔍 Inspect RAG Traces
Run step02_ragLagosHistory and click Inspect Trace to see exact retriever inputs, document chunks, and prompt injection.
Step 3: Giving Gemma Hands with TypeScript Tools
🌊 Tool 1: getLagosWeatherAndMarine
Calls live Open-Meteo Forecast & Marine APIs for Ponta da Piedade (37.0805°N, 8.6694°W) + supports workshop scenarios (live, calm, nortada_windy, atlantic_swell).
🛶 Tool 2: findLagosTours
Filters data/lagos-tours.json against live wind & wave limits. Automatically closes sea caves when wind > 20 km/h or swell > 1.2 m and suggests land alternatives!
Static RAG (Step 2) vs. Agentic RAG (Step 4)
❌ Static RAG Pipeline (Hardcoded)
Every user message forces a vector search—even if the user just asks "What is the wind speed right now?" or says "Obrigado!"
✅ Agentic RAG Pipeline (Autonomous)
Vector retrieval is a Tool (searchLagosKnowledge). gemma4:e2b reasons about intent and chains Vector Search + Live APIs on demand!
Step 4: Building the Agentic RAG Pipeline
🧭 Tool 3: searchLagosKnowledge
Wraps ai.retrieve({ retriever: lagosRetriever }) inside ai.defineTool().
- Autonomous Query Formulation: Gemma extracts focused search terms (e.g. "1913 Ponta da Piedade lighthouse A Cozinha") from long user prompts.
- Multi-Tool Chaining: Combines historical RAG chunks with live Open-Meteo marine telemetry in a single turn.
Step 5: Taking Local Agents to Production
🛡️ 4 Production Upgrades in Step 5
- 1. Multi-Turn Session Memory: Passes conversation history across turns so follow-up questions retain context.
- 2. Deterministic Safety Guardrail: Cross-checks LLM output against the live marine sensor verdict (UNSAFE_CAVES_CLOSED can never be downgraded!).
- 3. Execution Telemetry: Tracks latencyMs, toolCallCount, and exact tool inputs/outputs.
- 4. Resilient Tool Loop: Supports both native Ollama tool calls & fallback JSON tool invocation.
Muito Obrigado, DevFest Lagos 2026! 🇵🇹⛵
You just built a 100% local, privacy-preserving Agentic RAG tourism concierge powered by Gemma 4 (gemma4:e2b) and Google Genkit.
⌨️ All 5 Workshop Commands
- npm run step 1 — Static Prompt & Zod Output Baseline
- npm run step 2 — Local Vector Store & Grounded RAG
- npm run step 3 — Live Open-Meteo Weather & Tour Tools
- npm run step 4 — Autonomous Agentic RAG Pipeline
- npm run step 5 — Production Multi-Turn Agent & CLI
🧪 Genkit Developer UI Playgrounds
- npm run step 2 ui — Test Vector Retriever & gemma4:e2b directly in UI
- npm run step 4 ui — Test searchLagosKnowledge tool & Agentic RAG in UI
- npm run step 5 -- interactive — Live terminal chat with Capitão Gil
- npm run ui — Open full workshop workspace at http://localhost:4000