Inspiration

Every college student knows the feeling, it's Sunday night, you have five assignments due across four classes, a midterm on Thursday, and you've spent the last two hours staring at your to-do list trying to figure out where to even start. That paralysis isn't laziness. It's decision fatigue, and it's one of the biggest hidden productivity killers in college.

We wanted to eliminate that moment entirely. What if you never had to ask "when should I study for this?" the answer was just already on your calendar?

What it does

AutoSched is an agentic AI scheduler that takes a student's Canvas assignments and automatically builds their entire study week in Google Calendar. Given a set of deadlines and estimated hours, it:

  • Fetches real free/busy data from Google Calendar
  • Prioritizes assignments by urgency and estimated effort
  • Splits long tasks into multiple sessions, never exceeding 3 hours per block
  • Checks for conflicts before every calendar write — hard-enforced, not just suggested
  • Alerts the student if deadlines genuinely cannot be met given available time
  • Discovers campus events (lectures, workshops, free food) that fit in remaining gaps

The core scheduling constraint is simple: for any assignment with estimated time $T$ hours, the agent creates $$\left\lceil \frac{T}{3} \right\rceil \text{ sessions}$$

each of duration

$$d_i = \min(T_{\text{remaining}},\ 3) \text{ hours}$$

placed in the first available conflict-free slot.

How we built it

The stack is a Next.js frontend with a Node.js agentic backend, running against the Google Calendar API.

The agent follows a ReAct pattern — at each step the LLM reasons about what to do, calls a tool, observes the result, and loops:

$$\text{Thought} \;\rightarrow\; \text{Action} \;\rightarrow\; \text{Observation} \;\rightarrow\; \text{Thought} \;\rightarrow\; \cdots$$

We defined six tools the agent can call:

Tool Purpose
get_assignments Load Canvas deadlines and estimated hours
get_free_slots Fetch free time from Google Calendar
get_learned_estimates Pull calibrated time estimates by assignment type
check_conflicts Verify a time slot is open
create_study_block Write the event to Google Calendar
send_alert Notify the student of scheduling issues

The LLM (Llama 3.3 70b via Groq) orchestrates tool calls autonomously until the schedule is complete.

A key design decision was making conflict checking enforcement logic, not agent logiccreate_study_block runs a freeBusy query internally and hard-refuses to write if the slot is taken, regardless of what the LLM decided. The frontend streams agent updates over Server-Sent Events so the UI updates in real time as each block lands on the calendar.

Challenges we ran into

The hardest problem was the LLM ignoring tool results. Early versions would call check_conflicts, receive { conflict: true }, and schedule the block anyway. We solved this by moving the conflict check inside create_study_block itself — the API call fails at the infrastructure level if there's a conflict, so the model has no choice but to retry with a different time.

With no paid subscriptions between us, we were running entirely on Groq's free tier. A multi-agent loop making $n = 15$–$20$ LLM calls per run burns through a daily quota of $L = 100{,}000$ tokens faster than expected:

$$R_{\max} = \left\lfloor \frac{L}{\bar{t} \cdot n} \right\rfloor$$

Mid-hackathon we hit the wall. We made the call to switch to Ollama with Qwen 2.5:7b running locally — slower, but consistent and free. It kept us shipping when the cloud quota ran dry.

OAuth was its own adventure — access tokens expire after $t_{\text{access}} = 3600\text{s}$, and wiring up a refresh token flow that worked seamlessly inside a Next.js API route without leaking credentials took more debugging than expected. Once we understood that the refresh token never expires and the access token always does, the whole model clicked.

Accomplishments that we're proud of

  • Built a multi-agent system where two autonomous agents run independently — one scheduling study blocks around deadlines, another scanning campus events for gaps in the calendar — and neither can corrupt the other's work
  • The conflict enforcement is airtight — fixed events like classes, exams, and gym sessions are never touched, and study blocks never overlap, enforced at the infrastructure level not the prompt level
  • The entire pipeline from "here are my assignments" to "here is your populated week" completes in under 15 seconds
  • The UI streams updates over Server-Sent Events so you watch your calendar fill in real time — every block appears the moment it's written
  • Shipped a working OAuth refresh token flow with zero stored access tokens — credentials rotate automatically on every run

What we learned

  • Multi-agent systems need hard boundaries, not soft ones. Each agent owns its domain — the scheduling agent touches study blocks, the campus events agent touches social events — and neither crosses that line. Prompting alone can't enforce this; the tool implementations have to.
  • The ReAct loop $\text{Thought} \rightarrow \text{Action} \rightarrow \text{Observation}$ is powerful but expensive. Every redundant tool call costs tokens and time. We learned to push logic into the tools themselves so the LLM does less reasoning per iteration.
  • Free tier constraints force good architecture. Hitting Groq's quota mid-hackathon forced us to reduce $n$, batch smarter, and switch to Ollama — and the resulting agent was leaner than the original.
  • LLMs are bad at reliably following negative instructions ("don't schedule here") but very good at responding to hard failures ("this call returned an error, try again"). Design for the latter.

What's next for AutoSched

  • Real Canvas API integration — replace mock assignment data with live pulls so the agent always has the current truth, not a snapshot (this was hindered due to lack of Canvas API Access for the time-being, although we have requested access and hope to make it live)

  • Reinforcement learning on scheduling quality — track whether students actually complete work during scheduled blocks and use that signal to improve future placement. Over time the agent learns that $\alpha_{\text{coding}} > \alpha_{\text{reading}}$ — coding sessions need more buffer than estimated:

$$T_{\text{scheduled}} = \alpha_k \cdot T_{\text{estimated}}, \quad \alpha_k \leftarrow \alpha_k + \eta \cdot \left(\frac{T_{\text{actual}}}{T_{\text{estimated}}} - \alpha_k\right)$$

  • Gmail API integration — scan the inbox for deadline change notifications, meeting invites, announcements etc. and automatically reschedule affected blocks without the student lifting a finger
  • Personality-aware event suggestions — the campus events agent currently filters by keywords. Next step is building a preference profile from past attended events and using cosine similarity to rank new ones:

$$\text{score}(e) = \frac{\vec{p} \cdot \vec{e}}{|\vec{p}||\vec{e}|}$$

where $\vec{p}$ is the student's interest vector and $\vec{e}$ is the event embedding — so introverts stop getting suggested networking mixers

  • More scheduling customization — let students set hard preferences like "never schedule before 10am", "always keep Sundays free", or "max 2 study blocks per day" and have the agent treat these as inviolable constraints

  • Pomodoro-aware block scheduling — instead of scheduling raw hour blocks, the agent breaks each session into customizable Pomodoro intervals. A 3-hour block becomes:

$$n_{\text{pomodoros}} = \left\lfloor \frac{T_{\text{session}}}{t_{\text{work}} + t_{\text{break}}} \right\rfloor$$

where $t_{\text{work}}$ and $t_{\text{break}}$ are set by the student (default $25\text{min}$ work, $5\text{min}$ break). The agent schedules each interval as its own calendar event with a built-in break buffer, so the calendar itself becomes a Pomodoro timer — no separate app needed

Share this project:

Updates