Resume Screening Assistant

Automatically screen resumes against job requirements with objective, structured analysis.

**Important:** This tool assists human recruiters - it does not make hiring decisions. Always have humans review and make final decisions. Monitor for potential bias and validate regularly.

Implementation

1import { relay } from "@relayplane/workflows";
2
3const result = await relay
4 .workflow("hiring-screen")
5
6 // Step 1: Parse resume into structured data
7 .step("parse-resume")
8 .with("openai:gpt-4o")
9 .prompt(`Extract structured data from this resume:
10
11{{resumeText}}
12
13Return JSON:
14{
15 "name": "...",
16 "contact": { "email": "...", "phone": "...", "location": "..." },
17 "summary": "2-3 sentence professional summary",
18 "experience": [
19 {
20 "title": "...",
21 "company": "...",
22 "duration": "months as number",
23 "responsibilities": ["..."],
24 "achievements": ["..."]
25 }
26 ],
27 "skills": {
28 "technical": ["..."],
29 "soft": ["..."],
30 "certifications": ["..."]
31 },
32 "education": [
33 {
34 "degree": "...",
35 "field": "...",
36 "institution": "...",
37 "year": "..."
38 }
39 ],
40 "totalYearsExperience": number
41}
42
43Extract exact information, don't infer.`)
44
45 // Step 2: Match against job requirements
46 .step("match-requirements")
47 .with("anthropic:claude-3.5-sonnet")
48 .depends("parse-resume")
49 .prompt(`Match candidate against job requirements:
50
51Parsed Resume: {{parse-resume.output}}
52
53Job Requirements:
54{{jobRequirements}}
55
56For each requirement, assess:
57- Meets: Yes/Partial/No
58- Evidence: specific resume content supporting assessment
59- Gap: what's missing (if any)
60
61Categories:
62- Required Technical Skills
63- Years of Experience
64- Education
65- Preferred/Nice-to-have Skills
66- Industry Experience
67
68Be objective and evidence-based.`)
69
70 // Step 3: Score candidate
71 .step("score-candidate")
72 .with("openai:gpt-4o")
73 .depends("parse-resume", "match-requirements")
74 .prompt(`Calculate match score:
75
76Resume: {{parse-resume.output}}
77Requirements Match: {{match-requirements.output}}
78
79Scoring weights:
80- Required skills: 40%
81- Experience level: 25%
82- Industry relevance: 15%
83- Education: 10%
84- Nice-to-haves: 10%
85
86Return:
87- Overall score (0-100)
88- Breakdown by category
89- Confidence level (high/medium/low)
90
91Thresholds:
92- 80+: Strong match
93- 60-79: Good match
94- 40-59: Partial match
95- Below 40: Not a match`)
96
97 // Step 4: Generate screening summary
98 .step("screening-summary")
99 .with("anthropic:claude-3.5-sonnet")
100 .depends("parse-resume", "match-requirements", "score-candidate")
101 .prompt(`Generate screening summary for recruiter:
102
103Candidate: {{parse-resume.output}}
104Requirements: {{match-requirements.output}}
105Score: {{score-candidate.output}}
106
107Format:
108## Candidate Overview
109- Name and current role
110- Total experience
111- Match score and recommendation
112
113## Strengths
114- Top 3-5 reasons this candidate fits
115- Specific achievements relevant to role
116
117## Concerns/Gaps
118- Missing requirements
119- Areas to probe in interview
120
121## Interview Questions
122- 3-5 targeted questions based on gaps or verification needs
123
124## Recommendation
125- ADVANCE / HOLD / PASS
126- Brief justification
127
128Objective, professional tone.
129Focus on job-relevant factors only.`)
130
131 .run({
132 resumeText: candidateResume,
133 jobRequirements: {
134 title: "Senior Software Engineer",
135 required: [
136 "5+ years software development experience",
137 "Proficient in TypeScript and React",
138 "Experience with PostgreSQL or similar RDBMS",
139 "Background in CI/CD and DevOps practices",
140 ],
141 preferred: [
142 "Experience with AI/ML integration",
143 "Open source contributions",
144 "Previous startup experience",
145 ],
146 education: "Bachelor's in CS or equivalent experience",
147 },
148 });
149
150// Save to ATS
151await updateATS({
152 candidateId: candidate.id,
153 screeningScore: JSON.parse(result.steps["score-candidate"].output).score,
154 screeningSummary: result.steps["screening-summary"].output,
155 status: result.steps["screening-summary"].output.includes("ADVANCE")
156 ? "Phone Screen"
157 : "Reviewed",
158});

