Чат, CLI, SDK или голый HTTP: какой слой доступа к LLM выбрать под задачу

Одна и та же LLM за четырьмя интерфейсами ведёт себя по-разному. Различается не модель, а оркестрация вокруг неё — управление контекстом, детерминированность, кэш, роутинг.

Чат, CLI, SDK или голый HTTP: какой слой доступа к LLM выбрать под задачу

Один и тот же промпт можно отправить в модель четырьмя способами: набрать в веб-чате, отдать консольному агенту, вызвать из кода через SDK или собрать HTTP-запрос руками. Ответ вернётся похожий. А вот система вокруг него получится разной по задержке, стоимости, предсказуемости и безопасности — и разница эта возникает не в модели, а в слое, через который вы к ней обращаетесь.

Это не вопрос удобства. «Мне комфортнее в чате» и «под этот сервис нужен SDK» — утверждения из разных плоскостей. Первое про то, как вам работать сегодня вечером. Второе про то, будет ли ваш продакшн-пайплайн воспроизводимым, дешёвым и наблюдаемым через полгода. Когда эти два вопроса путают, получают систему, которая в демо работает, а под нагрузкой течёт: непредсказуемый формат ответа, оплата токенов, которые можно было закэшировать, и невозможность понять, на каком шаге генерация ушла не туда.

Разберём четыре слоя: как каждый устроен внутри, чем отличаются экосистемы (Anthropic/Claude, OpenAI, Google/Gemini) и под какую инженерную задачу какой слой действительно нужен. Я хожу по этой лестнице каждый день: мой собственный контент-пайплайн зовёт модели через HTTP с роутингом «простое — дёшево, сложное — дорого», так что на «чат против API» смотрю из эксплуатации, а не из документации.

Веб-чат — это не модель, это продукт над моделью

Начнём с веб-чата. Веб-чат — готовое продуктовое приложение над LLM. Между вашим текстом и моделью стоит оркестратор, который решает целый ворох задач, о которых вы даже не думаете:

  • Индексация и RAG. Загрузили документ — интерфейс сам режет его на фрагменты (chunks), гоняет через эмбеддинги и складывает во временное векторное хранилище для контекстного поиска.
  • Управление контекстным окном. Диалог растёт — оркестратор на ходу сжимает (summarize) или отсекает старые сообщения, чтобы не пробить лимит контекста модели. Вы этого не видите.
  • Специфичный рендеринг. Текстовый поток отделяется от структурированных сущностей. У Claude AI это Artifacts — отдельные процессы рендеринга для кода, SVG, диаграмм Mermaid или целых HTML-приложений. В ChatGPT ту же роль играет редактор Canvas.
  • Предзаданные системные инструкции. Режимы вроде Projects (Claude) или Custom GPTs (OpenAI) — не «другая модель», а механизм, который подставляет ваш системный промпт в каждый уходящий HTTP-запрос. Прикреплённые файлы при этом либо кладутся в контекст целиком, либо подтягиваются через retrieval (RAG) — в зависимости от объёма.

Чат — удобная обёртка, которая берёт на себя всё жизнеобеспечение вокруг модели. И это ровно то, что делает его хорошим для одних задач и негодным для других.

Куда чат ложится по-хорошему — исследовательские (ad-hoc) задачи, прототипирование, работа с неструктурированной документацией, разовые скрипты. Дальше расходятся акценты экосистем:

  • Claude AI (Claude.ai) — длинные тексты, анализ крупных кодовых баз (за счёт глубокой связки Projects и Artifacts), сложная техническая документация.
  • ChatGPT — когда нужна мультимодальность из коробки (генерация картинок, голос) или быстрый поиск свежих данных в интернете.
  • Gemini — анализ ультрадлинных медиа (часы видео, длинные аудио) и плотная интеграция с Google Drive.

Ключевое свойство чата, которое надо унести дальше: управление контекстом здесь автоматическое и скрытое, а детерминированность — низкая. Вы не контролируете, что именно ушло в модель и что из истории было отрезано. Для разведки боем это плюс. Для системы, которую надо воспроизводить, — дисквалификация.

CLI: модель выходит из чата в операционную систему

Следующий слой меняет саму роль модели. В чате она — собеседник в песочнице. В CLI она становится автономным агентом, который живёт в вашей ОС: читает и пишет файлы, запускает shell-команды, ходит во внешние сервисы.

