Почему ваши промпты не работают так, как должны
Вы даете агенту задачу: "Создай компонент кнопки на React". Он возвращает код. Работает? Технически да. Эффективно? Абсолютно нет.
Проблема не в том, что модели плохие (на январь 2026 года у нас есть Claude 3.7 Sonnet, GPT-4.5 Turbo, Qwen2.5-72B). Проблема в том, что мы просим их делать слишком много предположений. Каждая неявная инструкция - это точка отказа. Каждая двусмысленность - шанс на нерелевантный ответ.
Zero-shot prompting убивает вашу продуктивность. Вы тратите 80% времени не на кодирование, а на исправление того, что агент понял неправильно.
Что такое few-shot prompting и почему он работает
Few-shot prompting - это не просто "дай несколько примеров". Это способ перепрограммировать поведение модели без тонкой настройки. Вы показываете не просто шаблон, а контекст принятия решений.
Вот почему это работает: модели на январь 2026 года (особенно те, что используют архитектуры типа MoE - Mixture of Experts) научились распознавать паттерны в примерах. Они не просто копируют синтаксис - они копируют логику принятия решений.
Как НЕ надо делать: классические ошибки
Сначала покажу плохие примеры. Потому что 90% разработчиков делают именно так - и удивляются, почему результаты такие посредственные.
✗ Ошибка 1: Слишком общие примеры
Вот как выглядит типичный плохой промпт:
# ПЛОХОЙ ПРИМЕР
Пример 1:
Задача: Создать функцию сложения
Код:
def add(a, b):
return a + b
Задача: Создать функцию умножения
Код:
def multiply(a, b):
return a * b
Теперь создай функцию деления
Что не так? Примеры тривиальные. Они не показывают контекст, edge cases, стиль кодирования. Модель скопирует шаблон, но не логику.
✗ Ошибка 2: Примеры без объяснений
# ПЛОХОЙ ПРИМЕР
Пример компонента:
function Button() {
return <button>Click me</button>;
}
Создай компонент Input
Нет объяснения, почему кнопка выглядит именно так. Нет требований к доступности, нет обработчиков событий, нет пропсов.
Правильный few-shot prompting: практические примеры
Теперь перейдем к тому, как делать правильно. Я разбил это на конкретные сценарии из реальной веб-разработки.
1 Сценарий 1: React компоненты с TypeScript и тестами
Вам нужен не просто компонент. Вам нужен production-ready компонент со всеми атрибутами качества.
// Пример 1: Кнопка с полной спецификацией
Задача: Создать доступную, типизированную кнопку для React с поддержкой тем
Требования:
- TypeScript с strict режимом
- Поддержка тем через CSS variables
- Полная доступность (ARIA)
- Storybook-совместимые пропсы
- Unit тесты с Testing Library
Код:
// Button.tsx
import React from 'react';
import './Button.css';
type ButtonVariant = 'primary' | 'secondary' | 'ghost';
type ButtonSize = 'sm' | 'md' | 'lg';
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
isLoading?: boolean;
children: React.ReactNode;
}
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', isLoading = false, children, className = '', ...props }, ref) => {
return (
<button
ref={ref}
className={`btn btn-${variant} btn-${size} ${isLoading ? 'loading' : ''} ${className}`}
disabled={isLoading || props.disabled}
aria-busy={isLoading}
{...props}
>
{isLoading ? (
<>
<span className="sr-only">Loading</span>
<div className="spinner" aria-hidden="true" />
<>
) : children}
</button>
);
}
);
Button.displayName = 'Button';
// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('renders with children', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
});
it('shows loading state', () => {
render(<Button isLoading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
});
});
// Пример 2: Поле ввода с валидацией
Задача: Создать поле ввода с валидацией email и debounce
Требования:
- Валидация email в реальном времени
- Debounce 300ms для onChange
- Показ ошибок под полем
- Поддержка маски телефона
Код:
// Input.tsx
import React, { useState, useCallback } from 'react';
import debounce from 'lodash/debounce';
type InputType = 'text' | 'email' | 'tel';
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
type?: InputType;
label?: string;
error?: string;
onValueChange?: (value: string, isValid: boolean) => void;
}
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ type = 'text', label, error, onValueChange, onChange, className = '', ...props }, ref) => {
const [internalError, setInternalError] = useState<string | undefined>(error);
const validateEmail = useCallback((email: string): boolean => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}, []);
const debouncedChange = useCallback(
debounce((e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
let isValid = true;
if (type === 'email' && value) {
isValid = validateEmail(value);
setInternalError(isValid ? undefined : 'Invalid email format');
}
onValueChange?.(value, isValid);
}, 300),
[type, validateEmail, onValueChange]
);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(e);
debouncedChange(e);
};
return (
<div className={`input-wrapper ${className}`}>
{label && (
<label className="input-label" htmlFor={props.id}>
{label}
</label>
)}
<input
ref={ref}
type={type}
className={`input ${internalError ? 'input-error' : ''}`}
onChange={handleChange}
aria-invalid={!!internalError}
aria-describedby={internalError ? `${props.id}-error` : undefined}
{...props}
/>
{internalError && (
<div id={`${props.id}-error`} className="input-error-message" role="alert">
{internalError}
</div>
)}
</div>
);
}
);
Input.displayName = 'Input';
Теперь создай компонент Select с:
- Виртуализацией для списков более 100 элементов
- Поиском по options
- Multi-select поддержкой
- Кастомными render функциями для options
Видите разницу? Примеры не просто показывают синтаксис. Они показывают:
- Структуру проекта (отдельные файлы для компонентов и тестов)
- Требования к качеству (доступность, типизация)
- Обработку edge cases (loading состояния, ошибки)
- Использование современных паттернов (forwardRef, custom hooks)
2 Сценарий 2: API эндпоинты с валидацией и ошибками
Backend разработка - это не просто CRUD. Это обработка ошибок, валидация, документация.
# Пример 1: FastAPI эндпоинт с полной обработкой ошибок
Задача: Создать эндпоинт для создания пользователя
Требования:
- FastAPI с Pydantic v2
- Валидация email и пароля
- Хеширование пароля с bcrypt
- Обработка дубликатов
- Логирование
- Swagger документация
Код:
# schemas.py
from pydantic import BaseModel, EmailStr, field_validator
import re
class UserCreate(BaseModel):
email: EmailStr
password: str
username: str
@field_validator('password')
@classmethod
def validate_password(cls, v: str) -> str:
if len(v) < 8:
raise ValueError('Password must be at least 8 characters')
if not re.search(r'[A-Z]', v):
raise ValueError('Password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('Password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('Password must contain at least one digit')
return v
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not re.match(r'^[a-zA-Z0-9_]{3,20}$', v):
raise ValueError('Username must be 3-20 characters, alphanumeric and underscores only')
return v
class UserResponse(BaseModel):
id: int
email: str
username: str
created_at: datetime
# crud.py
from sqlalchemy.orm import Session
import bcrypt
from . import models, schemas
import logging
logger = logging.getLogger(__name__)
def create_user(db: Session, user: schemas.UserCreate) -> models.User:
# Проверка на дубликат email
existing_user = db.query(models.User).filter(models.User.email == user.email).first()
if existing_user:
logger.warning(f"Attempt to create duplicate user with email: {user.email}")
raise HTTPException(
status_code=400,
detail="User with this email already exists"
)
# Хеширование пароля
hashed_password = bcrypt.hashpw(user.password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
db_user = models.User(
email=user.email,
hashed_password=hashed_password,
username=user.username
)
try:
db.add(db_user)
db.commit()
db.refresh(db_user)
logger.info(f"User created successfully: {user.email}")
return db_user
except Exception as e:
db.rollback()
logger.error(f"Failed to create user {user.email}: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal server error"
)
# endpoints.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from . import crud, schemas, database
import logging
router = APIRouter(prefix="/users", tags=["users"])
logger = logging.getLogger(__name__)
@router.post("/", response_model=schemas.UserResponse, status_code=201)
async def create_user(
user: schemas.UserCreate,
db: Session = Depends(database.get_db)
):
"""
Create a new user.
- **email**: must be valid email format
- **password**: at least 8 chars, uppercase, lowercase, digit
- **username**: 3-20 chars, alphanumeric and underscores only
Returns created user data without password.
"""
try:
db_user = crud.create_user(db, user)
return db_user
except HTTPException:
raise
except Exception as e:
logger.error(f"Unexpected error in create_user: {str(e)}")
raise HTTPException(
status_code=500,
detail="Internal server error"
)
# Пример 2: Эндпоинт для загрузки файлов с валидацией
Задача: Создать эндпоинт для загрузки изображений
Требования:
- Валидация MIME типа
- Максимальный размер 5MB
- Ресайз изображений
- Watermark
- S3 upload
- Асинхронная обработка
Код:
# (здесь будет аналогичный подробный пример)
Теперь создай эндпоинт для reset password с:
- Rate limiting (5 попыток в час)
- JWT токенами
- Email отправкой через Celery
- Валидацией токена по времени
- Логированием попыток
Продвинутые техники few-shot prompting
Когда базовые примеры освоены, можно переходить к продвинутым сценариям.
Техника 1: Цепочки примеров
Показывайте не отдельные примеры, а связанные цепочки. Например:
// Шаг 1: Создание схемы базы данных
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
hashed_password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
// Шаг 2: Создание модели SQLAlchemy
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, index=True, nullable=False)
hashed_password = Column(String(255), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
// Шаг 3: Создание Pydantic схемы
class UserCreate(BaseModel):
email: EmailStr
password: str
@validator('password')
def validate_password(cls, v):
# валидация пароля
return v
// Шаг 4: Создание CRUD функции
def create_user(db: Session, user: UserCreate):
# логика создания
pass
// Шаг 5: Создание эндпоинта
@router.post("/users")
def create_user_endpoint(user: UserCreate, db: Session = Depends(get_db)):
# обработка запроса
pass
Модель учится не просто синтаксису, а архитектурным паттернам. Она понимает связи между слоями приложения.
Техника 2: Примеры с контекстом ошибок
Показывайте не только правильный код, но и типичные ошибки с исправлениями:
# ПЛОХО: Уязвимость к SQL инъекциям
def get_user(username: str):
query = f"SELECT * FROM users WHERE username = '{username}'"
# Уязвимость!
# ХОРОШО: Использование параметризованных запросов
def get_user(username: str):
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
# Безопасно
# ПЛОХО: Нет обработки исключений
def read_file(path: str):
with open(path, 'r') as f:
return f.read()
# Что если файла нет?
# ХОРОШО: Полная обработка ошибок
def read_file(path: str) -> str:
try:
with open(path, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
logger.error(f"File not found: {path}")
raise HTTPException(status_code=404, detail="File not found")
except UnicodeDecodeError:
logger.error(f"Encoding error: {path}")
raise HTTPException(status_code=400, detail="Invalid file encoding")
except Exception as e:
logger.error(f"Unexpected error reading {path}: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
Интеграция с мультиагентными системами
Few-shot prompting становится особенно мощным в сочетании с архитектурой суб-агентов. Представьте:
- Агент-анализатор получает задачу и создает few-shot промпт на основе контекста
- Агент-кодер использует этот промпт для генерации кода
- Агент-ревьюер проверяет результат на соответствие примерам
Это не теория. В статье про паттерны Amazon я рассказывал, как они используют подобные системы в продакшене.
| Подход | Точность кода | Количество правок | Время на задачу |
|---|---|---|---|
| Zero-shot | 35-45% | 4-6 итераций | 15-20 минут |
| Few-shot (3 примера) | 75-85% | 1-2 итерации | 5-7 минут |
| Few-shot (5+ примеров) | 90-95% | 0-1 итерация | 2-4 минуты |
Оптимизация промптов для скорости
Больше примеров - больше токенов. Больше токенов - больше латентность. Как разорвать этот круг?
Используйте технику "сжатых примеров". Вместо полного кода показывайте только сигнатуры и комментарии:
# Пример 1: FastAPI эндпоинт с валидацией
# Структура: schemas.py -> crud.py -> endpoints.py
# Особенности: полная валидация Pydantic v2, обработка ошибок, логирование
# Смотри полный пример в кодовой базе: /api/examples/user_create_full.py
# Пример 2: Асинхронная обработка файлов
# Структура: upload_router.py + celery_tasks.py + s3_client.py
# Особенности: валидация MIME, ресайз, watermark, S3, Celery
# Смотри полный пример: /api/examples/file_upload_full.py
Создай по аналогии эндпоинт для...
Модели на январь 2026 года достаточно умны, чтобы понять ссылки на структуру. Они не нуждаются в полном коде - им нужны паттерны.
Не перегружайте промпты. 3-5 качественных примеров лучше 10 посредственных. Каждый пример должен добавлять новую информацию, а не повторять уже показанное.
Частые вопросы и ошибки
Сколько примеров нужно?
Закон убывающей отдачи работает и здесь. После 5-7 примеров качество растет незначительно, а латентность увеличивается линейно. Моя рекомендация: начинайте с 3 примеров разной сложности.
А если контекстное окно маленькое?
Используйте техники оптимизации контекста. Сжимайте примеры, убирайте комментарии, используйте более краткие имена переменных.
Модели забывают примеры в длинных сессиях
Да, это проблема. Решение: сохраняйте few-shot примеры в отдельном файле и инжектируйте их в начало каждого нового контекста. Или используйте систему с суб-агентами, где один агент отвечает только за поддержание примеров.
Примеры устаревают
Актуальность критична. На январь 2026 года не используйте примеры с устаревшими библиотеками. Если показываете работу с React - используйте React 19+ с новыми хуками. Если FastAPI - Pydantic v2 с новым синтаксисом валидации.
Почему это увеличивает эффективность в 5 раз
Давайте посчитаем:
- Без few-shot: 15 минут на задачу × 4 итерации правок = 60 минут работы
- С few-shot: 4 минуты на задачу × 1 итерация = 4 минуты работы
- Разница: 60 / 4 = 15x (в идеальных условиях)
- Реально: 5-7x из-за времени на подготовку примеров
Но главная экономия - не в минутах. Она - в ментальной нагрузке. Вы перестаете быть корректором ИИ и становитесь архитектором. Вы проектируете паттерны, а не исправляете ошибки.
Попробуйте сегодня. Возьмите одну задачу, подготовьте 3 подробных примера, дайте агенту. Результат вас удивит. А через неделю вы будете недоумевать, как вообще работали по-старому.
И помните: лучшие примеры - это ваш собственный код. Начните собирать библиотеку few-shot примеров из своего production кода. Через месяц у вас будет собственная база знаний, которая сделает ваших агентов в 10 раз эффективнее.