# Why the Hiring Agent Parses Each Resume Section with the LLM Separately

> Discover why our Hiring Agent parses resume sections individually with LLMs. Learn about improved accuracy, efficient token use, and simplified error handling for better candidate screening.

- Repository: [HackerRank/hiring-agent](https://github.com/interviewstreet/hiring-agent)
- Tags: architecture
- Published: 2026-07-09

---

**The Hiring Agent processes résumés section-by-section to stay within token limits, enable focused prompting, and simplify error handling.**

The `interviewstreet/hiring-agent` repository implements a robust résumé parsing pipeline that extracts structured data by sending individual LLM requests for each logical section. Rather than submitting the entire document in a single prompt, the `PDFHandler` class isolates calls for basics, work experience, education, and other fields. This architectural choice in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) delivers higher accuracy and resilience when dealing with varied document lengths and complex provider constraints.

## Token-Limit Safety and Provider Constraints

LLM providers like Ollama and Gemini impose strict limits on prompt and input token counts. Feeding an entire résumé—often several pages of dense text—risks truncation or complete request failure. By iterating over discrete sections, the `PDFHandler._call_llm_for_section` method ensures each payload remains well within provider boundaries.

As implemented in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) lines 66-99, each section is dispatched with a compact, purpose-built prompt that minimizes token consumption.

## Focused Prompting for Higher Accuracy

Tailored system messages and user prompts allow the model to concentrate on the specific schema required for each résumé component. The `template_manager.render_template` function loads section-specific instructions—for example, `basics.jinja` for personal information or `work.jinja` for employment history.

This targeted approach, visible in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) lines 79-84 where `section_name_param=section_name` is passed, yields cleaner extraction than a generic, monolithic prompt attempting to parse everything at once.

## Fine-Grained Error Handling and Debugging

When the LLM fails on a single section, the pipeline can isolate and log the error without corrupting the entire extraction. In `_extract_all_sections_separately`, a missing or malformed section triggers an early return with a clear log entry, preventing partially-valid JSON from propagating downstream.

This logic appears in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) lines 95-99, where the function returns `None` if any section extraction fails, maintaining data integrity for subsequent evaluation stages.

## Architectural Benefits: Parallel Execution and Simpler Logic

The modular design separates concerns in ways that support both current sequential execution and future optimization. Although the current implementation processes sections in a loop, the isolation of each call inside `_extract_section_data` makes it trivial to introduce concurrency later via `asyncio.gather`.

Additionally, the transformation layer benefits from smaller inputs. The `transform_parsed_data` function in [`main/transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/transform.py) expects the shape of a single section, avoiding complex nested parsing logic. After each LLM call returns JSON, the normalized data merges into the final `JSONResume` object, as shown in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) lines 101-108 and defined in [`main/models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/models.py).

## Implementation Example

The following code demonstrates how `PDFHandler` orchestrates the per-section extraction workflow:

```python
from pdf import PDFHandler

handler = PDFHandler()

# Pass a local PDF path; the handler will:

#   1️⃣ Extract raw text with PyMuPDF.

#   2️⃣ Call the LLM separately for each section.

#   3️⃣ Assemble a JSONResume object.

resume = handler.extract_json_from_pdf("candidate_resume.pdf")

if resume:
    print("✅ Résumé parsed successfully!")
    print(resume.json(indent=2))
else:
    print("❌ Failed to parse résumé.")

```

## Summary

- **Token efficiency**: Per-section requests avoid exceeding LLM provider limits on input size.
- **Schema accuracy**: Dedicated prompts for each section (basics, work, education, etc.) improve extraction fidelity.
- **Fault isolation**: Failed sections are caught and logged individually, preventing partial data corruption.
- **Extensibility**: The isolated call structure supports future parallelization and simplifies transformation logic in [`main/transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/transform.py).

## Frequently Asked Questions

### Does parsing sections separately increase API costs?

Processing sections individually does generate multiple API calls, but the smaller payload sizes often use fewer total tokens than a single massive request with complex instructions. The trade-off prioritizes accuracy and reliability over marginal cost differences.

### Can the Hiring Agent process multiple sections in parallel?

While the current implementation in [`main/pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/main/pdf.py) runs sequentially, the architecture is designed for concurrency. The loop over sections in `_extract_all_sections_separately` can be refactored to use `asyncio.gather` without changing the underlying extraction logic.

### What happens if one section fails to parse?

If any section fails during `_call_llm_for_section`, the pipeline logs the specific failure and returns `None` for the entire extraction. This prevents downstream systems from receiving incomplete `JSONResume` objects with missing critical fields.

### Which LLM providers are supported?

The system uses [`llm_utils.py`](https://github.com/interviewstreet/hiring-agent/blob/main/llm_utils.py) to abstract provider-specific details through `initialize_llm_provider`, supporting Ollama, Gemini, and other compatible endpoints. Each section request respects the token limits and response formats of the configured provider.