Глава 5: Принцип специфичности
Принцип
Чем конкретнее промпт, тем предсказуемее результат. Каждая неявная деталь — потенциальное расхождение с ожиданием.
Спектр специфичности
Низкая Высокая
|--------------------------------------------------|
"Напиши код" "Напиши функцию" "Напиши функцию валидации
email по RFC 5322,
возвращающую bool"
Измерение специфичности
Для каждого промпта определи:
-
Сколько решений удовлетворяют промпту?
-
Сколько вопросов нужно задать для полного понимания?
Пример анализа
Промпт: "Напиши функцию сортировки"
Открытые вопросы:
- Что сортировать? (список, dict, файл?)
- По какому критерию?
- По возрастанию или убыванию?
- In-place или новая структура?
- Стабильная сортировка?
- Обработка None/NaN?
- Тип элементов?
Число вопросов: 7 → низкая специфичность
Промпт: "Напиши функцию сортировки:
- Вход: list$dict$ с ключом 'date' (datetime)
- Сортировка: по убыванию даты
- None значения: в конец
- Возврат: новый список (не in-place)"
Открытые вопросы:
- Обработка отсутствующего ключа 'date'?
Число вопросов: 1 → высокая специфичность
Техники повышения специфичности
1. Явные типы
Плохо:
"Функция принимает данные и возвращает результат"
Хорошо:
"Функция принимает list$dict\[str, Any$\] и возвращает DataFrame"
Пример:
\`\`\`python
def process(data: list$dict\[str, Any$\]) -\> pd.DataFrame:
"""
Args:
data: Список словарей с ключами 'id', 'name', 'value'
Returns:
DataFrame с колонками $'id', 'name', 'value'$
"""
\#\#\# 2. Граничные условия
Плохо: "Функция делит a на b"
Хорошо: "Функция делит a на b
-
При b=0: возвращает None (не исключение)
-
При a=0: возвращает 0.0
-
Результат: float, округление до 2 знаков"
\#\#\# 3. Примеры ввода-вывода
Плохо: "Парсер URL"
Хорошо: "Парсер URL
Примеры: 'https://example.com/path?q=1' → { 'scheme': 'https', 'host': 'example.com', 'path': '/path', 'query': {'q': '1'} }
'example.com' → { 'scheme': None, 'host': 'example.com', 'path': None, 'query': {} }"
\#\#\# 4. Явные исключения
Плохо: "Валидатор email"
Хорошо: "Валидатор email
Валидные:
Невалидные:
-
user@
-
@example.com
-
user@example (без домена верхнего уровня)"
\#\# Примеры по уровням
\#\#\# Пример 1: Обработка данных
Уровень 0 (S ≈ 0.01): "Обработай данные"
Уровень 1 (S ≈ 0.1): "Обработай CSV файл"
Уровень 2 (S ≈ 0.3): "Прочитай CSV файл и удали дубликаты"
Уровень 3 (S ≈ 0.7): "Прочитай CSV файл (UTF-8, разделитель ;) Удали дубликаты по колонке 'id' Сохрани в новый файл"
Уровень 4 (S ≈ 0.95): "ВХОД: CSV файл
-
Кодировка: UTF-8
-
Разделитель: ;
-
Колонки: id (int), name (str), email (str)
ОБРАБОТКА:
-
Удалить дубликаты по 'id' (оставить первый)
-
Удалить строки с пустым email
-
Привести email к lowercase
ВЫХОД: новый CSV файл
-
Тот же формат
-
Суффикс '_clean' в имени
ОШИБКИ:
-
Файл не найден: вернуть код 1
-
Невалидный CSV: вернуть код 2"
\#\#\# Пример 2: API endpoint
Уровень 0: "Сделай API"
Уровень 1: "Сделай REST API для пользователей"
Уровень 2: "Сделай endpoint GET /users на FastAPI"
Уровень 3: "GET /users на FastAPI:
-
Пагинация
-
Фильтрация по email
-
Сортировка по дате"
Уровень 4: "GET /users на FastAPI
Query параметры:
-
page: int = 1 (>= 1)
-
limit: int = 20 (1-100)
-
email: str | None (фильтр contains, case-insensitive)
-
sort: 'created_asc' | 'created_desc' = 'created_desc'
Response 200:
\{
'items': $\{'id': int, 'email': str, 'created\_at': str\}$,
'total': int,
'page': int,
'pages': int
\}
Response 400:
-
page < 1
-
limit не в диапазоне
Зависимости:
-
AsyncSession через Depends
-
Модель User с полями id, email, created_at"
\#\#\# Пример 3: Алгоритм
Уровень 0: "Напиши поиск"
Уровень 1: "Напиши бинарный поиск"
Уровень 2: "Напиши бинарный поиск в отсортированном списке"
Уровень 3: "Бинарный поиск:
-
Вход: отсортированный list, искомое int
-
Выход: индекс или -1
-
Сложность: O(log n)"
Уровень 4: "Бинарный поиск
Сигнатура: def binary_search(arr: list, target: int) -> int
Поведение:
-
arr отсортирован по возрастанию
-
Если target есть: вернуть индекс (при дубликатах — первый)
-
Если target нет: вернуть -1
-
Пустой список: вернуть -1
Сложность:
-
Время: O(log n)
-
Память: O(1) — без рекурсии
Примеры: binary_search(, 3) → 2 binary_search(, 6) → -1 binary_search(, 2) → 2 binary_search($$, 1) → -1"
\#\# Чеклист специфичности
Перед отправкой промпта проверь:
- $$ Типы входных данных указаны
- $$ Типы выходных данных указаны
- $$ Граничные случаи описаны
- $$ Обработка ошибок определена
- $$ Формат ответа задан
- $$ Ограничения явные
- $$ Примеры есть (для нестандартных задач)
\#\# Баланс
Избыточная специфичность тоже проблема:
Плохо (переспецифицировано): "Напиши функцию на Python 3.11.4 используя только стандартную библиотеку версии 3.11.4 в файле с именем utils.py на строках 45-60 с отступом 4 пробела в кодировке UTF-8 без BOM ..."
Хорошо (достаточно специфично): "Напиши функцию на Python 3.11+ только stdlib docstring в Google style"
Правило: специфицируй то, что влияет на корректность решения.