Латентность работает как скрытый налог на любой продукт, построенный на Claude. Можно собрать самого умного агента в мире, но если первый токен появляется через три секунды, пользователь решит, что всё сломалось. В 2026 году Opus 4.8 и Sonnet 4.6 уже в продакшене, а agent fan-out стал архитектурой по умолчанию, поэтому тюнинг латентности давно не прихоть, а требование. Это плейбук, которым мы пользуемся внутри и который советуем командам, работающим с Claude API через codexkey.
Из чего складывается время запроса к Claude API
Сначала нужен общий словарь. Стоит отслеживать три числа:
- TTFT (time to first token): время по часам от отправки запроса до прихода первого стримового токена. Именно его пользователь воспринимает как «началось ли вообще что-то».
- Total time: TTFT плюс время генерации остального ответа. Грубая модель:
total = TTFT + output_tokens / tokens_per_second. - Tail latency (p99): самый медленный 1% запросов. От хвоста зависит, сработают ли таймауты и забьются ли очереди. Медиана 800ms при p99 12s хуже, чем медиана 1.2s при p99 2.5s.
Кто смотрит только на средние значения, летит вслепую. Считайте все три метрики по модели, по региону и по шаблону промпта.
TTFT по моделям: что мы намерили
Мы постоянно гоняем пробы с EU PoP к шлюзу codexkey, который стоит в том же регионе. Ниже медианы за 30 дней на коротких промптах (до 1K input-токенов) без кеша:
- gpt-5.6-luna: TTFT около 250ms, генерация около 180 токенов/сек.
- Sonnet 4.6: TTFT около 380ms, генерация около 95 токенов/сек.
- Opus 4.8: TTFT около 700ms, генерация около 55 токенов/сек.
В нашей линейке нет тира Haiku. Самая дешёвая модель gpt-5.6-luna, но это другое семейство: формат запросов совместимый, а поведение и промпты могут отличаться, поэтому проверьте её на своей задаче.
Закономерность устойчивая: чем больше модель, тем длиннее TTFT и медленнее стрим. Opus примерно втрое медленнее gpt-5.6-luna на токен. Для интерактивного UI это самый мощный рычаг. Шаг классификации, который крутится на Opus «потому что команда стандартизировалась на Opus», скорее всего отнимает секунду воспринимаемой латентности и не даёт никакого выигрыша в качестве.
Почему стриминг вдвое снижает воспринимаемую латентность
Не-стриминговый запрос блокируется, пока не сгенерируется весь ответ. Для ответа в 500 токенов на Sonnet это примерно 380ms + 500/95 ≈ 5.6s, и только потом UI что-то покажет. Стриминг меняет картину: рендер начинается с момента TTFT (380ms), а пользователь читает ответ по мере генерации. Total time остаётся прежним, зато воспринимаемая латентность падает с 5.6s до 380ms. Пользователи стабильно называют стриминговые ответы «быстрыми», даже когда суммарное время больше, чем у не-стримингового варианта.
В 2026 году нет причин вызывать Messages API без stream: true на любом пользовательском пути. Исключения: батчевые джобы и оркестрация tool-call, где перед действием нужен полный message.
Prompt caching сокращает TTFT на кешированных блоках примерно на 50%
Prompt caching от Anthropic обычно подают как способ сэкономить: кешированные input-токены тарифицируются примерно в 10% от обычной ставки. Но на латентность он влияет не слабее. Если запрос попадает в кешированный префикс, TTFT падает примерно на 50%, потому что модели не нужно заново обрабатывать кешированные токены. На Sonnet системный промпт в 20K токенов обычно добавляет к TTFT ~600ms, а после первого кеширования эта добавка схлопывается до ~300ms.
Длинный системный промпт, схему инструментов или стабильный few-shot блок пометьте cache_control: { type: "ephemeral" }, и со второго запроса TTFT пойдёт вниз. TTL в пять минут короткий, но на загруженной проде вы будете попадать в него почти на каждом запросе.
Длина ответа: самый большой рычаг
Самый недооценённый приём прост: генерируйте меньше токенов. Каждый output-токен на Sonnet стоит ~10ms wall time. Сжатие ответа с 800 токенов до 300 экономит пять секунд. Три конкретных приёма:
- Жёстко выставляйте
max_tokens. Не передавайтеmax_tokens: 4096«на всякий случай». Берите реальный потолок задачи: 200 для причины классификации, 500 для саммари, 1500 для правки кода. - Просите краткость. Добавьте в системный промпт «Ответь в пределах 100 слов» или «Выведи только JSON-объект, без объяснений». Модели в 2026 году отлично держат ограничения по длине.
- Применяйте stop sequences. Если ответ заканчивается на
</answer>или}, передайте это как stop sequence. Модель остановится мгновенно, не дописывая хвостовые пробелы и прощальные фразы.
Параллельные вызовы: agent fan-out через Promise.all
Если агенту нужно сделать три вызова модели для независимых подзадач (извлечь сущности, оценить тональность, сделать саммари), не выстраивайте их в очередь. Последовательные вызовы складывают свои TTFT, а параллельные прячут их за самым медленным.
const [entities, sentiment, summary] = await Promise.all([
client.messages.create({ model: "gpt-5.6-luna", ... }),
client.messages.create({ model: "gpt-5.6-luna", ... }),
client.messages.create({ model: "claude-sonnet-5", ... }),
])
Total time теперь равен max(TTFT_a, TTFT_b, TTFT_c) + max(generation_a, b, c), а не сумме. У трёхшагового агента это обычно сокращает общее время вдвое.
География влияет сильнее, чем кажется
Основной inference Anthropic находится в США. Round-trip из Франкфурта в us-east-1 добавляет около 90ms на запрос ещё до того, как модель начнёт работать. Если стриминговый UI делает за сессию 10 последовательных вызовов, это почти секунда чистой сетевой латентности.
EU PoP codexkey терминирует TLS-хендшейк в регионе и проксирует запрос к модели по тёплому пуленному соединению. Европейским клиентам это обычно убирает 60-120ms из TTFT по сравнению с прямым обращением к Anthropic через Атлантику. Дешевле выигрыша по латентности не бывает: код менять не нужно, достаточно другого base URL.
Код: от baseline до краткого ответа с кешем
Одна и та же задача (суммаризировать документ в 5K токенов) в четырёх вариантах, от baseline до самого быстрого, против https://api.codexkey.ru/v1:
import Anthropic from "@anthropic-ai/sdk"
const client = new Anthropic({
apiKey: process.env.CODEXKEY_API_KEY,
baseURL: "https://api.codexkey.ru/v1",
})
// 1. Baseline: без стриминга, без кеша, без дисциплины max_tokens
// Замер: TTFT 4.2s (т.к. для не-стриминга TTFT == total time)
await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 4096,
messages: [{ role: "user", content: longDocument + "\n\nСделай саммари." }],
})
// 2. Добавляем стриминг. Total time не меняется, воспринимаемый TTFT: 0.5s
const stream = await client.messages.stream({
model: "claude-sonnet-5",
max_tokens: 4096,
messages: [{ role: "user", content: longDocument + "\n\nСделай саммари." }],
})
// 3. Добавляем prompt caching на документе
await client.messages.stream({
model: "claude-sonnet-5",
max_tokens: 4096,
system: [
{ type: "text", text: longDocument, cache_control: { type: "ephemeral" } },
],
messages: [{ role: "user", content: "Сделай саммари документа выше." }],
})
// 4. Добавляем инструкцию на краткость + жёсткий max_tokens
await client.messages.stream({
model: "claude-sonnet-5",
max_tokens: 300,
stop_sequences: ["</summary>"],
system: [
{ type: "text", text: longDocument, cache_control: { type: "ephemeral" } },
],
messages: [{
role: "user",
content: "Саммари в пределах 150 слов, оберни в <summary>...</summary>.",
}],
})
Как мерить
Тюньте то, что измеряете. На каждом запросе стоит снимать две метрики:
const t0 = performance.now()
let ttft: number | null = null
let outputTokens = 0
const stream = await client.messages.stream({ ... })
for await (const event of stream) {
if (event.type === "content_block_delta" && ttft === null) {
ttft = performance.now() - t0
}
if (event.type === "message_delta") {
outputTokens = event.usage.output_tokens
}
}
const total = performance.now() - t0
metrics.histogram("claude.ttft_ms", ttft, { model, cached })
metrics.histogram("claude.total_ms", total, { model, cached })
metrics.histogram("claude.output_tokens", outputTokens, { model })
TTFT и total снимайте отдельно. Тегируйте замеры по модели, по факту попадания в кеш и по шаблону промпта. Без этого не понять, помогли ли ваши правки.
Итог: 4.2s → 1.6s на реальной нагрузке
Пример выше (суммаризация документа в 5K токенов на Sonnet) в baseline занимает 4.2 секунды total wall time, а после стриминга, кеширования, жёсткого max_tokens и работы через EU PoP codexkey укладывается в 1.6 секунды. Это −62% по total time и −91% по воспринимаемому TTFT (с 4.2s до 380ms).
Ни одна из техник не экзотична. Все они доступны в официальном SDK, описаны в API-референсе Anthropic и не требуют перестройки инфраструктуры, кроме смены baseURL на https://api.codexkey.ru/v1. Команды, которые в 2026 году шипят самые быстрые продукты на Claude, не пользуются секретной моделью. Они просто дисциплинированы в TTFT, длине вывода и cache hit rate.
Если ваш p99 всё ещё измеряется секундами, начните со стриминга, затем кеширование, затем дисциплина вывода, затем география. Именно в таком порядке. Основной выигрыш даст первая пара.