# How Hiring Agent Extracts Text from PDF Resumes: A Complete Technical Guide

> Learn how Hiring Agent extracts text from PDF resumes using PyMuPDF and LLM parsers. Get a technical deep dive into resume data extraction for recruiters.

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

---

**Hiring Agent extracts text from PDF resumes by converting each page into clean Markdown using PyMuPDF, then feeding that structured text to LLM-based parsers to generate a JSON representation of the resume.**

The `interviewstreet/hiring-agent` repository provides a robust pipeline for parsing unstructured PDF resumes into structured data. At the heart of this system is a sophisticated text extraction process that leverages **PyMuPDF** for document conversion and large language models for semantic understanding. This article examines how the system extracts text from PDF resumes through its modular architecture, tracing the journey from binary PDF files to structured JSON objects.

## The PDF Processing Architecture

The extraction workflow follows a three-stage pipeline: document ingestion, Markdown conversion, and LLM-based structured parsing. This architecture separates concerns between low-level document processing and high-level semantic extraction, enabling reliable handling of diverse resume formats.

## Step 1: PDF Ingestion and Validation

### The PDFHandler Entry Point

The primary interface for extracting text from PDF resumes resides in [`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py), specifically within the `PDFHandler` class. The method `extract_text_from_pdf` serves as the main entry point, accepting a file path and returning a Markdown string representation of the document.

According to the source code in [`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py) (lines 47-62), this method first validates that the file exists on disk, then opens the document using PyMuPDF's `pymupdf.open()` function. It delegates the heavy lifting of format conversion to the `to_markdown` helper function, ensuring that the handler remains focused on orchestration rather than low-level text extraction.

### Direct Markdown Conversion

For debugging or standalone usage, developers can interact directly with the lower-level conversion layer. The `to_markdown` function in [`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py) accepts a `pymupdf.Document` object and returns a formatted Markdown string. This function processes the document page by page, preserving hierarchical structure through header detection and maintaining formatting for tables and emphasis.

## Step 2: Intelligent Content Extraction

### Header Detection via IdentifyHeaders

The `to_markdown` function implements sophisticated layout analysis through the `IdentifyHeaders` class. This component maps font sizes in the PDF to Markdown header levels (H1-H6), ensuring that section hierarchies in the original resume—such as "Experience" or "Education"—are preserved in the extracted text.

As implemented in [`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py) (lines 30-86), the extraction routine walks through each page, collecting text blocks, tables, and images. It emits properly formatted Markdown including headings, lists, code blocks, bold/italic styling, and hyperlinks. Tables are rendered as Markdown tables, while images can be either embedded or saved to disk depending on configuration.

## Step 3: LLM-Based Structured Parsing

Once the raw Markdown text is obtained, `PDFHandler` transitions from extraction to semantic parsing. The class invokes a series of section-specific methods such as `extract_basics_section`, `extract_work_section`, and similar private methods prefixed with `_call_llm_for_section`.

Each method sends a specialized prompt to the configured LLM (default model configuration defined in [`prompt.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompt.py)), passing the relevant Markdown excerpt. The LLM returns a JSON fragment that the system transforms into strongly-typed Pydantic models (`JSONResume`, `Basics`, `Work`, etc.) defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py). The [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) module handles the conversion between raw LLM output and these final data structures.

## Practical Implementation Examples

The following examples demonstrate how to extract text from PDF resumes using the Hiring Agent codebase.

Extracting raw Markdown text:

```python
from pdf import PDFHandler

# Initialize the handler

handler = PDFHandler()

# Extract clean Markdown text from a PDF resume

markdown = handler.extract_text_from_pdf("alice_resume.pdf")
print(markdown[:500])  # Preview first 500 characters

```

Parsing into structured JSON:

```python

# Convert the extracted text into a structured JSON resume

structured = handler.extract_json_from_pdf("alice_resume.pdf")
print(structured.json())  # JSON representation of the resume

```

Direct low-level access for debugging:

```python
import pymupdf as fitz
from pymupdf_rag import to_markdown

# Open document directly with PyMuPDF

doc = fitz.open("alice_resume.pdf")

# Convert specific page range to Markdown

md = to_markdown(doc, pages=range(doc.page_count))
print(md[:400])

```

## Core Components and File Responsibilities

Understanding the file structure helps when extending or debugging the text extraction pipeline:

- **[`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py)**: Contains the `PDFHandler` class, the main entry point for PDF-to-text conversion and LLM orchestration. Implements `extract_text_from_pdf` and the section-specific extraction methods.

- **[`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py)**: Houses the `to_markdown` implementation that transforms PyMuPDF documents into clean Markdown. Includes the `IdentifyHeaders` class for layout analysis and handles tables, images, and formatting.

- **[`prompt.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompt.py)**: Defines the default LLM model configuration and API key handling used by `PDFHandler` for semantic parsing.

- **[`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py)**: Contains Pydantic data models (`JSONResume`, `Basics`, `Work`, `Education`, etc.) that receive the parsed JSON from the LLM.

- **[`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py)**: Provides helper functions that convert raw JSON responses from the LLM into the typed model objects defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py).

## Summary

- **Hiring Agent** extracts text from PDF resumes using a two-phase approach: first converting PDFs to Markdown with **PyMuPDF**, then parsing that Markdown with LLMs.
- The **`PDFHandler`** class in [`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py) orchestrates the workflow, while **`to_markdown`** in [`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py) handles the low-level document conversion.
- **Header detection** preserves document structure by mapping PDF font sizes to Markdown heading levels.
- The system supports **tables and images**, rendering them as Markdown tables and embedded files respectively.
- **Section-specific LLM calls** transform the Markdown text into structured JSON using Pydantic models defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py).

## Frequently Asked Questions

### What library does Hiring Agent use to extract text from PDF resumes?

Hiring Agent uses **PyMuPDF** (imported as `pymupdf` or `fitz`) as its core PDF processing library. The `extract_text_from_pdf` method in `PDFHandler` opens documents with `pymupdf.open()`, and the `to_markdown` function processes the document object to extract text while preserving formatting and layout information.

### How does Hiring Agent handle tables and images when extracting text from PDF resumes?

The `to_markdown` function in [`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py) specifically handles tables by rendering them as Markdown tables with proper column alignment. For images, the system can either embed them using data URIs or save them to disk depending on the configuration. This ensures that visual information in resumes is preserved as structured data or references rather than being lost during text extraction.

### Can I extract text from PDF resumes without using the LLM parsing?

Yes. The `PDFHandler.extract_text_from_pdf` method returns the raw Markdown text without invoking any LLM calls. You can also use the low-level `to_markdown` function directly from [`pymupdf_rag.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pymupdf_rag.py) by passing a `pymupdf.Document` object, which is useful for debugging or when you only need the text content without structured data extraction.

### Where is the LLM model configuration defined in the Hiring Agent repository?

The default LLM model configuration and API key handling are defined in [`prompt.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompt.py). This file contains the settings used by `PDFHandler` when calling `_call_llm_for_section` methods to parse specific resume sections like work experience or education. The configuration determines which model processes the Markdown text extracted from the PDF.