Merge pull request #89 from tvarol/community-contributions-branch
Week1-Day1 project: Created a resume analyzer for job postings
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1c6700cb-a0b0-4ac2-8fd5-363729284173",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# AI-Powered Resume Analyzer for Job Postings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a2fa4891-b283-44de-aa63-f017eb9b140d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This tool is designed to analyze resumes against specific job postings, offering valuable insights such as:\n",
|
||||
"\n",
|
||||
"- Identification of skill gaps\n",
|
||||
"- Keyword matching between the CV and the job description\n",
|
||||
"- Tailored recommendations for CV improvement\n",
|
||||
"- An alignment score reflecting how well the CV fits the job\n",
|
||||
"- Personalized feedback \n",
|
||||
"- Job market trend insights\n",
|
||||
"\n",
|
||||
"An example of the tool's output can be found [here](https://tvarol.github.io/sideProjects/AILLMAgents/output.html)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8a6a34ea-191f-4c54-9793-a3eb63faab23",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Imports\n",
|
||||
"import os\n",
|
||||
"import io\n",
|
||||
"import time\n",
|
||||
"import requests\n",
|
||||
"import PyPDF2\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"from IPython.display import Markdown, display\n",
|
||||
"from openai import OpenAI\n",
|
||||
"from ipywidgets import Textarea, FileUpload, Button, VBox, HTML"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "04bbe1d3-bacc-400c-aed2-db44699e38f3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load environment variables\n",
|
||||
"load_dotenv(override=True)\n",
|
||||
"api_key = os.getenv('OPENAI_API_KEY')\n",
|
||||
"\n",
|
||||
"# Check the key\n",
|
||||
"if not api_key:\n",
|
||||
" print(\"No API key was found!!!\")\n",
|
||||
"else:\n",
|
||||
" print(\"API key found and looks good so far!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "27bfcee1-58e6-4ff2-9f12-9dc5c1aa5b5b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"openai = OpenAI()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c82e79f2-3139-4520-ac01-a728c11cb8b9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Using a Frontier Model GPT-4o Mini for This Project\n",
|
||||
"\n",
|
||||
"### Types of Prompts\n",
|
||||
"\n",
|
||||
"Models like GPT4o have been trained to receive instructions in a particular way.\n",
|
||||
"\n",
|
||||
"They expect to receive:\n",
|
||||
"\n",
|
||||
"**A system prompt** that tells them what task they are performing and what tone they should use\n",
|
||||
"\n",
|
||||
"**A user prompt** -- the conversation starter that they should reply to"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0da158ad-c3a8-4cef-806f-be0f90852996",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define our system prompt \n",
|
||||
"system_prompt = \"\"\"You are a powerful AI model designed to assist with resume analysis. Your task is to analyze a resume against a given job posting and provide feedback on how well the resume aligns with the job requirements. Your response should include the following: \n",
|
||||
"1) Skill gap identification: Compare the skills listed in the resume with those required in the job posting, highlighting areas where the resume may be lacking or overemphasized.\n",
|
||||
"2) Keyword matching between a CV and a job posting: Match keywords from the job description with the resume, determining how well they align. Provide specific suggestions for missing keywords to add to the CV.\n",
|
||||
"3) Recommendations for CV improvement: Provide actionable suggestions on how to enhance the resume, such as adding missing skills or rephrasing experience to match job requirements.\n",
|
||||
"4) Alignment score: Display a score that represents the degree of alignment between the resume and the job posting.\n",
|
||||
"5) Personalized feedback: Offer tailored advice based on the job posting, guiding the user on how to optimize their CV for the best chances of success.\n",
|
||||
"6) Job market trend insights, provide broader market trends and insights, such as in-demand skills and salary ranges.\n",
|
||||
"Provide responses that are concise, clear, and to the point. Respond in markdown.\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ebdb34b0-85bd-4e36-933a-20c3c42e833b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The job posting and the CV are required to define the user prompt\n",
|
||||
"# The user will input the job posting as text in a box here\n",
|
||||
"# The user will upload the CV in PDF format, from which the text will be extracted\n",
|
||||
"\n",
|
||||
"# You might need to install PyPDF2 via pip if it's not already installed\n",
|
||||
"# !pip install PyPDF2\n",
|
||||
"\n",
|
||||
"# Create widgets - to create a box for the job posting text\n",
|
||||
"job_posting_area = Textarea(\n",
|
||||
" placeholder='Paste the job posting text here...',\n",
|
||||
" description='Job Posting:',\n",
|
||||
" disabled=False,\n",
|
||||
" layout={'width': '800px', 'height': '300px'}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Define file upload for CV\n",
|
||||
"cv_upload = FileUpload(\n",
|
||||
" accept='.pdf', # Only accept PDF files\n",
|
||||
" multiple=False, # Only allow single file selection\n",
|
||||
" description='Upload CV (PDF)'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"status = HTML(value=\"<b>Status:</b> Waiting for inputs...\")\n",
|
||||
"\n",
|
||||
"# Create Submit Buttons\n",
|
||||
"submit_cv_button = Button(description='Submit CV', button_style='success')\n",
|
||||
"submit_job_posting_button = Button(description='Submit Job Posting', button_style='success')\n",
|
||||
"\n",
|
||||
"# Initialize variables to store the data\n",
|
||||
"# This dictionary will hold the text for both the job posting and the CV\n",
|
||||
"# It will be used to define the user_prompt\n",
|
||||
"for_user_prompt = {\n",
|
||||
" 'job_posting': '',\n",
|
||||
" 'cv_text': ''\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Functions\n",
|
||||
"def submit_cv_action(change):\n",
|
||||
"\n",
|
||||
" if not for_user_prompt['cv_text']:\n",
|
||||
" status.value = \"<b>Status:</b> Please upload a CV before submitting.\"\n",
|
||||
" \n",
|
||||
" if cv_upload.value:\n",
|
||||
" # Get the uploaded file\n",
|
||||
" uploaded_file = cv_upload.value[0]\n",
|
||||
" content = io.BytesIO(uploaded_file['content'])\n",
|
||||
" \n",
|
||||
" try:\n",
|
||||
" pdf_reader = PyPDF2.PdfReader(content) \n",
|
||||
" cv_text = \"\"\n",
|
||||
" for page in pdf_reader.pages: \n",
|
||||
" cv_text += page.extract_text() \n",
|
||||
" \n",
|
||||
" # Store CV text in for_user_prompt\n",
|
||||
" for_user_prompt['cv_text'] = cv_text\n",
|
||||
" status.value = \"<b>Status:</b> CV uploaded and processed successfully!\"\n",
|
||||
" except Exception as e:\n",
|
||||
" status.value = f\"<b>Status:</b> Error processing PDF: {str(e)}\"\n",
|
||||
"\n",
|
||||
" time.sleep(0.5) # Short pause between upload and submit messages to display both\n",
|
||||
" \n",
|
||||
" if for_user_prompt['cv_text']:\n",
|
||||
" #print(\"CV Submitted:\")\n",
|
||||
" #print(for_user_prompt['cv_text'])\n",
|
||||
" status.value = \"<b>Status:</b> CV submitted successfully!\"\n",
|
||||
" \n",
|
||||
"def submit_job_posting_action(b):\n",
|
||||
" for_user_prompt['job_posting'] = job_posting_area.value\n",
|
||||
" if for_user_prompt['job_posting']:\n",
|
||||
" #print(\"Job Posting Submitted:\")\n",
|
||||
" #print(for_user_prompt['job_posting'])\n",
|
||||
" status.value = \"<b>Status:</b> Job posting submitted successfully!\"\n",
|
||||
" else:\n",
|
||||
" status.value = \"<b>Status:</b> Please enter a job posting before submitting.\"\n",
|
||||
"\n",
|
||||
"# Attach actions to buttons\n",
|
||||
"submit_cv_button.on_click(submit_cv_action)\n",
|
||||
"submit_job_posting_button.on_click(submit_job_posting_action)\n",
|
||||
"\n",
|
||||
"# Layout\n",
|
||||
"job_posting_box = VBox([job_posting_area, submit_job_posting_button])\n",
|
||||
"cv_buttons = VBox([submit_cv_button])\n",
|
||||
"\n",
|
||||
"# Display all widgets\n",
|
||||
"display(VBox([\n",
|
||||
" HTML(value=\"<h3>Input Job Posting and CV</h3>\"),\n",
|
||||
" job_posting_box, \n",
|
||||
" cv_upload,\n",
|
||||
" cv_buttons,\n",
|
||||
" status\n",
|
||||
"]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "364e42a6-0910-4c7c-8c3c-2ca7d2891cb6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Now define user_prompt using for_user_prompt dictionary\n",
|
||||
"# Clearly label each input to differentiate the job posting and CV\n",
|
||||
"# The model can parse and analyze each section based on these labels\n",
|
||||
"user_prompt = f\"\"\"\n",
|
||||
"Job Posting: \n",
|
||||
"{for_user_prompt['job_posting']}\n",
|
||||
"\n",
|
||||
"CV: \n",
|
||||
"{for_user_prompt['cv_text']}\n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3b51dda0-9a0c-48f4-8ec8-dae32c29da24",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Messages\n",
|
||||
"\n",
|
||||
"The API from OpenAI expects to receive messages in a particular structure.\n",
|
||||
"Many of the other APIs share this structure:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"[\n",
|
||||
" {\"role\": \"system\", \"content\": \"system message goes here\"},\n",
|
||||
" {\"role\": \"user\", \"content\": \"user message goes here\"}\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3262c0b9-d3de-4e4f-b535-a25c0aed5783",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define messages with system_prompt and user_prompt\n",
|
||||
"def messages_for(system_prompt_input, user_prompt_input):\n",
|
||||
" return [\n",
|
||||
" {\"role\": \"system\", \"content\": system_prompt_input},\n",
|
||||
" {\"role\": \"user\", \"content\": user_prompt_input}\n",
|
||||
" ]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2409ac13-0b39-4227-b4d4-b4c0ff009fd7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# And now: call the OpenAI API. \n",
|
||||
"response = openai.chat.completions.create(\n",
|
||||
" model = \"gpt-4o-mini\",\n",
|
||||
" messages = messages_for(system_prompt, user_prompt)\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Response is provided in Markdown and displayed accordingly\n",
|
||||
"display(Markdown(response.choices[0].message.content))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "86ab71cf-bd7e-45f7-9536-0486f349bfbe",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## If you would like to save the response content as a Markdown file, uncomment the following lines\n",
|
||||
"#with open('yourfile.md', 'w') as file:\n",
|
||||
"# file.write(response.choices[0].message.content)\n",
|
||||
"\n",
|
||||
"## You can then run the line below to create output.html which you can open on your browser\n",
|
||||
"#!pandoc yourfile.md -o output.html"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user