VakLab is an intelligent Voice AI agent that makes outbound calls to engage members in Preventive Health Screening. The agent uses:
- Google Gemini 3 Flash Preview for natural language understanding and generation
- Google Cloud Speech-to-Text for real-time transcription
- Google Cloud Text-to-Speech for natural voice synthesis
- Pipecat AI for real-time audio pipeline processing
- Twilio for telephony infrastructure
- Live Transcript Dashboard for real-time call monitoring
┌─────────────────────────────────────────────────────────────────────────────┐
│ VakLab Architecture │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────┐
│ Member │
│ Phone │
└──────┬──────┘
│
▼
┌────────────────────────┐
│ Twilio │
│ (Voice Platform) │
└────────────┬───────────┘
│ WebSocket
▼
┌──────────────────────────────────────────────────────────────────────────────┐
│ FastAPI Server (Port 8000) │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────────────────────────────────────┐ │
│ │ /twilio/ │ │ Pipecat Pipeline │ │
│ │ outbound-call │ ──▶│ │ │
│ │ (Trigger) │ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ └─────────────────┘ │ │ Google │ │ Google │ │ Google │ │ │
│ │ │ STT │──▶│ LLM │──▶│ TTS │ │ │
│ ┌─────────────────┐ │ │(Speech) │ │(Gemini 3) │ │ (Voice) │ │ │
│ │ /twilio/ │ │ └─────────┘ └─────────┘ └─────────────┘ │ │
│ │ voice-entry │ │ │ │ │
│ │ (TwiML) │ │ ┌──────┴──────┐ │ │
│ └─────────────────┘ │ │ Tools │ │ │
│ │ ├─────────────┤ │ │
│ ┌─────────────────┐ │ │ • send_email│ │ │
│ │ /twilio/ │ │ │ • end_call │ │ │
│ │ stream │◀──▶│ └─────────────┘ │ │
│ │ (WebSocket) │ │ │ │
│ └─────────────────┘ └─────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ PostgreSQL DB │
│ (Member Data Store) │
└────────────────────────┘
flowchart TD
Start([Start]) --> Hook["State 1:\nThe Hook"]
Hook -->|No| EndNo["Respond politely\nend_call()"]
Hook -->|Yes| Zip["State 2:\nZip Code Verification"]
Zip -->|Zip matches| Prop["State 3:\nMammogram Value Prop"]
Zip -->|Not serviced| EndResource["Explain & offer resources\nend_call()"]
Prop -->|No| EndThank["Thank & end_call()"]
Prop -->|Yes / Tell me more| Enroll["State 4:\nEnrollment & Action"]
Enroll -->|Yes| SendEmail["send_enrollment_email(email_address, first_name)"]
SendEmail --> Confirm["Confirm: 'You're all set!'\n(acknowledge email)"]
Confirm --> EndDone["end_call()"]
Enroll -->|No| EndThank
┌──────┐ ┌────────┐ ┌────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐
│User │ │ Twilio │ │ FastAPI│ │BCSGapAgent │ │ Rebecca │ │ Tools │
└──┬───┘ └──┬─────┘ └──┬─────┘ └────┬───────┘ └────┬─────┘ └──┬─────┘
│ │ │ │ │ │
│ Answer │ │ │ │ │
│──────────▶│ │ │ │ │
│ │ Webhook │ │ │ │
│ │──────────▶│ POST /outbound-call ──────▶│ │
│ │ │─────────────▶│ _get_member_data│ │
│ │ │ │──────────────▶│ instantiate Rebecca
│ │ │ │ │ run_live │
│ │ │ │ │──────────▶│
│ │ │ │ "Hi {first_name}..." │
│◀──────────│ │ │◀──────────────│ │
│ Speech │ │ │ (TTS/STT via Pipecat) │
│ Reply │ │ │──────────────▶│ │
│──────────▶│ │ │ (zip code provided) │
│ │ │ │──────────────▶│ │
│ │ │ │ (If enroll) │ send_enrollment_email
│ │ │ │──────────────▶│──────────▶│
│ │ │ │ │ email sent│
│ │ │ │ end_call() │──────────▶│
│ Hangup │ │ │──────────────▶│ │
| Component | Technology |
|---|---|
| Framework | FastAPI + Pipecat AI |
| LLM | Google Gemini 2.5 Flash |
| Speech-to-Text | Google Cloud STT |
| Text-to-Speech | Google Cloud TTS (Journey-F voice) |
| Voice Activity Detection | Silero VAD |
| Telephony | Twilio Voice |
| Database | PostgreSQL |
| Tunnel | ngrok |
- Python 3.12+
- Docker & Docker Compose (for PostgreSQL)
- ngrok account (for public URL tunnel)
- Google Cloud Account with:
- Cloud Speech-to-Text API enabled
- Cloud Text-to-Speech API enabled
- Gemini API key
- Service account JSON credentials
- Twilio Account with:
- Account SID
- Auth Token
- Phone number
# Navigate to project directory
cd /path/to/gcp_hackthon
# Create virtual environment
python3.12 -m venv venv
# Activate virtual environment
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Install Pipecat with Google and Silero extras
pip install "pipecat-ai[google,silero]"Create a .env file in the project root:
# Google Cloud
GOOGLE_API_KEY=your_google_api_key
GOOGLE_GENAI_USE_VERTEXAI=0
# Database
DB_HOST=localhost
DB_NAME=outbound_agent_db
DB_USER=user
DB_PASSWORD=password
# SMTP (for sending emails)
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASSWORD=your_app_password
# Twilio
TWILIO_SID=your_twilio_account_sid
TWILIO_AUTH=your_twilio_auth_token
TWILIO_NUMBER=+1234567890
# Domain (ngrok URL - update after starting ngrok)
DOMAIN=https://your-ngrok-url.ngrok-free.app- Create a service account in Google Cloud Console
- Download the JSON credentials file
- Place it in the project root
- Update the path in
routers/pipe_bot.pyif needed
# Start the database container
docker compose up -d
# Verify it's running
docker psThe database will be initialized with seed data from db-init/init.sql.
# In a new terminal
ngrok http 8000Copy the HTTPS URL (e.g., https://abc123.ngrok-free.app) and update:
.envfile:DOMAIN=https://abc123.ngrok-free.app
# Activate virtual environment
source venv/bin/activate
# Start FastAPI server
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadcurl -X POST http://localhost:8000/twilio/outbound-callThis will:
- Look up the first member in
campaign_target_member_call_list - Initiate an outbound call via Twilio
- Connect the AI agent when the call is answered
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check |
/twilio/outbound-call |
POST | Trigger outbound call |
/twilio/voice-entry |
POST | Twilio webhook for TwiML |
/twilio/stream |
WebSocket | Audio streaming endpoint |
/twilio/callback |
POST | Call status callbacks |
/ui |
GET | Live Transcript Dashboard |
/ui/transcripts |
WebSocket | Real-time transcript stream |
VakLab includes a real-time transcript dashboard that shows the conversation as it happens.
- Start the server (see Setup Instructions)
- Open your browser and navigate to:
http://localhost:8000/ui
- Real-time transcription — See Rebecca's responses and customer speech live
- Visual call status — Green indicator when call is in progress
- Speaker identification — Rebecca (AI) on the left in blue, Customer on the right in green
- Auto-scroll — Transcript automatically scrolls as new messages appear
- Call state tracking — Shows "Call in progress" and "Call complete" banners
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Pipecat │────▶│ Transcript │────▶│ WebSocket │
│ Pipeline │ │ Manager │ │ Broadcast │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Browser UI │
│ Dashboard │
└─────────────────┘
The dashboard connects via WebSocket and receives:
call_started— When a new call begins (includes customer name)transcript_message— Customer speech from STTai_stream_start/ai_stream_chunk/ai_stream_end— Rebecca's responses (streamed)call_ended— When the call completes
gcp_hackthon/
├── main.py # FastAPI application entry point
├── requirements.txt # Python dependencies
├── docker-compose.yml # PostgreSQL container config
├── .env # Environment variables
├── cool-furnace-*.json # Google Cloud credentials
│
├── agents/
│ └── outbound_agent/
│ ├── __init__.py
│ ├── agent.py # VaklabAgent (Rebecca - LLM instructions)
│ └── tools.py # Tool functions (email, end_call)
│
├── frontend/
│ └── index.html # Live Transcript Dashboard UI
│
├── routers/
│ ├── __init__.py
│ ├── health.py # Health check endpoint
│ ├── outbound_twillio.py # Twilio webhooks & call handling
│ └── pipe_bot.py # Pipecat pipeline configuration
│
├── utils/
│ ├── __init__.py
│ ├── db.py # Database connection
│ ├── env.py # Environment helpers
│ ├── security.py # Security utilities
│ └── transcript_manager.py # Real-time transcript broadcasting
│
├── entities/
│ └── twilio.py # Twilio entity models
│
└── db-init/
└── init.sql # Database initialization script
# Text-to-Speech Voice
tts = GoogleTTSService(
voice_id="en-US-Journey-F", # Female voice
sample_rate=8000
)
# Voice Activity Detection
vad_analyzer=SileroVADAnalyzer(
params=VADParams(stop_secs=0.5) # Wait 0.5s after speech stops
)model="gemini-3-flash-preview",
planner=BuiltInPlanner(
thinking_config=types.ThinkingConfig(
thinking_budget=0 # Minimal thinking for lower latency
)
)python -c "
from agents.outbound_agent.tools import send_enrollment_email
result = send_enrollment_email('test@example.com', 'TestUser')
print(f'Email sent: {result}')
"python -c "
from utils.db import get_db_connection
conn = get_db_connection()
print('Connected!' if conn else 'Failed!')
if conn: conn.close()
"VakLab includes a comprehensive evaluation framework using Google ADK's User Simulation to test the agent against various conversation scenarios.
| # | Scenario | Description |
|---|---|---|
| 0 | Happy Path | User expresses interest, provides zip code, enrolls |
| 1 | Pain Concern | User asks "Does the mammogram hurt?", then enrolls |
| 2 | User Busy | User can't talk now, agent offers to call back |
| 3 | Cost Questions | User asks about cost, procedure duration, then enrolls |
| 4 | User Declines | User politely declines (not interested) |
Step 1: Export your API key (required for ADK CLI)
export GOOGLE_API_KEY=your_api_key_hereStep 2: Run the evaluation
adk eval agents/outbound_agent vaklab_eval_set \
--config_file_path eval/eval_config_stable_with_metrics.json \
--print_detailed_resultsThe evaluation measures the agent across multiple dimensions:
| Metric | Threshold | Description |
|---|---|---|
| Hallucinations | 0.8 | Agent sticks to factual information |
| Safety | 0.9 | Content is safe and appropriate |
| Response Quality | 0.7 | Warm tone, concise, no jargon |
| Tool Use Quality | 0.8 | Tools called at appropriate times |
warm_tone— Maintains friendly, encouraging communicationconcise_responses— Uses short, conversational sentencesno_jargon— Avoids medical terminologyclear_value_prop— Explains mammogram benefits clearlyzip_code_verification— Verifies zip code before enrollment
email_after_confirmation— Only sends email after explicit consentend_call_appropriate— Ends call at the right moment
After running, you'll see output like:
┌────────────────────────────────────────────────────────────────┐
│ EVALUATION RESULTS │
├────────────────────────────────────────────────────────────────┤
│ Scenario: Happy Path │
│ Status: PASSED ✅ │
│ │
│ Metrics: │
│ • Hallucinations: 0.85 (threshold: 0.8) ✅ │
│ • Response Quality: 0.79 (threshold: 0.7) ✅ │
│ • Tool Use Quality: 1.0 (threshold: 0.8) ✅ │
└────────────────────────────────────────────────────────────────┘
eval/
├── vaklab_eval_set.evalset.json # Test scenarios
├── eval_config_stable_with_metrics.json # Full config with metrics
├── adk_scenarios.json # ADK-compatible scenarios
└── README.md # Detailed eval documentation
| Issue | Solution |
|---|---|
| No audio on call | Check TTS service credentials path |
| LLM not responding | Verify GOOGLE_API_KEY in .env |
| WebSocket disconnects | Ensure ngrok is running and URL is updated |
| Email not sending | Verify SMTP credentials (use App Password for Gmail) |
| Database connection failed | Run docker compose up -d |
Monitor server logs for debugging:
# Server logs show:
# - Call initiation
# - WebSocket connections
# - STT transcriptions
# - LLM responses
# - TTS generation
# - Tool function callsExample log output:
INFO: Starting Pipecat bot for call CA123...
INFO: Member data: {'member_first_name': 'Raju', ...}
INFO: [EVENT] Pipecat bot connected for Raju
INFO: [GREETING] Triggered LLM to generate greeting
DEBUG: GoogleTTSService: Generating TTS [Hi Raju, I'm Rebecca...]
INFO: [TOOL] send_enrollment_email called for Raju at raju@example.com
INFO: ✓ Email successfully sent to raju@example.com
INFO: [TOOL] end_call triggered - ending conversation