Automated multi-agent pipeline for intelligent news analysis and bias detection
Built for the Google ADK Project Competition 2025
MANIS is a fully autonomous 6-stage multi-agent pipeline that collects news from multiple sources via Google News, analyzes sentiment and political bias, scores credibility, and delivers intelligent email digests to your inbox 3 times daily—completely hands-free.
In today's information landscape:
- Millions of articles published daily across numerous sources
- Bias blindness: Readers unaware of how sources frame stories differently
- Time constraints: Reading multiple outlets for balanced perspective takes hours
- Credibility uncertainty: Distinguishing quality journalism from clickbait is difficult
MANIS automates what humans don't have time for:
- Aggregates news from diverse sources (left, center, right)
- Detects sentiment and political bias in framing
- Scores source credibility (0-100 scale)
- Compares how different outlets cover the same topics
- Delivers professional HTML digests 3x daily
Example Output: "Ukraine story covered by 5 sources - Central News (neutral, 85 credibility), National Tribune (negative tone, right-leaning, 72 credibility), Global Wire (neutral, 90 credibility)..."
- ✅ Multi-Source Collection: Google News RSS aggregates from multiple credible outlets
- ✅ Sentiment Analysis: Positive/negative/neutral tone detection (TextBlob)
- ✅ Bias Detection: Identifies left/center/right framing patterns
- ✅ Credibility Scoring: 0-100 reliability scores for 15+ major news sources
- ✅ Entity Extraction: People, organizations, locations (spaCy NER)
- ✅ Comparative Analysis: Shows how different sources frame the same stories
- ✅ Professional HTML Emails: 6-section digest with all insights
- ✅ Automated Scheduling: Runs 3x daily (7:30 AM, 12:30 PM, 4:30 PM) via cron
- ✅ Gmail Integration: SMTP delivery with app password authentication
- ✅ Comprehensive Logging: Timestamped logs for debugging
- ✅ Sequential Agent Pipeline: 6-stage data enrichment workflow
- ✅ Multi-Model Strategy: Optimized Gemini model selection (flash-lite, flash, 2.5-flash)
- ✅ Custom Tools: 8 domain-specific tools (RSS, NLP, email)
- ✅ State Management: Inter-agent communication via
tool_context.state - ✅ Production Ready: Robust error handling, session persistence
┌─────────────────────────────────────────────────────────────────┐
│ MANIS Root Agent (SequentialAgent) │
│ │
│ [1. Collector] ──> Fetches from Google News RSS (11 articles) │
│ │ │
│ ▼ │
│ [2. Preprocessor] ──> Cleans text, extracts entities (spaCy) │
│ │ │
│ ▼ │
│ [3. Fact Checker] ──> Scores credibility, detects bias │
│ │ │
│ ▼ │
│ [4. NLP Analyst] ──> Sentiment analysis (TextBlob), keywords │
│ │ │
│ ▼ │
│ [5. Summarizer] ──> Generates HTML digest (6 sections) │
│ │ │
│ ▼ │
│ [6. Email Delivery] ──> Sends via Gmail SMTP │
│ │
└─────────────────────────────────────────────────────────────────┘
│
▼
tool_context.state
(Shared state dictionary)
Visual Architecture Diagrams:
| Agent | Model | Tools | Purpose |
|---|---|---|---|
| Collector | gemini-2.0-flash-lite | fetch_google_news_rss |
Fetch RSS feeds from 3 topics |
| Preprocessor | gemini-2.0-flash-lite | preprocess_articles |
Clean text, extract entities (spaCy) |
| Fact Checker | gemini-2.0-flash | score_credibility, flag_claims |
Score source reliability |
| NLP Analyst | gemini-2.5-flash | analyze_sentiment, detect_bias, extract_keywords |
Sentiment & bias analysis |
| Summarizer | gemini-2.5-flash | get_analysis_results |
Generate HTML digest |
| Email Delivery | gemini-2.0-flash-lite | send_email_digest |
Send via Gmail SMTP |
Each agent enriches the data:
Raw RSS → [+metadata] → [+entities, claims] → [+credibility scores]
→ [+sentiment, bias] → [+HTML digest] → [Email sent]
State keys: collected_articles → preprocessed_articles → fact_checked_articles
→ nlp_analyzed_articles → daily_digest → email_sent
- macOS (tested on macOS 14+) or Linux
- Python 3.8+ (recommended: 3.12 or 3.13)
- Gmail account for receiving digests
- Google API Key (free tier)
# 1. Clone the repository
git clone https://github.com/yourusername/manis.git
cd manis
# 2. Create virtual environment
python3 -m venv adk-env
source adk-env/bin/activate # On Windows: adk-env\Scripts\activate
# 3. Install dependencies
pip install google-adk[database]==0.3.0
pip install -r requirements.txt
# 4. Download NLP models
python -m spacy download en_core_web_sm
python -c "import nltk; nltk.download('brown'); nltk.download('punkt')"
# 5. Configure environment
cp manis_agent/.env.example manis_agent/.env
# Edit manis_agent/.env with your credentials (see Configuration below)
# 6. Test run
./run_manis.shExpected: Pipeline runs for ~2-3 minutes, email arrives at your inbox!
- Go to Google AI Studio
- Sign in with Google account
- Click "Create API Key"
- Copy the key (starts with
AIza...)
Important: You need a Gmail App Password, not your regular password.
- Go to Google Account Security
- Enable 2-Step Verification (required)
- Search for "App passwords"
- Select: App: Mail, Device: Other (Custom name) → "MANIS"
- Click Generate
- Copy the 16-character password (e.g.,
ciao come stai pass)
Edit manis_agent/.env:
# Google ADK Configuration
GOOGLE_GENAI_USE_VERTEXAI=FALSE
GOOGLE_API_KEY=AIzaSyBxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # ← Your API key
# MANIS Configuration
[email protected] # ← Where to receive digests
MANIS_RUN_MODE=daily
# Gmail SMTP (REQUIRED for email delivery)
[email protected] # ← Your Gmail address
GMAIL_APP_PASSWORD="ciao come stai pass" # ← App password (keep quotes!)Verify configuration:
source manis_agent/.env
echo $GOOGLE_API_KEY # Should show your key
echo $RECIPIENT_EMAIL # Should show your emailMANIS includes unit tests for critical tools to ensure reliability.
pip install pytest pytest-mock pytest-asyncio# Run all unit tests
pytest tests/unit/ -v
# Run specific test file
pytest tests/unit/test_google_news_tools.py -v
# Run with coverage report
pytest tests/unit/ --cov=manis_agent --cov-report=term-missingtests/unit/test_google_news_tools.py::TestFetchGoogleNewsRSS::test_fetch_google_news_success PASSED
tests/unit/test_google_news_tools.py::TestFetchGoogleNewsRSS::test_fetch_google_news_invalid_topic PASSED
tests/unit/test_credibility_scoring.py::TestScoreArticleCredibility::test_score_known_source_reuters PASSED
tests/unit/test_credibility_scoring.py::TestScoreArticleCredibility::test_score_unknown_source_defaults PASSED
tests/unit/test_email_delivery.py::TestSendEmailDigest::test_send_email_success_via_smtp PASSED
============ 15+ tests passed in 3.2s ============
Test Coverage:
- ✅ Google News Collector - RSS fetching, state management, error handling
- ✅ Credibility Scoring - Source scoring, stats calculation, unknown sources
- ✅ Email Delivery - SMTP sending, error handling, state updates
All tests use mocks—no real API calls, internet access, or credentials required.
Run the pipeline manually to test:
# Option 1: Shell script (recommended)
./run_manis.sh
# Option 2: Direct ADK command
adk run manis_agent --input "Run full pipeline"
# Option 3: Interactive web UI
adk web
# Open browser at http://localhost:8000
# Type: "Run daily news collection and analysis"Check results:
- Look for email in your inbox (RECIPIENT_EMAIL)
- Check logs:
tail -f logs/manis_*.log
./setup_cron.shWhat this does:
- Backs up existing crontab
- Installs 3 cron jobs (7:30 AM, 12:30 PM, 4:30 PM)
- Creates
logs/directory - Logs all output to
logs/cron.log
Verify installation:
crontab -lYou should see:
# MANIS - Multi-Agent News Intelligence System
# Runs at 7:30 AM, 12:30 PM, and 4:30 PM daily
30 7 * * * cd '/path/to/project' && '/path/to/run_manis.sh' >> '/path/to/logs/cron.log' 2>&1
30 12 * * * cd '/path/to/project' && '/path/to/run_manis.sh' >> '/path/to/logs/cron.log' 2>&1
30 16 * * * cd '/path/to/project' && '/path/to/run_manis.sh' >> '/path/to/logs/cron.log' 2>&1
Problem: Cron jobs won't run if your Mac is asleep.
Solution 1 - Keep Mac awake (recommended for testing):
caffeinate -s &This prevents sleep while plugged in. To stop:
pkill caffeinateSolution 2 - Prevent sleep permanently:
# Never sleep when plugged into power
sudo pmset -c sleep 0
# To revert
sudo pmset -c sleep 10Solution 3 - Clamshell mode (external display):
- Connect Mac to external monitor
- Connect to power
- Close the lid → Mac stays awake
# Watch cron logs in real-time
tail -f logs/cron.log
# View recent pipeline logs
ls -lt logs/manis_*.log | head -5
# Check latest run
cat $(ls -t logs/manis_*.log | head -1)Edit crontab:
crontab -eCron syntax: minute hour day month weekday command
Examples:
30 7 * * *- 7:30 AM daily0 */4 * * *- Every 4 hours0 9 * * 1-5- 9:00 AM weekdays only
# Edit crontab and delete MANIS lines
crontab -e
# Or remove all cron jobs
crontab -rEach digest includes 6 comprehensive sections:
- Total articles analyzed per run
- Time period coverage
- Sentiment breakdown (positive/negative/neutral %)
- Bias distribution (left/center/right)
Organized by topic (Politics, Technology, Europe):
- Article headline with source
- Credibility score (0-100)
- Sentiment indicator
- Key entities mentioned
- Link to full article
- List of all sources analyzed
- Sentiment comparison across sources
- How left vs. right outlets frame the same stories
- Examples of contrasting language
- High/medium/low credibility distribution
- Average credibility score
- Flagged claims requiring verification
- Top 10 keywords with frequency
- Most mentioned people, organizations, locations
- Emerging topics
- What you need to know (key takeaways)
- Perspective differences to be aware of
- Links for further reading
Solution:
source adk-env/bin/activate
adk --versionScripts use full path adk-env/bin/adk automatically.
Solution:
python -m spacy download en_core_web_sm
# Verify
python -c "import spacy; nlp = spacy.load('en_core_web_sm'); print('✓ OK')"Solution:
python -c "import nltk; nltk.download('brown'); nltk.download('punkt')"Causes:
- Incorrect Gmail app password
- 2-Step Verification not enabled
- Password not quoted in .env
Solutions:
- Verify 2-Step Verification is enabled: Google Account Security
- Regenerate App Password
- Ensure quotes in .env:
GMAIL_APP_PASSWORD="ciao come stai pass" - Check no extra spaces:
[email protected](no spaces)
Cause: Unquoted values with spaces in .env
Solution:
# Correct
GMAIL_APP_PASSWORD="ciao come stai pass"
# Wrong
GMAIL_APP_PASSWORD=ciao come stai passPossible causes:
- Mac was asleep → Run
caffeinate -s & - Cron not installed → Verify:
crontab -l - Permission issues → Check:
ls -la run_manis.sh(should be executable)
Debugging steps:
# 1. Check cron is running (macOS)
sudo launchctl list | grep cron
# 2. Check cron logs
cat logs/cron.log
# 3. Check system logs
log show --predicate 'process == "cron"' --last 1h
# 4. Test manually
./run_manis.sh
# 5. Check permissions
chmod +x run_manis.sh setup_cron.shmacOS specific: Give Terminal Full Disk Access
- System Preferences → Security & Privacy → Privacy
- Full Disk Access → Add Terminal/iTerm
Check:
- Spam folder - MANIS emails might be filtered
- Logs - Check for errors:
grep -i error logs/manis_*.log - State - Verify digest was created (should see HTML in logs)
- Gmail quota - Check you haven't hit daily send limit
manis/
├── manis_agent/ # Main package
│ ├── agent.py # Root SequentialAgent
│ ├── .env # Configuration (gitignored)
│ ├── pictures/ # Architecture diagrams & screenshots
│ └── agents/ # 6-stage pipeline
│ ├── collectors/
│ │ └── google_news_collector/
│ │ ├── agent.py # LlmAgent (flash-lite)
│ │ └── tools.py # fetch_google_news_rss()
│ ├── preprocessor/
│ │ ├── agent.py # LlmAgent (flash-lite)
│ │ └── tools.py # preprocess_articles(), spaCy NER
│ ├── fact_checker/
│ │ ├── agent.py # LlmAgent (flash)
│ │ └── tools.py # score_credibility(), credibility DB
│ ├── nlp_analyst/
│ │ ├── agent.py # LlmAgent (2.5-flash)
│ │ └── tools.py # sentiment, bias, keywords
│ ├── summarizer/
│ │ ├── agent.py # LlmAgent (2.5-flash)
│ │ └── tools.py # HTML digest generation
│ └── delivery/
│ ├── agent.py # LlmAgent (flash-lite)
│ └── tools.py # Gmail SMTP
│
├── run_manis.sh # Pipeline runner script
├── setup_cron.sh # Cron installer
├── requirements.txt # Python dependencies
├── logs/ # Execution logs (gitignored)
├── tests/ # Unit tests
│ └── unit/ # Unit tests for tools
└── README.md # This file
- Runtime: 2-3 minutes
- Articles: 11 articles from 3 topics
- API Requests: ~50-100 (Gemini)
- Cost: FREE (within Gemini free tier: 1,500 requests/day)
- Daily: 11 articles × 3 runs = 33 articles/day
- Monthly: ~1,000 articles analyzed
- Cost: $0 (well within free tier)
- Model selection: flash-lite for simple tasks (cost-optimized), 2.5-flash for complex analysis
- Parallel collection: Use ParallelAgent for multiple sources simultaneously
- Caching: Source credibility scores cached in-memory
| Layer | Technology | Purpose |
|---|---|---|
| Framework | Google ADK 0.3.0 | Agent orchestration |
| LLM | Gemini 2.0/2.5 Flash | Natural language processing |
| NLP | spaCy 3.8+ | Entity extraction, NER |
| Sentiment | TextBlob | Sentiment analysis |
| RSS | feedparser 6.0 | News feed parsing |
| SMTP/Gmail API | Digest delivery | |
| Automation | cron (macOS/Linux) | Scheduled execution |
| Language | Python 3.8+ | Core implementation |
Q: How much does this cost to run? A: Free! Google Gemini API has a generous free tier (1,500 requests/day). MANIS uses ~150 requests/day (3 runs × 50 requests).
Q: Can I change the schedule?
A: Yes! Edit setup_cron.sh before running, or modify crontab directly with crontab -e.
Q: Does this work on Windows/Linux? A: Code is cross-platform. For Windows, replace cron with Task Scheduler. For Linux, same cron setup as macOS.
Q: How do I stop automated runs?
A: crontab -e (delete MANIS lines) or crontab -r (remove all jobs).
Q: What happens if my Mac sleeps during a scheduled run?
A: The job is skipped (won't run when Mac wakes). Use caffeinate -s & to prevent sleep.
Q: How long does each run take? A: 2-3 minutes for the full pipeline (collect → analyze → email).
This project was built for the Google ADK Project Competition 2025. Contributions welcome after the competition!
- Additional news sources (international, topic-specific)
- Advanced fact-checking with external APIs (Google Fact Check, PolitiFact)
- Multi-language support
- Slack/Telegram/Discord delivery
- Web dashboard for historical trends
- Topic modeling with Gensim
- ML-based bias classifier
MIT License - see LICENSE file for details.
- Google ADK Team - For the excellent agent framework
- Kaggle - For hosting the 5 Days Google AI Agents Intensive course
- Gemini API - Powering all LLM intelligence
- spaCy - Entity extraction and NER
- TextBlob - Sentiment analysis
- Google News RSS - Multi-source news aggregation
- Google ADK Documentation: Available at https://google.github.io/adk-docs/
Built with: Google ADK • Gemini AI • Python • spaCy • TextBlob
⭐ Star this repo if you find it useful!


