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

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. 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.

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 (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.


# 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 (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.


# 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. 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 (lines 69–88) requires text_content as the primary variable for all section prompts.
  • Pipeline integration: Both PDFHandler (pdf.py) and ResumeEvaluator (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 (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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →