From the uDemy course on LLM engineering.
https://www.udemy.com/course/llm-engineering-master-ai-and-large-language-models
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
11 KiB
314 lines
11 KiB
{ |
|
"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" |
|
] |
|
}, |
|
{ |
|
"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 |
|
}
|
|
|