Технологии

Расписание на естественном языке: «напомни каждый вторник»

Превращение фразы в расписание — отдельная задача со своими провалами: часовые пояса, «через неделю», «в конце месяца». Разбираем, что разбирается однозначно, а что нет.

Классическое расписание задают пятью числами со звёздочками. Это точно, воспроизводимо и совершенно нечитаемо: строку, написанную полгода назад, приходится расшифровывать заново. Естественная формулировка читается сразу — и ровно поэтому неоднозначна.

«Напомни в понедельник» — в ближайший или через неделю, если сегодня понедельник? «Через неделю» — в тот же день недели или ровно через семь суток от текущего момента? «В конце месяца» — тридцатого, тридцать первого или в последний рабочий день? Человек уточнит. Программа обязана выбрать заранее и делать это одинаково.

#Короткий ответ

Разбор естественной формулировки в расписание — это не «понимание», а сопоставление с набором правил. Работающая реализация решает три подзадачи по отдельности:

  1. Найти дату или период в свободном тексте.
  2. Отделить их от текста задачи — то, что останется, и есть напоминание.
  3. Явно назначить поведение в неоднозначных случаях и сообщить о нём пользователю.

Третий пункт пропускают чаще всего, и именно он определяет, будет ли пользователь доверять напоминаниям через месяц.

#Как это устроено

Два подхода, и выбор между ними не про качество, а про предсказуемость.

Детерминированные правила. Набор шаблонов: дни недели, «завтра», «через N дней», абсолютная дата цифрами. Быстро, бесплатно, покрывается тестами, работает одинаково каждый раз. Не понимает того, чего в шаблонах нет.

Разбор моделью. Понимает произвольные формулировки, включая «в предпоследнюю пятницу квартала». Стоит вызова модели, работает медленнее и — главное — может дать разный ответ на одну и ту же фразу в разные дни.

Практичная схема — правила как основной путь, модель как необязательное усиление для того, что правила не разобрали. Так это и сделано в Persona: разбор напоминаний в app/chat/reminder_nl.py построен на детерминированных правилах и покрыт тестами, а обращение к локальной модели — дополнительный, необязательный слой для размытых формулировок.

#Таблица разбора

Что происходит с типовыми формулировками. Столбец «результат» показывает поведение реализации на детерминированных правилах, где базой считается текущая дата.

Формулировка Что распознаётся Результат Однозначно
напомни завтра позвонить маме «завтра» дата +1 день, тело «позвонить маме» ✓
через 3 дня оплатить хостинг «через 3 дня» дата +3 дня ✓
в пятницу созвон день недели ближайшая пятница ✗
20.06 подать отчёт абсолютная дата 20 июня ✓
через неделю проверить «через неделю» дата +7 дней ✗
напомни в 15:00 позвонить время суток сегодня, «в 15:00» остаётся в тексте ✗
в конце месяца не распознаётся сегодня, пометка «дата не найдена» ✗
каждый вторник не распознаётся сегодня, пометка «дата не найдена» ✗

Две последние строки — не недоработка описания, а точное положение дел, и о нём стоит сказать прямо. Повторяющихся расписаний в Persona нет. Разбор извлекает одну дату для одного напоминания; конструкции вида «каждый вторник» правилами не покрыты и приведут к напоминанию на сегодня с пометкой, что дата не найдена. Если вам нужен повторяющийся цикл — это не наш сценарий, и лучше знать об этом до, а не после.

Строка про время суток тоже требует пояснения. В нашей схеме напоминание хранит дату без времени, поэтому «в 15:00» остаётся частью текста напоминания и не превращается в момент срабатывания. Это осознанное упрощение хранилища, а не ошибка разбора.

#Неоднозначные случаи

Список формулировок, где двух разумных людей можно поставить по разные стороны. Для каждой нужно принять решение и записать его.

«В пятницу», когда сегодня пятница. Сегодня или через неделю? Распространённое решение — ближайшая будущая, то есть через неделю; но пользователь, пишущий это утром, обычно имеет в виду сегодня.

«Через неделю». Ровно семь суток от текущего момента или тот же день недели на следующей? Для дат без времени разница исчезает, для расписаний с временем — нет.

«В конце месяца». Последнее число, последний рабочий день или условное «после 25-го»? Универсального ответа нет, выбирать приходится вам.

Часовой пояс. Самая дорогая неоднозначность. Пользователь пишет «в 9 утра», сервер живёт в UTC, пользователь в другом поясе, а зимой ещё и переводит часы. Правило, которое спасает: хранить момент срабатывания в абсолютном времени, а пояс пользователя применять только при отображении и при разборе ввода.

Дата в прошлом. «Напомни 5 марта», когда сегодня 20 марта. Следующий год, ошибка или ближайшее 5 марта? Молчаливый выбор здесь особенно неприятен.

Общее правило для всего списка: если формулировка неоднозначна, покажите результат разбора пользователю в момент создания. «Напоминание на 27 августа: подать отчёт» — одна строка, которая снимает большую часть будущих претензий. Именно так наш инструмент и отвечает: подтверждением с датой и текстом, а не молчаливым «готово».

