Гайд · TNWS AI

Как посчитать токены русского промпта через /tokenize в llama.cpp

4 мин—

Считаем токены конкретным токенизатором GGUF, проверяем структуру tokens и риск переполнения контекста.

Задача и применимость

Поисковый вопрос: «как посчитать токены русского текста llama.cpp tokenize with_pieces». Оценить размер промпта тем же токенизатором, который обслуживает локальную модель. llama.cpp server с загруженной GGUF-моделью и маршрутом POST /tokenize; описание маршрута сверено по официальной документации 20 сентября 2026 года. Длина токенизации зависит от файла модели и настроек add_special/parse_special.

Пример рассчитан на уже имеющийся файл модели, полученный законным способом по условиям его лицензии. Здесь не утверждается, что любая GGUF-модель одинаково понимает русский язык или имеет один и тот же чат-шаблон. Фактический номер сборки узнавайте локально через llama-server --version и повторяйте контрольный опыт после обновления бинарника.

Что подтверждено документацией

POST /tokenize принимает обязательное content и необязательные add_special, parse_special, with_pieces. В режиме with_pieces:false ответ содержит массив целочисленных ID tokens; в режиме true элементы представлены объектами id и piece. Некоторые piece могут быть массивом байтов, а не строкой Unicode. add_special по умолчанию false, parse_special по умолчанию true.

Порядок действий

  1. Сначала определите фактически загруженный GGUF и настроенный размер контекста.
  2. Подготовьте конечный текст, включая русские символы, числа и пунктуацию, без искусственных сокращений.
  3. Вызовите /tokenize с content и выведите длину массива tokens.
  4. Повторите с with_pieces:true, чтобы увидеть границы разбиения, но не показывайте пользователю секретные части текста.
  5. Добавьте бюджет для шаблона чата и ответа до сопоставления с контекстом модели.

Локальный запуск для описанного сценария (замените путь к файлу на ваш):

./build/bin/llama-server -m /models/your-chat-model.gguf --host 127.0.0.1 --port 8080

Готовый шаблон запроса и проверки

import json, urllib.request
url = "http://127.0.0.1:8080/tokenize"
text = "Заказ № 17: доставка завтра в Смоленск."
payload = {"content": text, "with_pieces": True, "add_special": False}
request = urllib.request.Request(
    url, data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
    headers={"Content-Type": "application/json"}, method="POST")
with urllib.request.urlopen(request, timeout=15) as reply:
    result = json.load(reply)
tokens = result["tokens"]
assert all(isinstance(t.get("id"), int) for t in tokens)
print("Токенов:", len(tokens))
print("Фрагменты:", [t["piece"] for t in tokens[:8]])

Входные данные и ожидаемый результат

Вход: «Заказ № 17: доставка завтра в Смоленск.» Ожидаемый результат — положительное целое число и список объектов с id/piece, однако точное число намеренно не задано: оно меняется при смене токенизатора. Сравнивайте повторные вызовы на одном GGUF, а не числа из статей про чужую модель.

Почему важна именно эта проверка

Один символ, одно слово и один токен — разные вещи. Кириллица, знак номера и числительное могут разбиваться совсем не так, как английский текст. Это объясняет, почему правило «тысяча слов равна фиксированному числу токенов» плохо работает при расчёте локального контекста. Результат /tokenize привязан к модели на данном сервере; после смены GGUF измерьте промпты заново. Если with_pieces:true и отдельный piece представлен байтами, не пытайтесь насильно декодировать каждый фрагмент отдельно как завершённую UTF-8 строку: граница токена может проходить внутри символа.

Работа с реальными данными

Для чата важно учитывать не только текст пользователя. Системное сообщение, история и специальные маркеры шаблона тоже входят в общий контекст. Поэтому замер только строки content — нижняя оценка, а не расчёт всей переписки. Практический контроль: отдельно сформируйте полный шаблон через /apply-template и затем измерьте его, если используете этот формат, либо ориентируйтесь на серверный счётчик входных токенов при /v1/chat/completions/input_tokens. Не конвертируйте размер текстового файла в токены умножением байтов: размерности разные.

Проверка после изменения модели или нагрузки

Когда приложение отправляет материалы для RAG, сохраните несколько контрольных наборов: короткий вопрос, длинный русский документ, смешанный текст с артикулами и URL. Проверьте максимальную длину из реальных случаев и оставьте запас под ответ модели. Если сервер сообщает об усечении в /completion, считайте такой ответ невалидным для задач, где важен конец источника. Для воспроизводимости запишите хеш GGUF, параметры шаблона и add_special: без них два измерения легко окажутся несопоставимыми. Не используйте число токенов как оценку качества текста: оно характеризует способ кодирования.

Для точного учёта чат-ввода в README предусмотрен отдельный маршрут /v1/chat/completions/input_tokens, который принимает тело chat completions. Его удобнее использовать как дополнительный тест, если в цепочке присутствуют системная инструкция и много реплик. Сопоставление результата с простым /tokenize покажет накладные токены шаблона; не объявляйте разницу постоянным коэффициентом. При изменении chat template накладные расходы тоже меняются. Этот тест полезнее среднего коэффициента «токены на слово» именно для длинных русских бесед.

Критерии приёмки

  1. Каждый элемент tokens содержит числовой id.
  2. Повтор на той же модели и настройках даёт ту же длину.
  3. Промпт и резерв ответа вместе помещаются в выделенный контекст.

Частые ошибки и чего не делать

  • Не считать слова или байты эквивалентом токенов.
  • Не декодировать байтовый piece отдельно от остальных фрагментов.
  • Не забывать служебные токены шаблона при оценке диалога.

Краткие ответы на вопросы

Одинаково ли считают токены разные модели?

Нет, используйте токенизатор загруженного GGUF.

Можно ли получить сами фрагменты?

Да, с параметром with_pieces:true.

Почему число не указано заранее?

Оно зависит от модели и параметров токенизации.

Официальные источники

Частые вопросы

Одинаково ли считают токены разные модели?

Нет, используйте токенизатор загруженного GGUF.

Можно ли получить сами фрагменты?

Да, с параметром with_pieces:true.

Почему число не указано заранее?

Оно зависит от модели и параметров токенизации.

Читайте также

Комментарии

Пока тихо. Скажите первое слово