Batch Resume Processing

1// Process application queue
2import { relay } from "@relayplane/workflows";
3
4async function processApplicationQueue(jobId: string) {
5 const applications = await getNewApplications(jobId);
6 const jobRequirements = await getJobRequirements(jobId);
7
8 const results = [];
9
10 for (const app of applications) {
11 const screening = await relay
12 .workflow("hiring-screen")
13 .run({
14 resumeText: app.resume,
15 jobRequirements,
16 });
17
18 const score = JSON.parse(screening.steps["score-candidate"].output);
19
20 results.push({
21 candidateId: app.candidateId,
22 score: score.score,
23 recommendation: score.recommendation,
24 summary: screening.steps["screening-summary"].output,
25 });
26 }
27
28 // Sort by score and return top candidates
29 const ranked = results.sort((a, b) => b.score - a.score);
30
31 // Auto-advance top scorers
32 const autoAdvance = ranked.filter(r => r.score >= 80);
33 for (const candidate of autoAdvance) {
34 await schedulePhoneScreen(candidate.candidateId);
35 }
36
37 return ranked;
38}

ATS Webhook Integration

1// Greenhouse/Lever webhook handler
2app.post("/webhooks/greenhouse", async (req, res) => {
3 const { action, payload } = req.body;
4
5 if (action === "candidate.applied") {
6 const { candidate, application, job } = payload;
7
8 // Queue for screening
9 await queue.add("screen-resume", {
10 candidateId: candidate.id,
11 resumeUrl: application.resume.url,
12 jobId: job.id,
13 });
14 }
15
16 res.sendStatus(200);
17});
18
19// Queue processor
20queue.process("screen-resume", async (job) => {
21 const resume = await downloadResume(job.data.resumeUrl);
22 const requirements = await getJobRequirements(job.data.jobId);
23
24 const result = await relay
25 .workflow("hiring-screen")
26 .run({
27 resumeText: resume,
28 jobRequirements: requirements,
29 });
30
31 // Update Greenhouse
32 await greenhouse.candidates.addNote(job.data.candidateId, {
33 body: result.steps["screening-summary"].output,
34 user_id: BOT_USER_ID,
35 });
36});

Sample Output

1## Candidate Overview
2**Sarah Chen** - Staff Software Engineer at DataCorp
3**Experience:** 8 years in software development
4**Match Score:** 87/100 (Strong Match)
5**Recommendation:** ADVANCE to phone screen
6
7## Strengths
81. **Strong TypeScript/React experience** - 5 years building production React apps at scale (DataCorp processes 1M+ daily transactions)
92. **Relevant PostgreSQL expertise** - Led database optimization project reducing query times by 60%
103. **DevOps/CI-CD** - Implemented GitHub Actions pipeline, Kubernetes deployments
114. **Architecture experience** - Designed microservices architecture serving 10M users
125. **Startup background** - Previous role at early-stage startup (20 employees)
13
14## Concerns/Gaps
151. **AI/ML experience not mentioned** - Resume doesn't show direct AI work (but this is preferred, not required)
162. **Open source** - No public contributions visible
173. **Career gap** - 6-month gap in 2021 (may have explanation)
18
19## Interview Questions
201. "Tell me about your database optimization project at DataCorp. What specific techniques did you use?"
212. "Have you worked with any AI/ML technologies? Any interest in that area?"
223. "I see a gap in early 2021 - what were you doing during that time?"
234. "Describe a time you had to make a difficult architectural decision with trade-offs."
245. "What interests you about moving from a large company to our startup?"
25
26## Recommendation
27**ADVANCE** - Strong technical match with 8 years experience and relevant stack expertise. Verify culture fit and interest in AI during phone screen. Clarify career gap.

Bias Mitigation

  • Blind screening: Remove names, photos, demographics before AI analysis
  • Structured criteria: Score only on job-related requirements
  • Audit trail: Log all decisions for bias review
  • Diverse validation: Regularly validate against diverse candidate pools
  • Human review: All candidates reviewed by humans, AI is advisory only
Legal Note: Consult with legal/HR before implementing. Some jurisdictions have specific requirements for AI in hiring. Always maintain human decision-making authority.

Benefits

  • Time Savings: Screen 100 resumes in minutes vs hours
  • Consistency: Same criteria applied to every candidate
  • Quality: Never miss a qualified candidate due to fatigue
  • Documentation: Clear reasoning for all recommendations