Суммаризация длинных статей через языковые модели упирается в ограничение контекста. Если отправить текст целиком, есть риск упереться в лимит токенов и получить дорогую ошибку. Рабочий вариант — разбить текст на куски, обработать каждый, а затем сшить результаты. Разбираем этот пайплайн на примере Node.js и OpenAI-совместимого Chat Completions API.
Почему один гигантский запрос — плохая идея
Один запрос ко всей статье выглядит заманчиво: почти нет кода. Но приложение остаётся в неведении, влезет ли вход в контекст, а если запрос упадёт, повторять его дорого. Пайплайн «подсчёт токенов → разбиение на чанки → суммаризация → объединение» менее элегантен, зато его проще эксплуатировать: расход токенов предсказуем, упавший кусок можно перезапросить локально, не переотправляя весь текст.
| Критерий | Один запрос | Пайплайн с чанками |
|---|---|---|
| Сложность реализации | Минимальная | Больше кода, но предсказуемо |
| Риск превышения лимита | Высокий | Контролируется заранее |
| Повторная попытка | Дорогая — весь текст заново | Дешёвая — только упавший чанк |
| Масштабирование | Упирается в окно контекста | Работает с текстом любой длины |
Как это устроено: четыре шага
1. Подсчёт токенов
Перед отправкой нужно точно знать, сколько токенов занимает текст. Резать по символам нельзя: две строки одинаковой длины могут токенизироваться совершенно по-разному. Используйте эндпоинт токен-каунтинга (в Infrai это /v1/ai/tokens/count). Важно закладывать в бюджет не только текст, но и промпт, и запас на ответ модели.
2. Выбор модели
Модель берите из каталога (/v1/models), проверяйте, что она обслуживает нужный регион (US или EU), и храните её идентификатор в конфигурации. Не зашивайте имя модели в код: смена модели должна быть решением при развёртывании, а не правкой исходника.
3. Разбиение на чанки
Делите статью на ограниченные по токенам куски. Каждый чанк получает одну и ту же инструкцию и один и тот же контракт вывода. Небольшое перекрытие между соседними чанками помогает не терять контекст (например, когда в одном куске упомянут человек, а в следующем он назван местоимением). Универсального значения перекрытия нет: для эссе, расшифровок и технической документации оно разное. Подбирайте перекрытие на репрезентативном наборе текстов и фиксируйте дублирующиеся пункты или пропущенные ссылки.
4. Двухпроходная суммаризация
Первый проход обрабатывает каждый чанк и возвращает компактный объект. Второй проход объединяет эти объекты в один — исходная статья ему уже не нужна. Такая схема держит финальный запрос маленьким, а повторная попытка остаётся локальной для конкретного этапа.
JSON-контракт — это не формальность
У объекта должны быть стабильные поля: title, summary, bullets, key_takeaways. Стабильные ключи упрощают хранение, рендеринг и валидацию. Но проверять JSON-синтаксис недостаточно. Приложение обязано отбрасывать объект, где summary нет, bullets — не массив, или в массиве встречаются не строки. Иначе ошибки уползут в интерфейс.
Типичные ошибки
- Нарезка текста по символам без учёта токенов
- Игнорирование размера промпта и резерва на ответ при подсчёте бюджета
- Одно и то же перекрытие для разных типов контента
- Пропуск финального теста объединения — сначала нужно проверить, как модель склеивает промежуточные объекты, а потом радоваться
- Отсутствие валидации типов: модель может вернуть корректный JSON с неожиданной структурой
Практические детали реализации
В примере на TypeScript используется OpenAI-клиент с baseURL для Infrai и maxRetries: 0 — управление ретраями переходит в код приложения. При получении HTTP 429 нужно сделать экспоненциальную паузу, а если сервер прислал заголовок Retry-After — подчиниться ему. Идемпотентный ключ генерируется как SHA-256 от модели, этапа и входных данных, чтобы повторный запрос не создавал новую операцию. Функция parseSummary проверяет каждый ответ на соответствие контракту и бросает ошибку, если что-то не так.
Вывод
Пайплайн с чанками и двухпроходным объединением — не самый красивый, но самый надёжный способ суммаризировать длинные тексты. Он контролирует токены, локализует сбои и заставляет модель возвращать структурированные данные, которые легко проверить. А это именно то, что нужно для боевого API.
Комментарии (0)
Войдите, чтобы комментировать.
Пока нет комментариев. Будьте первым.