┌─────────────────────────────────────────────────────────┐
│                      CLI Agent                          │
│  ┌──────────────────┐  ┌─────────────────────────────┐  │
│  │ Read/Write Files │  │ Execute Shell Commands      │  │
│  └─────────┬────────┘  └──────────────┬──────────────┘  │
└────────────┼──────────────────────────┼─────────────────┘
             │                          │
             ▼                          ▼
┌─────────────────────────────────────────────────────────┐
│              Model Context Protocol (MCP)               │
│        (Database, Git, Internal Services, Tools)        │
└─────────────────────────────────────────────────────────┘

Внутри всё держится на агентной петле (Agent Loop):

  1. Модель получает задачу, смотрит на текущий контекст и локальное окружение.
  2. Возвращает ответ с вызовом функции (tool_use) — например, «прочитать файл src/main.py» или «запустить npm test».
  3. CLI-клиент перехватывает этот ответ, локально исполняет команду в ОС и отправляет результат (tool_result) обратно в модель.
  4. Цикл повторяется, пока задача не закрыта.

Разница между системами здесь — в степени интеграции протоколов. Claude Code и экосистема Anthropic опираются на Model Context Protocol (MCP) — открытый стандарт, по которому CLI-агент единообразно подключается к локальным и удалённым источникам: Git-репозиториям, СУБД, баг-трекерам. Плюс в CLI модели Claude отдают структуры рассуждений (thinking tokens) — показывают промежуточные шаги логики до того, как начнут менять код. Для инженера это возможность перехватить неверное намерение раньше, чем оно превратится в правку файла.

Вне экосистемы Claude картина другая: там чаще берут сторонние провайдер-агностичные агенты (Aider, OpenHands — работают в том числе и с Claude) или локальный рантайм для open-source моделей (Ollama).

Куда CLI ложится по задаче — автоматический рефакторинг и поиск багов на уровне всего репозитория, генерация и прогон миграций СУБД, автоматизация CI/CD и написание integration-тестов. Управление контекстом тут уже гибрид: часть автоматическая, часть — ваша. Детерминированность средняя. Это рабочая лошадь локальной разработки, но ещё не то, на чём стоит бэкенд.

SDK: слой, на котором начинается продакшн

SDK — граница между «поиграться с моделью» и «встроить модель в сервис». SDK — абстракция над сетевым протоколом, дающая строго типизированные интерфейсы под Python, TypeScript, Go или Java. Управление контекстом тут полностью ручное: что положили в запрос — то и ушло, никакого скрытого оркестратора. И именно на этом уровне провайдеры расходятся в трёх важных механиках.

Кэширование контекста (Prompt Caching). В SDK Anthropic разработчик руками помечает статические блоки — огромную системную инструкцию, спецификацию, кусок кодовой базы — структурой cache_control: {"type": "ephemeral"}. При повторных вызовах это срезает стоимость токенов до 90%, а задержку — до 85% на длинных промптах. В SDK OpenAI похожий механизм работает преимущественно автоматически на стороне сервера (implicit caching), без явной разметки в коде.

# Пример явной разметки Prompt Caching в Anthropic SDK
import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "Большой системный контекст или спецификация API...",
            "cache_control": {"type": "ephemeral"} # Инструкция кэширования
        }
    ],
    messages=[{"role": "user", "content": "Сгенерируй контроллер на основе спецификации"}]
)

Глубина рассуждений (Reasoning / Extended Thinking). На моделях класса reasoning SDK даёт параметры управления тем, сколько модель думает перед ответом. Показательно, как быстро эта ручка эволюционирует: на поколении Claude 3.7 Sonnet бюджет размышлений задавался жёстко и явно — thinking: {"type": "enabled", "budget_tokens": 2048}. На актуальных поколениях Anthropic эта форма уже вытеснена: budget_tokens отклоняется, а глубину задаёт adaptive thinking (thinking: {"type": "adaptive"}) в связке с уровнем усилия (effort) — модель сама решает, когда и насколько думать. Смысл прежний — балансировать время отклика против качества, — но инженер теперь управляет этим декларативно, а не фиксированным числом токенов.

Валидация вывода (Structured Outputs). OpenAI SDK встроенно интегрируется с Pydantic и гарантирует соблюдение JSON-схемы на уровне декодирования токенов (Guaranteed Structured Outputs). У Anthropic сегодня есть нативная схема вывода на уровне API (output_config.format и/или строгий strict: true на инструменте); исторически же тот же результат добивался в обход — через строгий вызов инструментов (tool_use) и парсинг ответа. Оба провайдера пришли к гарантированной схеме, но разными путями.

