By Sagar Shankaran, Founder of CallSphere
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.
Key takeaways
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.
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
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.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
# 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,
)
The tracker manages a list of applications with status transitions and timeline logging.
# 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()
# 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
# 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.
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.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
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.
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

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Step-by-step build of a working agent with the OpenAI Agents SDK — Agent class, tools, handoffs, tracing — plus an eval pipeline that catches regressions before merge.
Enterprise CIO Guide perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Smolagents lets agents write Python instead of JSON. Why code-as-action reduces tool errors and where the security trade-offs are for production deployments.
SMB Founder Playbook perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Healthcare Practice Use Case perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Modal turns a Python function into autoscaling serverless compute with optional GPU. Deploy a LiveKit Agent with one command and get pay-per-second billing.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.