English translation is not available yet. Showing Russian content.
Настроить templating (Jinja2) для переменных {context} и {question}
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить templating (Jinja2) для переменных {context} и {question}
1. Цель задачи
Научиться применять Jinja2 как систему шаблонизации для динамического формирования промптов в LLM-пайплайнах. Вы создадите шаблон, в который будут подставляться переменные {{ context }} и {{ question }} из внешних источников, и настроите его так, чтобы один и тот же шаблон порождал корректные промпты для разных запросов. Задача закладывает основу для production‑ready Prompt Management: отделение логики промпта от данных, централизованное хранение шаблонов, возможность A/B‑тестирования.
Ключевой результат Работающий Jinja2‑шаблон, который для любых корректных значений context и question генерирует валидный промпт, пригодный для передачи в LLM (например, OpenAI, YandexGPT). Шаблон поддерживает условную логику (например, пропуск пустого контекста) и подключается через Python‑скрипт.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
Python 3.9+ с установленным jinja2 | Установить pip install jinja2 |
| Текстовые примеры контекста и вопроса | Создать файл data.json с 5–10 парами (реальные или синтетические) |
| Базовый шаблон промпта (в черновике) | Написать вручную, например: "Ответь на вопрос, используя контекст: {{ context }}. Вопрос: {{ question }}" |
| (Опционально) Доступ к LLM API | OpenAI / YandexGPT / GigaChat для финальной проверки |
Если нет реального LLM‑API — симулируем:
- Замените вызов LLM на заглушку: функция fake_llm(prompt) просто возвращает длину промпта и первые 100 символов.
- Все тесты на корректность генерации проводите без реального API.
- Убедитесь, что сгенерированный промпт соответствует ожидаемому формату (не содержит ошибок экранирования, пустых блоков).
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.9+ | Исполнение скриптов, интеграция |
| Библиотека шаблонизации | Jinja2 (>=3.0) | Рендеринг промптов с переменными |
| Формат данных | JSON / YAML | Хранение тестовых пар context+question |
| Тестирование | pytest (опционально) | Автоматическая проверка шаблона |
| (Опционально) LLM API | OpenAI Python SDK / YandexGPT | Финальная валидация сгенерированного промпта |
4. Этапы выполнения
Этап 1: Инициализация проекта и установка Jinja2 (15 минут)
Действия
- Создайте структуру каталогов:
prompt_templating/ ├── templates/ │ └── prompt.j2 ├── data/ │ └── examples.json ├── src/ │ └── renderer.py ├── tests/ │ └── test_template.py └── requirements.txt - Установите зависимости: pip install jinja2 pyyaml pytest.
- Создайте базовый шаблон
templates/prompt.j2с содержимым:Используй следующий контекст для ответа: {{ context }} Вопрос: {{ question }} Ответь развёрнуто.
Ожидаемый результат этапа Структура проекта готова, Jinja2 установлен, шаблон существует на диске.
Этап 2: Подготовка тестовых данных и первого рендера (30 минут)
Действия
- Создайте data/examples.json с минимум 3 примерами:
[ { "context": "Квантовый компьютер использует кубиты для вычислений.", "question": "Что такое кубит?" }, { "context": "", "question": "Какая сегодня погода?" }, { "context": "Jinja2 — это мощный шаблонизатор для Python.", "question": "Как установить Jinja2?" } ] - Напишите скрипт
src/renderer.py, который:- Загружает шаблон через
FileSystemLoader - Загружает данные из JSON
- Рендерит шаблон для каждого примера
- Выводит результат на экран
from jinja2 import Environment, FileSystemLoader import json env = Environment(loader=FileSystemLoader('templates')) template = env.get_template('prompt.j2') with open('data/examples.json', 'r') as f: examples = json.load(f) for i, example in enumerate(examples, 1): rendered = template.render(example) print(f"=== Example {i} ===") print(rendered) print() - Загружает шаблон через
- Запустите скрипт и убедитесь, что все три примера рендерятся без ошибок.
Ожидаемый результат этапа Скрипт рендерит промпты, видны проблемы второго примера (пустой контекст создаёт пустую строку).
Этап 3: Добавление условной логики и фильтров (1 час)
Действия
-
Избавьтесь от пустого контекста — добавьте условие в шаблон:
{% if context %} Используй следующий контекст для ответа: {{ context }} {% else %} Ответь на вопрос без дополнительного контекста. {% endif %} Вопрос: {{ question }} Ответь развёрнуто. -
Добавьте фильтр trim для обрезки лишних пробелов в вопросе:
{{ question | trim }}. -
Реализуйте безопасное экранирование — если в контексте или вопросе есть HTML/спецсимволы, используйте
{{ context | e }}или применитеautoescapeв окружении:env = Environment(loader=..., autoescape=True) -
Добавьте цикл для списка документов — измените структуру данных на:
{ "documents": ["док1", "док2"], "question": "Резюмируй" }И шаблон:
{% for doc in documents %} - {{ doc }} {% endfor %}(Опционально — если задание требует продвинутого использования).
-
Протестируйте все модификации на трёх наборах:
- с непустым контекстом
- с пустым контекстом
- с HTML в контексте (например,
<script>alert(1)</script>)
Ожидаемый результат этапа Шаблон корректно обрабатывает пустой контекст, обрезает пробелы, экранирует вредоносные символы. Все примеры рендерятся без Jinja2‑ошибок.
Этап 4: Интеграция с LLM‑пайплайном и тестирование (30 минут)
Действия
- Создайте симуляцию LLM (если нет реального API):
def fake_llm(prompt: str) -> str: # симуляция: возвращаем длину и первые символы return f"[LLM RSP] len={len(prompt)}: {prompt[:60]}..." - Допишите
renderer.py— после рендеринга вызывайтеfake_llmи выводите ответ. - Напишите тест
tests/test_template.py(pytest):import json, pytest from jinja2 import Environment, FileSystemLoader @pytest.fixture def env(): return Environment(loader=FileSystemLoader('templates')) def test_renders_with_context(env): template = env.get_template('prompt.j2') out = template.render(context="test", question="q") assert "test" in out assert "q" in out def test_handles_empty_context(env): template = env.get_template('prompt.j2') out = template.render(context="", question="q") assert "без дополнительного контекста" in out def test_trim_filter(env): template = env.get_template('prompt.j2') out = template.render(context=" c ", question=" q ") assert "c" in out # лишние пробелы убраны - Запустите тесты командой
pytest tests/ -v.
Ожидаемый результат этапа Все тесты проходят. Промпты успешно подаются в симулятор LLM.
Этап 5: Документирование и финальная упаковка (15 минут)
Действия
- Добавьте README.md в корень проекта:
- Описание шаблона, поддерживаемые переменные
- Инструкция по запуску
- Примеры вывода
- Закрепите версию Jinja2 в
requirements.txt:jinja2>=3.0,<4.0 - Создайте Makefile (опционально) для удобных команд:
make render,make test.
Ожидаемый результат этапа Проект готов к передаче/использованию, документация покрывает основные сценарии.
5. Критерии приемки (Definition of Done)
- Jinja2 установлен и работает в виртуальном окружении.
- Создан шаблон
prompt.j2с переменными{{ context }}и{{ question }}. - Шаблон содержит условный блок для обработки пустого контекста.
- Использован хотя бы один встроенный фильтр (trim, e или другой).
- Написаны 3 pytest‑теста (на корректный рендер, пустой контекст, фильтр).
- Скрипт
renderer.pyпринимает путь к данным и шаблону, выводит результат. - Все тесты проходят (
pytest tests/ -v— зелёный). - В README описаны назначение шаблона и пример запуска.
- Данные в
examples.jsonсодержат как минимум один пример с пустым контекстом. - Код соответствует PEP 8 (проверено
flake8или подобным).
6. Ожидаемый результат
-
Основной артефакт Каталог
prompt_templating/со следующей структурой:templates/prompt.j2— финальный шаблонsrc/renderer.py— скрипт рендерингаdata/examples.json— тестовые данныеtests/test_template.py— автоматические тестыrequirements.txt— зависимостиREADME.md— документация
-
Содержимое шаблона (пример финальной версии):
{% if context | trim %} Используй следующий контекст для ответа: {{ context }} {% else %} Ответь на вопрос без дополнительного контекста. {% endif %} Вопрос: {{ question | trim }} Ответь развёрнуто. -
Дополнительно (по желанию): Makefile, скрипт для A/B‑тестирования двух версий шаблона, поддержка вложенных структур (например, список документов).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
Jinja2 выкидывает ошибку из‑за неэкранированных символов { в данных | Использовать raw‑блок в шаблоне либо экранировать через {% raw %}...{% endraw %} |
| Пустой контекст приводит к висящей пустой строке в промпте | Добавить {% if context %}...{% endif %} и фильтр trim |
| HTML/JavaScript в контексте попадает в промпт и может сломать логику | Включить autoescape=True в окружении или применить `{{ context |
| Вопрос содержит перевод строки | Фильтр replace('\n', ' ') или `{{ question |
| Шаблон слишком большой, сложно поддерживать | Вынести повторяющиеся части в {% macro %} и подключать через {% import %} |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Инициализация проекта и установка Jinja2 | 15 мин |
| Этап 2: Подготовка тестовых данных и первый рендер | 30 мин |
| Этап 3: Добавление условной логики и фильтров | 1 час |
| Этап 4: Интеграция с LLM‑пайплайном и тестирование | 30 мин |
| Этап 5: Документирование и упаковка | 15 мин |
| Итого | 2,5 часа |
Примечание Для первого раза с учётом изучения документации Jinja2 заложите дополнительно 30–60 минут.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 42 | Как безопасно экранировать пользовательский ввод в промптах? |
| 57 | Лучшие практики хранения шаблонов промптов в production |
| 89 | Использование Jinja2 для A/B‑тестирования инструкций |
| 131 | Управление версиями промптов через Git |
| 204 | Интеграция шаблонизации с LangChain / LlamaIndex |
| 315 | Динамическая подстановка {{ few_shot_examples }} |
| 444 | Фильтры Jinja2 для форматирования дат и чисел в промптах |
| 567 | Обработка ошибок рендеринга: что делать, если не хватает переменной |
| 689 | Нагрузочное тестирование шаблонов: производительность рендеринга |
| 777 | Переход от f‑строк к Jinja2: миграция легаси‑кода |
10. Чек-лист самопроверки
- Я создал виртуальное окружение и установил Jinja2.
- Я написал хотя бы один тест, который проверяет подстановку
contextиquestion. - Я проверил, что шаблон корректно обрабатывает пустой контекст (выводит альтернативный текст).
- Я добавил фильтр trim и убедился, что лишние пробелы удаляются из вопроса.
- Я запустил
pytest tests/и получилpassedдля всех тестов. - README содержит команду для запуска рендера:
python src/renderer.py.