Поэтому SDK становится основным инструментом продакшн-систем: микросервисов, бэкенд-логики, специализированных RAG, автономных бизнес-агентов. Ручной контекст, явный кэш, управляемый reasoning и предсказуемый формат ответа — это ровно тот набор, которого чат не даёт by design. Когда я говорю «под продакшн — SDK или HTTP, а не чат», это не вкусовщина: детерминированность, кэш и явный роутинг между моделями либо есть на этом уровне, либо их нет нигде выше.

Голый HTTP: когда убирают и последнюю зависимость

Нижний слой — прямые вызовы по HTTP/HTTPS без всяких библиотек. Здесь вы говорите с API провайдера на языке заголовков и JSON-тел, и различия форматов вылезают наружу.

[ HTTP REST API ]
  ├── Anthropic API: POST /v1/messages
  │    ├── Header: anthropic-version: 2023-06-01
  │    ├── Header: anthropic-beta: prompt-caching-2024-07-25
  │    └── JSON Body: { "system": "...", "messages": [...] }
  │
  └── OpenAI API Standard: POST /v1/chat/completions
       └── JSON Body: { "messages": [ {"role": "system", ...}, {"role": "user", ...} ] }

Структура JSON-нагрузки. У Anthropic Messages API (/v1/messages) системный промпт вынесен в отдельный параметр верхнего уровня system, а массив messages содержит только роли user и assistant. У OpenAI (/v1/chat/completions) системное указание — это обычный элемент массива messages с ролью system ({"role": "system", "content": "..."}). Формат OpenAI стал де-факто индустриальным стандартом: его повторяют локальные серверы (vLLM, Ollama) и большинство провайдеров open-source моделей (Groq, Together AI, DeepSeek). На практике это значит, что связка «формат OpenAI + свой роутер» позволяет держать под одним интерфейсом разных вендоров — что и делает эту схему рабочей для балансировки стоимости.

Версионирование и экспериментальные функции. Anthropic использует жёсткий заголовок версии anthropic-version и заголовок anthropic-beta для подключения возможностей, пока они в бете. Пример ниже показывает сам формат beta-заголовка на исторической функции: anthropic-beta: prompt-caching-2024-07-25. Оговорка — prompt caching давно вышел в GA, и этот beta-заголовок для кэша больше не нужен (достаточно cache_control в теле); anthropic-beta остаётся механизмом для тех фич, что ещё не в GA:

curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "anthropic-beta: prompt-caching-2024-07-25" \
     --header "content-type: application/json" \
     --data '{
       "model": "claude-3-7-sonnet-20250219",
       "max_tokens": 1024,
       "system": "You are a professional system architect.",
       "messages": [{"role": "user", "content": "Analyze system design."}]
     }'

Потоковая передача (Server-Sent Events). При stream: true Anthropic отдаёт строго декомпозированный поток SSE-событий:

  • message_start — инициализация структуры ответа и подсчёт входных токенов.
  • content_block_start — начало текстового блока или блока рассуждений (thinking).
  • content_block_delta — инкрементальные чанки (text_delta или thinking_delta).
  • content_block_stop — закрытие очередного блока.
  • message_delta — финальные метаданные сообщения: stop_reason и агрегат usage (в т.ч. выходные токены).
  • message_stop — маркер конца потока.

Эта детализация позволяет точно видеть, на каком этапе находится генерация: рассуждение, формирование ответа или вызов внешнего инструмента. Для API-шлюза, который логирует и проксирует чужие запросы, это не украшательство, а наблюдаемость.

Куда HTTP ложится по задаче — разработка на языках без официальных SDK (Rust, C++, Elixir, Swift), сборка высокопроизводительных API-шлюзов, где надо срезать накладные расходы внешних зависимостей, и тонкая настройка балансировки, проксирования и логирования сетевых запросов. Управление контекстом полностью ручное, а контроль над запросом — полный: ни одного скрытого решения между вами и моделью. Оговорка по-инженерному честная: «полный контроль» — это про запрос и воспроизводимость пайплайна, а не про выход модели. Сам ответ LLM недетерминирован даже при temperature=0 (батчинг и железо дают дрейф), и на уровне payload SDK и голый HTTP шлют идентичный запрос — разница между ними в зависимостях и накладных расходах, а не в «предсказуемости» генерации. Это цена и одновременно смысл слоя: вы отвечаете за всё сами.

Разница не в модели, а в слое вокруг неё

Соберём четыре уровня в одну таблицу — это и есть карта, по которой стоит выбирать:

