File size: 18,478 Bytes
1457228
 
90901b6
1457228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e7602c4
1457228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e7602c4
1457228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2288d1d
1457228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff8b322
1457228
 
 
601a0b4
1457228
 
 
90901b6
 
 
 
 
1457228
 
cc21f28
1457228
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
import json
from anthropic import Anthropic
import os

def process_ksas(job_description):
    ksas = _ksas(job_description)
    job_duties, req_quals, pref_quals = ksas['job_duties'], ksas['required_qualifications'], ksas['preferred_qualifications']
    ksas_str = 'Job Duties\n' + '\n'.join(job_duties) + '\nRequired Qualifications\n' + '\n'.join(req_quals) + '\nPreferred Qualifications\n' + '\n'.join(pref_quals)
    return ksas_str

def _ksas(job_description):
    prompt = _ksa_extraction_prompt(job_description)
    system = "You are a job description analyzer. Return only JSON in the format: {\"job_duties\": [\"duty 1\", \"duty 2\"], \"required_qualifications\": [\"qual 1\", \"qual 2\"], \"preferred_qualifications\": [\"pref 1\", \"pref 2\"]}"
    return call_claude(prompt=prompt, system=system)

def _ksa_extraction_prompt(job_description):
    return f"""Extract Key Selection Areas (KSAs) from this job description, maintaining exact original wording. Organize them into these categories:

            Job Duties:
            - List duties/responsibilities as stated in description
            - Include any key activities or deliverables mentioned

            Required Qualifications:
            - List all explicitly stated required qualifications
            - Include any specific metrics (e.g., years of experience)
            - Include required technical skills, expertise, or knowledge areas

            Preferred Qualifications:
            - List only qualifications explicitly marked as "preferred," "desired," or similar
            - Maintain any specific metrics mentioned

            Do not list any implied requirements.
            
            Format the response as JSON:
            {{
                "job_duties": ["duty 1", "duty 2", ...],
                "required_qualifications": ["qual 1", "qual 2", ...],
                "preferred_qualifications": ["pref 1", "pref 2", ...]
            }}

            Job Description:
            {job_description}"""

def process_analysis(resume_data, job_description, ksas, freeform_context):
    raw_analysis_output = _analysis(resume_data, job_description, ksas, freeform_context)
    output = ["Analysis:"]
    for a in raw_analysis_output['analysis']:
        output.append("- " + a)
    output.append("Questions:")
    for q in raw_analysis_output['questions']:
        output.append("- " + q)
    output.append("Recommendation: " + raw_analysis_output['recommendation']['decision'])
    output.append("Rationale: " + raw_analysis_output['recommendation']['rationale'])
    return("\n".join(output))

def _analysis(resume_data, job_description, ksas, freeform_context):
    prompt = _analysis_prompt(resume_data, job_description, ksas, freeform_context)
    system = """You are an expert resume consultant analyzing candidate fit. Return only JSON in the format: {
                    "analysis": ["key insight 1", "key insight 2", ...],
                    "questions": ["question 1", "question 2", ...],
                    "recommendation": {
                        "decision": "Go/No Go",
                        "rationale": "Brief explanation"
                    }
                }"""

    return call_claude(prompt=prompt, system=system)

def _analysis_prompt(resume_data, job_description, ksas, freeform_context):
    return f"""You are an expert resume consultant analyzing a candidate's background for a specific role.

            Target Job Description:
            {job_description}

            Core Requirements (for reference):
            {ksas}

            Candidate's Resume Data:
            {resume_data}

            Additional Context and Background:
            {freeform_context}

            Task:
            Review all materials and provide two things:
            1. Key insights about how the candidate's experience aligns with the role
            2. If applicable, clarifying questions that would help optimize the candidate's application with respect to the core requirements
            3. A clear "Go/No Go" recommendation with brief justification

            Requirements for Analysis:
            - Identify strongest alignment points between experience and job requirements
            - Note any potential gaps or areas needing clarification
            - Consider both explicit and implicit job requirements
            - Focus on relevant experience only
            - Do not make assumptions about undocumented experience

            Requirements for Questions:
            - Ask for specific details that would strengthen the application
            - Focus questions on areas where additional context could help highlight relevant experience
            - Ask about any ambiguous experiences that might be valuable if clarified
            - Only ask questions that would materially improve the application if answered
            - Prioritize questions about experiences mentioned in the materials, not hypothetical experience
            - Questions should be relevant to the core requirements

            Requirements for Recommendation:
            - Provide a clear "Go" or "No Go" recommendation
            - Base recommendation on alignment between requirements and documented experience
            - Consider both minimum and preferred qualifications
            - Include 1-2 sentences explaining the recommendation
            - Be direct and honest while remaining constructive

            Important:
            - Only reference information provided in the materials
            - Questions should be specific and actionable
            - Don't feel like you always need to ask a specific number of questions if you have all of the information you need
            - Focus on details that would be appropriate to include in a resume or cover letter
            - Consider the role's specific requirements when forming questions
            """

