Imports
We start by importing a few hand Python libraries. The key ones are openai for accessing GPT-4o-mini, as well as markdown and weasyprint to create a PDF version of the final resume. Note: An OpenAI API key is required for this project, which I imported from a separate Python script.
from IPython.display import display, Markdown
from openai import OpenAI
from top_secret import my_skfrom markdown import markdown
from weasyprint import HTML
Step 1: Input Resume & JD
Next, we will load our input resume into Python as a string and use Python’s input() function to allow us to copy-paste it into any job description when we run the script.
# open and read the markdown file
with open("resumes/resume.md", "r", encoding="utf-8") as file:
resume_string = file.read()# input job description
jd_string = input()
A detail here is that the resume is saved in a markdown format. This is important because it will encourage GPT-4o-mini to generate a new resume in markdown, which we can easily style into a PDF. Note: ChatGPT (or the like) can convert your PDF resume to markdown.
Step 2: Construct Prompt
With our resume and JD imported, we can now craft a prompt to instruct the model to optimize the resume. A pro tip here is to use ChatGPT to write an initial version of this prompt because 1) it’s pretty long, and 2) LLMs tend to write instructions more aligned with the expectations of other LLMs.
After some experimentation, I ended up with the following prompt template, which rewrites the resume and makes additional suggestions for improvement if skill gaps exist.
prompt_template = lambda resume_string, jd_string : f"""
You are a professional resume optimization expert specializing in tailoring \
resumes to specific job descriptions. Your goal is to optimize my resume and \
provide actionable suggestions for improvement to align with the target role.### Guidelines:
1. **Relevance**:
- Prioritize experiences, skills, and achievements **most relevant to the \
job description**.
- Remove or de-emphasize irrelevant details to ensure a **concise** and \
**targeted** resume.
- Limit work experience section to 2-3 most relevant roles
- Limit bullet points under each role to 2-3 most relevant impacts
2. **Action-Driven Results**:
- Use **strong action verbs** and **quantifiable results** (e.g., \
percentages, revenue, efficiency improvements) to highlight impact.
3. **Keyword Optimization**:
- Integrate **keywords** and phrases from the job description naturally to \
optimize for ATS (Applicant Tracking Systems).
4. **Additional Suggestions** *(If Gaps Exist)*:
- If the resume does not fully align with the job description, suggest:
1. **Additional technical or soft skills** that I could add to make my \
profile stronger.
2. **Certifications or courses** I could pursue to bridge the gap.
3. **Project ideas or experiences** that would better align with the role.
5. **Formatting**:
- Output the tailored resume in **clean Markdown format**.
- Include an **"Additional Suggestions"** section at the end with \
actionable improvement recommendations.
---
### Input:
- **My resume**:
{resume_string}
- **The job description**:
{jd_string}
---
### Output:
1. **Tailored Resume**:
- A resume in **Markdown format** that emphasizes relevant experience, \
skills, and achievements.
- Incorporates job description **keywords** to optimize for ATS.
- Uses strong language and is no longer than **one page**.
2. **Additional Suggestions** *(if applicable)*:
- List **skills** that could strengthen alignment with the role.
- Recommend **certifications or courses** to pursue.
- Suggest **specific projects or experiences** to develop.
"""
Step 3: Make API Call
Using the above prompt template, we can dynamically construct a prompt using the input resume and JD and then send it to OpenAI via their API.
# create prompt
prompt = prompt_template(resume_string, jd_string)# setup api client
client = OpenAI(api_key=my_sk)
# make api call
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Expert resume writer"},
{"role": "user", "content": prompt}
],
temperature = 0.7
)
# extract response
response_string = response.choices[0].message.content
Step 4: Save New Resume
Finally, we can extract the optimized resume and suggestions for improvement.
# separate new resume from improvement suggestions
response_list = response_string.split("## Additional Suggestions")
For the resume, we can convert the markdown output to HTML using the markdown library. Then, convert the HTML to a PDF using weasyprint.
# save as PDF
output_pdf_file = "resumes/resume_new.pdf"# Convert Markdown to HTML
html_content = markdown(response_list[0])
# Convert HTML to PDF and save
HTML(string=html_content).write_pdf(output_pdf_file,
stylesheets=['resumes/style.css'])
Here’s what the final result looks like.
For the improvement suggestions, we can print those directly.
display(Markdown(response_list[1]))
Bonus: Build a GUI
While the code above streamlines this process to some extent, we can do better. To improve the usability of this tool, we can create a simple web interface using Gradio.
The final product is shown below. A user can upload a markdown file of their resume and paste it into any job description more straightforwardly. I also added an area where users can edit the new resume before exporting it as a PDF.
The example code is available at the GitHub repository here. Check out the YouTube video to see me talk through the code.
While tailoring one’s resume to specific job descriptions is an effective way to make an application stand out, it can be quite tedious. Here, we walk through the implementation of an AI-powered resume optimization tool using Python and OpenAI’s API.
If you have any questions or want to dive deeper into any of the topics covered, let me know in the comments 🙂
—
y2b.io helped me write this article.