Структура ответа и учёт токенов
Разбираем ответ модели по косточкам: где текст, почему она остановилась и сколько это стоило.
usage — блок ответа со счётчиками токенов входа и выхода; по нему считают стоимость запроса.
Из чего состоит ответ Anthropic
| Поле | Что значит |
content | список блоков ответа (текст, вызовы инструментов и т.д.) |
stop_reason | почему модель остановилась |
usage | счётчики токенов: input_tokens, output_tokens |
model | какая модель ответила |
stop_reason — почему остановилась
end_turn | модель закончила ответ сама — нормальный случай |
max_tokens | упёрлась в лимит длины — ответ обрезан |
stop_sequence | встретила вашу стоп-строку |
tool_use | хочет вызвать инструмент (см. раздел 4) |
Обрабатывайте stop_reason в коде: если это max_tokens, ответ неполный — стоит увеличить лимит или дозапросить продолжение; если tool_use — надо выполнить инструмент и вернуть результат.
Разбор готового JSON-ответа (запускаемо)
Возьмём типичный ответ Anthropic как строку и извлечём из него всё нужное — чистый Python:
import json
raw = '''
{
"id": "msg_01ABC",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [
{"type": "text", "text": "Вот три факта о Луне:"},
{"type": "text", "text": " она спутник Земли."}
],
"stop_reason": "end_turn",
"usage": {"input_tokens": 25, "output_tokens": 18}
}
'''
resp = json.loads(raw)
# 1. Собираем текст из всех текстовых блоков
text = "".join(b["text"] for b in resp["content"] if b["type"] == "text")
print("Текст:", text)
# 2. Проверяем причину остановки
if resp["stop_reason"] == "max_tokens":
print("ВНИМАНИЕ: ответ обрезан по лимиту")
else:
print("Остановка:", resp["stop_reason"])
# 3. Считаем стоимость по usage (пример цен)
u = resp["usage"]
cost = u["input_tokens"] / 1_000_000 * 5.0 + u["output_tokens"] / 1_000_000 * 25.0
print(f"Токены вход/выход: {u['input_tokens']}/{u['output_tokens']}")
print(f"Стоимость запроса: ${cost:.6f}")
Вывод:
Текст: Вот три факта о Луне: она спутник Земли. Остановка: end_turn Токены вход/выход: 25/18 Стоимость запроса: $0.000575
А у OpenAI?
Структура другая, смысл тот же: текст в choices[0].message.content, причина в finish_reason, токены в usage (prompt_tokens, completion_tokens, total_tokens).
{
"choices": [
{"message": {"role": "assistant", "content": "4"}, "finish_reason": "stop"}
],
"usage": {"prompt_tokens": 10, "completion_tokens": 1, "total_tokens": 11}
}
Итог
- Текст извлекают из
content-блоков (Claude) илиchoices[].message.content(OpenAI). stop_reason/finish_reasonговорит, почему модель остановилась — это надо обрабатывать.usageдаёт точные токены для подсчёта стоимости и логов.
Проверьте себя
1. Что означает stop_reason равный max_tokens?
AМодель закончила ответ естественно
BОтвет обрезан по лимиту длины и, скорее всего, неполный
CМодель хочет вызвать инструмент
DПроизошла ошибка авторизации
2. Откуда брать точное число токенов запроса для подсчёта стоимости?
AИз заголовка retry-after
BИз блока usage в ответе (input_tokens/output_tokens)
CИз поля model
DЭто число недоступно
3. Как называется поле с причиной остановки у OpenAI?
Astop_reason
Bfinish_reason
Cend_turn
Dcompletion_status