Page 9: Using OpenAI API to analyze resume

Oat Wongsajjathiti
4 min readMar 31, 2023

--

We want to provide our users with some valuable insights and analysis on their resumes.

If they provide their resume, we can provide a general analysis of the resume like typos, tone, completeness of the sections, etc.

If they provide what roles they are applying for, we can find top ATS keywords, hard skills, and soft skills required for that role. We can then map each of these and check for their mentions in the resume.

Lastly, if they provide the job description, we can scan it for the skills required and map them with those included in the resume.

The interface for the analysis would be:

export interface AnalyzeResult {
jobSpecific?: SpecificData;
roleSpecific?: SpecificData;
general?: GeneralData;
}

export interface GeneralData {
// if we are unable to retrieve it
errors?: { [key: string]: string };

message?: string;

spellingErrors?: AnalyzeResultItem;

missingSections?: AnalyzeResultItem;

// does the resume contain necessary sections/info
content?: {
phone?: AnalyzeResultItem;
email?: AnalyzeResultItem;
physicalAddress?: AnalyzeResultItem;
jobTitle?: AnalyzeResultItem;
education?: AnalyzeResultItem;
skills?: AnalyzeResultItem;
experience?: AnalyzeResultItem;
hobbies?: AnalyzeResultItem;
};
}

export interface SpecificData {
// if we are unable to retrieve it
errors?: { [key: string]: string };

jobTitleMatch?: AnalyzeResultItem;
educationMatch?: AnalyzeResultItem;
skillMatch?: AnalyzeResultItem;
experienceMatch?: AnalyzeResultItem;

keywords?: AnalyzeResultItem[];
hardSkills?: AnalyzeResultItem[];
softSkills?: AnalyzeResultItem[];
}

export interface AnalyzeResultItem {
name?: string;
present?: boolean;
message?: string;
}

One thing that was clear very early: we cannot retrieve all this analysis within one prompt to OpenAI. We need the following:

A) For general data, “here is the resume…”

  1. “Give me a summary of how this resume would perform”
  2. “Does the resume has missing sections?”
  3. “Does the resume have any typos?”
  4. etc.

B) For role-specific analysis, “here is the role…”

  1. “Give me the top 5 technical skills for this role. For each of these roles, check for their availability in this resume: {resume}”
  2. “Give me the top 5 soft skills for this role. For each of these roles, check for their availability in this resume: {resume}”
  3. “Give me all the ATS keywords for this role. For each of these roles, check for their availability in this resume: {resume}”
  4. etc. and similar for job-specific analysis

Formatting the data

Boolean

For some data points, we not only want the analysis but also some action items as well.

For instance, if you have typos, click this button to rewrite to fix those typos. Or if you are missing this keyword, click here to add it somewhere in your resume.

For this, we need to change the prompt and add “reply in yes, or no”.

return {
present: response.data === 'yes'
} as AnalyzeResultItem;

Arrays

For this query:

“Give me the top 5 technical skills for this role. For each of these roles, check for their availability in this resume: {resume}”

We are actually making 2 queries.

  1. Top 5 skills for this role
  2. For these 5, fetch their availabilities

In the end, we want to return results in a tabular manner like:

name | is present? | description

React.js | true | “Candidate mentioned of React.js in their Google job experience section”

// Top 5 skills for this role
export const analyzeRoleOrJDForSkills = (
data: AnalyzeRequest,
type: "hard" | "soft"
) => {
const { document, job } = data;
if (!document) throw new Error("Please provide a document to analyze");
if (!job) throw new Error("Please provide a job or industry to analyze");
return `
List the top 5 ${
type === "hard" ? "technical" : "soft"
} skills for a role of ${job.role} in an array of strings format.
`;
};

// check in the resume
export const analyzeResumeForSkills = (
data: AnalyzeRequest,
skillsNeeded: string | null
) => {
const { document, job } = data;
if (!document) throw new Error("Please provide a document to analyze");
if (!job) throw new Error("Please provide a job or industry to analyze");
if (!skillsNeeded) throw new Error("Please provide skills needed");
return `
${document}
--------------------
To what extend does this resume contain each of the following skills.
--------------------
${skillsNeeded}
--------------------

Respond in the format of an JSON format like this:
"[
{
"name": "some technical skill",
"present": true,
"message": "Candidate is skilled in this skill as they have done this and that"
},
{
"name": "some other skill",
"present": false,
"message": "Candidate is not skilled in this skill as they have not done this and that"
}
]"
`;
};

Tying everything together

We want to hit all these calls in parallel. OpenAI API can be flaky and return 500 due to high traffic, so splitting these requests is a good way to chunk them out.

We will fetch 4 different calls from our frontend

A) Get general data: this will split up into 3 different calls to openAI API:

  1. Get summary
  2. Find typos
  3. Find missing sections

B) Get hard skills for a role: this will split up into 2 calls to openAI API:

  1. Get the list of the top 5 hard skills for this role
  2. Check if any of these 5 are listed

(Repeat B for soft skills and keywords.)

Now let’s try it. We will fill in the role of “software engineering manager”

Result:

*chef kisses

Exactly what we needed! Chunking them makes sure that when there are errors in each section, we still give some partial answers.

Next we will explore redesign and authentication, maybe dealing with rate limiting middleware!

--

--

Oat Wongsajjathiti
Oat Wongsajjathiti

Written by Oat Wongsajjathiti

Chemical engineer turned self-taught developer. Artist, dancer and aspiring creator.

No responses yet