|
from openai import OpenAI |
|
|
|
import json |
|
from stqdm import stqdm |
|
|
|
SYSTEM_PROMPT = "You are a smart assistant to career advisors at the Harvard Extension School. You will reply with JSON only." |
|
|
|
CV_TEXT_PLACEHOLDER = "<CV_TEXT>" |
|
|
|
SYSTEM_TAILORING = """ |
|
You are a smart assistant to career advisors at the Harvard Extension School. Your take is to rewrite |
|
resumes to be more brief and convincing according to the Resumes and Cover Letters guide. |
|
""" |
|
|
|
TAILORING_PROMPT = """ |
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Your task is to rewrite the given CV. Follow these guidelines: |
|
- Be truthful and objective to the experience listed in the CV |
|
- Be specific rather than general |
|
- Rewrite job highlight items using STAR methodology (but do not mention STAR explicitly) |
|
- Fix spelling and grammar errors |
|
- Writte to express not impress |
|
- Articulate and don't be flowery |
|
- Prefer active voice over passive voice |
|
- Do not include a summary about the candidate |
|
|
|
Improved CV: |
|
""" |
|
|
|
BASICS_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface Basics { |
|
name: string; |
|
email: string; |
|
phone: string; |
|
website: string; |
|
address: string; |
|
} |
|
|
|
Write the basics section according to the Basic schema. On the response, include only the JSON. |
|
""" |
|
|
|
EDUCATION_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface EducationItem { |
|
institution: string; |
|
area: string; |
|
additionalAreas: string[]; |
|
studyType: string; |
|
startDate: string; |
|
endDate: string; |
|
score: string; |
|
location: string; |
|
} |
|
|
|
interface Education { |
|
education: EducationItem[]; |
|
} |
|
|
|
|
|
Write the education section according to the Education schema. On the response, include only the JSON. |
|
""" |
|
|
|
AWARDS_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface AwardItem { |
|
title: string; |
|
date: string; |
|
awarder: string; |
|
summary: string; |
|
} |
|
|
|
interface Awards { |
|
awards: AwardItem[]; |
|
} |
|
|
|
Write the awards section according to the Awards schema. Include only the awards section. On the response, include only the JSON. |
|
""" |
|
|
|
PROJECTS_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface ProjectItem { |
|
name: string; |
|
description: string; |
|
keywords: string[]; |
|
url: string; |
|
} |
|
|
|
interface Projects { |
|
projects: ProjectItem[]; |
|
} |
|
|
|
Write the projects section according to the Projects schema. Include all projects, but only the ones present in the CV. On the response, include only the JSON. |
|
""" |
|
|
|
SKILLS_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
type HardSkills = "Programming Languages" | "Tools" | "Frameworks" | "Computer Proficiency"; |
|
type SoftSkills = "Team Work" | "Communication" | "Leadership" | "Problem Solving" | "Creativity"; |
|
type OtherSkills = string; |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface SkillItem { |
|
name: HardSkills | SoftSkills | OtherSkills; |
|
keywords: string[]; |
|
} |
|
|
|
interface Skills { |
|
skills: SkillItem[]; |
|
} |
|
|
|
Write the skills section according to the Skills schema. Include only up to the top 4 skill names that are present in the CV and related with the education and work experience. On the response, include only the JSON. |
|
""" |
|
|
|
WORK_PROMPT = """ |
|
You are going to write a JSON resume section for an applicant applying for job posts. |
|
|
|
Consider the following CV: |
|
<CV_TEXT> |
|
|
|
Now consider the following TypeScript Interface for the JSON schema: |
|
|
|
interface WorkItem { |
|
company: string; |
|
position: string; |
|
startDate: string; |
|
endDate: string; |
|
location: string; |
|
highlights: string[]; |
|
} |
|
|
|
interface Work { |
|
work: WorkItem[]; |
|
} |
|
|
|
Write a work section for the candidate according to the Work schema. Include only the work experience and not the project experience. For each work experience, provide a company name, position name, start and end date, and bullet point for the highlights. Follow the Harvard Extension School Resume guidelines and phrase the highlights with the STAR methodology |
|
""" |
|
|
|
|
|
def generate_json_resume(cv_text, api_key, model="gpt-3.5-turbo"): |
|
"""Generate a JSON resume from a CV text""" |
|
sections = [] |
|
client = OpenAI(api_key=api_key) |
|
|
|
for prompt in stqdm( |
|
[ |
|
BASICS_PROMPT, |
|
EDUCATION_PROMPT, |
|
AWARDS_PROMPT, |
|
PROJECTS_PROMPT, |
|
SKILLS_PROMPT, |
|
WORK_PROMPT, |
|
], |
|
desc="This may take a while...", |
|
): |
|
filled_prompt = prompt.replace(CV_TEXT_PLACEHOLDER, cv_text) |
|
response = client.chat.completions.create( |
|
model=model, |
|
messages=[ |
|
{"role": "system", "content": SYSTEM_PROMPT}, |
|
{"role": "user", "content": filled_prompt}, |
|
], |
|
) |
|
|
|
try: |
|
answer = response.choices[0].message.content |
|
answer = json.loads(answer) |
|
|
|
if prompt == BASICS_PROMPT and "basics" not in answer: |
|
answer = {"basics": answer} |
|
|
|
sections.append(answer) |
|
except Exception as e: |
|
print(e) |
|
|
|
final_json = {} |
|
for section in sections: |
|
final_json.update(section) |
|
|
|
return final_json |
|
|
|
|
|
def tailor_resume(cv_text, api_key, model="gpt-3.5-turbo"): |
|
filled_prompt = TAILORING_PROMPT.replace("<CV_TEXT>", cv_text) |
|
client = OpenAI(api_key=api_key) |
|
|
|
try: |
|
response = client.chat.completions.create( |
|
model="gpt-3.5-turbo", |
|
messages=[ |
|
{"role": "system", "content": SYSTEM_TAILORING}, |
|
{"role": "user", "content": filled_prompt}, |
|
], |
|
) |
|
|
|
answer = response.choices[0].message.content |
|
return answer |
|
except Exception as e: |
|
print(e) |
|
print("Failed to tailor resume.") |
|
return cv_text |
|
|