About the Project — JobJam
Tagline: Connect. Practice. Grow — Career Prep, Humanized.
Inspiration
Most career-prep tools lean heavily on AI simulations and miss the human connection that makes practice feel real. I built JobJam to pair people with similar goals for anonymous, real-time video practice, so they can improve interviews, public speaking, and networking without needing existing connections or social capital.
What I Learned
- Integrating real-time video in iOS with Agora and a secure token server.
- Using semantic embeddings (Hugging Face
intfloat/e5-large-v2) for meaning-based matching. - Architecting anonymous, privacy-first flows with Firebase Auth and Firestore.
- Designing a responsive matchmaking pipeline with vector search and thresholding.
How I Built It
Stack
- Frontend: SwiftUI (iOS)
- Auth & DB: Firebase Authentication (Anonymous), Firestore
- AI Matching: Hugging Face Inference API —
intfloat/e5-large-v2 - Video: Agora SDK (token server on Render)
- Assistant: Gemini (JobBot) for in-call prompts and tips
Workflow
- Anonymous sign-in (Firebase Auth).
- User enters a short job description (d).
- The app requests an embedding (\mathbf{v}_d \in \mathbb{R}^n) from the model and stores it in Firestore along with the user’s queue state.
- The matchmaker computes similarity against queued users and selects the best candidate above a threshold (\theta).
- On a match, the client requests an Agora token from the token server and joins the video channel.
- During the call, users can mute, toggle video, leave, and ask JobBot for prompts—without leaving the session.
The Matching Math
To compare two users’ descriptions, JobJam uses cosine similarity:
[ \operatorname{sim}(\mathbf{v}_i,\mathbf{v}_j) = \frac{\mathbf{v}_i \cdot \mathbf{v}_j}{|\mathbf{v}_i|\;|\mathbf{v}_j|} ]
A match occurs when: [ \operatorname{sim}(\mathbf{v}_i,\mathbf{v}_j) \ge \theta, ] where (\theta) is tuned empirically to balance match quality and wait time.
Selection rule (argmax with constraint): [ j^* = \arg\max_{j \in \mathcal{Q}} \operatorname{sim}(\mathbf{v}i,\mathbf{v}_j) \quad \text{s.t.} \quad \operatorname{sim}(\mathbf{v}_i,\mathbf{v}{j^*}) \ge \theta, ] with (\mathcal{Q}) the set of queued users.
Notes on efficiency: For small queues, a linear scan (O(|\mathcal{Q}|)) is sufficient. As traffic grows, approximate nearest-neighbor (ANN) indexing (e.g., HNSW) can reduce latency while preserving match quality.
Challenges I Faced
- Anonymous-first design: Preserving privacy while still enabling meaningful pairing.
- Low-latency matching: Keeping the end-to-end path (embed → compare → connect) snappy on mobile networks.
- Concurrency & state: Coordinating queue updates, race conditions, and call setup without mismatches or “double-pairs.”
- Video + AI coexistence: Ensuring JobBot never blocks the UI or degrades call quality.
Impact & What’s Next
- Impact: Faster, more human practice loops—users can jump in, match, and practice within seconds.
- Next: Add local audio recording + transcription + speaking feedback (e.g., fluency, filler words) as a post-call report; personalize JobBot prompts; and introduce scalable vector search for larger queues.
Note: Assembly/transcription features are planned but not yet implemented in this version.
Log in or sign up for Devpost to join the conversation.