*This is a # I Built a Private AI Agent That Reads My Slack & Gmail Every Morning β Runs 100% on My Laptop
No API keys. No cloud. No monthly bill. Just Hermes3 running locally, summarizing my entire day before I drink my first coffee.
Every morning I opened 47 Slack messages and 23 emails and spent 40 minutes figuring out what actually needed my attention.
The obvious fix? Ask ChatGPT or Claude to summarize them.
But wait β that means sending every private Slack message, every client email, every internal discussion to someone else's server. For a startup, that's a non-starter.
So I built my own. Fully local. Fully private. Free forever.
A Python agent that:
Every morning at 9 AM, this runs automatically and gives me:
π΄ URGENT β ACTION NEEDED
- Production server throwing 503s (#dev) β Ravi needs help
- Client contract must be signed by EOD β priya@client.com
π¬ IMPORTANT DISCUSSIONS
- Q3 roadmap debate ongoing in #product
- Partnership proposal from Zoho needs response
π’ FYI β ANNOUNCEMENTS
- Monday is a public holiday (HR, #general)
ποΈ LOW PRIORITY
- 3 newsletters, 2 LinkedIn notifications
In under 60 seconds. On my machine. No data leaving my laptop.
This is the question worth answering properly.
| ChatGPT / Claude API | Hermes3 (Local) | |
|---|---|---|
| Your data | Sent to their servers | Never leaves your machine |
| Cost | ~$30β50/month at scale | Free forever |
| Customization | Prompt only | Fine-tune on your own data |
| Internet required | Always | Never |
| Speed | Network latency | Pure local speed |
| Restrictions | Content policies | Full control |
Hermes3 is an open-source model by Nous Research, built on LLaMA 3. It's specifically fine-tuned to be excellent at instruction-following and summarization β exactly what this agent needs.
Slack API + Gmail API
β
Python agent
β
Ollama (local runner)
β
Hermes3 (LLM)
β
Smart grouped summary
curl -fsSL https://ollama.com/install.sh | sh
ollama run hermes3
Test it works:
curl http://localhost:11434/api/generate \
-d '{"model": "hermes3", "prompt": "Say hello", "stream": false}'
daily-summary-agent/
βββ slack_reader.py
βββ gmail_reader.py
βββ summarizer.py
βββ main.py
βββ credentials.json β Gmail OAuth file
βββ .env
python
import os
from datetime import datetime
from slack_sdk import WebClient
from dotenv import load_dotenv
load_dotenv()
client = WebClient(token=os.environ["SLACK_BOT_TOKEN"])
def get_today_messages():
all_messages = {}
oldest = datetime.now().replace(hour=0, minute=0, second=0).timestamp()
result = client.conversations_list(types="public_channel,private_channel")
for ch in result["channels"]:
if not ch.get("is_member"):
continue
try:
history = client.conversations_history(
channel=ch["id"], oldest=oldest, limit=100
)
messages = [
m["text"] for m in history["messages"]
if m.get("text") and not m.get("bot_id")
]
if messages:
all_messages[ch["name"]] = messages
except Exception as e:
print(f"Skipping #{ch['name']}: {e}")
return all_messages
python
import os
from datetime import datetime
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
def get_gmail_service():
creds = None
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES)
creds = flow.run_local_server(port=0)
with open("token.json", "w") as f:
f.write(creds.to_json())
return build("gmail", "v1", credentials=creds)
def get_today_emails():
service = get_gmail_service()
today = datetime.now().strftime("%Y/%m/%d")
results = service.users().messages().list(
userId="me", q=f"after:{today}", maxResults=50
).execute()
emails = []
for msg in results.get("messages", []):
data = service.users().messages().get(
userId="me", id=msg["id"], format="full"
).execute()
headers = data["payload"]["headers"]
emails.append({
"subject": next((h["value"] for h in headers if h["name"] == "Subject"), ""),
"from": next((h["value"] for h in headers if h["name"] == "From"), ""),
"snippet": data.get("snippet", "")
})
return emails
This is where the magic happens. The prompt design is everything:
import requests
def summarize(slack_data, email_data):
prompt = """You are a smart executive assistant.
Analyze these messages and group them into exactly 4 sections:
π΄ URGENT β ACTION NEEDED (deadlines, outages, decisions needed today)
π¬ IMPORTANT DISCUSSIONS (ongoing debates, replies needed soon)
π’ FYI β ANNOUNCEMENTS (good to know, no action needed)
ποΈ LOW PRIORITY (newsletters, notifications, can ignore)
For each item write: channel/sender β one line summary.
Be ruthlessly concise. Max 10 words per item.
=== SLACK ===\n"""
for channel, msgs in slack_data.items():
prompt += f"\n#{channel}:\n" + "\n".join(f"- {m}" for m in msgs)
prompt += "\n\n=== EMAILS ===\n"
for e in email_data:
prompt += f"- From: {e['from']} | {e['subject']} | {e['snippet'][:100]}\n"
prompt += "\n\nNow give the grouped summary:"
response = requests.post("http://localhost:11434/api/generate", json={
"model": "hermes3",
"prompt": prompt,
"stream": False
})
return response.json()["response"]
python
from slack_reader import get_today_messages
from gmail_reader import get_today_emails
from summarizer import summarize
from datetime import datetime
def run_agent():
print(f"\nπ€ Daily Brief β {datetime.now().strftime('%B %d, %Y %I:%M %p')}")
print("=" * 50)
print("π₯ Reading Slack...")
slack = get_today_messages()
print("π§ Reading Gmail...")
emails = get_today_emails()
print("π§ Thinking with Hermes3...\n")
summary = summarize(slack, emails)
print(summary)
with open("daily_summary.txt", "w") as f:
f.write(summary)
return summary
if __name__ == "__main__":
run_agent()
crontab -e
0 9 * * 1-5 cd /path/to/daily-summary-agent && python3 main.py >> summary.log 2>&1
Before connecting Slack/Gmail, test Hermes summarization with fake data:
from summarizer import summarize
fake_slack = {
"dev": ["Server is down! 503 errors since 8am", "PR #234 needs review"],
"general": ["Team lunch at 1pm", "Please fill the feedback form"]
}
fake_emails = [
{"from": "boss@company.com", "subject": "Q3 Report Due Today", "snippet": "Submit by EOD"},
{"from": "newsletter@medium.com", "subject": "Top AI stories", "snippet": "Weekly digest..."}
]
print(summarize(fake_slack, fake_emails))
Run it:
python3 test_summarizer.py
No Slack token, no Gmail auth needed. Just Hermes running locally. β
π΄ URGENT β ACTION NEEDED
β’ #dev β Server down, 503s since 8am, fix immediately
β’ boss@company.com β Q3 report due today EOD
π¬ IMPORTANT DISCUSSIONS
β’ #product β Q3 roadmap priorities debated, needs decision
β’ partnerships@zoho.com β Integration call request this week
π’ FYI β ANNOUNCEMENTS
β’ #general β Monday public holiday, no standups
β’ HR β Feedback form deadline Friday
ποΈ LOW PRIORITY
β’ medium.com β Weekly AI newsletter
β’ linkedin.com β 3 connection requests
40 minutes of inbox anxiety β 60 seconds of clarity. Every morning.
Things I'm adding next:
pyttsx3
We're in a moment where running a capable LLM locally is genuinely possible for any developer. Hermes3 on a MacBook M-series is fast, smart, and completely private.
The real unlock isn't just summarization. It's your own AI that knows your context, your team, your language β without sending that context to anyone else.
Every workflow you currently do with a cloud API, ask yourself: does this data need to leave my machine?
Often the answer is no.
Built this over a weekend. Still running every morning. Zero cloud costs.
Have questions or improvements? Drop them in the comments β happy to help you set it up.
Tags: #ai
#python
#productivity
#ollama
#opensource
submission for the Hermes Agent Challenge: Write About Hermes Agent*