A voice-first autonomous news agent. You ask for the news you care about; NewsPilot researches the live web, evaluates and ranks the results, synthesizes a concise source-backed briefing, and reads it aloud then researches again to answer follow-up questions.
Built for the All Things Agentic Hackathon 2026 (Taskmaster track).
Live demo: https://newspilot-284521543847.us-central1.run.app
Staying informed usually means scrolling several feeds, opening many tabs, and mentally de-duplicating the same story reported by different outlets — or reading a generic AI summary that hides its sources and can't answer a follow-up.
NewsPilot is different in two ways. First, it's a genuine agent: it decides what to research, gathers real sources, filters and ranks them, and follows up with more research when a question needs it — rather than answering from a model's training data. Second, it's voice-first: it greets you, listens, and reads the briefing aloud in a natural voice.
It is not user → LLM → response. A single orchestrating agent plans a
multi-step task and calls specialized tools to complete it.
[ADD DEMO VIDEO]
[ADD SCREENSHOT]
You can try the deployed app at the live URL above. (Prototype: expect a cold start on the first request, and see Limitations.)
flowchart TD
A[User request - voice or text] --> B[Orchestrator interprets intent<br/>and reads saved preferences]
B --> C{Request scope}
C -->|too ambiguous| Q[Ask one clarifying question]
C -->|specific or broad| D[Search sub-agent:<br/>google_search grounding]
D --> E[deduplicate_stories]
E --> F[rank_stories]
F --> G[Synthesize briefing<br/>with source links]
G --> H[Read aloud - Cloud TTS]
H --> I{Follow-up question?}
I -->|needs more detail| D
I -->|answered| J[Done]
The orchestrator decides the scope (a specific topic, a broad sweep, or a clarifying question), gathers stories, then runs them through deterministic de-duplication and ranking before writing the spoken briefing. Follow-up questions re-enter the same loop and trigger fresh research when needed.
flowchart LR
subgraph Browser
UI["Voice UI<br/>Web Speech STT · audio-reactive orb"]
end
subgraph GCP["Google Cloud"]
subgraph CR["Cloud Run — FastAPI (server.py)"]
ORCH["Orchestrator agent<br/>Google ADK · Gemini"]
SUB["Search sub-agent<br/>google_search"]
DD["deduplicate_stories"]
RK["rank_stories"]
PREF["get / save_user_preferences"]
end
VTX["Vertex AI · Gemini"]
FS["Firestore · preferences"]
TTS["Cloud Text-to-Speech · Chirp 3 HD"]
end
UI -->|POST /api/chat| ORCH
ORCH --> SUB --> VTX
ORCH --> DD
ORCH --> RK
ORCH --> PREF --> FS
ORCH --> VTX
UI -->|POST /api/tts| TTS
| Component | Implementation |
|---|---|
| Main agent | A single ADK LlmAgent orchestrator that plans, synthesizes, and handles follow-ups |
| Tools | news_search_agent (as an AgentTool), deduplicate_stories, rank_stories, get_user_preferences, save_user_preferences |
| Search | A dedicated sub-agent holding only the built-in google_search tool |
| Model | Gemini via Vertex AI |
| Memory / state | Firestore for long-term preferences; ADK in-memory sessions for conversation context |
| Backend | FastAPI (server.py) exposing /api/chat and /api/tts |
| Frontend | Vanilla HTML/CSS/JS voice UI (no build step) |
| Cloud | Cloud Run (hosting), Vertex AI, Firestore, Cloud Text-to-Speech |
Why the search sub-agent is separate: ADK/Gemini does not allow the built-in
google_search tool to coexist with custom function tools in one agent. The
supported workaround is the Agent-as-Tool pattern google_search lives alone
in a sub-agent that the orchestrator calls as a tool. Sources are returned as
structured JSON so they survive the tool boundary.
- Voice-first interaction: browser speech-to-text in, natural Chirp 3 HD voice out
- Live web research via Google Search grounding
- Duplicate/overlap detection across outlets (deterministic)
- Story ranking by recency, source reputation, and breadth of coverage (deterministic)
- Source-aware briefings every briefing keeps real source links
- Follow-up research using conversation context
- Personalized preferences (topics, avoid-list, length, style) persisted across sessions
- Graceful failure handling (e.g. preference store falls back to local JSON if Firestore is unreachable)
| Component | Technology | Purpose |
|---|---|---|
| Agent framework | Google ADK (Python, 2.x) | Orchestrator + tool calling |
| LLM | Gemini via Vertex AI | Planning, synthesis, tool selection |
| Web research | Google Search grounding (ADK built-in tool) | Current, real sources |
| Voice output | Google Cloud Text-to-Speech (Chirp 3 HD) | Natural spoken briefings |
| Voice input | Browser Web Speech API | Speech-to-text |
| Database | Firestore | Long-term user preferences |
| Backend | FastAPI + Uvicorn | /api/chat, /api/tts, static UI |
| Frontend | HTML / CSS / JavaScript | Voice UI (no build step) |
| Cloud / hosting | Cloud Run (via Cloud Build) | Deployment |
newspilot/
├── server.py # FastAPI: /api/chat (agent) + /api/tts (voice) + serves the UI
├── main.py # Optional ADK dev-UI entry point (adk web / get_fast_api_app)
├── Dockerfile # Cloud Run container image
├── .gcloudignore # Keeps .venv/.env out of the Cloud Build upload
├── .env.example # Template for newspilot/.env (no secrets)
├── requirements.txt
├── start_newspilot.bat # Windows one-click local launcher
├── static/ # Voice UI
│ ├── index.html
│ ├── css/style.css
│ └── js/app.js # STT, cloud-TTS playback, audio-reactive orb
├── docs/ # architecture.svg, demo script, Devpost writeup
└── newspilot/ # The agent package
├── agent.py # root_agent (orchestrator) + tool wiring
├── prompts.py # All agent instructions
├── config.py # Model + auth configuration (from env)
├── memory.py # Firestore preference store (+ JSON fallback)
├── sub_agents/
│ └── search_agent.py # google_search specialist
└── tools/
├── dedup.py # deduplicate_stories
├── rank.py # rank_stories
└── preferences.py # get/save_user_preferences
- Python 3.10+
- A Google Cloud project with billing enabled
- The gcloud CLI
- A modern Chromium-based browser (Chrome/Edge) for voice input
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtNewsPilot authenticates to Google Cloud with Application Default Credentials (no hardcoded keys).
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID
gcloud services enable aiplatform.googleapis.com firestore.googleapis.com texttospeech.googleapis.com
gcloud firestore databases create --location=nam5 # one-timeCopy the template and edit it:
cp .env.example newspilot/.env| Variable | Description | Required |
|---|---|---|
GOOGLE_GENAI_USE_VERTEXAI |
TRUE to route Gemini through Vertex AI |
Yes |
GOOGLE_CLOUD_PROJECT |
Your GCP project ID | Yes (Vertex) |
GOOGLE_CLOUD_LOCATION |
Gemini location; use global for the newest models |
Yes (Vertex) |
NEWSPILOT_MODEL / NEWSPILOT_SEARCH_MODEL |
Gemini model IDs | No (has a default) |
NEWSPILOT_TTS_VOICE |
Chirp 3 HD voice name | No (defaults to en-US-Chirp3-HD-Aoede) |
GOOGLE_API_KEY |
AI Studio key — only if GOOGLE_GENAI_USE_VERTEXAI=FALSE |
Conditional |
HTTPS_PROXY / HTTP_PROXY |
Local-only proxy (e.g. to reach Google behind a VPN). Never set in the cloud. | No |
Model note: the code default is
gemini-3.7-flash. The live deployment runsgemini-3.6-flashon theglobalendpoint, because newer Gemini models are exposed there and may return 404 on regional endpoints for new projects. Any "Gemini 3.5 or newer" model can be selected viaNEWSPILOT_MODEL.
python server.py # -> http://localhost:9000 (open in Chrome/Edge)Windows users can instead double-click start_newspilot.bat, which activates the
venv, starts the server, and opens the browser.
User: "Give me today's AI news, especially new models and research."
NewsPilot: "Here's your briefing on new AI models:
1. ...
2. ...
3. ...
Sources: ..." (read aloud)
User: "Tell me more about the second story."
NewsPilot: (resolves "second story" from context, researches again, answers)
NewsPilot is deployed to Cloud Run, built from source by Cloud Build. It uses Vertex AI (Gemini), Firestore (preferences), Google Search grounding (research), and Cloud Text-to-Speech (voice).
gcloud run deploy newspilot --source . --region us-central1 --allow-unauthenticated \
--memory 1Gi --cpu 1 --timeout 300 \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=YOUR_PROJECT_ID,GOOGLE_CLOUD_LOCATION=global,NEWSPILOT_MODEL=gemini-3.6-flash,NEWSPILOT_SEARCH_MODEL=gemini-3.6-flash,NEWSPILOT_TTS_VOICE=en-US-Chirp3-HD-Aoede"Required IAM (granted once):
- Runtime service account:
roles/aiplatform.user,roles/datastore.user - Cloud Build service account:
roles/cloudbuild.builds.builder
The .gcloudignore keeps .venv and .env out of the build, so local-only
proxy settings never reach the container; runtime config is passed via
--set-env-vars.
NewsPilot qualifies as an agent because the orchestrator makes decisions and drives a multi-step workflow, rather than returning a single model completion.
The LLM-driven decisions include:
- Goal interpretation turning a request into a scope (specific topic, broad sweep, or a clarifying question), informed by saved preferences.
- Tool selection choosing when to research, when to de-duplicate, when to rank, and when to read or write preferences.
- Multi-step workflow search → de-duplicate → rank → synthesize.
- Follow-up investigation deciding a follow-up needs more detail and calling the search tool again instead of answering from memory.
- Context/state resolving references like "the second story" from the conversation.
Deterministic (non-LLM) logic is deliberately separated:
deduplicate_storiesmerges stories by normalized URL and title similarity.rank_storiesscores by recency, a source-reputation weighting, and breadth of coverage.memory.pyreads/writes preferences and falls back to local JSON on failure.
Hallucination and stale-news risk is reduced by grounding every briefing in Google Search results and returning real source URLs; the agent is instructed not to invent news, quotes, dates, or links, and to state plainly when a search returns nothing.
- Google ADK, single orchestrator + tools rather than a multi-agent swarm: simpler to reason about and debug, with the LLM deciding when to use each tool.
- Agent-as-Tool for search: required because ADK won't mix the built-in
google_searchtool with custom function tools in one agent; it also yields clean separation of concerns. - Deterministic de-dup and ranking: fast, free, and explainable, instead of making every step an LLM call.
- Firestore for preferences, ADK sessions for conversation: separates long-term memory from short-term context; a local JSON fallback keeps the app working if the database is unreachable during development.
- Custom FastAPI server: one clean, same-origin JSON contract
(
/api/chat,/api/tts) for the browser, with full control over errors. - Config via environment variables: the same code runs on Vertex AI or the AI Studio API by flipping one flag, with no code changes.
- Prototype deployment. Cloud Run scales to zero, so the first request incurs a cold start; briefings also take time because they involve multiple model and search calls.
- Voice input depends on the browser. Speech-to-text uses the Web Speech API (best in Chrome/Edge; unavailable in some browsers). Typing is always available.
- Session memory is in-memory. Conversation context is per server instance and not shared across Cloud Run instances or restarts; only preferences persist (in Firestore).
- Single-user preferences. There is no authentication; preferences are stored under one demo user.
- Rate limits / availability. Model and API quotas can throttle requests; the agent reports failures rather than crashing.
- Source ranking is heuristic, based on a small curated source-weight list and recency, not a full credibility model.
Planned (not yet implemented):
- Streaming voice for lower latency
- Scheduled/automatic morning briefings
- Multi-user accounts so preferences follow the user
- Richer source cards (outlet names, logos) and a fuller credibility model
- Multilingual briefings
- Improved interruption/barge-in handling
All Things Agentic Hackathon 2026 Taskmaster track.
The Taskmaster track focuses on agents that automate a real multi-step task rather than simply chat. NewsPilot automates the full news-research loop gather, filter, de-duplicate, rank, synthesize, and speak and performs additional research to answer follow-up questions, using Google ADK, Gemini on Vertex AI, Google Search grounding, Firestore, Cloud Text-to-Speech, and Cloud Run.
This is a hackathon project. Issues and pull requests are welcome; please keep changes focused and include a clear description.
MIT — see LICENSE.
- Google Agent Development Kit (ADK)
- Gemini and Vertex AI
- Google Search grounding
- Google Cloud Text-to-Speech (Chirp 3 HD), Firestore, and Cloud Run