Apply to jobs more effectively using AI

This post is part of a series on Machine Learning.

AI tools like the OpenAI API are revolutionizing how we handle documents.

This guide shows how AI can streamline the job application process, benefiting both applicants and hiring managers by pinpointing where an applicant can be most valuable.

Step 1: Collect job listings

Start by extracting job listings from platforms like LinkedIn. Focus on companies with over 200 openings, particularly in your area of interest. It’s fine if some jobs don’t seem like an exact fit—related listings can still provide valuable data.

Save the jobs to a file called jobs.json. Ensure the job descriptions are formatted properly in JSON, with special attention to characters that need to be escaped.

Step 2: Identify ATS Keywords

Prepare a script to extract crucial keywords from the job descriptions. These keywords will be categorized into ‘tools_technologies’, ‘soft_skills’, ‘hard_skills’, and ‘keywords’ and summarized in JSON files.

Let’s set up the script:

import os
import json
import time

import openai


openai.api_key = "..."
jobs_path = "jobs.json"

Next, let’s define the “extract_keywords()” function. Note that I wrote this before there was a “JSON mode” or “function calling”. Try implementing this on your own as a challenge!

def extract_keywords():

    print("Extracting keywords...")

    keywords_filename = jobs_path.split(".")[0]+"_keywords.json"
    if not os.path.isfile(keywords_filename):
        
        jobs_updated = []
        
        # Load JSON data
        with open(jobs_path, "r") as f:
            jobs = json.load(f)
        
        for job in jobs:
            try:
                print(f"Analyzing {job['title']}...")
                messages = [{"role": "system", "content": "You are an Applicant Tracking System (ATS) tasked with matching applicants to appropriate jobs. Part of your task is to extract ATS keywords from job descriptions so that you can compare these keywords to keywords that appear in the resumes of applicants later on."}, {"role": "user", "content": "Given the job description below, extract the fields in the exact format of the JSON string below. If an item does not appear in the job description, omit the field from the JSON string. Make sure to capture every keyword without including unneccesary associated prose or qualifiers. Do not change the content, but you may correct obvious typos.\n\nOutput example:\n```\n{\n  \"experience\": {\n    \"minimum\": 5,\n    \"maximum\": 7\n  },\n  \"tools_technologies\": [\"Software A\", \"Platform B\", \"Tool C\", ...],\n  \"hard_skills\": [\"Skill A\", \"Skill B\", \"Skill C\", ...],\n  \"soft_skills\": [\"Skill X\", \"Skill Y\", ...],\n  \"education\": [\"Degree A\", \"Degree B with specific criteria\", ...],\n  \"keywords\": [\"Keyword 1\", \"Keyword 2\"]\n}\n```\n\nCompany:\n" + job["company"] + "\n\nJob title:\n" + job["title"] + "\n    \nJob description:\n```\n" + job["description"] + "\n```"}]
                for retry in range(5):
                    try:
                        completion = openai.ChatCompletion.create(model="gpt-4", messages=messages)
                        break
                    except:
                        time.sleep(2**retry)
                response = completion.choices[0].message.content
                
                start_index = response.find('{')
                end_index = response.rfind('}')
            
                if start_index != -1 and end_index != -1:
                    json_str = response[start_index:end_index+1]
                    data = json.loads(json_str)
                    for key, value in data.items():
                        if key not in job:
                            job[key] = value
                else:
                    print("JSON could not be extracted")
            except Exception as e:
                print(e)
            jobs_updated.append(job)
        
        with open(keywords_filename, "w+") as f:
            f.write(json.dumps(jobs_updated, indent=2, ensure_ascii=False))
    
    with open(keywords_filename, "r") as f:
        jobs = json.load(f)
    
    tools_technologies = {}
    hard_skills = {}
    soft_skills = {}
    keywords = {}
    for job in jobs:
        if "tools_technologies" in job.keys() and type(job["tools_technologies"]) is list:
            for keyword in job["tools_technologies"]:
                try:
                    tools_technologies[keyword.lower()] += 1
                except KeyError:
                    tools_technologies[keyword.lower()] = 1
        if "hard_skills" in job.keys() and type(job["hard_skills"]) is list:
            for keyword in job["hard_skills"]:
                try:
                    hard_skills[keyword.lower()] += 1
                except KeyError:
                    hard_skills[keyword.lower()] = 1
        if "soft_skills" in job.keys() and type(job["soft_skills"]) is list:
            for keyword in job["soft_skills"]:
                try:
                    soft_skills[keyword.lower()] += 1
                except KeyError:
                    soft_skills[keyword.lower()] = 1
        if "keywords" in job.keys() and type(job["keywords"]) is list:
            for keyword in job["keywords"]:
                try:
                    keywords[keyword.lower()] += 1
                except KeyError:
                    keywords[keyword.lower()] = 1
    
    sorted_tools_technologies = dict(sorted([(key, value) for (key, value) in tools_technologies.items() if value > 1], key=lambda x: x[1], reverse=True))
    sorted_hard_skills = dict(sorted([(key, value) for (key, value) in hard_skills.items() if value > 1], key=lambda x: x[1], reverse=True))
    sorted_soft_skills = dict(sorted([(key, value) for (key, value) in soft_skills.items() if value > 1], key=lambda x: x[1], reverse=True))
    sorted_keywords = dict(sorted([(key, value) for (key, value) in keywords.items() if value > 1], key=lambda x: x[1], reverse=True))

    with open(jobs_path.split(".")[0]+"_keywords_summary.json", "w+") as f:
        json.dump({"tools_technologies": sorted_tools_technologies, "hard_skills": sorted_hard_skills, "soft_skills": sorted_soft_skills, "keywords": sorted_keywords}, f, indent=2, ensure_ascii=False)

Step 3: Customize Your Resume

Create a base resume in Word format that exports to a PDF in a way that is easy to copy and paste. This will make it easier for you to manipulate, and hiring managers are using AI to look at your resume too! It’s useful to have both formats available since some people prefer one or the other.

Write a prompt similar to the one below. Copy and paste your base resume into ChatGPT along with the ATS keywords collected from the last step.

Update the resume below to have as many of the keywords listed as possible without changing the roles themselves.

Resume:
```
Base resume goes here...
```

Keywords:
```
Keywords from the previous step go here...
```

Obviously this will result in a lot of false information and hallucinations. It is very important to be honest and truthful on your resume, so rewrite the result until it is totally correct. Use the output for inspiration only. Repeat this over and over until your resume has the same language as the job applications you’re applying for.

Step 4: Find the most viable positions for your resume

Use AI to categorize job matches into ‘disqualified’, ‘poor’, ‘average’, or ‘strong’. Focus on the ‘average’ matches and improve your resume based on AI feedback to aim for ‘strong’ matches. Continuously refine your resume and reassess to improve match ratings.

Code:

def get_viability():

    print("Getting viability...")

    if not os.path.isfile(jobs_path.split(".")[0]+"_viability.json"):
        
        jobs_updated = []
        
        # Load JSON data
        with open(jobs_path, "r") as f:
            jobs = json.load(f)
        
        for job in jobs:
            try:
                print(f"Analyzing {job['title']}...")
                resume_ = "Your resume from the previous step. Replace newlines with newline literals (\\n)"
                messages = [{"role": "system", "content": "You are a hiring manager tasked with finding the best matched applicants for the job available."}, {"role": "user", "content": "Given the job description below, rate the candidate based on their resume in the exact format of the JSON string below. Use the ratings as follows:\n\n\"disqualified\": The candidate only has work experience that is completely out of scope, has a completely different degree, or has an inadequate degree (for example the job calls for a PhD and the candidate only has a Bachelors).\n\"poor\": The candidate may have some experience, but it is not enough to meet the requirements, or the candidate has a degree that's one level below requirements with not enough experience to make up for it.\n\"average\": The candidate meets the requirements but there is nothing that stands out about them\n\"strong\": The candidate exceeds the requirements, or meets them and has some special wow factor that makes them stand out.\n\nOutput example:\n```\n{\n  \"rating\": \"[disqualified|poor|average|strong]\",\n  \"rating_reason\": \"text description of why they received the rating\",\n  \"opportunities_for_improvement\": \"Describe things the candidate can do, or items they can add to the resume that would bump them up to the next rating.\"]\n}\n```\n\nCompany:\n" + job["company"] + "\n\nJob title:\n" + job["title"] + "\n    \nJob description:\n```\n" + job["description"] + "\n```\n\nCandidate's resume:\n```\n" + resume_ + "\n```"}]
                for retry in range(5):
                    try:
                        completion = openai.ChatCompletion.create(model="gpt-4", messages=messages)
                        break
                    except:
                        time.sleep(2**retry)
                response = completion.choices[0].message.content
                
                start_index = response.find('{')
                end_index = response.rfind('}')
            
                if start_index != -1 and end_index != -1:
                    json_str = response[start_index:end_index+1]
                    data = json.loads(json_str)
                    for key, value in data.items():
                        if key not in job:
                            job[key] = value
                else:
                    print("JSON could not be extracted")
            except Exception as e:
                print(e)
            jobs_updated.append(job)
        
        with open(jobs_path.split(".")[0]+"_viability.json", "w+") as f:
            f.write(json.dumps(jobs_updated, indent=2, ensure_ascii=False))

Step 5: Apply to jobs with a strong match

Apply to jobs where your resume is a ‘strong’ match. This step, while time-consuming, is straightforward.

Summary

AI breaks down complex challenges into manageable tasks. With structured data, we can deploy multiple AI agents, each with specific roles, to create sophisticated workflows.

The potential of AI in streamlining processes like job applications is immense and exciting!


About the author



Hi, I'm Nathan. Thanks for reading! Keep an eye out for more content being posted soon.


Leave a Reply

Your email address will not be published. Required fields are marked *