Проблема: статический промпт не адаптируется к пользователю
По статистике, 70% компаний, внедривших LLM-ассистентов, сталкиваются с низкой релевантностью ответов. Статический промпт — главный виновник: он не различает контекст запроса, роль пользователя и историю диалога. Выход — динамическая сборка промпта в рантайме. Это ключевая техника промпт инжиниринга, обеспечивающая автоматическую генерацию промптов и персонализацию ответов LLM.
К нам обратилась компания с корпоративным ассистентом на 500 сотрудников. Ассистент отвечал на вопросы, но качество было низким: бухгалтер получал технические детали, а IT-инженер — слишком упрощённые объяснения. Статический промпт не учитывал ни должность, ни уровень знаний. Мы предложили решение — динамическую генерацию промптов. За две недели релевантность ответов выросла с 61% до 84%. Расскажу, как это работает и почему статический подход проигрывает.
Почему динамический промпт решает проблему низкой релевантности?
Динамическая генерация промптов — это сборка промпта в рантайме на основе контекста: профиля пользователя, результатов поиска, истории диалогов. Промпт перестаёт быть статическим текстом и превращается в артефакт, который подстраивается под каждую сессию. Такой подход даёт улучшение LLM ответов на 15–25% по сравнению с универсальным шаблоном.
Контекстно-зависимые промпты
from openai import OpenAI
from dataclasses import dataclass
from typing import Optional
import json
client = OpenAI()
@dataclass
class UserContext:
user_id: str
role: str # "admin", "manager", "employee"
department: str
language: str # "ru", "en"
expertise_level: str # "novice", "intermediate", "expert"
class DynamicPromptBuilder:
def build_system_prompt(self, context: UserContext) -> str:
"""Строит system prompt под конкретного пользователя"""
parts = [f"Ты — корпоративный ассистент."]
# Адаптация к уровню экспертизы
if context.expertise_level == "novice":
parts.append("Объясняй понятно, избегай технических терминов, используй аналогии.")
elif context.expertise_level == "expert":
parts.append("Используй технические термины без объяснений. Фокусируйся на деталях и edge cases.")
# Адаптация к роли
role_context = {
"admin": "Пользователь — системный администратор. Отвечай на технические вопросы развёрнуто.",
"manager": "Пользователь — руководитель. Акцентируй бизнес-последствия, не технические детали.",
"employee": "Пользователь — рядовой сотрудник. Давай пошаговые инструкции.",
}
if context.role in role_context:
parts.append(role_context[context.role])
# Язык ответа
if context.language == "en":
parts.append("Always respond in English.")
return " ".join(parts)
def build_user_prompt(
self,
question: str,
retrieved_docs: list[dict] = None,
conversation_history: list[dict] = None,
user_context: UserContext = None,
) -> str:
parts = []
# Добавляем релевантные документы
if retrieved_docs:
docs_text = "\n\n".join([
f"[{doc['title']}]:\n{doc['content'][:500]}"
for doc in retrieved_docs[:3]
])
parts.append(f"Релевантные документы:\n{docs_text}")
# Краткая история (последние 2 обмена)
if conversation_history and len(conversation_history) > 2:
recent = conversation_history[-4:] # 2 пары user/assistant
history_text = "\n".join([
f"{'Пользователь' if m['role'] == 'user' else 'Ассистент'}: {m['content'][:200]}"
for m in recent
])
parts.append(f"Контекст диалога:\n{history_text}")
parts.append(f"Вопрос: {question}")
return "\n\n".join(parts)
Почему статические промпты проигрывают динамическим?
Статический промпт не учитывает ни контекст, ни роль пользователя. Сравним:
| Параметр | Статический промпт | Динамический промпт |
|---|---|---|
| Релевантность ответов | 61% | 84% |
| Учёт роли пользователя | Нет | Да |
| Подгрузка документов | Нет | Да (RAG, до 3 фрагментов) |
| История диалогов | Нет | Да (последние 2 обмена) |
| Валидация токенов | Нет | Да (обрезка по лимиту) |
Отметим: как видите, динамический подход даёт почти 40% прироста в ключевых метриках. И это не предел: при тонкой настройке можно достичь 90%+. В частности, динамический промпт в 1.4 раза релевантнее статического.Наш A/B тест показал, что динамический промпт в 1.4 раза релевантнее статического
Компоненты системы динамической генерации промптов
| Компонент | Назначение | Пример реализации |
|---|---|---|
| UserContext | Профиль пользователя (роль, отдел, уровень) | LDAP, API HR-системы |
| RAG | Поиск релевантных фрагментов | ChromaDB + embeddings |
| History | Последние сообщения диалога | Redis, сессия пользователя |
| PromptCompiler | Сборка и валидация токенов | PromptCompiler.py |
Промпт из шаблона + данных
class DataDrivenPromptGenerator:
def generate_report_prompt(
self,
metrics: dict,
period: str,
audience: str,
focus_areas: list[str] = None,
) -> str:
# Определяем фокус на основе метрик
anomalies = self.detect_anomalies(metrics)
trend = self.calculate_trend(metrics)
prompt = f"""Создай отчёт за период: {period}
Аудитория: {audience}
Метрики:
{self.format_metrics(metrics)}
"""
if anomalies:
prompt += f"Аномалии (требуют объяснения):\n{json.dumps(anomalies, ensure_ascii=False)}\n\n"
if focus_areas:
prompt += f"Сфокусируйся на: {', '.join(focus_areas)}\n\n"
prompt += f"Общий тренд: {trend}\n\n"
# Формат зависит от аудитории
format_instructions = {
"ceo": "Формат: executive summary 3-4 предложения + bullet points. Без технических деталей.",
"finance": "Формат: таблица ключевых метрик + интерпретация отклонений. С цифрами.",
"team": "Формат: что сделано + что не сделано + следующие шаги.",
}
prompt += format_instructions.get(audience, "Формат: структурированный markdown.")
return prompt
def detect_anomalies(self, metrics: dict) -> list[dict]:
anomalies = []
for key, values in metrics.items():
if isinstance(values, list) and len(values) > 1:
last = values[-1]
prev = values[-2]
if prev > 0 and abs(last - prev) / prev > 0.2: # Изменение > 20%
anomalies.append({
"metric": key,
"change_pct": round((last - prev) / prev * 100, 1),
})
return anomalies
Промпт-компилятор с токен валидацией промптов
class PromptCompiler:
"""Компилирует промпт из компонентов с валидацией"""
MAX_CONTEXT_TOKENS = 60000
CHARS_PER_TOKEN = 4 # Приблизительно
def compile(
self,
components: list[dict], # [{"name": "...", "content": "...", "required": bool, "priority": int}]
query: str,
) -> str:
# Сортируем по приоритету
sorted_components = sorted(components, key=lambda x: x.get("priority", 5))
compiled_parts = []
current_tokens = len(query) // self.CHARS_PER_TOKEN
for component in sorted_components:
content = component["content"]
content_tokens = len(content) // self.CHARS_PER_TOKEN
if current_tokens + content_tokens > self.MAX_CONTEXT_TOKENS:
if component.get("required"):
# Обрезаем если обязательный
max_chars = (self.MAX_CONTEXT_TOKENS - current_tokens) * self.CHARS_PER_TOKEN
content = content[:max_chars] + "...[обрезано]"
else:
# Пропускаем если опциональный
continue
compiled_parts.append(f"## {component['name']}\n{content}")
current_tokens += content_tokens
compiled_parts.append(f"## Запрос\n{query}")
return "\n\n".join(compiled_parts)
Практический кейс: персонализированный ассистент
Из нашей практики: корпоративный ассистент LLM для 500 сотрудников разных отделов. Статический промпт давал нерелевантные ответы для разных ролей.
Отметим: Что сделали:
- При каждом запросе получаем профиль пользователя из LDAP → адаптация роли и уровня.
- RAG система: поиск по базе знаний → включение 3 релевантных фрагментов. Использовали Retrieval-Augmented Generation на ChromaDB.
- История: последние 4 сообщения → контекст диалога.
Результат: оценка релевантности ответов выросла с 61% до 84%. Гарантируем такой же прирост на ваших данных. Опыт показывает, что динамический промпт окупается за 2–3 недели эксплуатации. Окупаемость: снижение затрат на поддержку до 300 000 руб. в год.
Что входит в работу
- Аудит текущих промптов — анализ схем взаимодействия, выявление узких мест.
- Проектирование архитектуры — выбор компонентов (RAG, история, токен валидация промптов).
- Реализация DynamicPromptBuilder — код под вашу LLM и бизнес-логику.
- Интеграция с источниками данных — LDAP, базы знаний, CRM.
- Валидация и тестирование — A/B-тест на выборке, фиксация метрик.
- Документация и обучение — передача кода, описание правил сборки промптов.
- Поддержка на старте — 2 недели после внедрения.
Процесс внедрения (шаги)
- Аналитика (2 дня) — собираем профили пользователей, типы запросов, источники данных.
- Проектирование (3 дня) — проектируем схему сборки промпта, выбираем стеки (ChromaDB, LangChain).
- Реализация (5 дней) — пишем DynamicPromptBuilder, PromptCompiler, интеграция с RAG.
- Тестирование (2 дня) — A/B-тест на 10% трафика, измеряем релевантность.
- Деплой (1 день) — выкатываем на все сессии, мониторинг.
Типичные ошибки при внедрении
- Игнорирование лимита контекстного окна: без обрезки токенов модель теряет фокус на новых запросах.
- Отсутствие приоритизации компонентов: обязательные блоки (например, системный промпт) должны загружаться в первую очередь.
- Слабая валидация данных из RAG: нерелевантные документы снижают качество ответа — требуется фильтр по релевантности.
Сроки ориентировочно
- Базовая реализация (роль + контекст): 2–3 дня.
- Интеграция с RAG и историей: 1 неделя.
- Полная система с валидацией токенов: 2 недели.
Стоимость рассчитывается индивидуально. Оценим проект бесплатно — свяжитесь с нами для консультации. Закажите внедрение и получите прогноз экономии на ваших данных.
Пример: как изменяется промпт в зависимости от роли
Для администратора system prompt содержит просьбу давать развёрнутые технические ответы. Для руководителя — акцент на бизнес-последствия. Для сотрудника — пошаговые инструкции. Это реализуется через словарь role_context в DynamicPromptBuilder.







