How Hiring Agent Extracts Key Strengths and Areas for Improvement from Resumes

The system identifies key strengths and areas for improvement through a three-stage pipeline: parsing raw resumes into structured JSON, prompting an LLM with a specific evaluation template to grade candidate attributes, and flattening the results into semicolon-separated CSV columns.

The interviewstreet/hiring-agent repository automates candidate screening by extracting actionable insights directly from resume content. This open-source tool uses a structured evaluation pipeline to surface both competitive advantages and development gaps in applicant profiles. Understanding this workflow reveals how generative AI transforms unstructured resume text into standardized recruitment data.

The Resume Evaluation Pipeline

The identification of key strengths and areas for improvement occurs through a coordinated sequence involving data modeling, LLM orchestration, and data transformation.

Step 1: Structured Resume Parsing

Raw resume files (PDF, DOCX) first undergo normalization into a strongly-typed JSONResume object defined in main/models.py. This parser standardizes sections including basics, work, education, skills, and projects into a consistent schema that downstream components can consume.

Step 2: LLM-Driven Attribute Extraction

The ResumeEvaluator class in main/evaluator.py orchestrates the core intelligence layer. It loads a prompt template named resume_evaluation_criteria via the TemplateManager:

criteria_template = self.template_manager.render_template(
    "resume_evaluation_criteria", text_content=resume_text
)

This prompt explicitly instructs the LLM to return two JSON arrays:

  • key_strengths: A list of the candidate’s strongest professional attributes
  • areas_for_improvement: A list of skills or experiences requiring development

The LLM response is forced into the EvaluationData schema (defined in main/models.py) using the provider’s format argument, guaranteeing that both fields appear as lists of strings regardless of input variability.

Step 3: CSV Transformation

The transform_evaluation_response function in main/transform.py (lines 731–738) handles the final data shaping. It extracts the two lists from the EvaluationData instance and joins them into semicolon-separated strings suitable for tabular export:


# Lines 731-738 in transform.py

if evaluation and hasattr(evaluation, "key_strengths"):
    csv_row["key_strengths"] = "; ".join(evaluation.key_strengths)
else:
    csv_row["key_strengths"] = ""

if evaluation and hasattr(evaluation, "areas_for_improvement"):
    csv_row["areas_for_improvement"] = "; ".join(evaluation.areas_for_improvement)
else:
    csv_row["areas_for_improvement"] = ""

This transformation ensures the final CSV contains human-readable columns populated directly from the LLM’s semantic analysis of the resume.

Implementation Example

The following workflow demonstrates how to process a resume and extract the evaluation fields:

from evaluator import ResumeEvaluator
from transform import transform_evaluation_response, convert_json_resume_to_text

# 1. Load raw résumé text (e.g. from a PDF)

with open("candidate_resume.pdf", "rb") as f:
    resume_text = extract_text_from_pdf(f)          # helper in pdf.py

# 2. Ask the LLM to evaluate the résumé

evaluator = ResumeEvaluator()
evaluation = evaluator.evaluate_resume(resume_text)   # returns EvaluationData

# 3. Convert the raw JSONResume (already parsed elsewhere) to a CSV row

csv_row = transform_evaluation_response(
    file_name="candidate_resume.pdf",
    resume_data=parsed_resume,      # instance of JSONResume

    github_data=None,              # optional GitHub enrichment

    evaluation=evaluation
)

print(csv_row["key_strengths"])
print(csv_row["areas_for_improvement"])

Example Output:


key_strengths: "Strong backend engineering, Excellent problem-solving, Proven open-source contributions"
areas_for_improvement: "Limited front-end experience, No formal leadership roles"

Core Source Files

The following modules implement the strength and weakness identification logic:

  • main/models.py – Defines JSONResume and EvaluationData Pydantic models, including the key_strengths and areas_for_improvement list fields
  • main/evaluator.py – Contains ResumeEvaluator.evaluate_resume(), which orchestrates the LLM call and schema validation
  • main/prompts/template_manager.py – Loads the resume_evaluation_criteria prompt template used to solicit structured feedback
  • main/prompts/resume_evaluation_criteria.txt – The raw prompt text explicitly requesting JSON arrays for strengths and improvement areas
  • main/transform.py – Lines 731–738 contain the extraction logic that flattens EvaluationData into CSV-compatible strings

Summary

  • Structured Input: Raw resumes convert to JSONResume objects before evaluation, normalizing variable document formats
  • Schema Enforcement: The EvaluationData model guarantees key_strengths and areas_for_improvement fields exist as string lists via Pydantic validation
  • Prompt Engineering: The resume_evaluation_criteria template explicitly directs the LLM to return the two specific JSON arrays required for downstream processing
  • CSV Integration: The transform_evaluation_response function in main/transform.py safely extracts and joins these lists with semicolon delimiters for spreadsheet compatibility

Frequently Asked Questions

What prompt template generates the key strengths and areas for improvement?

The system uses resume_evaluation_criteria.txt (loaded via TemplateManager in main/prompts/template_manager.py) to instruct the LLM. This template explicitly requests two JSON arrays named key_strengths and areas_for_improvement containing concise string evaluations of the candidate’s profile.

How does the system ensure both evaluation fields are always present?

The ResumeEvaluator forces LLM responses into the EvaluationData Pydantic model defined in main/models.py. This schema validation requires both key_strengths and areas_for_improvement to exist as lists of strings, preventing malformed or partial responses from propagating through the pipeline.

Where does the semicolon-separated formatting occur?

The transformation happens in main/transform.py at lines 731–738. The transform_evaluation_response function checks for the attributes using hasattr(), then applies "; ".join() to convert the Python lists into strings suitable for CSV export columns.

Can the evaluation pipeline process resumes without supplementary GitHub data?

Yes. The transform_evaluation_response function accepts github_data=None as a valid parameter. The strength and improvement extraction depends solely on the EvaluationData instance and the parsed JSONResume object, making GitHub enrichment optional for basic resume evaluation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →