Как выглядит ответ
На успешный запрос сервер возвращает JSON примерно такой формы:
{
"content": [
{"type": "text", "text": "Здравствуйте! Ваш заказ №1542 уже в пути."}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 24,
"output_tokens": 17,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0
},
"model": "claude-opus-4-8"
}
content — это массив, не строка
Главная ловушка новичка: content — это массив блоков, а не одна строка. Блоки бывают разных типов (text, позже — tool_use). Текст надо брать из блоков с type == "text", а не хватать content[0] вслепую — там может оказаться, например, блок инструмента.
Правильно в Python:
resp = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{"role": "user", "content": "Привет!"}],
)
text = "".join(b.text for b in resp.content if b.type == "text")
Код вида resp.content[0].text сломается, как только в начале ответа окажется не текстовый блок. Всегда фильтруй по type == "text".
stop_reason и usage
stop_reason— почему модель остановилась:end_turn(закончила сама),max_tokens(упёрлась в лимит — ответ неполный),tool_use(хочет вызвать инструмент),refusal(отказ по правилам безопасности).usage— счётчики токенов.input_tokensиoutput_tokens— это то, за что ты платишь. Поляcache_read_input_tokensиcache_creation_input_tokensпонадобятся позже, в теме про кэширование.
Если stop_reason равен max_tokens — ответ обрезан. Это сигнал поднять max_tokens, а не баг модели.