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:

  1. Extracts a structured student profile
  2. Discovers relevant scholarships and internships
  3. Screens eligibility — and explains exactly why each one is ✅ Eligible, ⚠️ Borderline, or ❌ Not Eligible
  4. Ranks the best matches by fit and urgency
  5. Generates tailored résumé / cover letter / SOP
  6. 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.dumps on Pydantic models → Object of type ProfileExtractionResult is not JSON serializable. One jsonable_encoder fixed it.
  • A timing bug in the UI. The frontend jumped to the results screen the instant match_result appeared — 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.

Built With

Share this project:

Updates

Submission history