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

> Contribute to hires agent prompt management system by creating Jinja templates registering them in TemplateManager and invoking via render_template For detailed steps visit our GitHub repo.

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

---

**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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py), [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py), and [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py), [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py), [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/README.md) or create a dedicated "Prompt Templates" section linking to the new file. | [`README.md`](https://github.com/interviewstreet/hiring-agent/blob/main/README.md) or docs |

## Code Examples

### Creating a New Template File

Create `prompts/templates/cover_letter.jinja`:

```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`](https://github.com/interviewstreet/hiring-agent/blob/main/prompts/template_manager.py) (around line 38):

```python
template_files = {
    # … existing entries …

    "cover_letter": "cover_letter.jinja",
}

```

### Implementing the Caller

Create a new module that utilizes the template:

```python
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

```python
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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/pdf.py), [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py), and [`github.py`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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`](https://github.com/interviewstreet/hiring-agent/blob/main/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.