ДокументацияПрактические руководства (Cookbooks)Извлечение дат и времени

Извлечение дат и времени

Extracts absolute and relative dates by asking TypeSafe for the parts named in a document, then resolving and validating them in code with confidence-based review.

Извлекайте абсолютные и относительные даты, запрашивая у TypeSafe указанные в документе компоненты, а затем вычисляйте и валидируйте их в коде с проверкой на основе показателей уверенности (confidence).

Считывайте составные части даты из текста с помощью TypeSafe, а затем преобразуйте их в объект date в программном коде.

Создаваемая здесь функция extract_date(document, role) принимает документ и текстовое описание искомой даты (например, «крайний срок сдачи формы») и возвращает объект date вместе с показателем уверенности. Она отмечает результат с низкой уверенностью, а также случаи, когда компоненты вообще не складываются в корректную дату (включая ситуации, когда дата в документе вовсе не указана). Дата может быть записана явно («14 августа 2027») или относительно сегодняшнего дня («завтра», «в следующий четверг»).

TypeSafe отвечает на вопросы типа Choice о дате за один вызов: как именно записана дата, а также какой месяц, день, год или день недели назван в тексте. Программный код преобразует эти ответы в конкретный объект date. Модель лишь считывает то, что написано в тексте, и никогда не занимается календарной арифметикой.

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

Overview diagram

TypeSafe определяет, как записана дата и какие ее части названы в тексте. Код превращает эти ответы в объект date, отсчитывая дни от сегодняшнего числа для относительных дат, и либо принимает результат, либо отправляет его на проверку.

Настройка

BASH THEME={NULL} api.wedstack.ru/v1
pip install ipython "typesafe-sdk>=0.5.7" cooksafe --extra-index-url https://pypi.typesafe.ai/

Затем установите переменную TYPESAFE_API_KEY.

PYTHON EXPANDABLE THEME={NULL} api.wedstack.ru/v1
PYTHON THEME={NULL} api.wedstack.ru/v1
# The demo cells below run when this file is executed as the cookbook; the constants and the pure
# resolve/assemble code stay importable, so the calendar math can be unit-tested on its own.
if __name__ == "__cookbook__":
    client = TypeSafeClient(
        api_key=os.environ.get(
            "TYPESAFE_API_KEY", "cache-only"
        ),  # cached re-renders need no key
        base_url=os.environ.get("TYPESAFE_BASE_URL"),
        timeout=30.0,
    )

Вопросы к модели

Семь вопросов Choice отправляются в одном запросе. Вопрос mode определяет, как записана дата: absolute для календарной даты с указанием месяца, relative для даты относительно сегодняшнего дня и none, если документ вообще не содержит дату.

Остальные шесть вопросов считывают конкретные детали. Абсолютной дате требуются month, day и year. Относительной дате требуется day_anchor: сегодня, завтра, послезавтра или именованный день недели. Если указан день недели, вопросы weekday и week_offset определяют, какой именно день и на какой неделе имеется в виду. Код считывает только те части, которые требуются выбранным mode.

Вопрос year перечисляет по одному варианту на каждый год с 1900 по 2050, плюс два специальных варианта: none означает, что год в тексте не указан (код подставит его сам), а out_of_range — что в тексте указан год вне предложенного диапазона, и код пометит это вместо угадывания. Если столь длинный список вариантов кажется избыточным, можно предварительно извлечь числа, похожие на года, регулярным выражением и предложить модели только их.

PYTHON EXPANDABLE THEME={NULL} api.wedstack.ru/v1

Преобразование в коде

Функция read_parts выполняет вызов API. Функция assemble превращает полученные ответы в объект date: она подставляет год, если он не указан в тексте, и определяет конкретное число для именованного дня недели. Оба вычисления отталкиваются от опорной даты TODAY, которая зафиксирована для воспроизводимости относительных дат при каждом запуске. Кроме того, assemble фиксирует наименьшую уверенность среди использованных компонентов, поэтому ненадежный ответ по любой из частей может отправить всю дату на проверку.

Фраза «в следующий четверг» может трактоваться двояко, поэтому выбор логики остается за кодом. День недели без уточнений означает ближайший такой день, начиная с сегодняшнего. Вариант next означает следующую календарную неделю, а current — текущую неделю.

PYTHON EXPANDABLE THEME={NULL} api.wedstack.ru/v1

Запуск примера

Шесть вопросов по четырем коротким документам: две даты из договора с указанием годов, срок сдачи формы без указания года, опрос, который закрывается «сегодня», встреча по дизайну «в следующий четверг», и дата, о которой в форме нет ни слова. Все они рассчитываются относительно TODAY = 2026-07-30 (четверг).

