LLM API и интеграция
Логирование запросов к LLM: что писать в логи
Работа с моделями отличается от обычной разработки одной неприятной особенностью: когда что-то пошло не так, воспроизвести это нельзя. Тот же запрос вернёт другой ответ, а сама модель могла обновиться. Единственное, что остаётся, — то, что вы записали в момент обращения. И здесь большинство команд обнаруживает, что записали они стоимость, а нужны были совсем другие поля. Разбираем минимальный набор, который превращает счёт из загадки в таблицу, а разбор сбоя — из гадания в работу, и отдельно — что делать с содержимым запросов, потому что это уже не только инженерный вопрос.
Минимальный набор
Пять полей на каждое обращение. Ничего сложного, но их почти никогда нет сразу.
Идентификатор задачи. Не запроса, а задачи целиком. В агентском сценарии одна задача — это десятки обращений, и без общего идентификатора вы не сможете посчитать её стоимость.
Номер шага. Какое это обращение по счёту внутри задачи. Показывает, где цепочка становится дорогой.
Модель. Обязательно фактическая, а не та, что стоит в конфигурации по умолчанию. При наличии запасного варианта они расходятся, и это надо видеть.
Входные и выходные токены раздельно. Не сумма. Пока ставка симметрична, разницы для счёта нет, но она появляется мгновенно, стоит подключить модель с раздельными ценами — почему это ломает расчёт.
Признак повторной попытки. Была ли это первая попытка или повтор после сбоя. Отношение попыток к успешным задачам — самая информативная метрика качества вашей постановки.
Эти пять полей отвечают на вопрос «почему счёт такой» практически всегда. Без них остаётся только общая сумма за день, из которой не следует ничего.
Что добавить для отладки
Три поля сверх минимума, каждое решает свой класс проблем.
Время запроса. Не только для порядка: если ваш провайдер использует тарифы, зависящие от времени суток, без этого поля счёт не разобрать. Мы разбирали такой случай — тариф по часам.
Код завершения. Не «успех или ошибка», а конкретный код. Разные отказы требуют разной реакции, и без разбивки вы не увидите, какой из них у вас доминирует — разбор кодов.
Длина контекста на входе. Отдельно от токенов: сколько было в истории, сколько в подставленных документах, сколько в системном промпте. Это единственный способ понять, что именно раздувает запросы.
Последний пункт особенно важен в агентских сценариях, где история растёт квадратично — механика. Без разбивки по составляющим вы увидите только, что запросы стали дороже, но не поймёте, из-за чего.
Почему обычные подходы не подходят
Стоит объяснить, чем работа с моделями отличается от обычного сервиса, — иначе непонятно, зачем городить отдельный учёт.
Стоимость обращения непостоянна. В обычном сервисе запрос к базе стоит примерно одинаково всегда. Здесь два обращения к одной модели могут отличаться по цене в сотню раз в зависимости от длины контекста.
Ответ недетерминирован. Повторить сбой нельзя. В обычной разработке воспроизведение — первый шаг отладки; здесь его нет, и заменить его может только запись.
Ошибка не всегда ошибка. Модель может вернуть успешный ответ, который при этом бесполезен или неверен. Мониторинг по кодам ответа такое не увидит: формально всё в порядке.
Поведение меняется без вашего участия. Модель обновляется, статус маршрута меняется, ставка правится. Ваш код при этом не менялся, а результат стал другим — и без записей вы не поймёте, когда это началось.
Последний пункт — главный аргумент за то, чтобы логировать модель фактически, а не считать, что вы знаете, какая используется. За август мы наблюдали, как модели уходили из каталога, возвращались и меняли статус протокола по нескольку раз.
С чего начать, если логов нет вовсе
Практический порядок, чтобы не браться за всё сразу.
Первый день: добавьте токены раздельно и модель. Два поля, минимальная правка, и уже можно ответить на вопрос, куда уходят деньги.
Первая неделя: добавьте идентификатор задачи. Он потребует немного изменить структуру кода, зато сразу покажет стоимость сценариев, а не отдельных вызовов.
Первый месяц: добавьте повторы и коды завершения. К этому времени накопится статистика, и станет видно, какая доля обращений уходит впустую.
Дальше: разбивка входа по составляющим. Самое трудоёмкое и самое полезное для оптимизации.
Ошибка, которую стоит избежать: пытаться сразу построить полноценную систему наблюдаемости с дашбордами. Пять полей в обычной таблице дают девяносто процентов пользы; всё остальное можно добавлять по мере того, как упрётесь.
Про содержимое запросов
Здесь начинается та часть, где решение не только инженерное.
Соблазн логировать всё понятен: с полным текстом запроса и ответа отладка становится тривиальной. Но у этого есть цена, и её стоит осознавать.
Вы создаёте хранилище чувствительных данных. В промптах оказывается то, что прислали пользователи: персональные данные, коммерческая информация, иногда — по невнимательности — ключи и пароли. Всё это теперь лежит у вас с соответствующими обязанностями по доступу, сроку хранения и удалению.
Объём растёт быстро. Полные тексты при заметной нагрузке — это терабайты в месяц, и хранить их бесконечно никто не будет.
Утечка логов равна утечке переписки. Логи обычно защищены слабее основной базы, потому что воспринимаются как техническая информация.
Разумный компромисс выглядит так: хранить содержимое выборочно и недолго. Полные тексты — только для неудачных запросов и только на несколько дней. Для успешных достаточно длины и хеша, чтобы понять, повторяется ли запрос.
Отдельно стоит вычищать очевидные секреты перед записью — теми же простыми проверками, что и перед отправкой в модель. Мы разбирали набор в статье о том, как содержимое рассуждений извлекали через API.
Про наш контур: содержимое запросов на стороне KeyDealer не сохраняется. Остаются токены, модель, сумма, статус и время — перечень опубликован в llms.txt. Это значит, что логирование на вашей стороне — единственный источник данных для разбора, и рассчитывать, что мы восстановим текст запроса по обращению в поддержку, не стоит.
Что из этого строить
Три отчёта, которые окупают всю затею.
Стоимость задачи по типам. Средняя и девяносто пятый процентиль. Почти всегда выясняется, что небольшая доля задач съедает большую часть бюджета, — и оптимизировать надо именно их.
Доля повторов. Сколько обращений приходится на одну успешно завершённую задачу. Рост этого числа — ранний признак того, что что-то сломалось, задолго до жалоб пользователей. Причём сломаться могло не у вас: смена версии модели у вендора выглядит точно так же.
Разбивка входа по составляющим. Системный промпт, история, подставленные документы, описания инструментов. Показывает, что сокращать.
Ни один из трёх нельзя построить без полей, перечисленных выше. Поэтому заводить их стоит сразу, а не когда придёт неожиданный счёт: восстановить задним числом то, чего не записывали, невозможно, а первый по-настоящему неприятный счёт обычно приходит именно тогда, когда разбираться нечем.
Чего мы не утверждаем
Мы не даём рекомендаций по обработке персональных данных. Хранение содержимого запросов регулируется законом, и как это применимо к вашему продукту, должен смотреть ваш юрист.
Набор полей не универсален. Он покрывает типичные случаи; у вашей задачи могут быть свои.
Не описываем конкретные инструменты. Хранилище логов и способ построения отчётов — ваш выбор, и от него ничего в этой статье не зависит.
Не утверждаем, что этого достаточно для аудита. Речь про инженерную отладку и учёт расхода. Требования к журналированию для проверок и разбирательств бывают строже и определяются не нами.
Что держать в голове
Работу с моделями нельзя воспроизвести задним числом: тот же запрос даст другой ответ, а модель могла обновиться. Остаются только записи.
Пять полей — задача, шаг, модель, токены раздельно, признак повтора — закрывают большинство вопросов о счёте и о сбоях. Содержимое запросов логируйте выборочно и недолго: удобство отладки здесь оплачивается созданием хранилища, за которое вы отвечаете.
Как мы проверяем факты и что именно храним — на странице о проекте и в llms.txt. Отдельный ключ под каждый сценарий упрощает и учёт: keydealer.ru/login.
Частые вопросы
Что нужно логировать обязательно?
Идентификатор задачи, номер шага, модель, входные и выходные токены раздельно и признак повторной попытки. Пяти полей достаточно, чтобы разобрать и счёт, и большинство сбоев.
Нужно ли сохранять содержимое запросов?
Не всегда и не всё. Содержимое помогает при отладке, но создаёт хранилище с чувствительными данными и требует отдельных решений по доступу и сроку хранения.
Почему нельзя логировать просто стоимость?
Потому что стоимость без разбивки не показывает, что именно дорого. Найти дорогие сценарии по общей сумме за день невозможно.
Что хранит KeyDealer?
Токены, модель, сумму, статус и время. Содержимое запросов не сохраняется, перечень опубликован в llms.txt.
Как понять, что логов достаточно?
Если по ним можно ответить на вопрос «почему этот запрос стоил столько» и «что модель видела перед этим ответом» — достаточно.