Перейти к содержимому

1.7 · Скрипт: temperature, токены, стоимость

Неделя 1 · Основы LLM · ⏱ ~4 ч · Практика

Продукт на LLM платит за каждый токен, и счёт растёт незаметно: длинный системный промпт × тысячи запросов × история диалога. Инженер, который с первого дня видит цену запроса, принимает другие решения (короче промпт, дешевле модель, кэш). Второй вопрос — случайность: на собеседовании почти наверняка спросят «почему модель отвечает по-разному на одно и то же и как это контролировать».

В главе 1.6 (гл. 1.6) мы видели в каждом ответе поле usage и заметили, что ответы на один вопрос различаются. Здесь разбираемся в обоих: из чего складываются токены и их цена и откуда берётся разброс. Обе темы начинаются с одного понятия — токена.

В главе 1.5 (гл. 1.5) токен появился как единица, в которой считают цену и контекст. Теперь посмотрим, как текст на них режется. Модель видит не буквы и не слова, а токены — куски текста из фиксированного словаря (обычно 100–200 тыс. элементов). Словарь строится алгоритмом BPE (Byte Pair Encoding): начинают с байтов и многократно склеивают самые частые пары. Поэтому:

  • частые английские слова — один токен ( the, price);
  • редкие слова режутся на куски ( delivery может быть одним токеном, а доставка — тремя-четырьмя);
  • пробел обычно приклеен к началу слова;
  • числа режутся непредсказуемо (12345.67 — несколько токенов).
"Delivery is free today" → [Delivery][ is][ free][ today] ≈ 4 токена
"Доставка бесплатная" → [Д][ост][авка][ бес][платная] ≈ 5+ токенов

(Разбиение условное — у каждого токенизатора своё.)

Следствия, которые важны на практике:

  1. Русский текст «дороже». Словари токенизаторов исторически оптимизировались под английский; та же мысль по-русски обычно занимает заметно больше токенов. Насколько — измерим в практике.
  2. Модели плохо считают буквы («сколько r в strawberry»): она не видит букв, она видит токены.
  3. У каждого провайдера свой токенизатор. tiktoken считает токены для моделей OpenAI, и только для них. Для Claude нужен эндпоинт подсчёта токенов Anthropic (client.messages.count_tokens(...)) или поле usage в ответе. Даже у разных поколений Claude токенизаторы различаются — при смене модели число токенов на тех же текстах может измениться.

Грубое правило для английского — ~4 символа на токен; для русского отношение хуже. Но правило — для прикидки на салфетке; в коде считаем точно.

Модель генерирует ответ по одному токену. На каждом шаге она выдаёт не слово, а оценку для каждого токена словаря — числа, которые называют логитами (чем больше, тем вероятнее токен). Функция softmax превращает логиты в распределение вероятностей по всему словарю: «следующий токен mug — 41%, cup — 32%, teapot — 9%, …». Дальше токен выбирается — это и есть сэмплирование. Выбранный токен дописывается, шаг повторяется.

logits ──► softmax(logits / T) ──► [top_p-обрезка] ──► случайный выбор ──► токен
  • temperature (T) делит логиты перед softmax. T → 0: распределение становится «острым», почти всегда берётся самый вероятный токен (greedy). T = 1: исходное распределение. T > 1: «плоское», чаще выпадают маловероятные токены — больше разнообразия и больше бреда.
  • top_p (nucleus sampling): берём минимальный набор самых вероятных токенов, сумма вероятностей которых ≥ p, остальные отбрасываем. top_p = 0.9 отрезает «длинный хвост» странных вариантов.
  • top_k: оставить только k самых вероятных токенов.

Все три параметра вместе называют параметрами сэмплирования. Мини-пример на чистом Python (без API, просто чтобы почувствовать):