PYTHON THEME={NULL} api.wedstack.ru/v1
CONTRACT = "This agreement is effective January 1, 2025 and expires December 31, 2027."
FORM = "Please return the signed form by August 14."
SURVEY = "Heads up - the customer survey closes today at 5pm."
REVIEW = "Let's schedule the design review for next Thursday."

# (document, question phrase, expected date) -- the expected value is only for the scorecard.
EXAMPLES = [
    (CONTRACT, "the date the agreement takes effect", date(2025, 1, 1)),
    (CONTRACT, "the date the agreement expires", date(2027, 12, 31)),
    (FORM, "the deadline to return the form", date(2026, 8, 14)),
    (FORM, "the date of the kickoff call", None),
    (SURVEY, "the date the survey closes", date(2026, 7, 30)),
    (REVIEW, "the date of the design review", date(2026, 8, 6)),
]

if __name__ == "__cookbook__":
    print(f"{'':3}{'question':<38}{'expected':<12}{'got':<12}{'conf':>6}  flags")
    print("-" * 84)
    for document, role, expected in EXAMPLES:
        r = extract_date(document, role)
        got = r["date"].isoformat() if r["date"] else "none"
        exp = expected.isoformat() if expected else "none"
        mark = "OK" if r["date"] == expected else "XX"
        conf = f"{r['confidence']:.2f}" if r["confidence"] is not None else " n/a"
        flags = "  <== review" if r["needs_review"] else ""
        if r["note"]:
            flags += f"  ({r['note']})"
        print(f"{mark:<3}{role:<38}{exp:<12}{got:<12}{conf:>6}{flags}")
PLAINTEXT api.wedstack.ru/v1
question                              expected    got           conf  flags
------------------------------------------------------------------------------------
OK the date the agreement takes effect   2025-01-01  2025-01-01    0.97
OK the date the agreement expires        2027-12-31  2027-12-31    0.91
OK the deadline to return the form       2026-08-14  2026-08-14    0.95
OK the date of the kickoff call          none        none          0.46  <== review  (absolute date incomplete)
OK the date the survey closes            2026-07-30  2026-07-30    0.94
OK the date of the design review         2026-08-06  2026-08-06    0.92

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

Вводный звонок (kickoff call) — единственный случай, о котором в форме вообще ничего не говорится. В форме есть другая дата, но не эта, и пометка absolute date incomplete означает, что mode вернул absolute, но без указания месяца. В результате дата осталась пустой, уверенность составила 0.46, и строка была помечена для ручной проверки человеком.

Маршрутизация по показателю уверенности (confidence)

Каждый ответ сопровождается калиброванным показателем уверенности, и итоговая уверенность даты равна минимальной среди всех использованных компонентов. Дата со значением ниже REVIEW_BELOW = 0.60 направляется человеку, как и дата, которую коду вообще не удалось собрать. Остальные проходят конвейер автоматически.

PYTHON THEME={NULL} api.wedstack.ru/v1
if __name__ == "__cookbook__":
    confident = [
        (doc, role)
        for doc, role, _ in EXAMPLES
        if not extract_date(doc, role)["needs_review"]
    ]
    review = [
        (doc, role)
        for doc, role, _ in EXAMPLES
        if extract_date(doc, role)["needs_review"]
    ]
    print(f"auto-accept ({len(confident)}):")
    for _doc, role in confident:
        print(f"  - {role}")
    print(f"\nsend to review ({len(review)}):")
    for _doc, role in review:
        r = extract_date(_doc, role)
        print(
            f"  - {role}  (conf {r['confidence']:.2f} / {r['note'] or 'low confidence'})"
        )
PLAINTEXT api.wedstack.ru/v1
auto-accept (5):
  - the date the agreement takes effect
  - the date the agreement expires
  - the deadline to return the form
  - the date the survey closes
  - the date of the design review

send to review (1):
  - the date of the kickoff call  (conf 0.46 / absolute date incomplete)

Открыть в TypeSafe Playground

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

PYTHON THEME={NULL} api.wedstack.ru/v1
if __name__ == "__cookbook__":
    playground_link = make_playground_link(
        REVIEW, date_questions("the date of the design review"), models=[TYPESAFE_MODEL]
    )
    display(
        Markdown(
            f"🔗 [Open this document + questions in the TypeSafe playground]({playground_link})"
        )
    )

Открыть этот документ + вопросы в TypeSafe Playground →