Критерий Веб-чат (UI) Консоль (CLI) Программный SDK Прямой HTTP API
Уровень абстракции Высший (готовый продукт) Высокий (агентная среда) Средний (программный) Низкий (сетевой протокол)
Управление контекстом Автоматическое (скрыто) Автоматическое/ручное Полностью ручное Полностью ручное
Контроль над запросом / воспроизводимость пайплайна Низкие Средние Полные Полные
Основной кейс Исследования, ad-hoc задачи Локальная разработка, CI/CD Продакшн-бэкенд сервисы API-шлюзы, редкие стеки
Ключевой фокус Claude Artifacts, Projects Claude Code, MCP, Thinking Manual Prompt Caching Headers (anthropic-beta), /v1/messages

Читается таблица по одной оси: слева направо растёт контроль над запросом и падает магия. Чат берёт на себя контекст, кэш и рендеринг — вы получаете скорость входа ценой контроля. HTTP не берёт на себя ничего — вы получаете полный контроль ценой того, что всё жизнеобеспечение теперь ваше. SDK — разумная середина для большинства продакшн-задач: ручной контекст и явный кэш без необходимости руками собирать заголовки и парсить SSE.

Как сопоставить слой с задачей:

  • Разведка, прототип, разовый разбор документа — чат. Не городите SDK ради того, что решается вкладкой в браузере.
  • Локальная разработка, рефакторинг репозитория, CI/CD — CLI с агентной петлёй и MCP.
  • Бэкенд-сервис, RAG, автономный агент под нагрузкой — SDK: ручной контекст, prompt caching, управляемый reasoning, предсказуемый формат.
  • Шлюз, экзотический стек, максимальный контроль над сетью и стоимостью — голый HTTP.

Режимы отказа: как выбор слоя убивает проект

Разница между слоями становится ощутимой ровно тогда, когда система сталкивается с реальной нагрузкой. Типичные сценарии, в которых выбор не того слоя приводит к отказу.

Чат в роли продакшн-бэкенда. Команда встраивает логику в Custom GPT или Project и рассчитывает, что это будет работать как API. На объёме всплывает то, что оркестратор чата отрезает старые сообщения по своим правилам — контекст, который вы полагали «памятью», исчезает без предупреждения. Формат ответа плавает, потому что системный промпт, который вы аккуратно составили, проходит через предобработку чата и может быть переформатирован. Prompt caching недоступен — вы платите за каждый токен системной инструкции при каждом вызове. Результат: система, которая в демо работала, под нагрузкой теряет воспроизводимость и сжигает бюджет.

SDK без управления контекстом. Команда переходит на SDK, но продолжает работать с контекстом так, как привыкли в чате: отправляют весь диалог каждый раз. Без явной разметки cache_control статические блоки (системная инструкция, спецификация) оплачиваются и обрабатываются заново при каждом вызове. На тысячах запросов в день это разница между экономией 90% и нулевой экономией. SDK даёт инструменты управления стоимостью, но не применяет их автоматически.

HTTP без наблюдаемости. Команда выбирает голый HTTP для максимального контроля, но не реализует обработку SSE-событий. В случае ошибки или таймаута нет способа понять, на каком этапе генерации произошёл сбой: модель уже начала рассуждать, или упала на вызове инструмента? Без декомпозиции потока отладка превращается в гадание. Контроль означает ответственность за каждую деталь взаимодействия, включая наблюдаемость.

Роутинг моделей: то, чего в чате нет

Когда пайплайн работает на объёме, модель перестаёт быть «собеседником» и становится деталью конвейера, которую зовут из кода: массовые прогоны гоните через дешёвую и быструю модель, шаги, где нужно качество, — через модель посильнее и подороже. Такой роутинг «дёшево / дорого» и балансировка между вендорами живут только на SDK и HTTP — на слоях, где контекст, кэш и выбор модели у вас в руках, а не спрятаны в оркестраторе чата.

Мой контент-пайплайн работает именно так: простые задачи уходит на дешёвую модель, сложные — на дорогую, и всё это управляется через HTTP-роутер, который я контролирую. Попытка реализовать такую логику через чат привела бы к ручному переключению между вкладками и невозможности масштабировать.

Выбор слоя — это первое архитектурное решение о стоимости и предсказуемости всей системы. Принимайте его раньше, чем напишете первый промпт.

https://www.dobryakov.com/lead-magnets/llm-interaction-layers.html?utm_source=None&utm_medium=None&utm_campaign=llm-interaction-layers

Leave a Reply

Your email address will not be published. Required fields are marked *