import math, random
from collections import Counter
logits = {"mug": 2.0, "cup": 1.7, "teapot": 0.5, "rocket": -1.0}
def sample(logits: dict[str, float], t: float) -> str:
if t == 0:
return max(logits, key=logits.get) # greedy
exps = {k: math.exp(v / t) for k, v in logits.items()}
total = sum(exps.values())
r, acc = random.random(), 0.0
for k, e in exps.items():
acc += e / total
if r <= acc:
return k
return k
for t in (0, 0.5, 1.0, 2.0):
print(t, Counter(sample(logits, t) for _ in range(1000)))

Важная оговорка: temperature 0 — не гарантия детерминизма. Из-за параллельных вычислений на GPU, батчинга запросов и обновлений моделей одинаковый запрос иногда даёт чуть разные ответы. Воспроизводимость в продакшене обеспечивают не temperature, а логи, фиксированная версия модели и evals.

Классическая ручка случайности — temperature, но у крупных облачных провайдеров её всё чаще нет. Это то, чего нет в старых туториалах. У reasoning-моделей (которые «думают» перед ответом) сэмплирование управляется самим провайдером:

  • Anthropic: новые модели Claude принимают только значения по умолчанию, а в версии API 2026-10-21 запрос с temperature, top_p или top_k возвращает 400 на любой модели; новые Python SDK эти параметры уже не принимают. Вместо них качество/стоимость регулируют параметром effort (насколько глубоко модель думает) и промптом.
  • OpenAI: reasoning-модели (семейства o-series и GPT-5) тоже не принимают произвольный temperature; его поддерживают не-reasoning модели. Какие именно — видно на странице конкретной модели.

Зачем тогда учить temperature? Потому что (1) он встречается во всех open-weight моделях (Llama, Qwen, Mistral через Ollama/vLLM) и во многих чужих проектах; (2) понимание сэмплирования объясняет «почему модель иногда отвечает иначе» — это никуда не делось, просто ручку убрали; (3) это вопрос с собеседования. Практический вывод для эксперимента: берём модель, которая параметр ещё принимает (не-reasoning модель OpenAI или локальную модель), и проверяем в документации, что параметр поддерживается.

Когда низкая / когда высокая случайность (классическое правило; на моделях без ручки того же добиваются промптом и схемой):

Задача Случайность Почему
Извлечение данных, классификация, JSON низкая (T≈0) Нужен один правильный ответ, разнообразие = ошибки
Код, SQL низкая Синтаксис не любит креатива
Ответы на вопросы по документам низкая–средняя Точность важнее стиля
Идеи, нейминг, тексты для постов высокая (0.7–1.0) Нужно разнообразие вариантов
Генерация синтетических тестовых данных высокая Нужно покрытие разных случаев

Вторая тема главы — деньги. Мы уже знаем, что usage в ответе делит токены на input и output; цена считается по ним же. Провайдеры берут деньги за миллион токенов (MTok), отдельно за вход и выход:

cost = input_tokens / 1e6 * price_in + output_tokens / 1e6 * price_out

Например, для модели с ценой $1 / $5 за MTok запрос с 2 000 input и 500 output токенов стоит 2000/1e6·1 + 500/1e6·5 = $0.002 + $0.0025 = $0.0045. Примерно столько стоит ответ на одно сообщение в поддержку магазина (системный промпт, правила доставки, вопрос клиента — и короткий ответ); 1000 таких сообщений — $4.50. Копейки — пока не умножим на 100 000 запросов в день.

Нюансы, которые ломают наивную формулу:

  1. Output в несколько раз дороже input (типично в 4–5 раз). Генерация последовательная и дорогая для GPU, чтение — параллельное.
  2. Размышления (thinking/reasoning) оплачиваются как output, даже если их не видно в ответе. Reasoning-модель на простой вопрос может «подумать» на тысячу токенов.
  3. Кэш промптов: провайдер запоминает начало (префикс) недавних запросов, и повторное чтение этого префикса стоит дешевле. Прочитанные из кэша input-токены стоят ~10% цены, а запись в кэш — дороже обычного (глава 2.7 (гл. 2.7)). В usage это отдельные поля (cache_read_input_tokens, cache_creation_input_tokens у Anthropic; cached_tokens в деталях usage у OpenAI).
  4. Тарифы бывают ступенчатые: у некоторых моделей цена за токен выше, если промпт длиннее порога (например, 100K или 200K токенов).
  5. Batch API — обычно −50% за асинхронную обработку: запросы отправляются пачкой, а результаты приходят в течение часов (глава 2.7 (гл. 2.7)).
  6. Цены меняются. Никогда не зашиваем их глубоко в код без даты. Таблица цен живёт в одном месте с комментарием «проверено YYYY-MM-DD» и ссылкой на страницу pricing.

