Prompt Engineering: искусство общения с LLM

opensourceaillmprompt-engineeringit
← Back to Blog

Введение: промпт — это новый код

Prompt Engineering — это искусство формулировать запросы для LLM. Это не просто "написать текст", это программирование на естественном языке.

Факт: Разница между плохим и хорошим промптом может давать разницу в качестве ответа в 5-10x.


Что такое Prompt Engineering?

Проблема

Плохой промпт:
  User: "Напиши про погоду"
  LLM: "Погода — это состояние атмосферы..."
  ✗ Не то что нужно
  ✗ Нет структуры
  ✗ Нет контекста

Хороший промпт:
  User: "Опиши погоду в Москве 1 января 2026 в формате JSON"
  LLM: {"city": "Москва", "date": "2026-01-01", "temp": -15, ...}
  ✓ Конкретно
  ✓ Структурировано
  ✓ С контекстом

Почему это важно?

LLM — это как гениальный студент, который не читал ваш учебник.

Он знает ОЧЕНЬ много, но:
- Не знает контекст вашего проекта
- Не знает формат который вам нужен
- Не знает уровень детализации

Prompt Engineering — это способ объяснить LLM что именно нужно.

Уровень 1: Zero-shot Prompting

Что такое Zero-shot?

Zero-shot = LLM выполняет задачу без примеров

User: "Переведи на английский: Привет, мир!"
LLM: "Hello, world!"

Это работает потому что LLM обучен на огромном корпусе данных.

Basic Zero-shot

# Classification
response = llm.chat(messages=[{
    'role': 'user',
    'content': '''
Классифицируй отзыв по_sentiment:
Отзыв: "Отличный телефон, быстрая камера"
_sentiment:
'''
}])

# Результат: positive

# Translation
response = llm.chat(messages=[{
    'role': 'user',
    'content': '''
Переведи на французский:
Русский: "Доброе утро"
Французский:
'''
}])

# Результат: "Bonjour"

Zero-shot Limitations

✅ Zero-shot работает когда:
- Задача простая и распространенная
- LLM хорошо обучен на этой задаче
- Формат ответа простой

❌ Zero-shot НЕ работает когда:
- Нужна специфическая доменная экспертиза
- Нужен строгий формат
- НужнаFew-shot точность

Уровень 2: Few-shot Prompting

Что такое Few-shot?

Few-shot = даем LLM несколько примеров перед задачей

User: "Классифицируй отзывы"

Пример 1:
Отзыв: "Отличный телефон!"
Класс: positive

Пример 2:
Отзыв: "Ужасный сервис"
Класс: negative

Отзыв: "Нормально за свою цену"
Класс:

Few-shot Example

response = llm.chat(messages=[
    {'role': 'system', 'content': 'Ты классификатор отзывов.'},
    
    {'role': 'user', 'content': 'Отличный телефон!'},
    {'role': 'assistant', 'content': 'positive'},
    
    {'role': 'user', 'content': 'Приехал на 2 недели позже'},
    {'role': 'assistant', 'content': 'negative'},
    
    {'role': 'user', 'content': 'Средний телефон'},
    {'role': 'assistant', 'content': 'neutral'},
    
    {'role': 'user', 'content': 'Лучший телефон в 2026!'},
])

# LLM генерирует: "positive"

Why Few-shot Works

Few-shot работает потому что:

1. LLM видит pattern (паттерн ответа)
2. LLM понимает формат (структуру)
3. LLM получает контекст (домен)

Аналогия:
  Zero-shot   = экзамен без подготовки
  Few-shot    = экзамен с примерами вопросов
  Fine-tuning = экзамен после курса обучения

Уровень 3: Chain-of-Thought (CoT)

Что такое CoT?

Chain-of-Thought = просим LLM рассуждать перед ответом

Без CoT:
  User: "У меня 5 яблок, я съел 2, купил еще 3. Сколько?"
  LLM: "6"
  (угадал)

С CoT:
  User: "Рассуждай пошагово: У меня 5 яблок..."
  LLM: "Было: 5. Съел 2: 5-2=3. Купил 3: 3+3=6. Ответ: 6"
  (правильно!)

CoT Example

response = llm.chat(messages=[{
    'role': 'user',
    'content': '''
Рассуждай пошагово.

Задача: В корзине 12 яблок. Петер взял 3, Мария взяла в 2 раза больше.
Сколько яблок осталось?

Рассуждение:
1. Было: 12 яблок
2. Петер взял: 3
3. Мария взяла: 3 * 2 = 6
4. Осталось: 12 - 3 - 6 = 3

Ответ: 3
'''
}])

System-level CoT

response = llm.chat(messages=[{
    'role': 'system',
    'content': '''
Ты помощник который всегда рассужает пошагово.
Для каждой задачи:
1. Пойми что требуется
2. Разбей на шаги
3. Выполни каждый шаг
4. Проверь результат
5. Дай финальный ответ
'''
}, {
    'role': 'user',
    'content': 'Сколько будет 47 * 23?'
}])

# LLM:
# 1. Нужно умножить 47 * 23
# 2. 47 * 20 = 940
# 3. 47 * 3 = 141
# 4. 940 + 141 = 1081
# Ответ: 1081

Уровень 4: Role Prompting

Assigning Roles

# Role: Expert Developer
response = llm.chat(messages=[{
    'role': 'system',
    'content': 'Ты senior Python разработчик с 10 летним опытом.'
}, {
    'role': 'user',
    'content': 'Как оптимизировать SQL запрос?'
}])

# Результат: глубокий технический ответ с примерами

# Role: 5-year-old
response = llm.chat(messages=[{
    'role': 'system',
    'content': 'Объясни как будто мне 5 лет.'
}, {
    'role': 'user',
    'content': 'Как работает интернет?'
}])

# Результат: простое объяснение с аналогиями

Multiple Roles

response = llm.chat(messages=[{
    'role': 'system',
    'content': '''
Проведи дискуссию между тремя экспертами:
- Alice: security expert
- Bob: performance expert
- Charlie: UX expert

Каждый дает мнение по теме.
'''
}, {
    'role': 'user',
    'content': 'Как реализовать аутентификацию в веб-приложении?'
}])

# Результат:
# Alice: "Используй OAuth 2.0 с PKCE..."
# Bob: "Кэшируй сессии в Redis..."
# Charlie: "Добавь biometric login..."

Уровень 5: Structured Prompting

Template-based Prompts

def create_classification_prompt(text: str, categories: list):
    return f"""
Классифицируй текст в одну из категорий:
{', '.join(categories)}

Текст: {text}

Формат ответа:
Category: <категория>
Confidence: <0.0-1.0>
Reason: <объяснение>
"""

prompt = create_classification_prompt(
    "Телефон разбился через месяц",
    ["positive", "negative", "neutral"]
)

XML Tags in Prompts

response = llm.chat(messages=[{
    'role': 'user',
    'content': '''
Проанализируй текст по тегам:

<text>
Отличный телефон, но батарея слабая
</text>

<instructions>
1. Извлеки положительные аспекты
2. Извлеки отрицательные аспекты
3. Дай общий рейтинг 1-5
</instructions>

<output_format>
<positives>
- ...
</positives>
<negatives>
- ...
</negatives>
<rating>3</rating>
</output_format>
'''
}])

Уровень 6: Advanced Techniques

Self-Consistency

# Генерируем несколько ответов и выбираем большинство
responses = []
for _ in range(5):
    r = llm.chat(messages=[{
        'role': 'user',
        'content': 'Рассуждай пошагово: 15 * 27 = ?'
    }])
    responses.append(r)

# Выбираем наиболее частый ответ
from collections import Counter
best = Counter(responses).most_common(1)[0][0]

ReAct (Reasoning + Acting)