#Что из этого следует на практике

Тело напоминания надо чистить от даты и слов-триггеров. Иначе в списке дел вы увидите «напомни завтра позвонить маме» вместо «позвонить маме». Разбор обязан вырезать распознанную дату и вводные слова.

Ненайденная дата — это не ошибка, а состояние. Правильная реакция — создать напоминание на сегодня и явно пометить, что дата не распознана, а не отказаться или молча угадать.

Разбор должен быть проверяем тестами на фиксированной дате. Тест, зависящий от сегодняшнего дня, будет падать по вторникам. База отсчёта передаётся параметром, а не берётся из системных часов.

Создание напоминания — изменяющее действие. Оно пишет в базу, а значит не относится к тем, что модель может совершать без явного участия человека. В нашей классификации рисков создание напоминания отнесено к изменяющим инструментам именно поэтому.

Разбор дат в вопросах — отдельная задача. «Что я делал на прошлой неделе» — это не расписание, а диапазон для поиска. У нас это отдельный модуль со своим набором русских и английских формулировок; смешивать его с разбором напоминаний не стоит, у них разные требования.

Как это выглядит в связке с проактивностью и утренними сводками — в руководстве по брифингу и напоминаниям на естественном языке и в отдельном материале про напоминания на естественном языке.

#Где интуиция подводит

«Модель разберёт любую формулировку». Разберёт, но не гарантирует одинаковый ответ на одну фразу дважды. Для расписания воспроизводимость важнее покрытия: лучше честно не понять, чем понять по-разному в понедельник и в среду.

«Если не поняли — надо переспросить». Переспрашивание на каждое второе напоминание раздражает сильнее, чем неточная дата, которую видно в подтверждении. Показать результат разбора дешевле, чем спросить.

«Часовой пояс можно взять из браузера». Можно, и он будет верным для веб-интерфейса и неверным для напоминания, созданного из мессенджера с другого устройства. Пояс — свойство пользователя, а не сессии.

«Повторяющиеся напоминания — это тот же разбор, только с "каждый"». Совсем нет. Однократное напоминание — это одна дата в таблице. Повторяющееся — это правило, состояние «когда было в прошлый раз», обработка пропущенных срабатываний и вопрос, что делать после недели простоя. Отсюда и разница в объёме работы, и наше текущее отсутствие такой функции.

#Когда естественный язык не нужен

Если расписание задаётся один раз и работает годами — берите точный формат. Пять чисел со звёздочками читаются хуже, но не оставляют места для интерпретации, а перечитывать их раз в год не проблема.

Естественная формулировка окупается там, где задач много и они одноразовые: бытовые напоминания, разовые дела, «не забыть про». Именно в этом сценарии выигрыш от того, что не нужно открывать календарь, перевешивает риск неточного разбора.

Как напоминания встраиваются в проактивное поведение ассистента — в разборе проактивного агента и его бюджета внимания. Как это работает через мессенджер — в руководстве по агенту в Telegram. Где в общей архитектуре живут планировщики — в разборе агентной архитектуры Persona.

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

#Можно ли задать повторяющееся напоминание фразой?

В Persona — нет: разбор извлекает одну дату для одного напоминания, конструкции вида «каждый вторник» не распознаются, и такое напоминание будет создано на сегодня с пометкой, что дата не найдена. Повторяющиеся расписания требуют отдельного механизма с состоянием и обработкой пропусков.

#Что происходит, если дату не удалось распознать?

Напоминание всё равно создаётся — на сегодня, с явной пометкой, что дата не найдена. Это лучше двух альтернатив: отказаться и потерять задачу или угадать дату молча и создать ложную уверенность.

#Как быть с часовыми поясами?

Хранить момент срабатывания в абсолютном времени, а пояс пользователя применять только при разборе ввода и при показе. Пояс — свойство пользователя, а не устройства: напоминание, созданное из мессенджера, и напоминание из браузера должны сработать одинаково.

#Почему в тексте напоминания остаётся время?

Потому что хранилище может хранить только дату. В нашем случае это так: момент срабатывания — календарный день, а «в 15:00» остаётся частью текста. Ограничение честное, но его надо знать: напоминание сработает днём, а не в указанный час.

#Что надёжнее — правила или модель?

Правила надёжнее по воспроизводимости и бесплатны, модель шире по покрытию и дороже. Рабочая схема — правила основным путём, модель необязательной подстраховкой для того, что правила не разобрали, с обязательным показом результата пользователю.

#Как проверить, что разбор работает правильно?

Тестами с фиксированной базовой датой, передаваемой параметром, а не системными часами. Иначе тесты начнут падать в определённые дни недели, и вы будете чинить не разбор, а календарь.

#Вывод

Разбор естественной формулировки — это не понимание, а набор правил плюс явно принятые решения для неоднозначных случаев. Правила дают воспроизводимость, модель — покрытие, показ результата пользователю — доверие.

Если расписание постоянное и живёт годами — задавайте его точным форматом. Если вам нужны повторяющиеся напоминания — учитывайте, что в Persona их сегодня нет: разбор рассчитан на одно напоминание с одной датой.

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт