← НАЗАД К КУРСУ Этап 5 · Урок 4 из 5

Урок 5.4 — Оптимизация и стоимость: как не разориться на AI

Ты построил бота, подключил базу данных, задеплоил на сервер. Всё работает. Но потом приходит счёт от Anthropic или OpenAI — и оказывается, что бот за месяц съел $200. А если пользователей станет 1000? $2000. Оптимизация стоимости — это не «потом», это «с первого дня». В этом уроке — конкретные техники, которые снижают расходы на 60-95%.


Откуда берутся расходы

Каждый запрос к Claude или GPT стоит денег. Стоимость складывается из двух частей:

Стоимость одного запроса = Входные токены × Цена + Выходные токены × Цена

Входные токены (input):
  Системный промпт + История чата + Вопрос пользователя

Выходные токены (output):
  Ответ модели

Важно: выходные токены стоят в 3-5 раз дороже входных!

Цены основных моделей (2026)

Модель Вход (за 1M токенов) Выход (за 1M токенов) Для чего
Claude Haiku 4.5 $1.00 $5.00 Простые задачи, классификация
Claude Sonnet 4.6 $3.00 $15.00 Баланс цена/качество
Claude Opus 4.6 $5.00 $25.00 Сложные задачи, агенты
GPT-4o-mini $0.15 $0.60 Самый дешёвый от OpenAI
GPT-4o $2.50 $10.00 Основная модель OpenAI

Аналогия. Модели — как рестораны. Haiku — это столовая: быстро, дёшево, для простых блюд. Sonnet — кафе: хорошее качество за разумную цену. Opus — ресторан с мишленовской звездой: лучшее качество, но за каждое блюдо платишь втрое.

Источник: Anthropic — Pricing
https://platform.claude.com/docs/en/about-claude/pricing

Стратегия 1: Выбирай правильную модель

Самая простая и мощная оптимизация — не использовать дорогую модель там, где справится дешёвая.

┌─────────────────────────────────────────────────────────┐
│           КАКУЮ МОДЕЛЬ ДЛЯ КАКОЙ ЗАДАЧИ                  │
│                                                           │
│  Haiku ($1/$5)                                           │
│    Классификация: "позитивный / негативный"              │
│    Извлечение данных: "найди email в тексте"             │
│    Простые ответы: FAQ, шаблонные ответы                 │
│    Маршрутизация: "это вопрос про оплату или доставку?"  │
│                                                           │
│  Sonnet ($3/$15)                                         │
│    Генерация текста: статьи, письма, описания            │
│    Анализ: разбор документа, резюме                      │
│    Код: написание и объяснение кода                      │
│    Чат-бот: основной рабочий бот                         │
│                                                           │
│  Opus ($5/$25)                                           │
│    Сложный анализ: юридические документы, исследования   │
│    Агенты: многошаговые задачи с инструментами          │
│    Математика и логика: сложные вычисления               │
│    Критические задачи: где ошибка стоит дорого           │
└─────────────────────────────────────────────────────────┘

Паттерн: маршрутизатор моделей

Вместо одной дорогой модели на все запросы — маленькая модель решает, какую использовать:

# router.py — маршрутизатор: выбирает модель в зависимости от сложности

from anthropic import Anthropic

client = Anthropic()

def classify_complexity(question):
    """Haiku классифицирует сложность вопроса."""
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=50,
        messages=[{
            "role": "user",
            "content": f"""Оцени сложность вопроса: simple или complex.
simple = FAQ, факт, простой ответ
complex = анализ, рассуждение, многошаговая задача

Вопрос: {question}
Ответь одним словом: simple или complex"""
        }]
    )
    return response.content[0].text.strip().lower()


def smart_answer(question):
    """Отправляет вопрос в подходящую модель."""
    complexity = classify_complexity(question)

    if complexity == "simple":
        model = "claude-haiku-4-5"    # $1/$5 — дешёвый
        print(f"  → Haiku (простой вопрос)")
    else:
        model = "claude-sonnet-4-6"   # $3/$15 — средний
        print(f"  → Sonnet (сложный вопрос)")

    response = client.messages.create(
        model=model,
        max_tokens=1024,
        messages=[{"role": "user", "content": question}]
    )
    return response.content[0].text


# Примеры
print(smart_answer("Какая столица Франции?"))       # → Haiku
print(smart_answer("Объясни теорию относительности"))  # → Sonnet

Экономия: если 70% запросов простые — экономишь 60-70% бюджета.


Стратегия 2: Prompt caching (кеширование промптов)

Проблема

Каждый запрос к Claude содержит:
  Системный промпт:        500 токенов   (одинаковый каждый раз!)
  История разговора:       2000 токенов   (растёт с каждым сообщением)
  Вопрос пользователя:     50 токенов    (новый каждый раз)

Без кеширования: каждый запрос = 2550 входных токенов
За 1000 запросов: 2,550,000 токенов × $3/1M = $7.65

С кешированием: системный промпт + история кешируются
  Первый запрос: 2550 токенов (полная цена + запись в кеш)
  Последующие: 50 новых токенов + 2500 из кеша (скидка 90%)
  За 1000 запросов: ~$1.00

Экономия: ~87%

Как работает

Запрос 1 (первый раз):
  [Системный промпт] [История] [Вопрос]
  ────────────────── ────────── ───────
  Записывается в кеш    Записывается   Обрабатывается
  (×1.25 цена)          в кеш          (обычная цена)

Запрос 2 (из кеша):
  [Системный промпт] [История] [Новый вопрос]
  ────────────────── ────────── ─────────────
  Читается из кеша   Читается     Обрабатывается
  (×0.1 цена — 90% скидка!)       (обычная цена)

Код

# Автоматическое кеширование (рекомендуемый способ)
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},  # ← автоматическое кеширование
    system="Ты — помощник техподдержки компании X. Вот правила: ...(длинный текст)...",
    messages=[{"role": "user", "content": "Как вернуть товар?"}]
)

# Проверяем экономию
usage = response.usage
print(f"Входные токены: {usage.input_tokens}")
print(f"Из кеша: {getattr(usage, 'cache_read_input_tokens', 0)}")
print(f"Записано в кеш: {getattr(usage, 'cache_creation_input_tokens', 0)}")

Когда кеширование эффективно

Идеально:
  ✓ Большой системный промпт (500+ токенов)
  ✓ Много запросов с одинаковым контекстом
  ✓ Чат-бот с длинной историей
  ✓ RAG с одним и тем же набором документов

Не поможет:
  ✗ Каждый запрос уникальный (нет повторяющегося контекста)
  ✗ Промпт меньше 1024 токенов (минимум для кеширования)
  ✗ Запросы реже чем раз в 5 минут (кеш истекает)

Реальный результат: один разработчик описал снижение с $720 до $72 в месяц — экономия 90% — просто включив prompt caching.

Источники:
Anthropic — Prompt Caching
https://platform.claude.com/docs/en/build-with-claude/prompt-caching

Стратегия 3: Batch API (пакетная обработка)

Что это

Если запросы не требуют мгновенного ответа — отправляй их пакетом. API обработает их в фоне за 50% стоимости.

Обычный запрос:
  Отправил → Получил ответ через 2 секунды
  Цена: 100%

Batch (пакет):
  Отправил 1000 запросов → Получил все ответы через 1-24 часа
  Цена: 50% (скидка 50%)

Когда использовать

Идеально для batch:
  ✓ Обработка 1000 отзывов → классификация
  ✓ Перевод 500 описаний товаров
  ✓ Генерация резюме для 200 документов
  ✓ Еженочная аналитика данных
  ✓ Подготовка ответов на FAQ

Не подходит для batch:
  ✗ Чат-бот (пользователь ждёт ответ прямо сейчас)
  ✗ Интерактивные приложения
  ✗ Срочные задачи

Код

# batch_classify.py — классификация 100 отзывов за 50% стоимости

from anthropic import Anthropic
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request

client = Anthropic()

# Список отзывов для классификации
reviews = [
    "Отличный товар, буду заказывать ещё!",
    "Ужасное качество, верну обратно.",
    "Нормально, ничего особенного.",
    # ... ещё 97 отзывов
]

# 1. Создаём пакет запросов
requests = [
    Request(
        custom_id=f"review-{i}",
        params=MessageCreateParamsNonStreaming(
            model="claude-haiku-4-5",  # дешёвая модель для простой задачи
            max_tokens=50,
            messages=[{
                "role": "user",
                "content": f"Классифицируй отзыв одним словом (positive/negative/neutral): {review}"
            }]
        )
    )
    for i, review in enumerate(reviews)
]

# 2. Отправляем пакет
batch = client.messages.batches.create(requests=requests)
print(f"Пакет создан: {batch.id}")
print(f"Статус: {batch.processing_status}")

# 3. Ждём результатов (обычно 1-24 часа)
import time
while True:
    batch = client.messages.batches.retrieve(batch.id)
    if batch.processing_status == "ended":
        break
    print(f"Обработка... ({batch.request_counts.processing} в очереди)")
    time.sleep(60)

# 4. Собираем результаты
for result in client.messages.batches.results(batch.id):
    if result.result.type == "succeeded":
        text = next(
            (b.text for b in result.result.message.content if b.type == "text"),
            ""
        )
        print(f"{result.custom_id}: {text}")

Расчёт экономии

100 отзывов × Haiku:
  Обычный API:  ~$0.03
  Batch API:    ~$0.015 (скидка 50%)

  Для 100 отзывов разница копеечная.

100 000 отзывов × Haiku:
  Обычный API:  ~$30
  Batch API:    ~$15

  $15 экономии — уже заметно.

10 000 документов × Sonnet (генерация резюме):
  Обычный API:  ~$500
  Batch API:    ~$250

  $250 экономии — существенно.

Комбинация: batch + caching

Batch API:      -50%
Prompt caching: -90% на повторяющийся контекст
Вместе:         до -95% от обычной цены

Пример: 10 000 запросов с общим системным промптом
  Обычная цена:     $500
  Batch + caching:  $25-50
Источники:
Anthropic — Batch Processing
https://platform.claude.com/docs/en/build-with-claude/batch-processing

Стратегия 4: Оптимизация токенов

Выходные токены дороже — контролируй длину ответа

# Плохо: модель генерирует длинный ответ
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,  # ← разрешаем длинный ответ
    messages=[{"role": "user", "content": "Какая столица Франции?"}]
)
# Ответ: "Столица Франции — Париж. Париж является крупнейшим городом
# Франции и одним из важнейших... (500 токенов бла-бла-бла)"
# Стоимость вывода: 500 × $15/1M = $0.0075

# Хорошо: ограничиваем и инструктируем
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=256,   # ← ограничиваем длину
    messages=[{
        "role": "user",
        "content": "Какая столица Франции? Ответь одним предложением."
    }]
)
# Ответ: "Столица Франции — Париж."
# Стоимость вывода: 10 × $15/1M = $0.00015
# Экономия: 50 раз дешевле!

Оптимизация системного промпта

# Плохо: раздутый промпт (300 токенов)
system = """Ты — дружелюбный помощник по имени Алекс.
Ты работаешь в компании "Электроника Плюс".
Ты должен всегда быть вежливым и профессиональным.
Ты должен отвечать на русском языке.
Если ты не знаешь ответ, скажи что не знаешь.
Не придумывай информацию.
Используй простой язык, понятный обычному человеку.
Если вопрос не по теме магазина, вежливо перенаправь.
Форматируй ответы с помощью списков, когда уместно.
Всегда спрашивай, нужна ли ещё помощь в конце ответа."""

# Хорошо: лаконичный промпт (100 токенов)
system = """Помощник "Электроника Плюс". Отвечай кратко, на русском.
Не знаешь — скажи. Не по теме — перенаправь. В конце: "Чем ещё помочь?"."""

Управление историей чата

# Проблема: история растёт → каждое сообщение дороже

# Плохо: отправляем ВСЮ историю (может вырасти до 50 000 токенов)
messages = full_conversation_history  # 100 сообщений = 50K токенов

# Хорошо: ограничиваем историю последними N сообщениями
MAX_HISTORY = 10  # последние 10 сообщений

messages = full_conversation_history[-MAX_HISTORY:]

# Ещё лучше: суммаризация + последние сообщения
# (из урока 3.3 — память агента)
def optimize_history(messages, max_messages=10):
    """Если история длинная — суммаризируем старые сообщения."""
    if len(messages) <= max_messages:
        return messages

    old_messages = messages[:-max_messages]
    recent_messages = messages[-max_messages:]

    # Суммаризируем старые сообщения (дёшево, один раз)
    summary = summarize(old_messages)  # из урока 3.3

    return [
        {"role": "user", "content": f"Краткое содержание предыдущего разговора: {summary}"},
        {"role": "assistant", "content": "Понял, продолжаем."},
        *recent_messages
    ]

Стратегия 5: Effort parameter (уровень усилий)

Claude позволяет контролировать, сколько думать перед ответом:

# Простой вопрос — минимальные усилия
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    output_config={"effort": "low"},  # ← быстро и дёшево
    messages=[{"role": "user", "content": "Переведи 'hello' на русский"}]
)

# Сложная задача — максимальные усилия
response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=8192,
    output_config={"effort": "max"},  # ← глубокий анализ (только Opus)
    messages=[{"role": "user", "content": "Проанализируй этот контракт..."}]
)
Уровни усилий:
  low    — быстрый ответ, минимум рассуждений. Для простых задач.
  medium — стандартный баланс.
  high   — глубокий анализ (по умолчанию).
  max    — максимальная глубина (только Opus 4.6).

Используй low для простых задач — модель отвечает быстрее и тратит меньше выходных токенов.


Стратегия 6: Мониторинг расходов

Отслеживай каждый запрос

# cost_tracker.py — простой трекер расходов

import json
from datetime import datetime

COST_LOG = "api_costs.jsonl"

# Цены за 1 токен (для расчёта)
PRICES = {
    "claude-haiku-4-5": {"input": 1.0 / 1_000_000, "output": 5.0 / 1_000_000},
    "claude-sonnet-4-6": {"input": 3.0 / 1_000_000, "output": 15.0 / 1_000_000},
    "claude-opus-4-6": {"input": 5.0 / 1_000_000, "output": 25.0 / 1_000_000},
}


def log_cost(model, usage):
    """Записывает стоимость запроса в лог."""
    prices = PRICES.get(model, PRICES["claude-sonnet-4-6"])

    input_cost = usage.input_tokens * prices["input"]
    output_cost = usage.output_tokens * prices["output"]
    total_cost = input_cost + output_cost

    entry = {
        "timestamp": datetime.now().isoformat(),
        "model": model,
        "input_tokens": usage.input_tokens,
        "output_tokens": usage.output_tokens,
        "cost_usd": round(total_cost, 6),
    }

    with open(COST_LOG, "a") as f:
        f.write(json.dumps(entry) + "\n")

    return total_cost


def get_daily_spend():
    """Считает расходы за сегодня."""
    today = datetime.now().date().isoformat()
    total = 0.0

    try:
        with open(COST_LOG) as f:
            for line in f:
                entry = json.loads(line)
                if entry["timestamp"].startswith(today):
                    total += entry["cost_usd"]
    except FileNotFoundError:
        pass

    return total


# Использование
from anthropic import Anthropic
client = Anthropic()

model = "claude-sonnet-4-6"
response = client.messages.create(
    model=model,
    max_tokens=1024,
    messages=[{"role": "user", "content": "Привет!"}]
)

cost = log_cost(model, response.usage)
print(f"Запрос стоил: ${cost:.4f}")
print(f"Потрачено сегодня: ${get_daily_spend():.4f}")

Установи лимиты

# Проверяем бюджет перед каждым запросом
DAILY_BUDGET = 5.00  # $5 в день

def safe_request(model, messages, max_tokens=1024):
    """Запрос с проверкой бюджета."""
    daily_spend = get_daily_spend()

    if daily_spend >= DAILY_BUDGET:
        return "Дневной бюджет исчерпан. Попробуйте завтра."

    response = client.messages.create(
        model=model,
        max_tokens=max_tokens,
        messages=messages
    )

    cost = log_cost(model, response.usage)
    print(f"Расход: ${cost:.4f} | Сегодня: ${daily_spend + cost:.4f} / ${DAILY_BUDGET}")

    return response.content[0].text

Стратегия 7: Структурированный вывод

Когда нужен конкретный формат — используй structured output. Модель возвращает JSON по схеме, без лишнего текста.

# Без structured output:
# "Давайте проанализируем этот отзыв. Он содержит позитивные элементы,
# в частности... В целом, я бы классифицировал его как позитивный
# с уверенностью около 0.85..."
# → 80 токенов вывода

# Со structured output:
response = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=100,
    output_config={
        "format": {
            "type": "json_schema",
            "schema": {
                "type": "object",
                "properties": {
                    "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
                    "confidence": {"type": "number"}
                },
                "required": ["sentiment", "confidence"],
                "additionalProperties": False
            }
        }
    },
    messages=[{
        "role": "user",
        "content": "Классифицируй: 'Отличный товар, рекомендую!'"
    }]
)
# → {"sentiment": "positive", "confidence": 0.95}
# → 15 токенов вывода (в 5 раз меньше!)

Сводная таблица: все стратегии

┌─────────────────────────────────────────────────────────┐
│          СТРАТЕГИИ ОПТИМИЗАЦИИ СТОИМОСТИ                  │
│                                                           │
│  Стратегия            Экономия    Сложность   Когда       │
│  ─────────            ────────    ─────────   ─────       │
│  Правильная модель    60-80%      Низкая      Всегда      │
│  Prompt caching       до 90%      Низкая      Повторы     │
│  Batch API            50%         Средняя     Не срочно   │
│  Короткие ответы      50-90%      Низкая      Всегда      │
│  Управление историей  30-70%      Средняя     Чат-боты    │
│  Effort parameter     20-50%      Низкая      Простые     │
│  Structured output    50-80%      Средняя     Извлечение  │
│  Маршрутизатор        60-70%      Высокая     Много задач │
│                                                           │
│  Комбинация всех:     до 95%                              │
└─────────────────────────────────────────────────────────┘

Пример: сколько стоит бот в продакшене

Сценарий: Telegram-бот техподдержки
  1000 пользователей
  5 сообщений в день на пользователя
  = 5000 запросов в день
  = 150 000 запросов в месяц

Без оптимизации (Sonnet, длинные ответы):
  Средний запрос: 1000 входных + 500 выходных токенов
  Входные: 150M × $3/1M = $450
  Выходные: 75M × $15/1M = $1125
  Итого: ~$1575/мес

С полной оптимизацией:
  1. Маршрутизатор: 70% запросов → Haiku
     Haiku: 105K запросов × (1000×$1/1M + 200×$5/1M) = $210
     Sonnet: 45K запросов × (1000×$3/1M + 300×$15/1M) = $337
  2. Prompt caching: -90% на повторяющийся контекст
     Эффективная стоимость: ~$80
  3. Короткие ответы: "Ответь в 2-3 предложениях"
     Ещё -40% на выходных токенах: ~$50

  Итого: ~$50-80/мес (вместо $1575!)
  Экономия: 95%

Token counting: считай ДО отправки

# Проверяем стоимость запроса ДО отправки

count = client.messages.count_tokens(
    model="claude-sonnet-4-6",
    messages=messages,
    system=system_prompt
)

estimated_cost = count.input_tokens * 3.0 / 1_000_000
print(f"Входных токенов: {count.input_tokens}")
print(f"Оценка стоимости входа: ${estimated_cost:.4f}")

# Если слишком дорого — обрежь историю или используй модель дешевле
if count.input_tokens > 10_000:
    print("Слишком длинный контекст! Суммаризирую историю...")
    messages = optimize_history(messages)

Практика

Задание 1: Трекер расходов

Добавь log_cost() из этого урока в бота из урока 4.1. После каждого запроса к Claude записывай стоимость в файл. В конце дня выведи общую сумму.

Задание 2: Маршрутизатор моделей

Реализуй classify_complexity() из этого урока. Протестируй на 10 разных вопросах: простые («Который час?») и сложные («Объясни квантовую запутанность»). Правильно ли маршрутизатор выбирает модель?

Задание 3: Оптимизация промпта

Возьми системный промпт из бота (урок 4.1) и сократи его в 2-3 раза, сохранив смысл. Сравни качество ответов до и после. Изменилось ли что-то?


Задачки на закрепление

Задача 1: Почему выходные токены дороже входных?

Ответ Генерация текста (выход) требует больше вычислений, чем чтение текста (вход). При генерации модель должна последовательно предсказывать каждый следующий токен, что требует полного прохода через нейросеть для каждого токена. При чтении входных данных модель обрабатывает их параллельно за один проход. Поэтому провайдеры берут за выходные токены в 3-5 раз больше.

Задача 2: Бот отправляет 10 000 запросов в день с одинаковым системным промптом на 2000 токенов. Какую стратегию применить первой?

Ответ Prompt caching — первое, что нужно включить. 2000 токенов системного промпта × 10 000 запросов = 20 000 000 входных токенов в день, и все с одинаковым контекстом. Кеширование снизит стоимость этих 20M токенов на 90%. Одна строчка кода (`cache_control={"type": "ephemeral"}`) может сэкономить сотни долларов в месяц.

Задача 3: Когда НЕ стоит использовать Batch API?

Ответ Batch API не подходит, когда пользователь ждёт ответ в реальном времени: чат-боты, интерактивные приложения, голосовые ассистенты. Batch обрабатывает запросы в фоне за 1-24 часа. Он идеален для фоновых задач: массовая классификация, генерация отчётов, перевод документов, ночная аналитика.

Задача 4: Что эффективнее: сократить системный промпт с 1000 до 500 токенов или ограничить ответ с 500 до 100 токенов?

Ответ Ограничить ответ эффективнее. Выходные токены стоят в 3-5 раз дороже входных. Сокращение ответа с 500 до 100 токенов экономит 400 выходных токенов (= 400 × $15/1M для Sonnet = $0.006 за запрос). Сокращение промпта на 500 входных токенов экономит 500 × $3/1M = $0.0015 за запрос. Ответ в 4 раза эффективнее. А ещё лучше — сделать оба.

Глоссарий

Термин Что значит
Входные токены (input) Токены, которые ты отправляешь модели (промпт + история + вопрос)
Выходные токены (output) Токены, которые модель генерирует в ответ (стоят в 3-5× дороже)
Prompt caching Кеширование повторяющегося контекста на серверах Anthropic — скидка 90%
Batch API Пакетная обработка запросов в фоне — скидка 50%
Маршрутизатор моделей Паттерн: дешёвая модель решает, какую модель использовать для ответа
Effort parameter Управление глубиной рассуждений модели (low/medium/high/max)
Structured output Принудительный формат ответа (JSON по схеме) — меньше лишних токенов
Token counting Подсчёт токенов ДО отправки запроса для оценки стоимости
max_tokens Ограничение максимальной длины ответа модели
cache_control Параметр API для включения кеширования промптов
Суммаризация истории Замена длинной истории чата кратким пересказом для экономии токенов
TTL (Time to Live) Время жизни кеша: 5 минут (по умолчанию) или 1 час

Главное

Проблема:
  API стоит денег. Без оптимизации бот с 1000 пользователей
  может обходиться в $1500/мес.

7 стратегий (от простого к сложному):
  1. Правильная модель — Haiku для простого, Sonnet для среднего
  2. Prompt caching — 90% скидка на повторяющийся контекст
  3. Batch API — 50% скидка на фоновые задачи
  4. Короткие ответы — "ответь в 2 предложениях" = в 5 раз дешевле
  5. Управление историей — не отправлять 100 сообщений, достаточно 10
  6. Effort parameter — low для простых задач
  7. Structured output — JSON вместо текста = меньше токенов

Правила:
  • Выходные токены в 3-5× дороже входных → контролируй длину ответа
  • Считай расходы с первого дня → log_cost() в каждый запрос
  • Комбинируй стратегии → batch + caching = до 95% экономии
  • Ставь лимиты → DAILY_BUDGET защищает от сюрпризов

Что дальше?

Мы прошли все инструменты: от первого API-запроса до оптимизации на тысячи пользователей. В финальном уроке — архитектура AI-продуктов: как объединить всё изученное в одну систему. Проектирование, паттерны, масштабирование и реальные архитектуры продуктов.

← ПРЕДЫДУЩИЙ СЛЕДУЮЩИЙ →