def react_agent(question, tools):
    """
    ReAct = Reasoning + Acting
    
    Цикл:
    Thought → Action → Observation → Thought → ... → Answer
    """
    messages = []
    
    for _ in range(5):
        # Thought
        response = llm.chat(messages=[
            *messages,
            {'role': 'user', 'content': question}
        ])
        
        if response.contains_answer:
            return response.answer
        
        # Action
        action = response.action
        observation = execute_tool(action, tools)
        
        # Add to messages
        messages.append({'role': 'assistant', 'content': response.thought})
        messages.append({'role': 'user', 'content': f"Observation: {observation}"})

Tree of Thoughts

Tree of Thoughts = несколько веток рассуждений

                    [Задача]
                   /    |    \
              [Мысль1] [Мысль2] [Мысль3]
               /          |          \
          [Вывод1a]   [Вывод2a]   [Вывод3a]
             |            |            |
          [Ответ1]    [Ответ2]    [Ответ3]
                            |
                      [Лучший: Ответ2]

Уровень 7: Prompt Templates

Jinja2 Templates

{% raw %}
<system>
Ты помощник для {{domain}}.
Твоя задача: {{task}}
</system>

<user>
{{user_input}}
</user>

<output_format>
{% for field in output_fields %}
- {{field}}
{% endfor %}
</output_format>
{% endraw %}

Python Template Engine

class PromptTemplate:
    def __init__(self, template: str):
        self.template = template
    
    def render(self, **kwargs) -> str:
        return self.template.format(**kwargs)

# Usage
template = PromptTemplate("""
Классифицируй: {text}
Категории: {categories}

Формат:
Category: ...
Confidence: ...
""")

prompt = template.render(
    text="Телефон отличный!",
    categories=["positive", "negative", "neutral"]
)

Уровень 8: Evaluation

Prompt Evaluation Metrics

Metrics для промптов:
1. Accuracy: % правильных ответов
2. Consistency: одинаковость результатов
3. Robustness: устойчивость к вариациям
4. Latency: время генерации
5. Token Cost: стоимость токенов

A/B Testing Prompts

import random

def evaluate_prompt(prompt_a, prompt_b, test_cases):
    results_a = []
    results_b = []
    
    for case in test_cases:
        score_a = quality_score(llm.chat(prompt_a + case))
        score_b = quality_score(llm.chat(prompt_b + case))
        
        results_a.append(score_a)
        results_b.append(score_b)
    
    return {
        'prompt_a_avg': sum(results_a) / len(results_a),
        'prompt_b_avg': sum(results_b) / len(results_b),
        'winner': 'A' if results_a_avg > results_b_avg else 'B'
    }

Уровень 9: Common Patterns

Extraction Pattern

extraction_prompt = """
Извлеки информацию из текста:

<text>{text}</text>

Извлеки:
- Имена людей
- Даты
- Организации
- Локации

Формат: JSON
"""

Summarization Pattern

summary_prompt = """
Проанализируй текст и создай структурированное резюме:

<text>{text}</text>

Создай:
1. Заголовок (1 предложение)
2. Ключевые точки (3-5 пунктов)
3. Тон текста (positive/negative/neutral)
4. Целевая аудитория
"""

Code Generation Pattern

code_prompt = """
Напиши Python код для задачи:

<task>{task}</task>

Требования:
- Используй type hints
- Добавь docstrings
- Обработай ошибки
- Добавь примеры использования
"""

Уровень 10: Future of Prompt Engineering

Auto-Prompt Generation

Будущее:
- LLM сам генерирует лучшие промпты
- A/B тестирование промптов автоматически
- Adaptive prompts (меняются по контексту)
- Personalized prompts (под пользователя)

Prompt Compression

Исследование:
- Как сжать промпт без потери качества
- Minimal prompts (3-5 слов вместо 100)
- Embedding-based prompts
- Learned prompts (оптимизированные)

Заключение

Prompt Engineering — это навык №1 для работы с LLM.

Ключевые выводы:

  1. Few-shot > Zero-shot для сложных задач
  2. CoT значительно улучшает математические результаты
  3. Роли помогают задать тон и формат
  4. Структурированные промпты = структурированные ответы
  5. Всегда оценивайте и тестируйте промпты

Ресурсы: