All posts

AI Agents

Building an Automated Job Search Agent with Email Notifications

A hands-on build for a production-ready AI agent that searches for jobs daily and emails you the fresh listings — wired together with GPT web search, SMTP email, and a Python scheduler.

Jithin Kumar PalepuJuly 11, 202512 min read

Most job hunts die in the tab graveyard: a dozen open boards, the same filters re-typed every morning, and the best postings buried before you ever scroll to them. This build hands that grind to an agent. It searches for roles, keeps only the recent ones, and drops a clean summary into your inbox on a schedule — so you spend your energy applying, not refreshing.

Architecture Overview

The agent uses a simple, modular design built around decorators. Each capability — search, email, scheduling — is its own decorator you can stack onto any class. Add @search_tool, @email_tool, or @schedule_tool above a class and it inherits that behavior, with nothing tangled together.

@search_tool

Decorator that adds job search capabilities backed by GPT web search.

@email_tool

Decorator that adds email notification features over SMTP.

@schedule_tool

Decorator that adds daily scheduling and a run loop.

Prerequisites & Setup

Before writing any code, make sure you have:

  • Python 3.8+ installed
  • OpenAI API key with GPT-4 access
  • Email account for sending notifications (Gmail recommended)
  • App password for your email account

Install required libraries

pip install openai schedule smtplib email python-dotenv

1. Search Tool Decorator

The search tool decorator uses Python's @ syntax to attach job search capabilities to any class:

def search_tool(api_key):
    """Decorator that adds job search capabilities to any class"""
    def decorator(cls):
        cls._openai_client = OpenAI(api_key=api_key)

        def search_jobs(self, job_title, location, num_results=10):
            query = f"Find {num_results} recent job postings for '{job_title}' in {location}"

            response = self._openai_client.chat.completions.create(
                model="gpt-4",
                messages=[{"role": "user", "content": query}],
                tools=[{"type": "web_search_preview"}],
                max_tokens=1500
            )
            return response.choices[0].message.content

        cls.search_jobs = search_jobs
        return cls
    return decorator

2. Email Tool Decorator

The email tool decorator adds email notification capabilities:

def email_tool(email, password, smtp_server="smtp.gmail.com"):
    """Decorator that adds email capabilities to any class"""
    def decorator(cls):
        cls._email = email
        cls._password = password
        cls._smtp_server = smtp_server

        def send_email(self, to_email, subject, message):
            msg = MIMEText(message)
            msg['From'] = self._email
            msg['To'] = to_email
            msg['Subject'] = subject

            with smtplib.SMTP(self._smtp_server, 587) as server:
                server.starttls()
                server.login(self._email, self._password)
                server.send_message(msg)
            return True

        cls.send_email = send_email
        return cls
    return decorator

3. Schedule Tool Decorator

The schedule tool decorator adds daily scheduling functionality:

def schedule_tool(cls):
    """Decorator that adds scheduling capabilities to any class"""
    def schedule_daily(self, time_str, method_name):
        method = getattr(self, method_name)
        schedule.every().day.at(time_str).do(method)
        print(f"Scheduled {method_name} daily at {time_str}")

    def run_scheduler(self):
        try:
            while True:
                schedule.run_pending()
                time.sleep(60)
        except KeyboardInterrupt:
            print("Scheduler stopped")

    cls.schedule_daily = schedule_daily
    cls.run_scheduler = run_scheduler
    return cls

4. Building the CLI Job Agent

Now we assemble the CLI job agent using the decorators. The agent asks for job details interactively, then searches and emails on demand or on a daily schedule:

# Apply all three decorators to give our agent capabilities
@search_tool(api_key="your-openai-api-key")
@email_tool(email="your@email.com", password="your-app-password")
@schedule_tool
class JobAgent:
    def __init__(self):
        print("Welcome to JobAgent CLI!")
        print("=" * 40)

    def get_job_details(self):
        """Interactive CLI to get job search details"""
        print("\nLet's set up your job search:")

        job_title = input("Enter job title (e.g., 'Data Scientist'): ").strip()
        location = input("Enter location (e.g., 'New York, NY'): ").strip()
        recipient_email = input("Enter your email for notifications: ").strip()

        return job_title, location, recipient_email

    def find_and_send_jobs(self, job_title, location, recipient_email):
        """Main method that finds jobs and emails them"""
        print(f"\nSearching for '{job_title}' jobs in {location}...")
        print("Please wait while I search...")

        # Use the search capability (from @search_tool)
        jobs = self.search_jobs(job_title, location)

        # Format the email
        email_subject = f"Daily Jobs: {job_title} in {location}"
        email_body = f"""
Here are today's fresh job postings:

{jobs}

---
Sent by your JobAgent CLI
Time: {datetime.now().strftime('%Y-%m-%d %H:%M')}
        """.strip()

        # Use the email capability (from @email_tool)
        success = self.send_email(recipient_email, email_subject, email_body)

        if success:
            print("✓ Job search completed successfully!")
            print(f"✓ Email sent to {recipient_email}")
        else:
            print("✗ Something went wrong with the email")

    def start_cli(self):
        """Start the interactive CLI"""
        while True:
            print("\n" + "=" * 40)
            print("JobAgent CLI - What would you like to do?")
            print("1. Search for jobs once")
            print("2. Set up daily job search")
            print("3. Exit")

            choice = input("\nEnter your choice (1-3): ").strip()

            if choice == "1":
                job_title, location, recipient_email = self.get_job_details()
                self.find_and_send_jobs(job_title, location, recipient_email)

            elif choice == "2":
                job_title, location, recipient_email = self.get_job_details()
                schedule_time = input("Enter daily search time (HH:MM format, e.g., 09:00): ").strip()

                # Create a wrapper function for scheduling
                def scheduled_search():
                    self.find_and_send_jobs(job_title, location, recipient_email)

                # Use the schedule capability (from @schedule_tool)
                self.schedule_daily(schedule_time, scheduled_search)

                print(f"✓ Daily job search scheduled for {schedule_time}")
                print("✓ Running initial search...")
                self.find_and_send_jobs(job_title, location, recipient_email)

                print("\nStarting daily scheduler... Press Ctrl+C to stop")
                self.run_scheduler()

            elif choice == "3":
                print("Thanks for using JobAgent CLI!")
                break

            else:
                print("Invalid choice. Please enter 1, 2, or 3.")

5. Running Your CLI Agent

Create a simple main file to start the interactive CLI:

# main.py - Start your CLI job agent
from agent import JobAgent

def main():
    # Create and start your CLI job agent
    agent = JobAgent()
    agent.start_cli()

if __name__ == "__main__":
    main()

CLI output example

$ python main.py

Welcome to JobAgent CLI!
========================================

========================================
JobAgent CLI - What would you like to do?
1. Search for jobs once
2. Set up daily job search
3. Exit

Enter your choice (1-3): 1

Let's set up your job search:
Enter job title (e.g., 'Data Scientist'): Software Engineer
Enter location (e.g., 'New York, NY'): San Francisco, CA
Enter your email for notifications: john@email.com

Searching for 'Software Engineer' jobs in San Francisco, CA...
Please wait while I search...
✓ Job search completed successfully!
✓ Email sent to john@email.com

6. Setup and Installation

To set up and run the CLI job search agent:

# Install required packages
pip install openai schedule

# Update your API keys in the decorators (in your agent.py file)
# Run your interactive CLI agent
python main.py

Key Concepts Explained

Python decorators

  • Use the @ symbol to apply functions to classes
  • Add functionality without modifying original code
  • Stack multiple decorators for combined features
  • Reusable across different agent types

Modular design

  • Each tool is independent and testable
  • Easy to add new capabilities
  • Clean separation of concerns
  • Extensible for other automation tasks

Conclusion

You've built an AI agent using a clean, modular approach with decorators — one that actually solves a real problem instead of demoing a toy.

The decorator pattern here generalizes far beyond job hunting. Swap the search and email logic and the same skeleton powers price-monitoring agents for e-commerce, social media content agents, news summarization agents, stock market analysis agents, and plenty more.

Everything that matters in AI,
straight to your inbox.

Join 12,000+ readers — daily, free, no spam.