How to Customize Jinja Templates for Different Resume Formats in Hiring Agent
You customize resume parsing in the Hiring Agent by editing Jinja files in prompts/templates/, registering them in TemplateManager, and invoking them from pdf.py to handle any resume layout or section structure.
The Hiring Agent repository (interviewstreet/hiring-agent) uses a declarative template system to extract structured data from PDF resumes using Large Language Models (LLMs). By customizing Jinja templates for different resume formats, you can adapt the pipeline to parse custom sections, handle unconventional layouts, or add new fields like certifications without modifying core parsing logic.
Understanding the Template Architecture
The pipeline revolves around three core components that work together to convert PDF resumes into structured JSON:
- Jinja templates (
prompts/templates/*.jinja) – Declarative instructions that tell the LLM how to parse specific resume sections (basics, work, education, etc.) - TemplateManager (
prompts/template_manager.py) – Loads all templates at start-up, caches them, and renders chosen templates with resume markdown viarender_template() - PDF handler (
pdf.py) – Splits PDF-converted markdown into logical sections and orchestrates LLM calls
The flow works as follows: pymupdf_rag.py converts PDF pages to markdown, pdf.py extracts section blocks, TemplateManager.render_template() injects the markdown into the appropriate Jinja template, and models.py sends the rendered prompt to the LLM provider (Ollama or Gemini).
Creating Custom Templates for New Resume Sections
To support a resume format that lists certifications in a separate block, follow this four-step process:
1. Create the Template File
Add certifications.jinja under prompts/templates/:
Extract ONLY the certifications from this resume.
--- The input resume markdown starts here ---
{{ text_content }}
--- The input resume markdown ends here ---
Return ONLY a JSON object with this structure:
{
"certifications": [
{
"name": "Certification name",
"issuer": "Issuing organization",
"date": "YYYY-MM"
}
]
}
**IMPORTANT**: Return ONLY valid JSON.
2. Register the Template
Update TemplateManager._load_templates() in prompts/template_manager.py to include the new entry in the template_files dictionary:
"certifications": "certifications.jinja",
3. Expose the Section in the PDF Handler
In pdf.py, extract the markdown block for certifications and render the template:
cert_prompt = manager.render_template(
"certifications", text_content=cert_block
)
cert_json = llm.ask(cert_prompt)
4. Update the Data Model
Add a field to models.JSONResume (or a separate Pydantic schema) to hold the certifications list, and merge the LLM response when building the final résumé object.
Modifying Existing Templates for Format Variations
If a resume format swaps the order of "location" and "summary" in the basics block, you only need to modify prompts/templates/basics.jinja. To make the summary optional but capture it when present:
{% if "Summary" in text_content %}
**IMPORTANT**: If there is any About Me or Summary section, add that to the summary field.
{% endif %}
Because the template is re-rendered on each run, no other code changes are required to handle format variations.
Adding Conditional Logic and Preprocessing
Jinja supports conditionals, loops, and filters for preprocessing resume text. For example, filter out non-ASCII characters to prevent LLM confusion:
{% set clean_text = text_content | replace('[^\\x00-\\x7F]', '') %}
{{ clean_text }}
This preprocessing step helps when resumes contain exotic symbols that might confuse the parsing model.
Testing Your Template Customizations
Validate your changes using these three methods:
- Unit testing – Add a test in
tests/test_templates.pythat loads the modified template with a sample markdown snippet and asserts the JSON shape - End-to-end testing – Execute
python score.py path/to/sample_resume.pdfand verify the console output contains the newly added fields - Cache busting – If
DEVELOPMENT_MODEis enabled, clear thecache/directory to force a fresh LLM call after template changes
Summary
- Template files live in
prompts/templates/and define LLM instructions for each resume section - TemplateManager (
prompts/template_manager.py) handles loading and rendering viarender_template() - New sections require creating a
.jinjafile, registering it inTemplateManager._load_templates(), and calling it frompdf.py - Format variations can be handled by editing existing templates without touching Python code
- Jinja filters enable preprocessing like ASCII character filtering before LLM ingestion
- Testing involves unit tests in
tests/test_templates.pyand integration tests viascore.py
Frequently Asked Questions
How do I add a new section to the resume parser?
Create a new .jinja file in prompts/templates/, add the filename to the template_files dictionary in TemplateManager._load_templates(), then call manager.render_template('new_section', text_content=block) from pdf.py when processing that section. Finally, update models.JSONResume to include the new field in the final schema.
Where are the Jinja templates stored in the Hiring Agent repository?
All Jinja templates are stored in the prompts/templates/ directory, with files like basics.jinja, work.jinja, and education.jinja defining the extraction logic for each resume section.
Can I use Jinja filters to preprocess resume text before sending to the LLM?
Yes, Jinja supports filters and conditionals inside templates. For example, you can use {% set clean_text = text_content | replace('[^\\x00-\\x7F]', '') %} to strip non-ASCII characters, or use {% if %} blocks to conditionally include instructions based on content detection.
How do I test template changes without running the full pipeline?
You can write unit tests in tests/test_templates.py that instantiate TemplateManager and call render_template() with sample markdown snippets to verify the output structure. For integration testing, run python score.py path/to/resume.pdf after clearing the cache/ directory if DEVELOPMENT_MODE is enabled.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →