Skip to content
All projects

2025 · Case study

Smart Inbox Agent

An email assistant with a human still holding the pen.

An AI-powered professional email assistant that classifies incoming mail, drafts replies, and schedules meetings against the visitor's own Google Calendar — with a persistent human-in-the-loop review pipeline built on LangGraph interrupts.

  • Python
  • LangGraph
  • Streamlit
  • Groq API
  • Google Calendar
  • OAuth 2.0
  • AI Agents
  • NLP

01

The problem

Email automation usually fails in one of two directions: it's too dumb to be useful, or it's autonomous enough to send something embarrassing on your behalf.

Meeting scheduling makes it worse — proposing times requires real calendar state, and getting that wrong wastes more time than doing it manually.

The design goal was an assistant that does all the tedious work up front but never takes an irreversible action without an explicit human yes.

02

Architecture

The agent's workspace is a compiled LangGraph state machine. A Classify node categorises the incoming subject and body into urgent, meeting_request, newsletter or other using a Groq-hosted LLM.

The Draft node branches on that classification. For a meeting_request it queries the signed-in user's Google Calendar FreeBusy data and proposes exactly three available one-hour slots; otherwise it drafts a contextually appropriate reply.

The Approval node pauses execution with LangGraph's interrupt() and waits for human review. Feedback routes into a Refine node that polishes the response or slot list, capped at three revisions so it can't loop forever.

Only after approval — or discard, or hitting the revision cap — does the Action node execute: mock-sending the email or booking the chosen slot directly on the user's calendar.

Authentication is genuinely multi-user. Any visitor signs in with their own Google account and meetings land on their calendar, not on a shared developer account.

Compiled LangGraph state machine
Input
  • Incoming email (subject + body)
Classify
  • urgent / meeting_request / newsletter / other
Draft
  • Draft reply
  • Propose 3 calendar slots (FreeBusy)
Human in the loop
  • Approval node — LangGraph interrupt()
  • Refine (max 3 revisions)
Action
  • Send reply / book the slot

The approval node loops back into refine until the human approves, discards, or the revision cap is hit.

03

Tech stack & why

  • Agent runtime

    LangGraph (compiled StateGraph)

    interrupt() gives first-class human-in-the-loop pausing instead of ad-hoc flags.

  • LLM

    Groq API

    Low-latency inference keeps classification and drafting feeling interactive.

  • Interface

    Streamlit

    Fast path to a reviewable dashboard with a visible node execution log.

  • Calendar

    Google Calendar FreeBusy + Events API

    Slot proposals come from real availability, and booking writes back to the user's own calendar.

  • Auth

    Google OAuth 2.0, confidential web client

    Per-visitor authorisation with the client secret held server-side.

04

Challenges & solutions

  • 1

    OAuth vs Streamlit's rerun model

    PKCE is deliberately disabled (autogenerate_code_verifier=False). Streamlit's stateless rerun execution would otherwise lose or overwrite the code_verifier in session state during the redirect round-trip to Google and back. As a confidential web client holding its secret server-side, the app doesn't need PKCE to be safe.

  • 2

    Preventing infinite refinement

    Human feedback loops can cycle indefinitely. The revision counter caps refinement at three passes and then forces the graph forward to the action node.

  • 3

    Multi-user calendar isolation

    The architecture stores per-visitor credentials in session state so bookings always target the signed-in user's calendar rather than a global developer account.

05

What I'd build next

  • Persist credentials and thread state in a database so a session survives a browser close, not just a rerun.
  • Move from mock-sending to a real Gmail send integration behind the same approval gate.
  • Add learned tone profiles so drafts match how the user actually writes.

Next case study

Image Descriptor