Функция, которая будет жить во всех наших проектах:

from dataclasses import dataclass
@dataclass(frozen=True)
class Price:
input: float # USD per 1M input tokens
output: float # USD per 1M output tokens
cache_read: float | None = None
cache_write: float | None = None
# Source: provider pricing pages, checked on YYYY-MM-DD. Fill in yourself.
PRICES: dict[str, Price] = {
# "claude-haiku-5-5": Price(input=..., output=...),
# "gpt-5-mini": Price(input=..., output=...),
}
def estimate_cost(model: str, input_tokens: int, output_tokens: int,
cache_read_tokens: int = 0, cache_write_tokens: int = 0) -> float:
p = PRICES[model] # KeyError on purpose: unknown model must not cost $0
...

Обратим внимание на KeyError «нарочно»: если функция для неизвестной модели вернёт 0, можно месяц думать, что всё бесплатно.

Договоримся о смысле аргументов: input_tokens — вход, прочитанный не из кэша (по полной цене), cache_read_tokens и cache_write_tokens — части входа, прочитанные из кэша и записанные в него. Провайдеры раскладывают usage по-разному, и перед вызовом приводим их поля к этой схеме: у Anthropic input_tokens уже не включает кэш, у OpenAI cached_tokens входит в общий input_tokens и его нужно вычесть, у DeepSeek промах и попадание даны отдельно (ниже).

estimate_cost считает по usage, то есть после запроса. Иногда стоимость нужна заранее (например, «влезет ли этот документ в контекст и бюджет»):

  • Anthropic: client.messages.count_tokens(model=..., system=..., messages=[...]).input_tokens — точное число input-токенов для конкретной модели, без генерации.
  • OpenAI: tiktoken — локально, без сети: tiktoken.encoding_for_model(model) или tiktoken.get_encoding("o200k_base") для современных моделей; число токенов — len(enc.encode(text)). Служебные токены разметки сообщений добавляют немного сверху.

Output заранее не известен — его оценивают по истории (медиана output_tokens на таких запросах) и ограничивают max_tokens.

Хорошая новость: эксперимент с temperature, который на Claude уже невозможен, на DeepSeek и Ollama делается честно. А у обоих можно даже заглянуть в само распределение вероятностей — через logprobs, логарифмы вероятностей выбранного токена и его ближайших альтернатив. Дальше идём по темам теории: случайность, цена, подсчёт токенов.

(По API Reference и гайду Thinking Mode на 13.11.2026 — стоит перепроверить.)

  • temperature от 0 до 2, по умолчанию 1 — но действует только в режиме без размышления. В режиме размышления (а он включён по умолчанию!) temperature, presence_penalty, frequency_penalty молча игнорируются: ошибки нет, эффекта нет. Сравним с Claude, где тот же запрос даёт 400 — громкая ошибка лучше тихой, тут легко «провести эксперимент» и намерить шум.
  • top_p у deepseek-flash без размышления зафиксирован на 1.0 (переданное значение игнорируется); у deepseek-v4-pro без размышления — работает. Ручки top_k нет.
  • logprobs=True и top_logprobs (до 20) — есть: можно посмотреть альтернативы для каждого токена ответа.
import math # deepseek = OpenAI(...) client from chapter 1.6
r = deepseek.chat.completions.create(
model="deepseek-flash",
messages=[{"role": "user", "content": "Name a support bot for a small online home goods shop. Name only."}],
temperature=1.0,
max_tokens=20,
logprobs=True, top_logprobs=5,
extra_body={"thinking": {"type": "disabled"}}, # otherwise temperature has no effect
)
first = r.choices[0].logprobs.content[0]
for alt in first.top_logprobs:
print(repr(alt.token), round(math.exp(alt.logprob), 3)) # real next-token distribution

Локальная модель принимает всё классическое: temperature, top_p, top_k, seed (в нативном API — в options; через OpenAI-совместимый эндпоинт — temperature, top_p, seed). seed + одинаковый запрос на той же машине обычно дают одинаковый ответ — удобно для экспериментов, хотя и это не железная гарантия. Нативный /api/chat тоже умеет logprobs / top_logprobs (в OpenAI-совместимом режиме logprobs не поддерживается).

import ollama # official client from chapter 1.6
resp = ollama.chat(
model="qwen3:4b",
messages=[{"role": "user", "content": "Name a support bot for a small online home goods shop. Name only."}],
think=False,
options={"temperature": 1.0, "top_k": 40, "seed": 7},
)

У «думающих» локальных моделей (qwen3 и др.) выключаем размышление (think=False), иначе в ответе будет рассуждение и разброс измерять неудобно.

DeepSeek считает input по-разному в зависимости от того, попал ли префикс в кэш (кэш автоматический, ничего включать не надо), и даёт скидку 50% вне пиковых часов. Цены за 1M токенов на 13.11.2026 (Models & Pricing):

Модель Input, кэш-попадание Input, промах Output Когда
deepseek-flash $0.006 $0.30 $1.20 пик
deepseek-flash $0.003 $0.15 $0.60 вне пика
deepseek-v4-pro $0.044 $1.32 $3.96 пик
deepseek-v4-pro $0.022 $0.66 $1.98 вне пика

Пиковые часы — 01:00–04:00 и 06:00–10:00 UTC в будни (кроме китайских праздников), остальное время и выходные — дешевле. По Москве (UTC+3) пик — 04:00–07:00 и 09:00–13:00 в будни, так что вечерние прогоны и всё по выходным идут за полцены; в других часовых поясах окна стоит пересчитать от UTC. Эти цифры — только пример; цены меняются, вписываем их в PRICES с датой.

usage у DeepSeek несёт всё нужное: prompt_cache_hit_tokens, prompt_cache_miss_tokens (в сумме = prompt_tokens), completion_tokens и внутри completion_tokens_details.reasoning_tokens (размышление уже включено в completion_tokens). В терминах estimate_cost: промах — это input_tokens, попадание — cache_read_tokens, записи в кэш как отдельной статьи нет. Формула:

cost = (hit / 1e6 * p.cache_read + miss / 1e6 * p.input + out / 1e6 * p.output)
# and p depends on the time of the request (peak / off-peak) for DeepSeek

Значит, estimate_cost для DeepSeek должна знать время запроса: добавляем ей последний необязательный аргумент at (время запроса в UTC), а в PRICES храним для моделей DeepSeek две цены — пиковую и обычную. Ollama стоит $0 за токен — но не «бесплатно»: платим временем (токенов/с на своей машине) и памятью. Для Ollama логируем не деньги, а скорость: eval_count / (eval_duration / 1e9) токенов/с.

Остаётся прогноз до запроса: инструменты из теории (tiktoken, count_tokens) к нашим провайдерам не подходят.

  • DeepSeek: эндпоинта подсчёта токенов нет. Точное число — только usage после запроса; документация даёт прикидку «~0.3 токена на английский символ, ~0.6 на китайский» (для русского цифры нет — её мы измерим). Есть офлайн-токенизатор в виде zip-архива на странице Token & Token Usage — это код для запуска, так что по правилам курса сначала карантин и чтение, или просто обходимся usage (запрос на 500 символов стоит доли цента).
  • Ollama: токены входа — prompt_eval_count, выхода — eval_count в нативном ответе (или usage в совместимом режиме). Токенизатор у каждой локальной модели свой.
  • tiktoken не подходит ни для DeepSeek, ни для моделей Ollama — так же, как для Claude.
Возможность Claude OpenAI DeepSeek Ollama
temperature нет (новые модели/версия API — 400) только не-reasoning модели да, но только без размышления; в режиме размышления молча игнорируется да
top_p / top_k нет top_p у части моделей top_p зависит от модели и режима; top_k нет оба, в options
seed нет см. доку модели не документирован да
logprobs нет у части моделей да, до 20 альтернатив нативный API — да; совместимый — нет
Ручка «глубины» effort reasoning.effort reasoning_effort (low/high/max) + вкл/выкл размышления think (зависит от модели)
Подсчёт до запроса count_tokens tiktoken локально нет (только usage, офлайн-архив) нет отдельного, только по факту
Скидка за кэш чтение ~0.1×, запись дороже автоматически автоматически, попадание ≈ 1/50 цены промаха — (локально)
Batch −50% −50% Batch API в документации нет; есть скидка вне пика —
  • Эксперимент с temperature — основной: DeepSeek deepseek-flash с выключенным размышлением; второй ряд — Ollama qwen3:4b с think=False и фиксированным seed.
  • Распределение следующего токена — top_logprobs у DeepSeek или в нативном API Ollama: это настоящая версия игрушечного sample().
  • RU vs EN токены — usage DeepSeek и prompt_eval_count Ollama (оба считают по своим токенизаторам), tiktoken и count_tokens у Anthropic — для сравнения.
  • Тексты для эксперимента — публичные (правила доставки с сайта любого магазина, кусок README). Сообщения клиентов и журнал событий заказов — только в Ollama.
  1. Let’s build the GPT Tokenizer — Карпатый (видео ~2 ч 13 мин; на 1.25x — около 1 ч 45 мин). Если времени мало, хватит первых ~25 минут (зачем токенизация, Unicode, байты) и финального блока про «странности» LLM, которые объясняются токенизацией. Забираем оттуда: BPE, почему не-английские языки и числа токенизируются хуже, почему модели плохо считают буквы.
  2. Открываем веб-токенизатор OpenAI platform.openai.com/tokenizer (≈10 мин): вставляем одну фразу на английском и русском, число и артикул товара вроде MUG-NORTH-350. Смотрим глазами, где режется текст.
  3. tiktoken → README (≈15 мин): как получить кодировку для модели и посчитать токены. Отметим: это только про модели OpenAI.
  4. Документация Anthropic → страница Token counting (docs.anthropic.com/en/docs/build-with-claude/token-counting) (≈15 мин): как вызвать подсчёт, что он принимает (system, tools, изображения).
  5. Страницы цен (≈20 мин): Anthropic Pricing и OpenAI API Pricing. Выписываем в PRICES цены двух-трёх моделей, которыми пользуемся, с датой. Находим на странице Anthropic строки про cache reads/writes и batch.
  6. В документации OpenAI на странице модели, выбранной для эксперимента с temperature, проверяем, поддерживается ли параметр (≈5 мин). В документации Anthropic смотрим на effort в описании Messages API / adaptive thinking — это ручка, которая у Claude пришла на место «креативности».
  7. DeepSeek (≈30 мин): Models & Pricing — выписываем цены кэш-попадания/промаха/output и пиковые часы; Token & Token Usage — как считаются токены; в Create Chat Completion находим temperature, top_p, logprobs, top_logprobs и пометки, в каком режиме они действуют; в Thinking Mode — список параметров, которые в режиме размышления не работают.
  8. Ollama (≈15 мин): Chat API — options (temperature, top_p, top_k, seed, num_predict), logprobs, поля prompt_eval_count/eval_count и длительности в наносекундах; на странице OpenAI compatibility — какие из этих параметров доступны в совместимом режиме.
  • Не путаем max_tokens и бюджет. max_tokens — потолок одного ответа, не лимит денег. Бюджет — наш код, который суммирует estimate_cost по вызовам.
  • Молча игнорируемый параметр хуже 400. DeepSeek в режиме размышления и Ollama в OpenAI-совместимом режиме (logprobs, tool_choice) принимают параметр без ошибки и ничего с ним не делают. Перед экспериментом сверяемся с документацией, действует ли ручка в этом режиме.
  • Цена DeepSeek зависит от часов. Сравнивая стоимость двух прогонов, проверяем, что оба были в одном тарифном окне (пик/вне пика) — иначе разница «в 2 раза» окажется просто временем суток.
  • tiktoken для Claude — ошибка. Числа будут похожи, но не совпадут, и на русском расхождение может быть заметным.
  • Thinking-токены. Если у модели включено размышление, output_tokens включает его. «Короткий» ответ может стоить как длинный.
  • 5 прогонов — маленькая выборка. Для «разброса» это нормально, для выводов о качестве — нет (это тема evals: глава 2.5 · Упражнение: JSON из 20 текстов и неделя 6).
  • Temperature 0 ≠ одинаковые ответы. Если в эксперименте при T=0 ответы чуть различаются — это не ошибка, это реальность GPU-инференса.
  • Как это ломается в продакшене: тихо растущий промпт. Кто-то добавил в системный промпт бота поддержки «на всякий случай» всю базу знаний магазина, и средний запрос вырос с 800 до 12 000 токенов. Без логирования usage на каждом вызове об этом узнают из счёта в конце месяца.
  • Сравниваем модели по цене задачи, а не токена. Модель вдвое дешевле за токен, которой нужно три попытки, дороже.