def process_core_content(resume_data, job_description, ksas, freeform_context):
    experience_bullets = process_job_experience(resume_data, job_description, ksas, freeform_context)
    technical_skills = process_technical_skills(resume_data, job_description, ksas)
    return experience_bullets, technical_skills

def process_job_experience(resume_data, job_description, ksas, freeform_context):
    output = []
    # Process each job experience
    for job in resume_data['experience']:
        output.append(f"\nOptimizing bullets for: {job['title']}")
        optimized = optimize_job_experience(job, job_description, ksas, freeform_context)
        output.append("\nOptimized bullets:")
        for bullet in optimized['bullets']:
            output.append(f"• {bullet}")
    
    return "\n".join(output)

def optimize_job_experience(job_experience, job_description, ksas, freeform_context):
    prompt = _experience_optimization_prompt(job_experience, job_description, ksas, freeform_context)
    system = "You are an expert resume writer. Return ONLY valid JSON with no explanation, formatting, or additional text. The response must exactly match this format: {\"bullets\": [\"bullet1\", \"bullet2\", ...]}"
    return call_claude(prompt=prompt, system=system)

def _experience_optimization_prompt(job_experience, job_description, ksas, freeform_context):
    return f"""Given this job experience and target job description, create optimized bullet points that best position the candidate for the role. Choose the number of bullets based on the relevance and importance of this experience to the target role.

            Historical Job Title: {job_experience['title']}

            Complete Job Description:
            {job_description}

            Core Requirements (use these as your primary reference for optimization):
            {ksas}

            Available bullet points and variations:
            {_formatted_bullets(job_experience['bullets'])}

            Additional Context (use to enhance understanding of experiences):
            {freeform_context}

            Important:            
            - Read the complete job description for full context
            - When prioritizing content, focus primarily on the Core Requirements listed above
            - Use the Additional Context to better understand experience details. You can use the information from this section as necessary, but ensure use the experience bullets as the primary source of information
            - If there's ambiguity about an experience, refer to the Additional Context for clarification
            - Use strong action verbs
            - Quantify impact where possible
            - Highlight relevant technical skills
            - Focus on achievements over responsibilities
            - Maintain truthfulness - only use information present in the variations
            - The reader should not be able to tell AI wrote the bullet points
            """

def _formatted_bullets(bullets):
    formatted = []
    for bullet in bullets:
        formatted.append(f"Headline: {bullet['headline']}")
        formatted.append("Variations:")
        for variation in bullet['variations']:
            formatted.append(f"- {variation}")
        formatted.append("")  # Empty line between bullet groups
    
    return "\n".join(formatted)

def process_technical_skills(resume_data, job_description, ksas):
    output = []
    raw_tech_skills = resume_data['technical_skills']
    optimized_tech_skills = optimize_technical_skills(raw_tech_skills, job_description, ksas)
    
    output.append("Technical Skills:\n\n")
    for skill_cat in optimized_tech_skills['technical_skills']:
        output.append(skill_cat['category'] + ": " + ', '.join(skill_cat['skills']))
    
    return "\n".join(output)

def optimize_technical_skills(tech_skills, job_description, ksas):
    prompt = _skills_prompt(tech_skills, job_description, ksas)
    system = "You are an expert resume writer. Return ONLY valid JSON with no explanation or additional text. The response must exactly match this format: {\"technical_skills\": [{\"category\": \"Category Name\", \"skills\": [\"Skill 1\", \"Skill 2\"]}, ...]}"
    return call_claude(prompt=prompt, system=system)

def _skills_prompt(all_skills, job_description, ksas):
    return f"""You are an expert resume writer helping optimize a technical skills section for a specific role.

            Target Job Description:
            {job_description}

            Core Requirements (use these as your primary reference for optimization): 
            {ksas}

            Available skills and their current categories:
            {_formatted_skills(all_skills)}

            Task:
            Create an optimized technical skills section that best positions the candidate for this role.

            Requirements:

            1. Read the complete job description for context
            2. When selecting and ordering skills, prioritize those that directly match the Core Requirements
            3. If there's ambiguity about skill importance, defer to the Core Requirements
            4. Order categories from most to least relevant for this specific role
            5. Within each category, order skills from most to least relevant
            6. Create new categories or rename existing ones if it would better align with the job requirements
            7. Include only relevant skills; omit those that don't add value for this role
            8. Keep categories and skills lists concise and impactful
            9. Use standard industry terminology for categories

            Format the response as a JSON list:
            {{"technical_skills": [
                {{"category": "Category Name", "skills": ["Skill 1", "Skill 2", ...]}},
                ...
            ]}}

            Important:
            - Don't assume skill importance based on frequency of appearance
            - Only include skills that are present in the input list
            - Focus on skills that align with job requirements
            - Ensure category names reflect current industry standards
            """

def _formatted_skills(tech_skills):
    cat_str_out = [f"{cat['category']}: " + ', '.join(cat['skills']) for cat in tech_skills]
    return("\n".join(cat_str_out))

