Слогер Создать блог
Разработка

Как надёжно суммаризировать длинные тексты в Node.js с помощью Chat Completions API

Разбираем рабочий пайплайн: подсчёт токенов, разбиение на чанки, двухпроходное объединение и строгий JSON-контракт на примере TypeScript.

Суммаризация длинных статей через языковые модели упирается в ограничение контекста. Если отправить текст целиком, есть риск упереться в лимит токенов и получить дорогую ошибку. Рабочий вариант — разбить текст на куски, обработать каждый, а затем сшить результаты. Разбираем этот пайплайн на примере 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.

По материалам: ai. Текст переработан редакцией Слогера.

← На главную

Рекламное место — Конец поста
Реклама · Слогер

Комментарии (0)

Войдите, чтобы комментировать.

Пока нет комментариев. Будьте первым.