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

> Discover how the Hiring Agent system extracts candidate strengths and improvement areas from resumes. Learn about its three-stage pipeline: parsing, LLM evaluation, and CSV output.

- Repository: [HackerRank/hiring-agent](https://github.com/interviewstreet/hiring-agent)
- Tags: how-to-guide
- Published: 2026-07-19

---

**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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/main/evaluator.py) orchestrates the core intelligence layer. It loads a prompt template named **`resume_evaluation_criteria`** via the `TemplateManager`:

```python
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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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:

```python

# 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:

```python
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`](https://github.com/interviewstreet/hiring-agent/blob/main/main/models.py)** – Defines `JSONResume` and `EvaluationData` Pydantic models, including the `key_strengths` and `areas_for_improvement` list fields
- **[`main/evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/evaluator.py)** – Contains `ResumeEvaluator.evaluate_resume()`, which orchestrates the LLM call and schema validation
- **[`main/prompts/template_manager.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/prompts/template_manager.py)** – Loads the `resume_evaluation_criteria` prompt template used to solicit structured feedback
- **[`main/prompts/resume_evaluation_criteria.txt`](https://github.com/interviewstreet/hiring-agent/blob/main/main/prompts/resume_evaluation_criteria.txt)** – The raw prompt text explicitly requesting JSON arrays for strengths and improvement areas
- **[`main/transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/resume_evaluation_criteria.txt) (loaded via `TemplateManager` in [`main/prompts/template_manager.py`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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.