Kairos — Project Story
💡 Inspiration
It started with a number that wouldn't leave us alone: every year, billions of dollars in scholarships and internships go unclaimed.
Not because students aren't qualified — but because the process is brutal. Opportunities are scattered across hundreds of sites, each with eligibility rules buried in fine print: citizenship, GPA floors, field of study, degree level, deadlines. Verifying whether you even qualify takes hours. Then every application demands a freshly tailored résumé, cover letter, and statement of purpose. A single quality application can eat 3–6 hours.
So the students who need help most — first-generation, low-income, and international applicants — are exactly the ones who burn out and give up after two or three tries.
We didn't want to build another scholarship search engine. Search gives you a list and leaves the hard part to you. We wanted an agent that does the work: reads your résumé once, figures out what you qualify for, tells you why, writes your applications, and tells you what to do first. We named it Kairos — Greek for the opportune moment — because the whole point is matching the right student to the right opportunity, at the right time.
🎯 What it does
Upload a résumé once. Kairos autonomously:
- Extracts a structured student profile
- Discovers relevant scholarships and internships
- Screens eligibility — and explains exactly why each one is ✅ Eligible, ⚠️ Borderline, or ❌ Not Eligible
- Ranks the best matches by fit and urgency
- Generates tailored résumé / cover letter / SOP
- Builds a deadline-aware action plan
…all live, with a narrated reasoning stream you can watch.
🏗️ How we built it
Kairos is a blackboard multi-agent system orchestrated with LangGraph. Six specialized agents never talk to each other directly — they read from and write to one shared SessionState, and the graph owns sequencing:
Profile → Discovery → Eligibility → Match → Application → Tracker
Stack: FastAPI + LangGraph/LangChain + Pydantic on the backend; React + Vite on the frontend; Server-Sent Events stream the live narration; a single multi-stage Docker image builds the frontend and serves it from FastAPI, deployed free on Render.
The core design principle: the LLM proposes, deterministic logic disposes. The parts judges scrutinize — eligibility verdicts and match scores — are rule-based and reproducible, not model guesses. The LLM only handles extraction and writing.
The eligibility engine is our differentiator. Each opportunity is adjudicated rule-by-rule, every failing rule tagged hard (disqualifying) or soft (a near-miss). The verdict is a fixed rollup, and GPA uses a tolerance band so a close call becomes Borderline rather than a hard rejection:
$$ \text{verdict} = \begin{cases} \texttt{❌ Not Eligible} & \exists\ \text{hard rule failing} \[4pt] \texttt{⚠️ Borderline} & |\,\text{GPA} - \text{GPA}_{\min}\,| \le \tau \ \ (\tau = 0.2) \[4pt] \texttt{✅ Eligible} & \text{all rules pass} \end{cases} $$
That's what lets Kairos say "❌ Requires US Citizenship. Student = International Student" or "⚠️ GPA 3.7 is just under the 3.8 requirement — everything else passes."
Match scoring is an intentionally transparent weighted formula (each sub-score $\in [0,1]$):
$$ \text{match} = 0.30\,e + 0.25\,a + 0.20\,g + 0.15\,s + 0.10\,u $$
where $e$ = eligibility, $a$ = academic fit, $g$ = goals fit, $s$ = skills fit, $u$ = urgency. Eligibility is weighted highest — there's no point ranking something you can't win.
We also made it provider-agnostic: a single LLM_PROVIDER switch runs Kairos on Groq, Anthropic, Google, or OpenAI — and it runs end-to-end with no API key at all via a deterministic fallback, so the demo can never hang on a network call.
🧗 Challenges we ran into
- Making it feel like an agent, not a pipeline. Our first narration looked like server logs (
[EligibilityAgent] Screening 14...). We rewrote the entire narration layer into a first-person reasoning stream — what it decided and why — without leaking raw chain-of-thought. That single change did more for "perceived autonomy" than any backend work. - Demo determinism. Deadlines kept going stale and "days left" went negative. We pinned a fixed reference date so urgency math is reproducible and never embarrasses us on stage.
- The LLM quietly broke our demo persona. The deterministic parser hard-coded our demo student as international; when we switched to a real LLM, it (correctly) read her as a US citizen — which silently killed our headline citizenship-rejection moment. We learned to make the résumé state things explicitly so every provider agrees.
- A serialization bug hiding behind a working backend. The pipeline generated documents perfectly, but the SSE stream used raw
json.dumpson Pydantic models →Object of type ProfileExtractionResult is not JSON serializable. Onejsonable_encoderfixed it. - A timing bug in the UI. The frontend jumped to the results screen the instant
match_resultappeared — before the document and tracker agents finished. "It doesn't generate documents!" was really "it switched screens too early." We gated on the last node's output instead. - Real-world friction: Windows reserving port 8000 (Hyper-V), a leaked API key auto-revoked by the provider, env-var precedence traps where a stale key silently overrode the right one, and PDF résumés that the upload endpoint couldn't decode. Each one taught us to build graceful fallbacks instead of brittle happy paths.
📚 What we learned
- Determinism is a feature. Putting the verdict logic in rules — not the model — made Kairos defensible, debuggable, and identical with or without an LLM.
- A great demo needs visible rejections, not just matches. The ❌ and ⚠️ moments are what make the intelligence believable.
- The narration is the product surface. Exposing decisions and metrics — not deliberation — is what makes an agent feel autonomous.
- Graceful degradation beats a perfect happy path. Every fallback we added (deterministic mode, seed dataset, encoding fallbacks, provider switch) is a place the demo can't break.
🚀 What's next
- Live opportunity ingestion (real-time web search + dedup against the curated library)
- Real reminder delivery (email / calendar) for deadlines
- One-click "ready to submit" packaging with PDF export
- Broader coverage beyond the current ~100 curated opportunities
- Dynamic orchestration — the agent asking follow-up questions when a profile is ambiguous
Kairos — the right opportunity, at the right time. A week of application work, compressed into three minutes.
Log in or sign up for Devpost to join the conversation.