Не «как написать код», а как устроен сам разговор между твоим приложением и моделью. И почему понимание этой механики определяет всё, что ты построишь дальше.
LLM — это stateless-функция: ты передаёшь ей массив сообщений с ролями (system, user, assistant), она возвращает следующее сообщение. Она не помнит ничего. Память, контекст, диалог — это иллюзии, которые создаёт твой код, каждый раз пересобирая историю заново. Эта мысль — фундамент всего курса.
Когда ты пользуешься ChatGPT через сайт, есть ощущение, что ты разговариваешь с моделью. Она «помнит» предыдущее сообщение, «следует» за разговором, «развивает» мысль. Это — главная иллюзия, которую интерфейс создаёт за тебя.
В реальности модель не помнит ничего между вызовами. Каждый запрос — это полная пересборка диалога: код фронтенда упаковывает всю историю в один JSON и отправляет заново. Модель прочитывает массив, генерирует следующее сообщение и забывает обо всём.
LLM — это не собеседник. Это функция: (массив сообщений) → следующее сообщение. Всё ощущение «памяти» — работа кода вокруг неё.
Из этого следует практический вывод, который мы будем разворачивать весь курс: что попадёт в массив сообщений — то модель и «знает». Управление этим массивом и есть AI-инжиниринг.
На уровне сети взаимодействие с LLM — это обычный HTTP-запрос. Никакой магии: тот же протокол, что у любого другого API.
В минимальном запросе у тебя есть три обязательных элемента:
Какую именно версию вызываешь. У каждого провайдера свой список: gpt-4o-mini, claude-sonnet-4-5, llama-3.1-70b и т.д.
Массив объектов, каждый с ролью и текстом. Это и есть «разговор», который модель прочитает целиком.
В заголовке Authorization. Идентифицирует тебя для биллинга и квот провайдера.
Внутри массива messages каждое сообщение помечено ролью. Это не просто метка — она меняет то, как модель его воспринимает. Понимание ролей — это понимание того, как ты можешь управлять моделью.
Кликни по табу, чтобы увидеть смысл каждой роли:
Это инструкция-конституция. Задаётся один раз, в начале массива. Модель воспринимает её как свод правил, которые нельзя нарушать: личность, тон, формат ответов, ограничения.
Пример рычага: один и тот же user-запрос с разными system'ами даст принципиально разные ответы. «Отвечай как юрист с 20-летним стажем» и «Отвечай как друг в баре» — две разные модели на одном и том же запросе.
Это самый дешёвый рычаг качества в AI-инжиниринге. Если результат плохой — первое, на что смотрят, это system prompt.
Сообщение от тебя или твоего конечного пользователя. Именно на user-сообщение модель отвечает; system только задаёт рамки.
В сложных системах user-сообщение часто конструируется автоматически: пользователь написал короткий вопрос, а твой код перед отправкой обогащает его контекстом, найденными документами (RAG), результатами инструментов. Это и есть «промпт-инжиниринг под капотом».
Главное правило: user — это вход, доверенный или нет в зависимости от того, кто его прислал. На этом построены атаки prompt injection (см. День 0).
Предыдущие ответы самой модели. Кладутся в массив, когда ты ведёшь многоходовой диалог и хочешь, чтобы модель «помнила», что она уже отвечала.
Технически — это способ показать модели её собственную «историю». Она увидит «я уже говорила X» и продолжит логически. Без этой роли модель каждый раз начинала бы с нуля.
Тонкий момент: ты сам решаешь, что класть в assistant-роль. Можешь подсунуть «фальшивую» историю — модель примет её за свои прошлые слова. Это используется для few-shot prompting и направления стиля.
В ответ модель возвращает JSON. В нём — не только текст ответа, но и несколько критических полей, которые AI-инженер обязан читать всегда, не только в проде.
Минимальная структура ответа выглядит так:
// ↓ собственно текст ответа "choices": [{ "message": { "content": "..." }, "finish_reason": "stop" }], // ↓ счётчики токенов — это деньги "usage": { "prompt_tokens": 24, "completion_tokens": 87, "total_tokens": 111 }
В нём три вещи, на которые ты смотришь каждый раз:
То, что ты показываешь пользователю или передаёшь дальше по пайплайну. Очевидное поле — но обрати внимание: оно может быть пустым или обрезанным, и об этом тебе подскажет finish_reason, а не само поле.
Возможные значения:
stop — модель закончила сама, всё хорошо.length — упёрлась в max_tokens, ответ обрезан. Если ты ждал JSON — он невалидный.content_filter — модерация заблокировала ответ.tool_calls — модель решила вызвать инструмент (увидим в Дне 17+).Это поле — главный сигнал «что-то пошло не так». Игнорируешь — получаешь баги вида «иногда JSON не парсится».
prompt_tokens — сколько токенов ушло в запросе. completion_tokens — сколько модель сгенерировала. Биллинг считается по обоим, обычно по разным ставкам (input дешевле, output дороже).
Это поле — твой главный метрик-сигнал. Когда диалог растёт, токены растут квадратично (см. День 8). Когда вылезает счёт на $200 вместо $20 — ответ всегда находится в usage.
Привычка с первого дня: логируй usage в каждом запросе.
Программист пишет код. AI-инженер принимает решения о том, как должна вести себя система с LLM внутри. Уже на этапе первого запроса у тебя есть несколько развилок, на которых легко свернуть не туда.
У каждой задачи свой оптимум — не «самая мощная», а «достаточная при адекватной цене и скорости».
Простые задачи (классификация, извлечение, переписать в формат), массовые вызовы, чувствительность к стоимости и latency. Качество — 80% от топовой при 5–10% цены.
Сложные рассуждения, длинный контекст, многошаговые задачи, агенты с tool use, кейсы где цена ошибки выше цены запроса. Когда маленькая не справляется.
Можно работать напрямую с одним провайдером (OpenAI, Anthropic), а можно — через прослойку типа OpenRouter или LiteLLM, которая даёт доступ к десяткам моделей через один API.
Для обучения — бери прямой API одного провайдера, не усложняй. В проде — маршрутизатор полезен, когда хочешь fallback'и и A/B-тесты между моделями.
Частая ошибка новичка — пихать всё в user: и инструкции, и данные, и вопрос. Модель путается, что главное.
Правильная привычка: system — это правила игры (роль, тон, формат, ограничения), которые ты задал один раз. user — это конкретный запрос с данными для этого конкретного раза. Эта разница станет особенно важной в Дне 14, когда мы будем разбирать инварианты.
Самый частый и самый дорогой когнитивный баг. Модель не помнит ничего. Если в её ответе появилась преемственность — значит, твой код подложил историю в массив. Если её там нет — модель отвечает как впервые, даже если ты только что ей это писал.
Эта ошибка лечится тем, что ты с самого начала задаёшь себе вопрос: «что именно сейчас находится в массиве messages?».
Видишь поле content, копируешь его дальше — и теряешь сигнал о том, что ответ был обрезан, заблокирован или модель захотела вызвать инструмент. В обучении это терпимо, в проде — приводит к самым странным багам.
Привычка: всегда проверяй finish_reason === "stop" перед тем, как использовать ответ.
«Я не дал system — значит модель работает как есть». Это не так. Без явного system модель работает с дефолтным system'ом провайдера, который ты не видишь и не контролируешь. Он не нейтральный — он усреднённый под широкую аудиторию.
Для любой серьёзной задачи ты пишешь свой system. Это сразу повышает качество в разы — без всякой смены модели.
Поскольку курс про AI-инжиниринг, практика тут — про мышление и наблюдение, а не написание кода. Сделай эти три эксперимента — они займут полчаса в плейграунде провайдера (например, platform.openai.com/playground или console.anthropic.com).
В плейграунде задай один и тот же user-вопрос («Объясни мне, почему небо голубое»). Прогони четыре раза с разными system: «ты учёный-физик», «ты ребёнок 5 лет», «ты программист, объясняй через аналогии с кодом», «ты циничный философ». Сравни ответы и обрати внимание: сам факт, тон, длина, выбор слов — всё меняется. Это даст тебе интуицию про силу system.
Открой два отдельных запроса (не диалог, а именно два разных). В первом скажи: «Меня зовут Алекс». Во втором, новом, спроси: «Как меня зовут?». Модель не знает. Теперь сделай один запрос с массивом messages, где первое сообщение — твоё представление, второе — вопрос. Модель ответит правильно. Это и есть демонстрация того, что памяти нет — есть только массив.
В плейграунде или через любой UI, который показывает usage — отправь короткий запрос, посмотри prompt_tokens и completion_tokens. Потом скопируй большой кусок текста (5-10 параграфов) и попроси его пересказать. Сравни. Это даст тебе физическое ощущение того, что токены = деньги, и зачем мы будем учиться сжимать историю в Днях 8–10.