Урок 4. Параметры: max_tokens, temperature, stop_sequences
В прошлых уроках мы отправляли запросы почти вслепую: подставляли model, писали любое max_tokens и смотрели, что вернётся. Пора взять управление в руки. Три параметра решают, каким будет ответ: max_tokens — насколько длинным ему позволено быть, temperature — насколько предсказуемым, stop_sequences — где именно оборвать генерацию. Плюс отдельный system, который задаёт роль и тон.
anthropic.
from dotenv import load_dotenv
from anthropic import Anthropic
load_dotenv() # подтягиваем переменные окружения
client = Anthropic() # SDK сам ищет ANTHROPIC_API_KEY
Токены за минуту
Модель не «думает» целыми словами. Текст разбивается на токены — кусочки слов, из которых Claude читает ваш промпт и по одному собирает ответ. Ваш промпт превращается в токены на входе, а генерация идёт токен за токеном на выходе.
Грубая прикидка для английского: один токен — примерно 3,5 символа. Для русского и других языков соотношение другое, обычно токенов на тот же текст уходит больше.
Это важно по трём причинам: токены входа и выхода считаются в лимиты и стоимость API; чем больше токенов генерируется, тем дольше идёт ответ; и слишком маленький потолок обрежет ответ на полуслове.
max_tokens и stop_reason
max_tokens — обязательный параметр в каждом запросе. Он задаёт верхнюю границу числа токенов, которые Claude сгенерирует. Не «желаемую длину», не «примерный ориентир» — жёсткий потолок. Дошли до него — генерация обрывается ровно там, где стояла.
Попросим стихотворение и поставим потолок в 10 токенов:
truncated_response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=10,
messages=[
{"role": "user", "content": "Напиши мне стихотворение"}
]
)
print(truncated_response.content[0].text)
В ответ придёт огрызок вроде такого:
Вот стихотворение для тебя:
Ветер
Claude начал писать и замолчал на полуслове. Почему — можно спросить у самого ответа, у него есть свойство stop_reason:
truncated_response.stop_reason
# 'max_tokens'
Значение "max_tokens" означает ровно то, что написано: модель упёрлась в наш потолок. Поднимем его до 500 — и получим целое стихотворение, а stop_reason станет "end_turn": модель договорила сама, ей больше нечего было добавить.
max_tokens, когда пишет ответ. Этот параметр не делает текст короче или содержательнее — он только даёт место писать дальше (большое значение) или обрубает написанное (маленькое). Хотите короткий ответ по смыслу — просите об этом в промпте.
Обратное тоже верно: большое max_tokens не заставит Claude писать много. Попросите анекдот с потолком в 1000 токенов — получите анекдот и всё. Проверить фактический расход можно через response.usage.output_tokens: там будет что-нибудь вроде 55, а не 1000. Потолок — это потолок, а не задание.
И про скорость. Если попросить Claude написать очень длинный диалог трижды, с потолками 100, 1000 и 4096 токенов, картина будет примерно такой:
Сгенерировано токенов: 100
Время: 1.51 с
Сгенерировано токенов: 1000
Время: 8.33 с
Сгенерировано токенов: 3433
Время: 28.80 с
Чем больше токенов генерируется, тем дольше вы ждёте. Зависимость почти линейная — и это первое, куда стоит смотреть, когда «API тормозит».
Вы поставили max_tokens=10 и попросили стихотворение. Что произойдёт?
Стоп-последовательности
stop_sequences — список строк, при появлении которых генерация немедленно прекращается. По смыслу это указание: «как только напишешь вот это — замолчи».
Классический случай: просим JSON, а получаем JSON и три абзаца пояснений сверху.
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
messages=[{"role": "user",
"content": "Сгенерируй JSON-объект с полями name, email и phone."}]
)
print(response.content[0].text)
Ответ: сначала вступление, потом объект, потом подробный разбор каждого ключа. Если нам нужен только объект — обрываем генерацию на закрывающей фигурной скобке:
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
messages=[{"role": "user",
"content": "Сгенерируй JSON-объект с полями name, email и phone."}],
stop_sequences=["}"]
)
Теперь модель останавливается, как только доходит до }. И здесь важная деталь:
}, то в тексте этой скобки не будет — чтобы распарсить результат как JSON, скобку придётся дописать руками.
Как и в случае с потолком, ответ сам расскажет, почему замолчал:
response.stop_reason # 'stop_sequence'
response.stop_sequence # '}' — какая именно последовательность сработала
Стоп-последовательностей можно задать несколько: генерация прервётся на первой встреченной, а свойство stop_sequence покажет, какая именно это была. Например, если попросить стихотворение со стоп-списком ["b", "c"], три запуска дадут что-то вроде:
Ответ 1 остановлен по stop_sequence. Сработало: c
Ответ 2 остановлен по stop_sequence. Сработало: b
Ответ 3 остановлен по stop_sequence. Сработало: b
Обрывать текст на первой попавшейся букве — приём, конечно, бессмысленный. Зато он наглядно показывает механику: первая встреченная строка из списка гасит генерацию мгновенно.
Вы задали stop_sequences=["}"], и модель остановилась на скобке. Что вы увидите?
Temperature
temperature управляет разнообразием ответов. Чем выше значение, тем более непредсказуемыми и вариативными получаются формулировки. Чем ниже — тем сильнее модель держится самых вероятных, «безопасных» вариантов.
Что происходит под капотом: генерируя очередной токен, Claude оценивает распределение вероятностей возможных продолжений. Temperature это распределение искажает. При низком значении оно становится острым — вся вероятность собирается на самых ожидаемых токенах, и модель ведёт себя почти детерминированно. При высоком распределение сглаживается, шансы менее вероятных токенов растут — отсюда и разнообразие.
Правило простое: ближе к 0 — для аналитических и фактических задач, выше — для творческих и генеративных.
Проверим. Трижды спросим одно и то же — «Придумай название для планеты пришельцев. Ответь одним словом» — сначала с temperature=0, потом с temperature=1:
def demonstrate_temperature():
for temperature in [0, 1]:
print(f"Три запроса с temperature = {temperature}")
for i in range(3):
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=100,
messages=[{"role": "user",
"content": "Придумай название для планеты пришельцев. Ответь одним словом."}],
temperature=temperature
)
print(f"Ответ {i+1}: {response.content[0].text}")
Результат говорит сам за себя:
Три запроса с temperature = 0
Ответ 1: Xendor.
Ответ 2: Xendor.
Ответ 3: Xendor.
Три запроса с temperature = 1
Ответ 1: Xyron.
Ответ 2: Xandar.
Ответ 3: Zyrcon.
При нуле — три одинаковых ответа. При единице — три разные планеты.
temperature=0 ответы могут различаться от запуска к запуску; просто вероятность получить одно и то же сильно выше. Если ваш пайплайн полагается на побайтовое совпадение ответов — эта гарантия не оттуда.
Ещё нагляднее это видно на большой выборке. Если сто раз спросить «Назови любое животное одним словом» при temperature=0, Claude почти наверняка каждый раз ответит одинаково — скажем, «жираф». Сто запросов при temperature=1 дадут жирафа больше чем в половине случаев, но рядом появится целый зоопарк других вариантов.
Вы делаете классификатор обращений в поддержку: на входе письмо, на выходе одна из пяти категорий. Какое значение temperature разумнее?
Системный промпт
system — необязательный параметр, который задаёт рамку разговора: роль модели, тон, фоновый контекст. Он не входит в messages, а передаётся отдельно и действует на весь диалог.
- Работает на уровне разговора: влияет на все ответы, а не на одно сообщение.
- Избавляет от необходимости повторять инструкцию в каждой реплике пользователя.
- В него стоит класть роль, тон и контекст. Подробные инструкции, документы и примеры лучше работают, если положить их в первое сообщение
user— и там их тоже не нужно повторять в каждой следующей реплике.
message = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1000,
system="Ты — преподаватель иностранного языка и всегда отвечаешь по-французски.",
messages=[
{"role": "user", "content": "Привет, как дела?"}
]
)
print(message.content[0].text)
Вопрос обычный, ответ придёт на французском — мы не трогали промпт пользователя, изменилась только рамка.
Упражнения
Откройте Workbench или редактор с SDK — оба задания короткие.
Упражнение 4.1 — Генератор вопросов
Напишите функцию generate_questions(topic, num_questions), которая печатает num_questions вопросов по теме topic нумерованным списком. Условия: ограничьте ответ через max_tokens, задайте роль эксперта через system и используйте stop_sequences, чтобы модель гарантированно остановилась после нужного количества вопросов. Подсказка: если просим три вопроса — останавливаемся, как только модель напишет «4.».
Решение упражненияСначала попробуйте сами — потом сверьтесь
def generate_questions(topic, num_questions=3):
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
system=f"Ты эксперт по теме «{topic}». Задавай глубокие вопросы, "
f"которые заставляют думать. Отвечай нумерованным списком.",
messages=[
{"role": "user",
"content": f"Сформулируй {num_questions} вопросов по теме «{topic}» нумерованным списком."}
],
stop_sequences=[f"{num_questions+1}."]
)
print(response.content[0].text)
generate_questions(topic="свобода воли", num_questions=3)
Ключевой трюк — вычисляемая стоп-последовательность f"{num_questions+1}.". Модель может увлечься и написать четвёртый вопрос, но как только она наберёт «4.», генерация оборвётся. Роль в system, задание — в сообщении user: ровно то разделение, о котором говорили выше.
Упражнение 4.2 — Поймать три причины остановки
Соберите три запроса так, чтобы получить три разных значения stop_reason: "end_turn", "max_tokens" и "stop_sequence". Промпт можно брать один и тот же — меняйте только параметры.
Решение упражненияСначала попробуйте сами — потом сверьтесь
prompt = [{"role": "user", "content": "Перечисли пять городов Испании нумерованным списком."}]
# 1. Модель договорила сама
a = client.messages.create(model="claude-haiku-4-5", max_tokens=500, messages=prompt)
# 2. Упёрлись в потолок
b = client.messages.create(model="claude-haiku-4-5", max_tokens=5, messages=prompt)
# 3. Сработала стоп-последовательность
c = client.messages.create(model="claude-haiku-4-5", max_tokens=500,
messages=prompt, stop_sequences=["3."])
for r in (a, b, c):
print(r.stop_reason, "|", r.stop_sequence)
Ожидаемый вывод: end_turn | None, max_tokens | None, stop_sequence | 3.. Свойство stop_sequence заполняется только в третьем случае — по нему удобно понимать, какая именно строка из списка сработала.
Что запомнить
max_tokens— жёсткий потолок длины ответа. При его достижении текст обрывается, аstop_reasonстановится"max_tokens".- Модель не знает про этот потолок: он не делает ответ содержательнее и не заставляет писать длиннее. Нужна короткая форма — просите в промпте.
- Чем больше токенов генерируется, тем дольше идёт ответ и тем дороже запрос.
stop_sequencesобрывают генерацию на заданной строке; сама строка в ответ не попадает,stop_reason="stop_sequence", аstop_sequenceпокажет сработавшую.temperatureуправляет разнообразием: ближе к 0 — для фактических и аналитических задач, выше — для творческих. Ноль не гарантирует полной повторяемости.system— отдельный параметр, не частьmessages. В нём роль, тон и контекст; подробные инструкции и примеры — в первое сообщениеuser.
Дочитали и сделали упражнения? Зафиксируйте прогресс — отметка сохранится в вашем браузере.