A 3-agent CrewAI crew that pulls live weather and air-quality data for any city and turns it into a sarcastic Tanglish meme.
Part of AgentVerse by @explainpannu β an educational series teaching AI agent frameworks through small social-impact projects. One episode = one framework + one real-world problem.
Previous episode: BreezyBuddy β a simple ReAct weather agent.
You: "Chennai"
β
βΌ
ββββββββββββββββββββββββ
β Weather Reporter β β geocodes the city, fetches current weather
ββββββββββββ¬ββββββββββββ
β (lat/lon + summary)
βΌ
ββββββββββββββββββββββββ
β Pollution Analyst β β fetches AQI, PM2.5, PM10, NO2, O3
ββββββββββββ¬ββββββββββββ
β (air-quality briefing)
βΌ
ββββββββββββββββββββββββ
β Tamil Meme Writer β β writes a 4-6 line Tanglish meme
ββββββββββββββββββββββββ
All data comes from Open-Meteo (free, no API key). The only key you need is for the LLM.
- Framework: CrewAI (latest)
- LLM: Bring-your-own-key β Groq / Gemini / OpenAI / Ollama, auto-detected from
.env - Data APIs: Open-Meteo (weather, air-quality, geocoding) + ipinfo.io (location)
- Python: 3.10 β 3.12
- Intent detection β two-layer classifier (regex fast-path + LLM fallback) routes each message to the right path: city query β full crew, casual chitchat β direct LLM, settings command β nudge. Stops "hi" from kicking off a 3-agent run and stops Tamil chitchat ("enne chellam") from fuzzy-matching random villages.
- Safety guardrails β rule-based AQI override fires before the LLM: when
european_aqi >= 100, a hardcoded health alert is returned and the meme writer never gets to joke about hazardous air. - Emotion + consent override β if you sound sick, tired, sad, anxious, or say "leave me alone", personality drops away and the reply is warm, brief, and nudge-free. Health and consent override personality.
- Prompt-injection protection β soft regex sanitiser neutralises common "ignore previous instructions" / "you are now" / "reveal system prompt" phrasings before they reach the model. Defence-in-depth, not a hard boundary.
- 6 personalities Γ 4 languages β Sarcastic, Wholesome, Dad-jokes, Stoic-philosopher, Chaotic-genZ, Aunty-mode Γ English / Tanglish / Tamil / Hindi. Swap voices live from the Settings panel; no restart, no code edits.
- BYOK via Settings panel (or .env) β drop any one of
GROQ_API_KEY,GEMINI_API_KEY,OPENAI_API_KEYinto.env, or paste a key into the frontend Settings panel and it's used immediately (no restart). Ollama works with no key. Preference order: Groq β Gemini β OpenAI β Ollama. - Background AQI polling β the frontend periodically calls
/api/runto refresh the AQI pill so the badge stays current without user interaction. - Push notifications β in-tab Notification API + service worker fire a local alert when the AQI crosses a threshold. No VAPID keys, no server-side push endpoint required.
- City auto-detect β on CLI startup, your city is guessed from your public IP (via ipinfo.io, no key needed). Press Enter to accept, or type any other city to override. Pass a city on the CLI to skip the prompt entirely.
# 1. Clone & enter
git clone <this-repo>
cd social_impact_crew
# 2. Virtual env
python -m venv .venv
# Windows PowerShell:
.\.venv\Scripts\Activate.ps1
# macOS / Linux:
source .venv/bin/activate
# 3. Install
pip install -e .
# 4. Add your LLM key
cp .env.example .env
# then edit .env and paste your GROQ_API_KEY (get one free at https://console.groq.com/keys)
# 5. Run
python -m social_impact_crew.main # asks for a city
python -m social_impact_crew.main Bengaluru # or pass it on the CLIYou'll get verbose CrewAI logs as each agent works, and the final meme printed at the bottom.
You don't need to pick a provider explicitly β just put your key in .env and
the app auto-detects it. All four supported providers:
| Provider | Free? | Env var to set | Default model |
|---|---|---|---|
| Groq | yes | GROQ_API_KEY |
groq/llama-3.3-70b-versatile |
| Gemini | yes | GEMINI_API_KEY |
gemini/gemini-2.5-flash |
| OpenAI | no | OPENAI_API_KEY |
gpt-4o-mini |
| Ollama | yes (local) | (none β just run ollama serve) |
ollama/llama3.2 |
If you want to pin a specific model, set MODEL=<provider>/<model> in .env.
That always wins over auto-detection. Format follows LiteLLM's provider/model convention.
social_impact_crew/
βββ pyproject.toml
βββ .env.example
βββ architecture.md # Mermaid diagram + locked API contract
βββ src/social_impact_crew/
βββ main.py # CLI entry point + IP geolocation + OpenLIT
βββ api.py # FastAPI wrapper β POST /api/run + /api/chat
βββ crew.py # @CrewBase wiring
βββ llm.py # provider auto-detection (BYOK) + runtime overrides
βββ intent.py # two-layer intent classifier (regex + LLM fallback)
βββ personality.py # 6 personalities Γ 4 languages voice blocks
βββ preferences.py # JSON-file user prefs (async lock + atomic write)
βββ safety.py # AQI safety override + prompt-injection sanitiser
βββ config/
β βββ agents.yaml # role / goal / backstory per agent
β βββ tasks.yaml # description / expected_output / context
βββ tools/
βββ custom_tool.py # GeocodeTool, WeatherTool, PollutionTool
# + ContextVar side-channel for API capture
run_api # serves on http://127.0.0.1:8000
# POST http://127.0.0.1:8000/api/run {"city": "Chennai"}
# POST http://127.0.0.1:8000/api/chat {"message": "hi"}
# /api/run returns {city, coords, weather, pollution, aqi_level, meme}The AgentVerse PWA frontend lives in its own repo:
β github.com/sasilab/AgentVerse-Frontend
It's a single static PWA reused across every AgentVerse episode. To connect it to this backend:
- Start this backend:
run_api(serves onhttp://127.0.0.1:8000). - Clone & serve the frontend per its README.
- Open the Settings panel in the PWA and point the API base URL at
http://127.0.0.1:8000. Paste your LLM key, pick a personality and language, and you're done.
The POST /api/run contract is stable across every AgentVerse episode, so the
same frontend works against any episode backend.
openlit.init() is called at the top of main.py and api.py. By default
it ships traces/metrics to http://127.0.0.1:4318 (OTLP). To see them, run any
OTLP collector (Jaeger, Grafana Tempo, OpenLIT UI). No collector running = silent
no-op, nothing breaks.
- Geocoding as a tool (not hardcoded coords) so the meme works for any city you throw at it.
- YAML configs for agents and tasks so non-coders can tweak personalities and task prompts without touching Python.
- Sequential process because the meme literally depends on the upstream data β no point in running these in parallel.
- Tools only where needed β the meme writer has no tools because its job is pure creative writing over upstream context.
MIT. Built for learning β fork it, remix it, make your own episode.