Function calling и structured output в LLM-приложении
Как работает function calling и как добиться корректного structured output?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Модель генерирует tool call с именем функции и аргументами, приложение исполняет реальный код и возвращает результат в контекст. Structured output надежнее делать через JSON schema или constrained decoding.
Подробный разбор
Function calling - это договор между моделью и runtime. Runtime сообщает модели список инструментов: название, описание, JSON schema аргументов. Модель не вызывает API сама; она генерирует структурированный вызов. Приложение валидирует аргументы, выполняет функцию, добавляет tool result в контекст и просит модель сформулировать финальный ответ.
Structured output можно получать prompt-инструкциями, но надежнее использовать provider-native JSON mode, schema validation или constrained decoding. В constrained decoding на каждом шаге запрещаются токены, которые нарушили бы грамматику или JSON schema. Это снижает риск невалидного JSON, но не гарантирует бизнес-смысл аргументов.
В production нужны retry на parse/validation errors, лимиты на tools, allowlist действий, логирование tool calls и защита от prompt injection через результаты инструментов.
Типичные ошибки
- Думать, что LLM сама ходит в базу или API.
- Не валидировать аргументы tool call.
- Полагаться только на текстовый prompt для строгого JSON.
RAG по большому корпусу документов
Как построить RAG/search систему, если корпус по масштабу похож на большой веб-поиск?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Нужен каскад: индексация и фильтры, быстрый lexical retrieval и векторный retrieval, дедупликация, реранкинг top-N, контроль свежести и генерация ответа только по компактному контексту.
Подробный разбор
На большом корпусе нельзя отправить все документы в LLM или запускать дорогой reranker по всему индексу. Сначала строится ingestion: chunking, metadata, ACL, версии, язык, freshness и несколько индексов. Дешевый retrieval через BM25/filters/vector ANN достает тысячи или сотни кандидатов.
Дальше идут нормализация query, union/RRF/weighted merge источников, дедупликация, business/ACL фильтры и реранкинг top-N. Reranker может быть cross-encoder, LLM scorer или более дешевая ML-модель. Только после этого компактный контекст попадает в LLM.
Критичные production-вещи: latency budget на каждый этап, offline retrieval Recall@K, online success rate, логирование вкладов BM25/vector/reranker, fallback на lexical search и отказ от ответа при недостаточном контексте.
Типичные ошибки
- Делать только векторный поиск без lexical fallback.
- Пускать дорогой reranker по всему корпусу.
- Не учитывать ACL и свежесть документов.
Идемпотентность endpoint-а пополнения баланса
Пользователь повторно отправил запрос на пополнение баланса. Как сделать endpoint идемпотентным?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Клиент присылает idempotency key, сервер хранит результат первого выполнения и при повторе возвращает тот же результат, не меняя баланс второй раз.
Подробный разбор
Для операции с деньгами retry неизбежен: клиент мог не получить ответ, сеть оборвалась, gateway повторил запрос. Поэтому логический запрос должен иметь idempotency key. Сервер в транзакции проверяет, был ли такой key для этого пользователя/операции.
Если key новый, сервер выполняет операцию, записывает изменение баланса и сохраняет результат: status, operation id, amount, response payload. Если key уже был, сервер не делает новое начисление, а возвращает сохраненный результат. Важно ограничить scope ключа, например user_id + operation_type + key, и хранить TTL.
Нужны уникальный индекс в БД, транзакции, защита от concurrent duplicate requests и понятная обработка intermediate state: если первая попытка зависла, повтор должен либо дождаться результата, либо вернуть retryable статус без двойного списания/начисления.
Типичные ошибки
- Генерировать idempotency key на сервере после получения запроса.
- Проверять дубликат вне транзакции без unique constraint.
- Не сохранять response первого выполнения.
Transactional outbox для БД и Kafka
Что делать, если бизнес-изменение записалось в БД, а событие в Kafka не отправилось?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
В одной транзакции с бизнес-изменением записать событие в outbox-таблицу. Отдельный worker читает pending events, отправляет их в Kafka и помечает как sent с retry.
Подробный разбор
Проблема dual write: БД и Kafka не участвуют в одной простой транзакции. Если сначала обновить БД, а потом отправить event, между этими шагами процесс может упасть. Если сначала отправить event, а потом не записать БД, потребители увидят событие про несуществующее состояние.
Transactional outbox решает это так: бизнес-изменение и запись event в outbox-таблицу происходят в одной DB transaction. После commit отдельный worker или CDC-процесс читает outbox, публикует события в Kafka и помечает их как sent. При сбоях он ретраит.
Потребителям все равно нужна идемпотентность, потому что delivery обычно at-least-once. Для порядка событий используют aggregate id, sequence/version и partitioning key.
Типичные ошибки
- Делать два независимых write без recovery-механизма.
- Не делать consumers идемпотентными.
- Не хранить статус и retry count outbox-события.
Imbalanced classification: метрики, loss и leakage
Как обучать и оценивать модель, если положительный класс редкий?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Смотреть PR-AUC, precision/recall по порогу и бизнес-cost ошибок; использовать class weights, sampling, focal loss и строго проверять leakage/time split.
Подробный разбор
При сильном imbalance accuracy почти бесполезна: модель может всегда предсказывать majority class. Обычно смотрят PR-AUC, recall@precision, precision@recall, confusion matrix по выбранному порогу, calibration и бизнес-стоимость false positive/false negative.
Обучение можно стабилизировать class weights, over/under-sampling, hard negative mining, focal loss, balanced batches и аугментациями. Но sampling меняет распределение, поэтому probabilities после обучения могут требовать calibration.
Отдельная опасность - leakage. В product ML часто есть признаки из будущего: события после target time, агрегаты, построенные на всей истории, или дубликаты пользователей между train/test. Для временных задач нужен time split и point-in-time feature join.
Типичные ошибки
- Отчитываться accuracy при доле positives меньше нескольких процентов.
- Случайно включать будущие события в признаки.
- Выбирать threshold без бизнес-cost и calibration.
In-memory движок векторного поиска: что важно в реализации
Нужно устно спроектировать простой in-memory векторный поиск: add, search top-K, cosine similarity, stats. На что обратить внимание?
Сначала проговорите ответ вслух или тезисами.
Формулы, план решения, риски и примеры.
Откройте разбор только после своей попытки.
Показать разбор
Короткий ответ
Хранить id -> vector, нормализовать vectors для cosine, проверять размерность, искать top-K через heap или сортировку, отдельно вести stats и ошибки контракта.
Подробный разбор
Для маленького in-memory search достаточно словаря id -> vector и линейного скана по всем векторам. Если cosine similarity используется часто, vectors лучше нормализовать при добавлении, тогда score превращается в dot product. Нужно проверять одинаковую размерность, пустой индекс, duplicate id и top_k больше размера индекса.
Для top-K на малом N можно отсортировать все scores. Для большого N лучше держать min-heap размера K. Если требования растут, линейный in-memory поиск заменяется ANN-индексом: HNSW, FAISS, ScaNN или vector DB.
Stats лучше не смешивать с бизнес-логикой: count vectors, dimension, number of searches, average latency, maybe top score distribution. Важно явно определить, что возвращает search: ids, scores, metadata, порядок и поведение при равных score.
Типичные ошибки
- Не нормализовать vectors и получить неверный cosine.
- Не проверять dimension mismatch.
- Сортировать весь индекс там, где нужен bounded heap.