Skip to content
Learn Agentic AI15 min read0 views

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

Share this article
C

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.