Код кладём в exercises/week-01/. Модель для эксперимента с temperature — основная: DeepSeek deepseek-flash с выключенным размышлением (иначе temperature игнорируется); вторая — Ollama (qwen3:4b, think=False), установленная в главе 1.5 с проверкой подписи. Не-reasoning модель OpenAI — по желанию, для сравнения.

  • 1. Игрушечное сэмплирование. Запускаем пример sample() из теории для T = 0 / 0.5 / 1 / 2 по 1000 раз. Записываем распределения. Должно быть видно: при T=0 всегда mug, при T=2 rocket появляется заметно чаще.
  • 2. Эксперимент с temperature. exercises/week-01/temperature.py: вопрос, где есть пространство для вариантов, например «Придумай название для бота поддержки небольшого интернет-магазина товаров для дома. Ответь только названием». Для T ∈ {0, 0.5, 1} (у DeepSeek и Ollama можно добавить 1.5) — по 5 прогонов на DeepSeek и на Ollama. Сохраняем всё в results/temperature.jsonl (provider, model, temperature, run, text, input_tokens, output_tokens).
  • 2б. Ловушка размышления. Повторяем шаг 2 на DeepSeek без thinking: disabled для T=0 и T=1.5. Ошибки не будет. Есть ли разница в разбросе? Записываем вывод: где параметр молча игнорируется, эксперимент «доказывает» что угодно — всегда проверяем по документации, действует ли ручка в этом режиме.
  • 2в. Настоящее распределение. Для вопроса из шага 2 запрашиваем logprobs=True, top_logprobs=5 у DeepSeek (без размышления) и в нативном API Ollama. Выводим топ-5 первого токена с вероятностями exp(logprob) и сравниваем с игрушкой из шага 1. Смотрим, как они меняются при T=0.2 и T=1.5 (подсказка: логпробы обычно отражают распределение до сэмплирования — стоит проверить, так ли у конкретного провайдера).
  • 3. Второй вопрос — «фактический». «Сколько граммов чая в упаковке 0,25 кг? Ответь числом». Те же 3 × 5 прогонов. Сравниваем: на фактическом вопросе разброс должен быть меньше при любой T.
  • 4. Мера разброса. Для каждой T считаем число уникальных ответов из 5 (после strip().lower()). Это грубо, но наглядно.
  • 5. estimate_cost. Реализуем функцию из теории в exercises/week-01/cost.py, заполняем PRICES с датой проверки: обе модели DeepSeek (пик и вне пика, кэш-попадание и промах), модель Ollama с ценой 0 и, если пользуемся, Claude/OpenAI. Для DeepSeek функция принимает время запроса at (UTC) и сама выбирает тариф. Тесты: для модели с ценой $1/$5 и 2000/500 токенов должно выйти 0.0045 (а 1000 таких ответов поддержки — 4.5); для DeepSeek запрос в среду 08:00 UTC стоит вдвое дороже того же запроса в субботу. Добавляем assert.
  • 5б. Скорость Ollama. Для 5 прогонов на Ollama считаем токенов/с (eval_count / eval_duration * 1e9) и время загрузки модели (load_duration) на первом и последующих запросах. Добавляем колонку «ток/с» в таблицу шага 6.
  • 6. Итоговая таблица. Скрипт печатает Markdown-таблицу:
| temperature | вопрос | уникальных ответов из 5 | ср. output_tokens | стоимость 5 прогонов, $ |
|---|---|---|---|---|
| 0 | название | 1 | 6 | 0.00002 |
| ... |
  • 7. Русский vs английский. exercises/week-01/tokens_ru_en.py: берём 3 текста по 300–500 символов (например, абзац правил доставки или возврата с сайта магазина, описание товара, кусок README) в двух версиях — русской и английской. Считаем токены: основное — usage.prompt_tokens у DeepSeek и prompt_eval_count у Ollama (вычитаем токены пустого запроса, чтобы убрать служебную разметку чата); для сравнения — tiktoken (o200k_base) и count_tokens у Anthropic. Таблица: текст × язык × токенайзер × токены × токенов на символ. Выводим «русский дороже в X раз» для каждого токенайзера. Тексты — публичные; реальное сообщение клиента, если оно есть, — только в Ollama.
  • 8. Вывод. В notes/temperature-cost.md 5–7 строк: когда нужна низкая случайность, когда высокая; во сколько раз output дороже input у выбранных моделей; во сколько русский дороже английского; что делать на моделях, где temperature нет или где он молча игнорируется; сколько экономит DeepSeek вне пика.
  • Есть таблица temperature × разброс ответов × стоимость (шаг 6) и таблица RU/EN токенов (шаг 7).
  • estimate_cost(model, input_tokens, output_tokens) лежит в отдельном модуле, покрыта хотя бы одним assert, цены подписаны датой и источником; для DeepSeek учтены кэш-попадания и пиковые часы.
  • Мы видели реальное распределение следующего токена (top_logprobs) хотя бы у одного провайдера.
  • В notes/temperature-cost.md записан вывод: низкая случайность — извлечение данных, классификация, код; высокая — идеи, тексты, синтетические данные.

Вопрос 1

Что делает temperature с распределением вероятностей следующего токена?

  • a) меняет словарь модели
  • b) делит логиты перед softmax, делая распределение острее или площе
  • c) ограничивает длину ответа
  • d) выбирает модель

b. Логиты делятся на T: малая T усиливает разницу между вариантами, большая — сглаживает.

Вопрос 2

Что такое top_p = 0.9?

Сэмплирование только из минимального набора самых вероятных токенов с суммарной вероятностью ≥ 90%; маловероятный «хвост» отбрасывается.

Вопрос 3

Почему одна и та же фраза на русском обычно занимает больше токенов, чем на английском?

Словари BPE строились на корпусах, где доминирует английский: частые английские слова стали целыми токенами, а русские слова режутся на куски.

Вопрос 4

При T=0 на одинаковый запрос пришли два слегка разных ответа. Это баг SDK?

Нет. Параллельные вычисления на GPU и батчинг дают небольшую недетерминированность даже при greedy-выборе.

Вопрос 5

Запрос: 10 000 input, 1 000 output, цена $3 / $15 за MTok. Сколько стоит запрос?

10 000/1e6·3 + 1 000/1e6·15 = 0.03 + 0.015 = $0.045.

