Проблема: жорстко зашиті промпти вбивають гнучкість
Типова ситуація: ви написали промпт для класифікації тікетів підтримки, зашили його в код. Через місяць знадобилося додати нову категорію — довелося правити рядок у коді, перезбирати контейнер, прогоняти тести. А якщо таких промптів десятки? На кожен новий 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, часті зміни |







