How to Contribute to the Prompt Management System in Hiring-Agent

To contribute to the prompt management system in hiring-agent, you create Jinja template files in prompts/templates/, register them in the TemplateManager class, and invoke them through the render_template() method in your LLM caller modules.

The hiring-agent repository by InterviewStreet powers LLM-driven resume evaluation and candidate screening through a modular prompt architecture. Understanding how to contribute to the prompt management system in hiring-agent allows you to add new evaluation criteria, refine existing prompts, or extend capabilities to new data sources like GitHub projects or cover letters. The system uses a centralized TemplateManager that ensures every prompt is reusable, version-controlled, and instantly available across all LLM-driven features.

Understanding the Core Architecture

TemplateManager

The TemplateManager class serves as the central loader and rendering engine for all prompts. Located in prompts/template_manager.py, it builds a Jinja Environment pointing at the prompts/templates directory and eagerly loads every known template via the _load_templates method starting at line 35. The manager caches templates and exposes the render_template() method to supply structured prompts to any caller in the system.

Jinja Template Files

Jinja template files contain the raw prompt text stored in prompts/templates/*.jinja. Each file corresponds to a logical section—such as basics, work, or resume_evaluation_criteria—and defines the actual prompt strings sent to the LLM. These files use Jinja placeholder syntax ({{ variable }}) to inject dynamic data at runtime.

LLM-Aware Callers

LLM-aware callers are modules that request prompts from TemplateManager and subsequently call the LLM. Key examples include pdf.py, evaluator.py, and github.py. These callers combine the rendered user prompt with a system message and forward the complete payload to the LLM provider.

Model Configuration

The prompt.py file stores model-specific defaults, including the default model, provider settings, and per-model parameters. Callers import these defaults to ensure consistent LLM behavior across different components of the hiring-agent system.

How the Prompt System Works

  1. Initialization: When a caller such as PDFHandler is instantiated, it creates a TemplateManager() instance (see line 40 in pdf.py).

  2. Template loading: TemplateManager.__init__ builds a Jinja Environment and eagerly loads all templates via _load_templates (starting at line 35). Missing files are reported but do not crash the system.

  3. Rendering: The caller requests a specific section by invoking self.template_manager.render_template("section_name", variable=value).

  4. Prompt composition: The rendered user prompt is combined with a system message (typically from system_message.jinja) and passed to the LLM via the provider obtained from initialize_llm_provider.

  5. Result handling: The raw LLM response is cleaned using extract_json_from_response, parsed as JSON, transformed, and returned to the caller.

Because every component routes through TemplateManager, adding a new prompt once makes it instantly available everywhere.

Adding or Modifying Prompts

Step Action Location
Create template Create a .jinja file with placeholders ({{ variable }}) for dynamic data. prompts/templates/new_section.jinja
Register template Add the mapping section_name: "new_section.jinja" to the template_files dictionary inside _load_templates (lines 38-44). prompts/template_manager.py
Implement caller Call self.template_manager.render_template("new_section", your_key=your_value) in the module that needs the prompt. pdf.py, evaluator.py, github.py, or new modules
Add tests Verify the template renders without errors and that required variables are present. tests/ directory
Document Update README.md or create a dedicated "Prompt Templates" section linking to the new file. README.md or docs

Code Examples

Creating a New Template File

Create prompts/templates/cover_letter.jinja:

You are an AI assistant helping a candidate draft a concise cover letter.
Use the following resume details:

{{ resume_json }}

Write a 250-word cover letter tailored to the role: {{ job_title }}.

Registering the Template

Update template_files in prompts/template_manager.py (around line 38):

template_files = {
    # … existing entries …

    "cover_letter": "cover_letter.jinja",
}

Implementing the Caller

Create a new module that utilizes the template:

from prompts.template_manager import TemplateManager
from llm_utils import initialize_llm_provider, extract_json_from_response

DEFAULT_MODEL = "llama3.1"

class CoverLetterGenerator:
    def __init__(self):
        self.tm = TemplateManager()
        self.provider = initialize_llm_provider(DEFAULT_MODEL)

    def generate(self, resume_json: str, job_title: str) -> str:
        prompt = self.tm.render_template(
            "cover_letter",
            resume_json=resume_json,
            job_title=job_title,
        )
        if not prompt:
            raise RuntimeError("Failed to render cover-letter prompt")
        
        chat = {
            "model": DEFAULT_MODEL,
            "messages": [
                {"role": "system", "content": "You are a helpful AI assistant."},
                {"role": "user", "content": prompt},
            ],
            "options": {"temperature": 0.7, "top_p": 0.9},
        }
        response = self.provider.chat(**chat)
        return response["message"]["content"]

Rendering an Existing Template

from prompts.template_manager import TemplateManager

tm = TemplateManager()
resume_text = open("sample_resume.txt").read()

work_prompt = tm.render_template("work", text_content=resume_text)
print(work_prompt)  # Fully-filled Jinja prompt ready for the LLM

Best Practices for Prompt Contributions

  • Keep prompts declarative: Avoid embedding Python logic inside Jinja templates; perform data preparation in the caller and pass simple variables.
  • Validate rendering: TemplateManager.render_template returns None on failure; callers should check the return value and log meaningful errors.
  • Version-control templates: Since prompts are part of the model's behavior, treat them like code by reviewing changes via pull requests and running the full test suite.
  • Use consistent naming: Employ snake_case for section names (matching the key in template_files) and keep the filename identical (e.g., work.jinja for the work section).

Summary

  • The TemplateManager in prompts/template_manager.py is the central hub for loading and rendering all Jinja templates.
  • New prompts require three actions: creating a .jinja file in prompts/templates/, registering it in the template_files dictionary, and invoking it via render_template().
  • LLM callers in pdf.py, evaluator.py, and github.py demonstrate how to integrate rendered prompts with system messages and LLM providers.
  • Always validate that render_template() does not return None before passing to the LLM.
  • Treat prompt changes as code changes: add tests, run pytest, and document updates in README.md.

Frequently Asked Questions

What is the TemplateManager responsible for?

The TemplateManager loads Jinja templates from the prompts/templates/ directory, caches them in memory, and renders them with supplied variables via the render_template() method. It serves as the single source of truth for all LLM prompts in the hiring-agent system, ensuring consistency across PDF parsing, resume evaluation, and GitHub project analysis.

How do I test a new template before submitting a PR?

Add a unit test in the tests/ directory that verifies the template renders without errors and that all required variables are present. Run pytest to ensure your changes do not break existing functionality. The test should instantiate TemplateManager, call render_template() with mock data, and assert the output contains expected content.

Can I use Python logic inside Jinja templates?

While Jinja supports Python-like logic, the best practice is to keep templates declarative and perform data preparation in the caller module. Pass simple, pre-formatted variables to the template rather than embedding complex logic, making prompts easier to maintain and review by non-developers.

Where should I document new prompt changes?

Update README.md with a description of the new template and its purpose, or create a dedicated "Prompt Templates" section in the documentation that links to the new file. Since prompts directly influence model behavior, they require the same level of documentation and review as code changes.

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 →