TL;DR
Token-Aware Formatting — принцип форматирования входных данных, который манипулирует токенизацией через разделители, чтобы разбить текст на атомарные единицы. Вместо слитного текста "abcde" подаёшь "a, b, c, d, e" — каждый символ становится отдельным токеном, и модель видит структуру, которая была скрыта.
Современные токенизаторы (BPE) склеивают 2-4 символа в один токен для эффективности: "strawberry" превращается в два токена [straw][berry]. Модель не видит отдельные буквы внутри токена — для неё это непрозрачный блок. Поэтому GPT-4 не может посчитать "r" в "strawberry": буквы скрыты внутри токенов. То же с цифрами: "12345" может стать одним токеном, и модель не видит отдельные "1", "2", "3". Даже с Chain-of-Thought это не работает — если модель не видит атомарные единицы на входе, она не может с ними оперировать.
Решение — форсировать токенизацию на нужном уровне детализации. Четыре формата с нарастающей точностью: (a) слитный текст "abcde" → BPE склеивает как хочет; (b) пробелы "a b c d e" → лучше, но пробелы тоже могут склеиться; (c) запятые "a, b, c, d, e" → почти всегда разделяет; (d) явная структура "['a', 'b', 'c', 'd', 'e']" → гарантированно отдельные токены. Разница в точности — до 80% на задачах подсчёта и сортировки. Маленькая модель (GPT-4o-mini) с правильным форматом обгоняет o1 с плохим.
Схема метода
ШАГ 1: Определи атомарную единицу задачи
→ Для подсчёта букв: отдельный символ
→ Для сортировки слов: отдельное слово
→ Для арифметики с цифрами: отдельная цифра
ШАГ 2: Выбери формат разделения
→ Формат (b): пробелы между единицами — "a b c"
→ Формат (c): запятые — "a, b, c"
→ Формат (d): список — "['a', 'b', 'c']"
ШАГ 3: Переформатируй данные и подай в промпт
→ Один запрос к LLM
Форматы по нарастанию надёжности:
(a) Слитный → "abcde" — токенизатор решает сам, непредсказуемо
(b) Пробелы → "a b c d e" — лучше, но пробелы могут склеиться с соседями
(c) Запятые → "a, b, c, d, e" — почти всегда разделяет
(d) Список → "['a', 'b', 'c', 'd', 'e']" — максимальная гарантия
Пример применения
⚠️ Зона силы метода: Задачи, где нужна точная работа с отдельными символами, цифрами, словами — подсчёт, сортировка, поиск паттернов, арифметика. НЕ для: анализа смысла, генерации текста, общих рассуждений.
Задача: Проверяешь гипотезу — есть ли закономерность в номерах телефонов клиентов. Нужно посчитать, сколько раз встречается цифра "7" в списке из 25 номеров. Это простая задача, но GPT-4 ошибается при слитной подаче.
Промпт (плохой формат — слитный текст):
Посчитай, сколько раз встречается цифра 7 в этих номерах:
89171234567
89265557890
89037777123
...
Дай только число.
Промпт (хороший формат — токен-видимый):
Посчитай, сколько раз встречается цифра 7 в этих номерах.
Каждая цифра отделена запятой:
8, 9, 1, 7, 1, 2, 3, 4, 5, 6, 7
8, 9, 2, 6, 5, 5, 5, 7, 8, 9, 0
8, 9, 0, 3, 7, 7, 7, 7, 1, 2, 3
...
Дай только итоговое число.
Результат:
В первом случае модель выдаст неточный результат или откажется считать (особенно на длинных номерах). Во втором — правильный подсчёт с точностью близкой к 100%. Если добавить Chain-of-Thought ("покажи подсчёт для каждого номера"), точность в хорошем формате вырастет до 97%, а в плохом останется низкой.
Почему это работает
Слабость LLM: Модель работает с токенами, не с символами. Токенизатор BPE склеивает частые последовательности символов в единые блоки для эффективности обучения. "strawberry" превращается в [straw][berry]. Эмбеддинг токена [straw] — это единый вектор, внутри которого нет отдельных признаков для букв "s", "t", "r", "a", "w". Модель видит два объекта вместо десяти символов.
Сильная сторона LLM: Модель отлично работает, когда каждый токен = атомарная единица задачи. Если задача "посчитай буквы a" и каждая буква — отдельный токен, модель может применить внимание к каждой, последовательно обработать через CoT, получить точный результат.
Механика обхода: Разделители (пробелы, запятые, кавычки) ломают склейку в BPE. Токенизатор видит "a, b, c" и создаёт токены: [a][,][ ][b][,][ ][c] или ['a'][,][ ]['b']. Главное — буквы теперь изолированы в отдельных токенах. Модель получает гранулярный вход, CoT может пошагово оперировать каждым элементом.
Рычаги управления:
- Тип разделителя → пробел экономит токены, но менее надёжен; запятая точнее; список ['x'] максимально надёжен, но расходует больше токенов
- CoT → без разделителей CoT почти не помогает; с разделителями CoT резко улучшает точность (с 60% до 97%)
- Длина входа → чем длиннее слитный текст, тем хуже работает; с разделителями деградация медленнее
Исследование показало: разница между форматом (a) слитный и (d) список — до 80% точности на задачах подсчёта длиной 30-40 символов.
Шаблон промпта
Базовый шаблон для задач подсчёта/сортировки/обработки символов:
{инструкция_задачи}
Данные (каждый элемент отделён):
{элемент_1}, {элемент_2}, {элемент_3}, ...
{дополнительные_инструкции}
Для максимальной надёжности (формат-список):
{инструкция_задачи}
Данные:
['{элемент_1}', '{элемент_2}', '{элемент_3}', ...]
{дополнительные_инструкции}
Плейсхолдеры:
- {инструкция_задачи} — что нужно сделать: посчитать, отсортировать, найти паттерн
- {элемент_1}, {элемент_2}, ... — твои данные, разделённые запятыми или в формате списка
- {дополнительные_инструкции} — формат вывода, использование CoT и т.д.
Когда использовать какой формат: - Формат (c) с запятыми — универсальный, баланс надёжности и читаемости - Формат (d) список — когда критична 100% точность и можно потратить больше токенов - Формат (b) пробелы — когда экономия важнее и задача простая
Пример конкретного промпта:
Посчитай, сколько раз встречается буква "о" в этой фразе.
Данные (каждая буква отделена):
к, о, р, о, в, а, , д, а, л, а, , м, о, л, о, к, о
Покажи подсчёт пошагово, затем дай итоговое число.
Ограничения
⚠️ Расход токенов: Разделители увеличивают длину промпта в 2-3 раза — "abc" (3 символа) становится "a, b, c" (5 токенов). На длинных данных это дорого и может упереться в лимит контекста.
⚠️ Узкий класс задач: Метод работает только там, где нужна точная работа с атомарными единицами — символы, цифры, отдельные слова. Для задач анализа смысла, генерации текста, рассуждений общего характера — бесполезен.
⚠️ Читаемость: Формат "['a', 'b', 'c', ...]" выглядит неестественно. Для длинных текстов становится нечитаемым. Если нужна последующая обработка человеком — неудобно.
⚠️ Не универсальное решение: Модель всё равно может ошибаться на очень длинных последовательностях (>40 элементов). Точность падает даже с правильным форматом, хотя медленнее чем без него.
Как исследовали
Авторы провели систематическое тестирование на чёрных ящиках — без доступа к внутренностям моделей. Логика: токенизация — единственная переменная, задача остаётся той же.
Три типа задач: - Подсчёт — посчитать конкретную букву/цифру в строке - Сортировка — отсортировать символы/слова по алфавиту - Реверс — перевернуть последовательность
Четыре формата токенизации: - (a) Слитный текст "abcde" - (b) Пробелы "a b c d e" - (c) Запятые "a, b, c, d, e" - (d) Список "['a', 'b', 'c', 'd', 'e']"
Модели: GPT-4o-mini, Claude 3.5 Sonnet, Qwen Turbo, OpenAI o1
Объём: По 1000 случайных инстанций для каждой комбинации (задача × формат × длина). Длины: 10-20, 20-30, 30-40 символов для подсчёта; 5-30 для сортировки и реверса.
Метрика: Exact match accuracy — либо правильно, либо нет.
Ключевые находки:
Деградация с длиной резко различается: В формате (a) точность падает с 30% (10-20 символов) до 2% (30-40). В формате (d) — с 97% до 70%.
CoT помогает только с правильным форматом: В формате (a) CoT почти не улучшает результат (30% → 45%). В формате (d) CoT даёт скачок (60% → 97%).
Разные буквы — разная сложность: Подсчёт "a" точнее чем подсчёт "e" или "z" в формате (a) — потому что "a" реже склеивается в BPE. Это model-dependent эффект: для каждой модели свой токенизатор, свои паттерны склейки.
Маленькая модель обгоняет большую: GPT-4o-mini с форматом (d) показывает 97% точности. OpenAI o1 с форматом (a) — около 50%. Правильная токенизация важнее размера модели.
Теоретическая база:
Авторы формализуют через Token Awareness — функцию, которая показывает, есть ли в эмбеддинге токена информация о конкретном свойстве (например, "содержит букву r"). Если TokenAware(token, "содержит r") = 0, модель не может использовать это в рассуждениях.
Также вводят понятие CoT Expressiveness — насколько богат язык токенов для выражения промежуточных шагов. Если токены слишком грубые, даже CoT не может экстернализовать все нужные состояния.
Связь с другими техниками
Chain-of-Thought (CoT): Исследование показывает границу применимости CoT. Теория говорит "CoT делает модель Тьюринг-полной", практика — нет. Причина: токенизация ломает выразительность. CoT не волшебная палочка, он работает только когда входные данные видимы модели на токенном уровне.
Few-Shot и контекст: Принцип переносится — если в few-shot примерах данные слитные, модель выучит плохой паттерн. Если примеры в формате (c) или (d), модель обучится работать с правильной гранулярностью.
Structured Output: Схожая идея — дать модели явную структуру. Здесь структура на входе, не на выходе. Оба принципа используют силу LLM в работе с чёткими границами.
Ресурсы
Tokenization Constraints in LLMs: A Study of Symbolic and Arithmetic Reasoning Limits Xiang Zhang, Juntai Cao, Jiaqi Wei, Yiwei Xu, Chenyu You University of British Columbia, Zhejiang University, Cisco, Stony Brook University
Код, промпты и результаты: Anonymous GitHub
