Few-Shot Prompting для AI-агентов: увеличиваем эффективность кодирования в 5 раз | AiManual
AiManual Logo Ai / Manual.
25 Янв 2026 Гайд

Few-Shot Prompting для кодирования: как увеличить эффективность AI-агентов в 5 раз на практических примерах

Практическое руководство по few-shot prompting для AI-агентов в веб-разработке. Примеры промптов, техники увеличения производительности и снижения неоднозначнос

Почему ваши промпты не работают так, как должны

Вы даете агенту задачу: "Создай компонент кнопки на 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) научились распознавать паттерны в примерах. Они не просто копируют синтаксис - они копируют логику принятия решений.

💡
Few-shot prompting работает как документация для модели. Вы не говорите "делай так", вы показываете "вот как мы решаем подобные проблемы". Разница фундаментальная.

Как НЕ надо делать: классические ошибки

Сначала покажу плохие примеры. Потому что 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
- Валидацией токена по времени
- Логированием попыток
💡
Обратите внимание: примеры включают не только успешные сценарии, но и обработку ошибок. Это критически важно - модели часто забывают про edge cases, если их явно не показать.

Продвинутые техники 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 раз

Давайте посчитаем:

  1. Без few-shot: 15 минут на задачу × 4 итерации правок = 60 минут работы
  2. С few-shot: 4 минуты на задачу × 1 итерация = 4 минуты работы
  3. Разница: 60 / 4 = 15x (в идеальных условиях)
  4. Реально: 5-7x из-за времени на подготовку примеров

Но главная экономия - не в минутах. Она - в ментальной нагрузке. Вы перестаете быть корректором ИИ и становитесь архитектором. Вы проектируете паттерны, а не исправляете ошибки.

Попробуйте сегодня. Возьмите одну задачу, подготовьте 3 подробных примера, дайте агенту. Результат вас удивит. А через неделю вы будете недоумевать, как вообще работали по-старому.

И помните: лучшие примеры - это ваш собственный код. Начните собирать библиотеку few-shot примеров из своего production кода. Через месяц у вас будет собственная база знаний, которая сделает ваших агентов в 10 раз эффективнее.