Разработка Prompt Templates с переменными: шаблонизация промптов

Проблема: жёстко зашитые промпты убивают гибкость

Направления AI-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    998
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1267
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1003

Проблема: жёстко зашитые промпты убивают гибкость

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

Как мы реализуем шаблонизацию промптов?

Используем проверенный стек: Python 3.11+, jinja2, langchain_core, 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-пайплайны.

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 = "ru") -> 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-продакшне.

Сроки и объём

  • Базовые шаблоны для одного use case: от 1 дня.
  • Библиотека шаблонов с версионированием: 3–5 дней.
  • Динамический builder с тестами: от 1 недели.

Точные сроки рассчитываем после аудита вашей кодовой базы.

Как мы тестируем шаблоны?

  1. Пишем модульные тесты для каждого шаблона на pytest.
  2. Используем параметризацию для проверки всех комбинаций переменных.
  3. Добавляем тесты на граничные значения — пустые строки, null, специальные символы.
  4. Включаем тесты в CI/CD — каждый коммит в репозиторий шаблонов запускает полный прогон.
  5. Логируем ошибки форматирования в production и автоматически создаём issue.

Сравнение подходов к хранению шаблонов

Хранилище Простота Версионирование Runtime-доступ Когда использовать
Git + YAML Высокая Git (ветки, теги) Нет (требуется deploy) Большинство проектов
PostgreSQL Средняя Миграции Да (динамические обновления) Multi-tenant, частые изменения