Вопрос 6

Что не так в этом коде?

import tiktoken
enc = tiktoken.get_encoding("o200k_base")
def claude_cost(prompt: str) -> float:
return len(enc.encode(prompt)) / 1e6 * PRICE_IN["claude-haiku-5-5"]

tiktoken — токенизатор OpenAI, для Claude счёт неточен (нужен count_tokens или usage); считается только input, хотя output дороже; а ещё не учтены system, история и thinking-токены.

Вопрос 7

Запрос к новой модели Claude с temperature=0.2 вернул 400. Почему и что делать?

Новые модели Claude принимают только сэмплирование по умолчанию, а версия API 2026-10-21 отвергает эти параметры на любой модели. Убрать параметр; управлять поведением промптом, схемой вывода и effort.

Вопрос 8

Почему модели плохо отвечают на «сколько букв r в слове strawberry»?

Модель видит токены, а не буквы: strawberry — один-два токена, их внутренняя «буквенная» структура модели напрямую не видна.

Вопрос 9

Какая temperature (низкая/высокая) уместна для: (а) извлечения суммы заказа из письма клиента; (б) генерации 20 вариантов поста о новой коллекции кружек; (в) генерации SQL?

(а) низкая, (б) высокая, (в) низкая.

Вопрос 10

Почему функция estimate_cost должна падать на неизвестной модели, а не возвращать 0?

Нулевая цена молча скрывает расходы; падение сразу заставит добавить цену новой модели.

Вопрос 11

Модель с ценой за токен вдвое ниже дала верный ответ в 50% случаев, дорогая — в 95%. Ошибка обнаруживается и запрос повторяется. Какая дешевле «за правильный ответ», если output одинаковый?

Ожидаемое число попыток: дешёвая 1/0.5 = 2, дорогая 1/0.95 ≈ 1.05. Цена за правильный ответ: дешёвая 2 × 0.5 = 1.0 условной единицы, дорогая 1.05 × 1 ≈ 1.05 — почти одинаково; а если ошибки не обнаруживаются автоматически, дешёвая модель обходится намного дороже.

Вопрос 12

В deepseek-flash отправлены запросы с temperature=0 и temperature=1.5 без других параметров, ошибок нет, а разброс ответов одинаковый. Почему?

  • a) DeepSeek не поддерживает temperature вообще
  • b) по умолчанию включён режим размышления, в нём temperature молча игнорируется
  • c) temperature работает только выше 1.0
  • d) нужен seed

b. Режим размышления у DeepSeek включён по умолчанию, и temperature в нём не действует — без ошибки. Нужно выключить размышление (thinking: disabled) и повторить.

Вопрос 13

Запрос к DeepSeek: 10 000 input-токенов, из них 8 000 — попадание в кэш, 1 000 output. Цены (условные): попадание $0.01, промах $0.30, output $1.20 за MTok. Сколько стоит запрос? А если бы кэша не было?

8 000/1e6·0.01 + 2 000/1e6·0.30 + 1 000/1e6·1.20 = 0.00008 + 0.0006 + 0.0012 ≈ $0.0019. Без кэша: 10 000/1e6·0.30 + 0.0012 = $0.0042 — кэш сэкономил больше половины.

Вопрос 14

Почему Ollama — лучший провайдер этого курса для эксперимента с сэмплированием, и почему её «$0 за токен» не значит «бесплатно»?

Она принимает все классические ручки (temperature, top_p, top_k, seed) и отдаёт logprobs в нативном API, а данные не уходят с машины. Платим памятью, диском, временем генерации (токены/с на своей машине) и качеством небольшой модели.

Дальше неделя 2 и главный рычаг качества — [2.1 · Промптинг: роли, few-shot, структура (гл. 2.1)](/week-02/2-1/). Функция estimate_cost пригодится уже там, при сравнении версий промпта, и станет частью llm_utils.py в [2.7 · Стриминг, ретраи, rate limits, prompt caching (гл. 2.7)](/week-02/2-7/).