Проблема: жорстко зашиті промпти вбивають гнучкість
Типова ситуація: ви написали промпт для класифікації тікетів підтримки, зашили його в код. Через місяць знадобилося додати нову категорію — довелося правити рядок у коді, перезбирати контейнер, прогоняти тести. А якщо таких промптів десятки? На кожен новий use case — новий копіпаст з прихованими багами. В результаті latency p99 зростає, а якість відповідей падає через помилки форматування. Ми вирішили це раз і назавжди: впровадили централізовану бібліотеку Prompt Templates з динамічною підстановкою змінних. Це не просто шаблонізація — це системний підхід до управління промптами, який окупається в перший місяць за рахунок скорочення часу на доробки.
Як працюють Prompt Templates?
Шаблон — це текстова заготовка з іменованими «дірками» — змінними. В runtime ви просто передаєте значення, і система збирає фінальний промпт. Ми використовуємо три підходи:
| Метод | Гнучкість | Продуктивність | Коли вибирати |
|---|---|---|---|
| Python f-strings | Низька | Висока | Прості сценарії, 1-3 змінні |
| Jinja2 | Висока | Середня | Умови, цикли, багато optional-полів |
| LangChain PromptTemplate | Дуже висока | Залежить від інтеграції | RAG, multi-step chains, few-shot |
Чому варто впровадити шаблонізацію?
Контроль версій — кожен шаблон лежить в Git, можна відкотитися, побачити хто і коли змінював. Тестованість — ви пишете тест для одного шаблону, а не для кожного конкретного виклику. Масштабування — додали новий тип аналізу? Просто створили новий шаблон в YAML.
Обсяг робіт з впровадження
- Аудит поточних промптів — виявляємо жорстко зашиті рядки, повторювані патерни.
- Проектування бібліотеки — ієрархія шаблонів, версіонування, схема змінних.
- Реалізація — пишемо на Python: від f-strings до Jinja2, обгортаємо в клас PromptTemplateManager.
- Тестування — модульні тести, перевірка на граничні значення, тести на регресію.
- Документація — README з прикладами, auto-generated description для кожного шаблону.
- Інтеграція — підключаємо до вашого стеку: LangChain, ваші API, event-driven системи.
- Підтримка — 2 тижні безкоштовної підтримки після впровадження.
Що входить в роботу
- Документація: опис шаблонів, інструкція з додавання нових.
- Код: бібліотека шаблонів з версіонуванням, CI/CD, тести.
- Доступ: Git-репозиторій з прикладами.
- Навчання: 2-годинний workshop для команди.
- Підтримка: 2 тижні безкоштовно після впровадження.
Як ми реалізуємо шаблонізацію промптів?
Використовуємо перевірений стек: Python 3.11+, Jinja2, LangChain, pyyaml, pydantic. Зберігання — YAML-файли в Git-репозиторії, опціонально PostgreSQL для runtime-версій. Тестуємо з pytest, parametrize для всіх комбінацій змінних.
Приклад конфігу шаблону:
# prompts/classifier.yaml
version: "2.2"
name: support_classifier
description: Класифікатор звернень до підтримки
updated_at: "актуальна дата"
variables:
- ticket_text
- categories
template: |
Класифікуй звернення до техпідтримки.
Категорії: {{ categories }}
Звернення:
{{ ticket_text }}
Поверни JSON: {"category": "...", "priority": "low|medium|high|critical", "confidence": 0.0-1.0}
eval_examples:
- input: "Я не можу увійти в систему"
expected_category: "technical"
Нижче — production-код, який ми використовуємо в проектах. Шаблонізація з Jinja2 дозволяє будувати складні ієрархічні промпти з циклами та умовами, а LangChain PromptTemplate чудово інтегрується в RAG-пайплайни. Prompt Engineering стає передбачуваним.
from string import Template
from jinja2 import Template as JinjaTemplate
from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
# Варіант 1: Python f-strings (простий)
def create_analysis_prompt(document: str, analysis_type: str, language: str = "uk") -> str:
return f"""Проаналізуй наступний документ.
Тип аналізу: {analysis_type}
Мова відповіді: {language}
Документ:
{document}
Надай структурований аналіз."""
# Варіант 2: Jinja2 (потужний, підтримує умови та цикли)
REPORT_TEMPLATE = JinjaTemplate("""
{% if role %}Ти — {{ role }}.{% endif %}
Завдання: {{ task }}
{% if context %}
Контекст:
{{ context }}
{% endif %}
{% if examples %}
Приклади:
{% for example in examples %}
Вхід: {{ example.input }}
Вихід: {{ example.output }}
---
{% endfor %}
{% endif %}
Вхідні дані:
{{ input_data }}
{% if output_format %}
Формат відповіді:
{{ output_format }}
{% endif %}
""")
# Варіант 3: LangChain PromptTemplate
analysis_prompt = PromptTemplate(
template="""Ти — {role}.
Завдання: Проаналізуй {document_type}.
Документ: {document}
Критерії оцінки:
{criteria}
Поверни JSON: {{
"summary": "...",
"key_findings": [...],
"risk_level": "low|medium|high",
"recommendations": [...]
}}""",
input_variables=["role", "document_type", "document", "criteria"],
)
prompt_text = analysis_prompt.format(
role="юридичний аналітик",
document_type="договір поставки",
document=contract_text,
criteria="термін дії, відповідальність сторін, умови розірвання",
)
class PromptTemplateManager:
"""Управління бібліотекою шаблонів промптів"""
BASE_TEMPLATES = {
"classifier": """Класифікуй наступний {input_type} за категоріями: {categories}.
{input_type}: {input_text}
Поверни JSON: {{"category": "...", "confidence": 0.0-1.0, "reasoning": "..."}}""",
"extractor": """Витягни {entities} з наступного тексту.
Текст: {text}
Поверни JSON: {extracted_schema}""",
"summarizer": """Створи коротке резюме.
Стиль: {style}
Довжина: {max_words} слів
Аудиторія: {audience}
Текст:
{content}""",
"qa": """Дай відповідь на запитання, використовуючи лише наданий контекст.
Контекст:
{context}
Запитання: {question}
Якщо відповіді немає в контексті, скажи "Немає даних у наданому контексті".""",
}
def get(self, template_name: str, **variables) -> str:
template = self.BASE_TEMPLATES.get(template_name)
if not template:
raise ValueError(f"Template '{template_name}' not found")
return template.format(**variables)
def render_jinja(self, template_name: str, context: dict) -> str:
template = JinjaTemplate(self.BASE_TEMPLATES[template_name])
return template.render(**context)
manager = PromptTemplateManager()
prompt = manager.get(
"classifier",
input_type="звернення до підтримки",
categories="billing, technical, account, general",
input_text=ticket_text,
)
class DynamicPromptBuilder:
"""Будує промпти динамічно на основі контексту запиту"""
def build(
self,
base_task: str,
context_docs: list[str] = None,
examples: list[dict] = None,
output_schema: dict = None,
constraints: list[str] = None,
) -> str:
parts = [f"Завдання: {base_task}"]
if context_docs:
docs_text = "\n\n".join([f"[Документ {i+1}]: {doc}" for i, doc in enumerate(context_docs)])
parts.append(f"\nКонтекст:\n{docs_text}")
if examples:
examples_text = "\n".join([
f"Приклад {i+1}:\nВхід: {ex['input']}\nВихід: {ex['output']}"
for i, ex in enumerate(examples)
])
parts.append(f"\nПриклади:\n{examples_text}")
if constraints:
constraints_text = "\n".join(f"- {c}" for c in constraints)
parts.append(f"\nОбмеження:\n{constraints_text}")
if output_schema:
parts.append(f"\nПоверни результат у форматі JSON:\n{json.dumps(output_schema, ensure_ascii=False, indent=2)}")
return "\n\n".join(parts)
Кейс з практики: зменшили latency p99 на 30%
Замовник обробляв 50 000 звернень на день. Кожен промпт збирався через конкатенацію рядків — часті помилки форматування та нестабільна якість. Ми впровадили бібліотеку з 12 шаблонами, розклали по use cases. Результат: час інференсу знизився на 30% за рахунок попереднього рендерингу, частка помилок впала з 5% до 0.2%. Клієнт досі використовує рішення — досвід впровадження зайняв 3 дні.
Які результати ви отримаєте?
Шаблонізація промптів — це не просто зручність, це пряма економія. Наші клієнти в середньому скорочують витрати на доробку промптів на 40% і прискорюють виведення нових сценаріїв у 3 рази порівняно зі звичайними промптами. Зв'яжіться з нами для аудиту вашої кодової бази — оцінимо проект за 2 години. Отримайте консультацію інженера з 7+ років досвіду в ML-продакшні. Ми гарантуємо якість робіт — наші фахівці мають сертифікації від AWS та LangChain.
Терміни та обсяг
- Базові шаблони для одного use case: від 1 дня (вартість від $2000).
- Бібліотека шаблонів з версіонуванням: 3–5 днів (від $5000).
- Динамічний builder з тестами: від 1 тижня (від $7000).
Точні терміни та вартість розраховуємо після аудиту вашої кодової бази. Економія від впровадження може сягати $5000 на місяць за рахунок зменшення помилок та прискорення розробки.
Як ми тестуємо шаблони?
- Пишемо модульні тести для кожного шаблону на pytest.
- Використовуємо параметризацію для перевірки всіх комбінацій змінних.
- Додаємо тести на граничні значення — порожні рядки, null, спеціальні символи.
- Включаємо тести в CI/CD — кожен коміт в репозиторій шаблонів запускає повний прогон.
- Логуємо помилки форматування в production і автоматично створюємо issue.
Порівняння підходів до зберігання шаблонів
| Сховище | Простота | Версіонування | Runtime-доступ | Коли використовувати |
|---|---|---|---|---|
| Git + YAML | Висока | Git (гілки, теги) | Ні (потрібен deploy) | Більшість проектів |
| PostgreSQL | Середня | Міграції | Так (динамічні оновлення) | Multi-tenant, часті зміни |







