CareerByte AI: Building an Open-Source AI Career Copilot with Gemini and Prisma
2026-07-05 · 4 min read
Why Another Career Platform?
The job search process is broken into too many disconnected tools: LinkedIn for jobs, resume.io for resume building, Google Sheets for tracking, YouTube for interview prep. Every tool requires you to re-enter the same context. Nothing is connected.
CareerByte AI is a single platform that connects all four stages of the job search into one workflow.
The Four-Stage Pipeline
Job Discovery → ATS Optimization → Application Tracking → Interview Prep
↓ ↓ ↓ ↓
Gemini AI Gemini AI PostgreSQL Gemini AI
(relevance (ATS scoring, (application (questions,
scoring) bullet fixes) pipeline) answers)
Job Discovery with AI Relevance Scoring
Instead of just listing jobs, CareerByte AI scores each job's relevance to the candidate's profile:
async function scoreJobRelevance(
job: JobListing,
candidateProfile: CandidateProfile,
): Promise<JobScore> {
const prompt = `
Analyze the match between this candidate and job.
Candidate Profile:
- Skills: ${candidateProfile.skills.join(", ")}
- Experience: ${candidateProfile.yearsExperience} years as ${candidateProfile.currentRole}
- Target roles: ${candidateProfile.targetRoles.join(", ")}
Job Description:
Title: ${job.title}
Company: ${job.company}
Requirements: ${job.requirements}
Return JSON: {
overallScore: 0-100,
skillMatchScore: 0-100,
experienceMatchScore: 0-100,
matchingSkills: string[],
gapSkills: string[],
fitSummary: string
}
`;
const response = await gemini.generateContent({
contents: [{ role: "user", parts: [{ text: prompt }] }],
generationConfig: { responseMimeType: "application/json" },
});
return JSON.parse(response.response.text());
}
The responseMimeType: "application/json" forces Gemini to return valid JSON — critical for reliable parsing.
ATS Resume Optimization
The ATS optimizer takes a resume and a job description and returns specific, actionable improvements:
async function optimizeResumeForATS(
resumeText: string,
jobDescription: string,
): Promise<ATSOptimizationResult> {
const prompt = `
You are an expert ATS (Applicant Tracking System) specialist.
Job Description:
${jobDescription}
Current Resume:
${resumeText}
Analyze the resume for ATS compatibility against this job. Return JSON:
{
atsScore: 0-100,
missingKeywords: string[], // keywords in JD not in resume
bulletImprovements: [ // specific bullet point rewrites
{ original: string, improved: string, reason: string }
],
formatIssues: string[], // ATS-unfriendly formatting
summaryRewrite: string // improved professional summary
}
`;
const response = await gemini.generateContent({
contents: [{ role: "user", parts: [{ text: prompt }] }],
generationConfig: { responseMimeType: "application/json" },
});
return JSON.parse(response.response.text());
}
Prisma Schema for the Application Pipeline
model Candidate {
id String @id @default(cuid())
email String @unique
profile Json // skills, experience, targetRoles
resume String? // current resume text
createdAt DateTime @default(now())
applications Application[]
savedJobs SavedJob[]
}
model Application {
id String @id @default(cuid())
candidateId String
candidate Candidate @relation(fields: [candidateId], references: [id])
jobTitle String
company String
jobUrl String?
status AppStatus @default(SAVED)
atsScore Int?
matchScore Int?
appliedAt DateTime?
nextAction String?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([candidateId, status])
}
enum AppStatus {
SAVED
APPLIED
PHONE_SCREEN
INTERVIEW
OFFER
REJECTED
WITHDRAWN
}
The status field tracks the full application pipeline. The atsScore and matchScore are populated when the candidate optimizes their resume for that specific job.
Interview Preparation Track
async function generateInterviewPrep(
job: JobListing,
resume: string,
): Promise<InterviewPrep> {
const prompt = `
Generate targeted interview preparation for:
Job: ${job.title} at ${job.company}
Resume highlights: ${extractHighlights(resume)}
Create:
1. 5 technical questions specific to this role and company
2. 3 system design questions for this level
3. 5 behavioral questions (STAR format) based on resume experiences
4. 2 questions to ask the interviewer
For each question: provide model answer, common mistakes, follow-up questions.
Return as JSON.
`;
// ...
}
Why Gemini Over OpenAI?
For this project I used Gemini for three reasons:
- Generous free tier — good for an open-source project where contributors run it locally
- Large context window — job descriptions + full resume fit easily in one prompt
- JSON mode — reliable structured output with
responseMimeType: "application/json"
Open Source
CareerByte AI is Apache 2.0 licensed — production-ready for forks, extensions, and enterprise deployment.