def process_summary(job_description, ksas, experience_bullets, technical_skills, resume_context, summary_sample):
    summary = _summary(job_description, ksas, experience_bullets, technical_skills, resume_context, summary_sample)
    summary_str = 'Summary\n' + '\n'.join(summary['summary'])
    return summary_str

def _summary(job_description, ksas, experience_bullets, technical_skills, resume_context, summary_sample):
    prompt = _summary_prompt(job_description, ksas, experience_bullets, technical_skills, resume_context, summary_sample)
    system = "You are an expert resume writer. Return only JSON in the format: {\"summary\": [\"bullet1\", \"bullet2\", ...]}"
    return call_claude(prompt=prompt, system=system)

def _summary_prompt(job_description, ksas, experience_bullets, technical_skills, resume_context, summary_sample):
    return f"""You are an expert resume writer creating a powerful summary section for a specific role.

            Target Job Description:
            {job_description}

            Core Requirements (use as primary reference):
            {ksas}

            Candidate's Optimized Experience:
            {experience_bullets}

            Candidate's Optimized Technical Skills:
            {technical_skills}

            Additional Context:
            {resume_context}

            Writing Style Reference:
            {summary_sample}

            Task:
            Create 5-10 impactful summary bullets that will immediately convince the hiring manager to carefully review this resume.

            Requirements:
            1. Focus heavily on matching the Core Requirements, especially required qualifications
            2. Use only information provided in the experience bullets, technical skills, and resume context
            3. Do not fabricate or embellish experience
            4. Order bullets from most to least relevant to the role
            5. Keep total length under 100 words
            6. Match the style of the provided summary sample while prioritizing content alignment with job requirements
            7. Write for quick scanning - each bullet should build confidence in the candidate's fit
            8. If a job requirement isn't addressed, assume the candidate lacks that experience

            Format:
            Return 5-10 bullets in JSON format: {{"summary": ["bullet1", "bullet2", ...]}}

            Important:
            - Prioritize demonstrating fit for core job requirements
            - Use strong, active language
            - Be specific and quantifiable where possible
            - Focus on achievements and capabilities
            - Maintain truthfulness - only use provided information
            - The reader should not be able to tell AI wrote the summary
            """

def _cover_letter_prompt(job_description, ksas, experience_bullets, technical_skills, resume_context, summary, cover_letter_sample):
    return f"""You are writing a compelling cover letter for a specific role, matching the candidate's authentic writing style.

            Target Job Description:
            {job_description}

            Core Requirements (for reference):
            {ksas}

            Candidate's Qualifications and Experience:
            Summary:
            {summary}

            Experience Details:
            {experience_bullets}

            Technical Skills:
            {technical_skills}

            Additional Background:
            {resume_context}

            Writing Style Reference (match this tone and structure):
            {cover_letter_sample}

            Task:
            Write a one-page cover letter that demonstrates the candidate is an excellent fit for this role.

            Requirements:
            1. Match the candidate's writing style exactly - natural, professional, and personally engaging
            2. Focus on experiences that directly address the job's core requirements
            3. Use only information provided in the resume materials.
            4. Highlight achievements that differentiate the candidate
            5. Keep to one page in length
            6. Maintain first-person perspective throughout

            Important:
            - Do not fabricate or embellish any experiences
            - If a requirement isn't addressed in the provided materials, do not mention it
            - Focus on specific, concrete examples rather than generic statements
            - Match the personal, authentic tone of the sample letter
            - Match the general structure of the sample letter
            - The reader should not be able to tell AI wrote the cover letter

            Format the response as JSON: {{"cover_letter": "Dear Hiring Manager,\\n\\n[cover letter content]\\n\\nSincerely,"}}"""

def _cover_letter(job_description, ksas, experience_bullets, technical_skills, resume_context, summary, cover_letter_sample):
    prompt = _cover_letter_prompt(job_description, ksas, experience_bullets, technical_skills, resume_context, summary, cover_letter_sample)
    system = "You are an expert resume writer crafting cover letters. Return ONLY valid JSON with no explanation or additional text. The response must exactly match this format: {\"cover_letter\": \"Dear Hiring Manager,\\n\\n[letter content]\\n\\nSincerely,\"} Maintain natural writing style and first-person perspective."
    return call_claude(prompt=prompt, system=system)['cover_letter']

def call_claude(prompt, system, api_key=None):
    api_key = os.environ.get("ANTHROPIC_API_KEY")
    if not api_key:
        raise ValueError(
            "No API key found. Please add your Anthropic API key as a secret named 'ANTHROPIC_API_KEY' in your Space's settings."
        )
    client = Anthropic(api_key=api_key)
    response = client.messages.create(
        model="claude-3-7-sonnet-20250219",
            max_tokens=1500,
            temperature=0,
            system=system,
            messages=[{
                "role": "user",
                "content": prompt
            }]
    )
    try:
        result = json.loads(response.content[0].text)
        return result
    except Exception as e:
        print("Raw response:", response.content[0].text)  # Debug line
        raise ValueError(f"Failed to parse Claude's response: {e}")