OpportunityOS — Our Story
Inspiration
Every student on our team — NeuroNinjas — has lived the same broken workflow. You find a scholarship, an internship, or a hackathon on some portal at 11 PM. You bookmark it. You tell yourself you'll come back and apply. Three weeks later the deadline has passed, the tab is buried under forty others, and the opportunity is gone.
The tools that already exist — scholarship search engines, internship aggregators, LinkedIn job boards — are all optimized for discovery. They are very good at showing you a wall of opportunities. What none of them do is help you actually finish the work: checking whether you're even eligible, figuring out if the requirements overlap with a listing you saw last week under a different name, writing the essay, attaching the right transcript, and hitting submit before the deadline.
We realized the real bottleneck for students isn't information — it's execution. So we asked a different question than most opportunity platforms ask. Not "how do we help students find more opportunities?" but "how do we help students finish the ones they've already found?" That question became OpportunityOS.
What it does
OpportunityOS is an autonomous, multi-agent AI system that takes an opportunity all the way from raw discovery to a submission-ready application — with a human always in the loop for the final decision.
Concretely, it:
- Discovers scholarships, internships, hackathons, fellowships, and research grants continuously, pulling from live RSS feeds and REST connectors (Devpost, Amazon Careers, Google Student Portal, LFX Mentorship, and more).
- Deduplicates listings across portals using a deterministic canonical ID, so the same opportunity posted on three different sites doesn't show up three times:
$$ \text{CanonicalID} = \texttt{"OPP-"} + \text{SHA256}(\text{clean_url} \,|\, \text{normalized_org} \,|\, \text{normalized_title})[:16] $$
- Scores and ranks every opportunity against the student's profile using a deterministic 5-factor match engine:
$$ \text{MatchScore} = 0.35E + 0.25R + 0.15S + 0.15U + 0.10V $$
where $E$ is eligibility, $R$ is requirements/career relevance, $S$ is skill overlap, $U$ is deadline urgency, and $V$ is opportunity value.
- Drafts grounded application content — essay answers and form responses generated by AWS Bedrock, but constrained to only use facts pulled from the student's verified profile and their uploaded document vault (resume, transcript, certificates), with every claim tagged back to its source.
- Stops for a human. No application can move to submission unless a person explicitly approves it. The backend enforces this at the API level —
POST /api/applications/{id}/submitreturns anHTTP 400ifis_approvedisFalse, no matter what the frontend sends. - Shows its work in real time, streaming every agent action to the student through Server-Sent Events and logging execution metrics to Amazon CloudWatch, so the system never feels like a black box.
How we built it
We split OpportunityOS into two halves that had to be designed together from day one: an agent pipeline that does the thinking, and a guardrail layer that makes sure the thinking never turns into an unauthorized action.
Frontend — Next.js 14 with the App Router, styled around a dark glassmorphic theme built on one accent color, Electric Aqua (#42f5e3), so every glowing border and translucent card reinforces the same visual language. Fifteen pages cover the full lifecycle: a landing page, a live operations dashboard, an opportunity explorer, a profile and skill-matrix editor, a document vault with a native drag-and-drop uploader, a Kanban-style pipeline tracker, a grounded essay editor with an approval modal, and a dedicated agent-activity view for watching the SSE stream in real time.
Backend — Python 3.13 on FastAPI, organized as seven cooperating agents behind an orchestrator:
Discovery → Verification → Deduplication → Eligibility & Ranking →
Application Preparation → Human Approval Guardrail → Browser Automation / Submission
Each agent has one job and one job only. The Discovery Agent only ingests raw listings. The Deduplication Agent only computes canonical hashes. The Ranking Agent only computes the 5-factor score. Keeping the responsibilities this narrow made it possible to unit test each stage in isolation and to reason about failures without tracing through the entire pipeline.
Identity and security — Amazon Cognito is the authoritative identity provider; FastAPI independently verifies every Cognito-issued JWT against the /.well-known/jwks.json endpoint rather than trusting the frontend's claim of who's logged in. Locally managed recovery codes use PBKDF2-SHA256, generated from 256-bit CSPRNG entropy, alongside rate limiting on auth endpoints.
Persistence — DynamoDB and S3 for multi-tenant storage and document vaulting, with pre-signed GET URLs so the frontend never touches raw credentials to read a file, and a transparent JSON-based fallback so the whole system runs offline during development without any AWS account at all.
We wrote the scoring formula, the canonical hashing scheme, and the approval guardrail as pure, deterministic functions first, before wiring them into agents — which is what let us pin down their behavior in pytest early and trust them once the AI-driven parts were layered on top.
Challenges we ran into
Making "grounded" mean something. It's easy to have an LLM write a compelling scholarship essay. It's much harder to guarantee it isn't quietly inventing a GPA, an award, or a project the student never mentioned. We had to design the Application Preparation Agent so that every generated answer carries a sources_used field pointing back to specific fields in the student's profile or specific documents in their vault — and to treat any answer that can't cite a source as a failure state, not a stylistic issue.
Deduplication across messy, inconsistent sources. The same internship shows up on a company careers page and a third-party aggregator with different URL parameters, slightly different titles, and inconsistent org name casing. A naive hash of the raw URL fails constantly. We had to build a normalization step — cleaning the URL, standardizing the organization name, standardizing the title — before hashing, and iterate on that normalization against real scraped data until duplicate rates actually dropped.
Making the approval guardrail impossible to bypass. Early on, "requires approval" only lived in the frontend UI — a disabled button. That's not a guardrail, that's a suggestion. We moved the enforcement into the backend itself, so POST /api/applications/{id}/submit checks is_approved server-side and hard-fails with 400 if it's False, regardless of what the client sends. Any autonomous system that touches real-world submissions has to fail closed, not just look safe in the demo.
Balancing autonomy with trust. The whole point of the product is to remove manual grind, but the moment an agent submits something on a student's behalf without them seeing it first, the product becomes a liability instead of a help. We spent real time on the "Needs Review" stage — a state where everything is prepared but nothing is final — so the system's autonomy stops exactly at the point where consequences begin.
Accomplishments that we're proud of
- A working end-to-end pipeline — from live discovery through scored, deduplicated, grounded applications — not a mockup with static data.
- A hard backend security gate for AI-driven submissions, verified with its own dedicated test (
test_approval_guardrail.py). - A deterministic, auditable scoring formula instead of an opaque model score, so a student can see exactly why an opportunity ranked where it did.
- Real Cognito + JWKS verification instead of a toy auth layer.
- 12/12 passing tests across scoring math, deduplication hashing, connector determinism, and Cognito auth, alongside 15 fully compiled and linted Next.js pages.
What we learned
We came in thinking the hard part would be the AI — getting Bedrock to write good essays. It turned out the hard part was everything around the AI: normalizing messy real-world data so deduplication actually works, deciding what "grounded" has to mean precisely enough to test it, and designing guardrails at the layer an attacker (or an overeager agent) can't route around. We also learned to build the deterministic pieces — hashing, scoring, the approval gate — as boring, well-tested functions first, so that when we added the more unpredictable LLM-driven pieces on top, we always had a stable foundation underneath them.
What's next for OpportunityOS
- Live AWS end-to-end deployment and validation — local verification is complete, but production Cognito, DynamoDB, and S3 infrastructure still needs full live testing under
ENVIRONMENT=production. - Expanding connectors beyond the current portal set to cover more scholarship databases, university-specific opportunity boards, and regional programs.
- Smarter browser automation for the final submission step, handling the long tail of non-standard application forms.
- Confidence-tunable autonomy — building out the settings page so students can decide how much the agents pre-fill versus draft from scratch, without ever touching the non-negotiable human-approval gate.
Built With
- agentcore
- amazon-web-services
- amazoncloudwatch
- awsbuildercenter
- bedrock
- docker
- dockercompose
- fastapi
- github
- githubactions
- jwt
- nextjs
- pydantic
- pytest
- python
- react
- restapi
- strands
- typescript
- ug-mdu
- uvicorn
Log in or sign up for Devpost to join the conversation.