Build a Job Application Tracker Agent: Resume Parsing, Application Status, and Interview Prep
Create an AI agent that parses resumes, tracks job application statuses across companies, researches employers, and generates customized interview preparation questions — a complete job hunting assistant.
Why an AI Job Application Tracker
Job hunting is a multi-step process involving resume tailoring, application tracking, company research, and interview preparation. Most people manage this with spreadsheets, losing context and missing follow-ups. An AI agent can unify all these tasks: it parses your resume, tracks every application's status, researches companies, and generates targeted interview questions — all from a single conversational interface.
This tutorial builds a complete job application tracker agent with resume parsing, a status management system, company research simulation, and interview prep generation.
Project Setup
mkdir job-tracker-agent && cd job-tracker-agent
python -m venv venv && source venv/bin/activate
pip install openai-agents pydantic
mkdir -p src
touch src/__init__.py src/resume_parser.py src/tracker.py
touch src/research.py src/interview_prep.py src/agent.py
Step 1: Build the Resume Parser
The parser extracts structured data from plain-text resume content. In production you would use a PDF parsing library, but the extraction logic remains the same.
# src/resume_parser.py
import re
from pydantic import BaseModel
class ResumeData(BaseModel):
name: str
email: str
skills: list[str]
experience_years: int
recent_titles: list[str]
education: str
def parse_resume(text: str) -> ResumeData:
email_match = re.search(
r"[\w.+-]+@[\w-]+\.[\w.]+", text
)
email = email_match.group(0) if email_match else "unknown"
lines = text.strip().split("\n")
name = lines[0].strip() if lines else "Unknown"
skills_section = []
in_skills = False
for line in lines:
if "skills" in line.lower() and ":" in line:
raw = line.split(":", 1)[1]
skills_section = [
s.strip() for s in raw.split(",")
]
break
year_matches = re.findall(r"(\d{4})\s*[-–]\s*(\d{4}|present)", text.lower())
total_years = 0
for start, end in year_matches:
end_yr = 2026 if end == "present" else int(end)
total_years += end_yr - int(start)
title_patterns = [
"software engineer", "developer", "manager",
"analyst", "designer", "data scientist",
"product manager", "devops engineer",
]
found_titles = []
text_lower = text.lower()
for title in title_patterns:
if title in text_lower:
found_titles.append(title.title())
edu = "Not specified"
for line in lines:
ll = line.lower()
if any(d in ll for d in ["bachelor", "master", "phd", "b.s.", "m.s."]):
edu = line.strip()
break
return ResumeData(
name=name,
email=email,
skills=skills_section or ["Not parsed"],
experience_years=total_years,
recent_titles=found_titles or ["Not parsed"],
education=edu,
)
Step 2: Build the Application Tracker
The tracker manages a list of applications with status transitions and timeline logging.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
# src/tracker.py
from datetime import datetime
from pydantic import BaseModel
class Application(BaseModel):
company: str
role: str
status: str # applied, screening, interview, offer, rejected
date_applied: str
last_updated: str
notes: list[str]
class ApplicationTracker:
VALID_STATUSES = [
"applied", "screening", "interview", "offer", "rejected",
]
def __init__(self):
self.applications: dict[str, Application] = {}
def add_application(
self, company: str, role: str, notes: str = "",
) -> str:
key = f"{company}::{role}".lower()
now = datetime.now().strftime("%Y-%m-%d")
self.applications[key] = Application(
company=company,
role=role,
status="applied",
date_applied=now,
last_updated=now,
notes=[notes] if notes else [],
)
return f"Added: {role} at {company}"
def update_status(
self, company: str, role: str, new_status: str, note: str = "",
) -> str:
key = f"{company}::{role}".lower()
app = self.applications.get(key)
if not app:
return f"No application found for {role} at {company}"
if new_status not in self.VALID_STATUSES:
return f"Invalid status. Use: {self.VALID_STATUSES}"
app.status = new_status
app.last_updated = datetime.now().strftime("%Y-%m-%d")
if note:
app.notes.append(f"[{app.last_updated}] {note}")
return f"Updated {role} at {company} to '{new_status}'"
def get_summary(self) -> str:
if not self.applications:
return "No applications tracked yet."
lines = []
for app in self.applications.values():
lines.append(
f"- {app.role} at {app.company} | "
f"Status: {app.status} | Applied: {app.date_applied}"
)
return "\n".join(lines)
tracker = ApplicationTracker()
Step 3: Company Research and Interview Prep
# src/research.py
COMPANY_DATA = {
"google": {
"industry": "Technology",
"size": "180,000+ employees",
"culture": "Innovation-driven, data-oriented, 20% projects",
"interview_style": "Coding, system design, behavioral (Googleyness)",
"recent_news": "Expanding AI infrastructure and Gemini platform",
},
"stripe": {
"industry": "Fintech",
"size": "8,000+ employees",
"culture": "Writing-heavy culture, high autonomy, remote-friendly",
"interview_style": "Practical coding, API design, debugging exercises",
"recent_news": "Growing enterprise payment solutions globally",
},
}
def research_company(company: str) -> dict:
data = COMPANY_DATA.get(company.lower())
if data:
return data
return {
"industry": "Unknown",
"size": "Unknown",
"culture": "Research needed",
"interview_style": "Research needed",
"recent_news": "No data available",
}
# src/interview_prep.py
def generate_prep_questions(
role: str, company_data: dict, skills: list[str],
) -> list[str]:
questions = [
f"Tell me about a project where you used {skills[0]}."
if skills else "Walk me through your most impactful project.",
f"Why do you want to work in {company_data.get('industry', 'this industry')}?",
"Describe a time you disagreed with a teammate. How did you resolve it?",
f"How do you stay current with developments in {skills[0] if skills else 'your field'}?",
"What is your approach to debugging a production issue under time pressure?",
]
if "system design" in company_data.get("interview_style", "").lower():
questions.append(
"Design a URL shortener that handles 10 million requests per day."
)
if "coding" in company_data.get("interview_style", "").lower():
questions.append(
"Implement a function that finds the longest palindromic substring."
)
return questions
Step 4: Assemble the Agent
# src/agent.py
import asyncio
import json
from agents import Agent, Runner, function_tool
from src.resume_parser import parse_resume
from src.tracker import tracker
from src.research import research_company
from src.interview_prep import generate_prep_questions
@function_tool
def parse_my_resume(resume_text: str) -> str:
"""Parse resume text and extract structured data."""
data = parse_resume(resume_text)
return data.model_dump_json(indent=2)
@function_tool
def add_job_application(
company: str, role: str, notes: str = "",
) -> str:
"""Track a new job application."""
return tracker.add_application(company, role, notes)
@function_tool
def update_application(
company: str, role: str, status: str, note: str = "",
) -> str:
"""Update application status."""
return tracker.update_status(company, role, status, note)
@function_tool
def view_applications() -> str:
"""View all tracked applications."""
return tracker.get_summary()
@function_tool
def prep_for_interview(
company: str, role: str, skills: str,
) -> str:
"""Generate interview prep material."""
company_data = research_company(company)
skill_list = [s.strip() for s in skills.split(",")]
questions = generate_prep_questions(
role, company_data, skill_list,
)
lines = [f"Company Research: {json.dumps(company_data, indent=2)}"]
lines.append("\nPractice Questions:")
for i, q in enumerate(questions, 1):
lines.append(f" {i}. {q}")
return "\n".join(lines)
job_agent = Agent(
name="Job Application Tracker",
instructions="""You are a job application tracking assistant.
Help users manage their job search by parsing resumes, tracking
applications, researching companies, and preparing for interviews.
Always be encouraging and provide actionable next steps.""",
tools=[
parse_my_resume, add_job_application,
update_application, view_applications,
prep_for_interview,
],
)
async def main():
result = await Runner.run(
job_agent,
"I just applied to Google for a Senior Software Engineer role. "
"Track it and help me prepare for the interview. "
"My main skills are Python, system design, and distributed systems.",
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
The agent adds the application to the tracker, researches Google, and generates tailored interview questions based on your skills and Google's known interview style.
FAQ
How would I parse an actual PDF resume instead of plain text?
Use the PyMuPDF or pdfplumber library to extract text from PDF files first. Create a wrapper function that reads the PDF, extracts text content, and passes it to parse_resume(). The structured extraction logic stays the same because it operates on text regardless of the original document format.
Can the agent send me reminders about follow-ups?
Yes. Add a follow_up_date field to the Application model and a get_pending_followups tool that returns applications where the current date exceeds the follow-up date. Run the agent on a daily schedule using cron or a task queue to generate and send reminder emails through an SMTP tool.
How do I make the company research use real data?
Replace the static COMPANY_DATA dictionary with API calls to services like Crunchbase, Glassdoor, or LinkedIn's public company pages. You can also add a web search tool that lets the agent query recent news about the company in real time, providing up-to-date context for interview preparation.
#JobTracker #AIAgent #Python #ResumeParsing #InterviewPrep #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.