# How Prompt Variations Are Generated and Managed in the Hiring-Agent System

> Discover how Hiring-Agent generates prompt variations using a data-driven Jinja2 template architecture. Add new prompts easily without altering Python code.

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

---

**The Hiring-Agent system generates prompt variations using a data-driven Jinja2 template architecture where the `TemplateManager` class dynamically loads and renders templates from the `prompts/templates/` directory, allowing new variations to be added without modifying Python code.**

The `interviewstreet/hiring-agent` repository separates prompt content from application logic by storing all LLM instructions as Jinja2 templates. This approach centralizes prompt management and enables rapid iteration on LLM instructions without touching the core Python codebase. The system treats every prompt variation as a data asset, making it straightforward to customize instructions for different resume sections or evaluation criteria.

## Template-Based Architecture for Prompt Management

At the heart of the prompt generation system lies the **`TemplateManager`** class located in [`prompts/template_manager.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompts/template_manager.py). This singleton-style manager encapsulates all Jinja2 operations, providing a clean interface for discovering, loading, and rendering prompt variations stored as `.jinja` files.

### Template Discovery and Loading

When you instantiate `TemplateManager`, the `__init__` method (lines 21–33) configures a `jinja2.Environment` pointing to the `prompts/templates/` directory. The private method `_load_templates` (lines 35–55) then enumerates every template file—such as `basics.jinja`, `work.jinja`, and `system_message.jinja`—and compiles them into `jinja2.Template` objects stored in an internal dictionary called `self._templates`.

This automated discovery means the system recognizes new prompt variations immediately upon instantiation, provided they follow the `.jinja` naming convention and reside in the templates directory.

### Rendering Concrete Prompts

The public method `render_template(section_name, **kwargs)` (lines 69–88) retrieves the pre-compiled template by key and injects variables using Jinja2’s native rendering. The only required variable across all section prompts is **`text_content`**, which typically contains the extracted resume markdown to be processed.

```python
from prompts.template_manager import TemplateManager

# Instantiate manager (loads all .jinja files automatically)

tm = TemplateManager()

# Render the "work" section prompt

prompt = tm.render_template(
    "work",                  # Maps to prompts/templates/work.jinja

    text_content=resume_md   # Injected into the template

)
print(prompt)  # Final prompt text sent to the LLM

```

## Integration with Resume Processing Pipeline

The `TemplateManager` serves two primary consumers within the hiring pipeline: the PDF extraction handler and the resume evaluator. Both use the manager to generate context-specific prompts before calling the LLM.

### PDF Extraction via PDFHandler

The `PDFHandler` class in [`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py) (lines 38–44) leverages the manager to extract structured data from raw resume text. When processing a section like work history, the handler renders the appropriate template and passes the result to `_call_llm_for_section`.

```python

# Internal usage within PDFHandler

handler = PDFHandler()
pdf_text = handler.extract_text_from_pdf("candidate.pdf")
work_section = handler.extract_work_section(pdf_text)  # Renders work.jinja internally

```

### Resume Evaluation Workflow

Similarly, the `ResumeEvaluator` class in [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py) (lines 24–34) uses the manager to load evaluation-specific prompts. It retrieves templates for "resume evaluation criteria" and "system message" to construct the full context required for assessing candidate quality.

## Adding New Prompt Variations

The architecture is fully **data-driven**, meaning you can introduce new prompt variations by simply creating a new `.jinja` file in the templates directory. This eliminates the need to modify Python code when adjusting LLM instructions or adding support for new resume sections.

```python

# Step 1: Create prompts/templates/custom_section.jinja

# Content: "Extract the following information from this resume: {{ text_content }}"

# Step 2: Use immediately from code

tm = TemplateManager()
custom_prompt = tm.render_template("custom_section", text_content=resume_md)

```

The `TemplateManager` automatically picks up the new file on the next instantiation, making it available via `render_template` using the filename (minus extension) as the key.

## Centralized Model Configuration

While prompt content lives in the templates directory, model selection and inference parameters are centralized in [`prompt.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompt.py). This file defines **`DEFAULT_MODEL`**, **`MODEL_PARAMETERS`**, and provider mappings, maintaining a strict separation between *what* the LLM is asked (the template) and *how* it is asked (temperature, model version, etc.).

## Summary

- **Jinja2-based architecture**: All prompt variations are stored as `.jinja` files in `prompts/templates/` and managed by the `TemplateManager` class.
- **Automatic template discovery**: The `_load_templates` method scans the templates directory at instantiation, compiling all files into ready-to-render objects.
- **Unified rendering interface**: The `render_template` method in [`template_manager.py`](https://github.com/interviewstreet/hiring-agent/blob/main/template_manager.py) (lines 69–88) requires `text_content` as the primary variable for all section prompts.
- **Pipeline integration**: Both `PDFHandler` ([`pdf.py`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py)) and `ResumeEvaluator` ([`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py)) consume the manager to generate LLM prompts for extraction and evaluation tasks.
- **Data-driven extensibility**: Adding new prompt variations requires only dropping a new `.jinja` file into the templates directory, with no Python code changes necessary.

## Frequently Asked Questions

### What is the primary method for rendering prompt variations in the hiring-agent system?

The primary method is `render_template(section_name, **kwargs)` implemented in the `TemplateManager` class at [`prompts/template_manager.py`](https://github.com/interviewstreet/hiring-agent/blob/main/prompts/template_manager.py) (lines 69–88). This method looks up the pre-compiled Jinja2 template by section name and renders it by injecting the provided keyword arguments, most notably the `text_content` variable containing the resume data.

### How does the TemplateManager discover new prompt templates?

The `TemplateManager` discovers templates automatically through its private `_load_templates` method (lines 35–55), which is called during `__init__`. This method enumerates all files ending in `.jinja` within the `prompts/templates/` directory and stores them as compiled `jinja2.Template` objects in the `self._templates` dictionary, making them instantly available for rendering.

### What variable is required when rendering any prompt template in this system?

The **`text_content`** variable is required for all section prompts. This variable typically contains the extracted markdown text from a candidate's resume and is injected into the Jinja2 template during the `render_template` call to provide the LLM with the raw data to analyze.

### How can I add a custom prompt variation without modifying Python code?

To add a new variation, create a new file with the `.jinja` extension in the `prompts/templates/` directory (for example, `custom_section.jinja`). Ensure the template references the `{{ text_content }}` variable if it needs access to the resume data. The `TemplateManager` will automatically detect and load this file on the next instantiation, allowing you to reference it by filename (without extension) in `render_template` calls.