From 7c8c7cd31e52ab81799550ce28e3196af4f9175e Mon Sep 17 00:00:00 2001 From: baranylcn Date: Sun, 7 Dec 2025 15:44:41 +0300 Subject: [PATCH] feat: target role option --- levelup/app.py | 64 +++++++++++++++-- levelup/prompts.py | 167 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 192 insertions(+), 39 deletions(-) diff --git a/levelup/app.py b/levelup/app.py index 4493b58..179d0dc 100644 --- a/levelup/app.py +++ b/levelup/app.py @@ -1,6 +1,6 @@ import json import re -from typing import Any, cast +from typing import Any, Optional, cast import google.generativeai as genai import pandas as pd # type: ignore[import-untyped] @@ -41,8 +41,10 @@ def _extract_json_block(raw_text: str) -> str | None: return None -def analyzecv_pdf_withllm(text: str, report_language: str) -> dict[str, Any] | None: - prompt = get_resume_analysis_prompt(text, report_language) +def analyzecv_pdf_withllm( + text: str, report_language: str, target_role: Optional[str] = None +) -> dict[str, Any] | None: + prompt = get_resume_analysis_prompt(text, report_language, target_role) try: response = Model.generate_content(prompt) raw_text = (response.text or "").strip() @@ -334,8 +336,62 @@ def display_analysis_tabs(result: dict[str, Any]) -> None: index=language_options.index("English"), ) + role_options = [ + "No specific target role", + "Data Scientist", + "Data Analyst", + "Data Engineer", + "Machine Learning Engineer", + "AI Engineer", + "MLOps Engineer", + "Deep Learning Engineer", + "Business Intelligence Analyst", + "Data Architect", + "AI Product Manager", + "Software Engineer", + "Backend Engineer", + "Frontend Engineer", + "Full Stack Engineer", + "Mobile Developer", + "DevOps Engineer", + "Cloud Engineer", + "Solution Architect", + "Application Developer", + "QA Engineer", + "Test Automation Engineer", + "Manual Tester", + "Cybersecurity Analyst", + "Security Engineer", + "Security Architect", + "SOC Analyst", + "Penetration Tester", + "Product Manager", + "Product Owner", + "Scrum Master", + "Project Manager", + "System Administrator", + "Network Engineer", + "IT Support Specialist", + "Platform Engineer", + "UX Designer", + "UI Designer", + "Product Designer", + ] + + selected_role_label = st.selectbox( + "Choose a target role for the report", + role_options, + index=0, + ) + + selected_role: Optional[str] + if selected_role_label == "No specific target role": + selected_role = None + else: + selected_role = selected_role_label + if st.button("Analyze Resume"): with st.spinner("Analyzing Resume..."): - result = analyzecv_pdf_withllm(text, selected_language) + result = analyzecv_pdf_withllm(text, selected_language, selected_role) if result: display_analysis_tabs(result) diff --git a/levelup/prompts.py b/levelup/prompts.py index 0ca944e..8bf7bd6 100644 --- a/levelup/prompts.py +++ b/levelup/prompts.py @@ -1,10 +1,83 @@ -def get_resume_analysis_prompt(text: str, report_language: str) -> str: - """Builds the full LLM prompt for resume analysis. +def get_resume_analysis_prompt( + text: str, report_language: str, target_role: str | None = None +) -> str: + """Builds the full LLM prompt for resume analysis, with optional strong target-role conditioning.""" + + notes = { + "domain": "", + "competency": "", + "insights": "", + "recommendations": "", + "missing_skills": "", + "benchmarking": "", + "summary": "", + "strengths": "", + } + + target_role_block = "" + if target_role is not None: + target_role_block = f""" + TARGET ROLE FOCUS: + - The candidate explicitly targets the role: "{target_role}". + - All scores, justifications, insights, and recommendations must be evaluated relative to global expectations for "{target_role}" at the candidate’s apparent seniority level. + - Every numeric score (domain_scores, competency_scores, overall_score, role_suitability scores) must reflect how strong the candidate is for "{target_role}" (not for their past or current role). + - Every justification, strength, observation, insight, recommendation, and benchmarking comment must explicitly refer to the candidate’s fit for "{target_role}". + - You may mention other suitable roles or domains, but always describe them in relation to the candidate’s primary fit for "{target_role}". + + CONSISTENCY REQUIREMENTS FOR SCORES: + - The "overall_score" MUST always represent the candidate’s overall fit for "{target_role}" only. + - In "overall_summary.role_suitability": + - The FIRST item MUST have "role": "{target_role}". + - Its "score" MUST be EXACTLY equal to "overall_score". + - NO other role_suitability entry may have a score higher than "overall_score". + - When evaluating suitability for "{target_role}", heavily weight core role-specific capabilities. + - If the candidate’s experience is primarily in a different domain than "{target_role}" (e.g., Data Science vs Software Engineering), + and there is limited direct evidence of core "{target_role}" responsibilities (such as algorithms/data structures, software design, testing, CI/CD, scalable services), then: + - The "{target_role}" role_suitability score (and thus "overall_score") MUST NOT exceed 70. + - Scores of 80 or above for "{target_role}" require clear, explicit, repeated evidence of strong performance in core "{target_role}" responsibilities, not just adjacent or related work. +""" + notes["domain"] = f""" +- For each domain, interpret the score as: "How strong is this candidate in this domain while working as "{target_role}"?". +- At least one domain must clearly correspond to "{target_role}" (or a closely related domain label), and the justification must reference "{target_role}" explicitly. +""" + notes["competency"] = f""" +- Each competency score must be benchmarked against typical expectations for "{target_role}" at the candidate’s current level. +- Strengths and observations must clearly explain how they support or limit performance as "{target_role}". +""" + notes["insights"] = f""" +- Explicitly classify the candidate’s current fit for "{target_role}" as strong, moderate, or weak, with clear reasoning. +- State whether "{target_role}" is realistic in the short to medium term and under which conditions. +""" + notes["recommendations"] = f""" +- All recommendations must be framed as concrete actions that increase the candidate’s competitiveness for "{target_role}". +""" + notes["missing_skills"] = f""" +- When identifying missing skills, prioritize gaps that are most important for succeeding as "{target_role}" in the global market at this level. +""" + notes["benchmarking"] = f""" +- Compare the candidate specifically with typical professionals working as "{target_role}" at a similar career level, and state whether they are above, at, or below this benchmark. +""" + notes["summary"] = f""" +- Clearly state overall fit for "{target_role}", the main gaps that limit performance in this role, and whether the target is realistic now or requires significant upskilling. +""" + notes["strengths"] = f""" +- Only list the person's strengths that align with the {target_role} role. Do not list strengths that are unrelated to the {target_role} role. +- In all strength-related sections (including "overall_summary.key_strengths"), list strengths that directly support success as "{target_role}" or clearly demonstrate transferable potential toward becoming a stronger "{target_role}" candidate. +- Avoid listing strengths that are only relevant to the candidate’s original role and do not materially contribute to performance as "{target_role}". +- If a skill is strongly domain-specific to the candidate’s original role (e.g., ML/AI/LLM tooling for a Data Scientist), DO NOT list it as a strength for the target role unless it directly supports core responsibilities of "{target_role}" (such as scalable software development, software architecture, performance, reliability, system design, or production engineering). +- Focus on strengths that are clearly relevant to "{target_role}". Do not mention strengths that are not suitable for the target role. +""" + else: + notes["strengths"] = """ +- In all strength-related sections (including "overall_summary.key_strengths"), list strengths that best summarize the candidate’s value across their top likely roles inferred from the CV. +- Focus on strengths that are robust and transferable across multiple plausible career paths (e.g., strong programming fundamentals, problem solving, ownership, communication). +- Avoid overly narrow, niche strengths that are locked into a single, highly specific role unless that role is clearly dominant in the CV. +""" + + primary_role_for_json = ( + target_role if target_role is not None else "Primary Likely Role" + ) - Guides the model to analyze a CV across language, domain fit, - competencies, insights, recommendations, benchmarking, and summary. - Returns the complete formatted prompt string. - """ return f""" You are a globally experienced HR and career evaluation expert with deep cross-industry insight. You will perform an extremely detailed, holistic analysis of the CV provided below. Go beyond numeric scoring — offer professional interpretation, inferences, and personalized guidance based on the profile. @@ -26,44 +99,70 @@ def get_resume_analysis_prompt(text: str, report_language: str) -> str: - Pick one specific platform/standard per item instead of categories. - Exclude anything already present in the CV. Deduplicate closely related items. +{target_role_block} + * 1. LANGUAGE DETECTION Identify the dominant language of the CV. * 2. CAREER DOMAIN MATCHING Identify the top 3 most suitable career domains for this candidate. For each domain: -- Give a score out of 100 -- Justify why the candidate fits that domain (based on experience, skills, education, etc.) -- Optionally mention related roles the candidate could consider +- Give a score out of 100. +- Justify why the candidate fits that domain (based on experience, skills, education, and impact). +- Optionally mention related roles the candidate could consider. +{notes["domain"]} * 3. COMPETENCY EVALUATION -Evaluate the candidate across 10 dimensions. For each, give: -- A score out of 100 -- Specific strengths and examples from the CV -- Observations or red flags (if any) +Evaluate the candidate across 10 dimensions (for example: Core Technical Skills, Tools & Technologies, Problem Solving, Business Impact, Communication, Leadership, Collaboration, Learning Agility, Domain Knowledge, Delivery & Reliability). For each dimension: +- Assign a score out of 100. +- Describe concrete strengths and examples from the CV. +- Note any observations or red flags (e.g., lack of scale, missing ownership, shallow impact). +{notes["competency"]} * 4. STRATEGIC INSIGHTS & INTERPRETATION -- Based on the full CV, what type of roles is this candidate most suited for now? -- What future roles could be targeted with slight improvements? -- Are there signs of underutilized potential? -- Does the profile indicate a specialist or generalist tendency? -- Are there inconsistencies or missing data that should be improved? +Based on the full CV: +- Which types of roles is this candidate most suited for now? +- Which future roles could be realistic with incremental improvements? +- Are there signs of underutilized potential (e.g., skills that are not fully leveraged)? +- Does the profile suggest a specialist or generalist tendency? +- Are there any inconsistencies, gaps, or missing data that should be clarified or improved? +{notes["insights"]} * 5. DEVELOPMENT RECOMMENDATIONS -Provide clear, practical and personalized suggestions for how the candidate can improve: -- Skills, certifications, degrees -- Portfolio, communication, network +Provide clear, practical, and personalized suggestions on how the candidate can strengthen their profile: +- Skills, tools, and methods to learn or deepen. +- Certifications or degrees that would be valuable. +- Portfolio, project, or publication ideas. +- Improvements in communication, stakeholder management, and professional networking. +{notes["recommendations"]} -* 6. MISSING SKILLS & EXPERIENCE MISMATCH -Explicitly identify: -- Up to 5 missing or weakly represented skills relevant to the candidate’s likely target roles. -- Any mismatched experience (e.g., experience unrelated to stated goals or domain misalignment). +* 6. MISSING SKILLS & EXPERIENCE MISMATCH +Explicitly identify: +- Up to 5 missing or weakly represented skills that are relevant to the candidate’s likely or explicit target roles. +- Any mismatched experience (e.g., long periods in unrelated domains, activities that do not support their stated or implied career direction) and how this affects perceived fit. +{notes["missing_skills"]} * 7. COMPARATIVE BENCHMARKING -Compare the candidate’s profile against general industry and global professional standards at a similar career level. Use widely recognized benchmarks or norms (skills depth, experience breadth, impact level). Do not reference specific datasets or sources; base this on general market understanding. Indicate whether the candidate appears above, average, or below such benchmarks. +Compare the candidate’s profile against general industry and global professional standards at a similar career level. Consider: +- Depth and breadth of skills. +- Scope and impact of experience. +- Ownership, autonomy, and complexity of work. +Indicate whether the candidate appears above, at, or below typical benchmarks. +{notes["benchmarking"]} + +* 8. KEY STRENGTHS INSIGHTS (key_strengths) +Provide 3–5 high-level strategic insights about the candidate’s profile, career trajectory, and market positioning. +- Focus on strengths that are most relevant to the candidate’s current and near-future roles. +{notes["strengths"]} -* 8. OVERALL SUMMARY -Provide a concise synthesis of the entire evaluation. Include the total score, key strengths, areas for improvement, and an assessment of overall talent potential. +* 9. OVERALL SUMMARY +Provide a concise synthesis of the entire evaluation. Include: +- A single overall score (0–100) summarizing the profile. +- Key strengths. +- Priority areas for improvement. +- An overall view of talent potential (High / Moderate / Needs Development). +- A short assessment of the candidate’s role suitability across 2–3 key roles. +{notes["summary"]} Absolutely follow the JSON format shown below. Do not add any text, comments, or explanations outside the JSON structure. @@ -81,13 +180,11 @@ def get_resume_analysis_prompt(text: str, report_language: str) -> str: "Recommendation 2" ], "missing_skills": [ - {{"skill": "Python", "priority": "Critical"}}, - {{"skill": "PySpark", "priority": "Nice to have"}}, - {{"skill": "Docker", "priority": "Important"}} + {{"skill": "ExampleSkill1", "priority": "Critical"}}, + {{"skill": "ExampleSkill2", "priority": "Important"}} ], "mismatched_experience": [ - "Example 1", - "Example 2" + "Example of experience that does not fully align with the candidate’s direction" ], "comparative_benchmarking": "Paragraph comparing this candidate against general industry standards. If not applicable, leave empty.", "overall_summary": {{ @@ -96,8 +193,8 @@ def get_resume_analysis_prompt(text: str, report_language: str) -> str: "areas_to_improve": ["Weakness 1", "Weakness 2"], "talent_potential": "High / Moderate / Needs Development", "role_suitability": [ - {{"role": "Data Scientist", "score": 82}}, - {{"role": "Machine Learning Engineer", "score": 78}} + {{"role": "{primary_role_for_json}", "score": 82}}, + {{"role": "Secondary Likely or Related Role", "score": 78}} ] }} }}