Prompt Engineering: искусство общения с LLM
opensourceaillmprompt-engineeringit
Введение: промпт — это новый код
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.
Ключевые выводы:
- Few-shot > Zero-shot для сложных задач
- CoT значительно улучшает математические результаты
- Роли помогают задать тон и формат
- Структурированные промпты = структурированные ответы
- Всегда оценивайте и тестируйте промпты
Ресурсы: