Cookbook
Storylines · for AI agents

Компиляция потока в Сценарий

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

MCP tools:create_storylinevalidate_storylinepublish_storylinelist_storylinesupdate_storyline

Storyline — это направленный граф, привязанный к агенту: он превращает «ответить на один вопрос» в «выполнить задачу от начала до конца» (сбор данных, обучение, коучинг…). Узлы — это шаги (каждый со своей задачей / навыками / kb / инструментами и изменяемыми измерениями профиля); выходы несут условия (ai / user_choice / rule / callback / goto_storyline). Runtime никогда не редактирует «душу» агента или его границы безопасности — она постоянна от первого узла до последнего, что именно то, что нужно для регулируемых продуктов или продуктов для детей.

Создать черновик

create_storyline(
  agent_name="Support", key="loan-intake", name="Loan intake",
  profile_schema={"docs_ready": {"type": "bool"}},          # dims that accumulate across nodes
  graph={"nodes": [
    {"node_key": "n1", "title": "Understand need", "flags": {"is_entry": True},
     "on_enter_opening": "Hi — which kind of loan are you applying for?",
     "exits": [{"kind": "ai", "label": "need clear", "to_node_key": "n2",
                "ai_criteria": "the user stated the loan type and rough amount"}]},
    {"node_key": "n2", "title": "Collect documents",
     "task": "Collect the checklist items; chase missing ones across turns.",
     "exits": [{"kind": "user_choice", "label": "documents ready", "to_node_key": "n3",
                "user_choice": {"button_text": "I've uploaded everything"},
                "writes": [{"ref": "dim", "key": "docs_ready", "op": "set", "value": True}]}]},
    {"node_key": "n3", "title": "Hand off", "task": "Summarise the case and hand off to a human.",
     "flags": {"is_terminal": True}, "exits": []}
  ]},
  allow_agent_enroll=True,
  enroll_trigger="the visitor says they want to apply for a loan",
)

★ Проверить, затем опубликовать ★

validate_storyline(storyline_id="…")   # → {"ok": true} is required to publish
publish_storyline(storyline_id="…")    # freezes an immutable version and goes live

Enroll — кто входит и когда (требуется публикация)

  • is_default=True — один на агента; авто-enroll при первом разговоре.
  • allow_agent_enroll=True + enroll_trigger — агент делает enroll пользователя, когда считает триггер выполненным.
  • entry="user" (или "both") — поток отображается в меню guided-flgets виджета чата и в выпадающем списке New-chat, поэтому пользователь запускает его намеренно (кнопка, а не решение модели). Задайте display_name и однострочное description — это то, что показывает меню. В сочетании с concurrency="session" для потоков «один кейс на разговор»: каждый выбор открывает новый кейс.
  • Ручное назначение в консоли.

Что видит пользователь — видимость, баннер и карта только для чтения

user_visibility управляет представлением для конечного пользователя запущенного потока (по умолчанию invisible):

  • invisible — никакого UI; поток работает тихо (прежнее поведение).
  • named — тонкий баннер над чатом показывает имя потока. Ничего больше.
  • trail — баннер + кнопка View, открывающая карту только для чтения: пройденные шаги сохраняют свои заголовки; непройденные ветки и будущие шаги — анонимные серые блоки (редактируется на сервере — пользователь видит длину потока, но не его содержимое).
  • full — баннер с шагом x / y + полная карта: каждый шаг именован, раскрашен в зависимости от статуса done / current / upcoming.

Дополнительно: allow_exit=True добавляет кнопку Exit в баннер (выходящий из потока поток не делает авто-enroll в этом разговоре; поток, выбранный пользователем, может быть перезапущен из меню, прогресс сохраняется). show_profile=True отображает текущие значения измерений профиля под картой (числа в виде прогресс-баров, если схема объявляет min/max). Для каждого узла rewind: "reset" | "keep" позволяет пользователю нажать на пройденный шаг в полной карте и вернуться к нему — reset восстанавливает снимок профиля, сделанный при входе, keep сохраняет собранные данные (потоки обучения/проверки). Реальные побочные эффекты (поданные записи, отправленные документы) никогда не откатываются, поэтому отмечайте только шаги без побочных эффектов. В IM-каналах (Telegram/WhatsApp) команда /storyline сообщает о текущем потоке и, для trail/full, возвращает короткоживущую подписанную ссылку на ту же карту только для чтения.

Устаревшее имя параметра learner_visibility и его значения hidden / completed_only всё ещё принимаются (они отображаются в invisible / trail), но устарели — используйте user_visibility.

Конкурентность — следует ли прогресс за человеком или за кейсом?

concurrency решает, сколько запусков один пользователь может иметь на этой линии:

  • "user" (по умолчанию) — один запуск на человека; каждый разговор продолжает тот же прогресс. Подходит для учебных программ, онбординга, KYC: «насколько далеко зашёл этот человек».
  • "session"один запуск на разговор; новый чат начинает новый независимый кейс. Подходит для заявок на лицензию, тикетов поддержки, потоков по продукту: «насколько далеко зашёл этот кейс». Один и тот же пользователь может запустить три заявки на продукт в трёх чатах, каждый со своим прогрессом и blackboard.

Размещайте состояние там, где оно должно быть: состояние кейса на blackboard (он живёт и умирает вместе с запуском — название продукта, список документов этого приложения), факты о человеке в измерениях профиля (они следуют за пользователем между запусками и storylines — детали компании, подтверждённая личность). Один кейс на разговор — это намеренно: чтобы открыть другой кейс, откройте другой чат. Изменение concurrency влияет только на будущие enrollments; запуски, уже находящиеся в работе, сохраняют свой ключ.

Чеклисты, заблокированные выборы, файлы и детерминированные действия

Четыре возможности на уровне узла превращают «ИИ решает, когда мы закончили» в накопленное, детерминированное состояние:

  • checklist[{key, label}, …] на узле. Каждый ход платформа отмечает пункты из того, что посетитель фактически предоставляет (загрузки или описания с конкретными деталями — обещания не учитываются), и правило выхода {"op": ">=", "left": {"ref": "checklist"}, "right": {"value": 1}} продвигает момент, когда всё отмечено. Карта только для чтения показывает пользователю живой список ✓/○. Это правильная форма для потоков заявок/одобрения: шаг 1 объявляет, что нужно, шаг 2 с помощью чеклиста собирает это по пунктам.
  • choices_offer: "on_ready" — темп в стиле игр для кнопок выбора: они остаются скрытыми (и текстовое совпадение с ними отключено — нельзя пропустить вперёд, напечатав слово кнопки), пока шаг не будет завершён — чеклист завершён или choices_ready_criteria, которую вы предоставляете, признана истинной. Используйте user_choice для настоящих решений между альтернативами; одинокая кнопка «продолжить» — это признак проблемы.
  • {"ref": "files"} в правилах выхода — количество загруженных файлов/изображений пока этот узел активен (сбрасывается при переходе). files >= 2 — полностью детерминированный шлюз «документы получены».
  • questions — ледокольные чипсы на уровне узла (макс. 6): вопросы, которые пользователь, вероятно, задаст на этом шаге, отображаются как нажимаемые предложения, пока он находится на нём. В сочетании с on_enter_opening, чтобы каждый шаг представлял себя и предлагал способ входа — пользователь никогда не должен задуматься «что мне здесь supposed делать».
  • on_enter_actions / on_complete_actions[{tool, args}] платформа выполняет детерминированно, когда шаг входит / завершает поток (например, подаёт лид с помощью mcp__agent4__submit_lead на терминальном шаге). Строковые аргументы интерполируют ключи измерений и blackboard с помощью шаблонов в фигурных скобках. Ошибки логируются и никогда не ломают чат.

Контекст остаётся плоским независимо от длины storyline: каждый ход инжектирует только текущий узел (задача, ресурсы, выборы), измерения профиля и blackboard с ограничением размера — никогда не весь граф или прошлые шаги. Долгие путешествия (учебный год, сезонная игровая арка) должны быть связаны storylines (on_complete: goto_next): blackboard каждой линии начинается заново, пока измерения профиля переносятся, что и есть «подвести итог прошлому, сохранить настоящее дословно».

Судья выхода теперь видит окно недавних разговоров и собранное состояние, а не только последнее сообщение — но держите ai_criteria о том, что посетитель сказал или сделал; механизм чеклиста выше, чем любые текстовые критерии.

Ловушки дизайна — каждая из них привела к зависшему потоку в продакшене

Вы (агент, строящий это) — наиболее вероятный автор этих багов. Проверьте каждый перед публикацией:

  1. Выход rule требует писателя. readiness >= 100 — тупик, если никто фактически не пишет readiness: writes выхода (детерминированный — предпочтительно), profile_writes узла с source: "ai" (оценено из разговора), другой storyline или ручные изменения в консоли. validate_storyline теперь помечает это как warn.rule_unwritten_dim — относитесь к этому предупреждению как к багу, если вы не знаете о внешнем писателе. Эта ошибка однажды попала в продакшен: трёхшаговый сбор данных, второй шаг которого блокировался на измерении, которое никто не писал; пользователи никогда не могли пройти его.
  2. Оценённые ИИ измерения почти никогда не достигают точных границ. Если измерение оценивается моделью, >= 100 (или == max) практически никогда не срабатывает — модели оценивают консервативно. Блокируйте на реалистичном пороге (>= 70), или сделайте завершение шага user_choice и используйте writes выхода для детерминированной установки значения.
  3. user_choice точно совпадает с текстом кнопки — и метка должна говорить, что она делает. Сообщение посетителя должно равняться button_text. На видимых линиях виджет отображает их как настоящие кнопки, поэтому пишите полные, точные глагольные фразы — «Начать сбор документов», а не «Начать» (начать что? — реальная обратная связь от тестирования). Держите их разными и на языке аудитории. На invisible линиях нет кнопок — повторите варианты в task узла. Предпочитайте user_choice для настоящих решений (какая ветка, отправить или продолжать редактирование); «закончил ли я этот шаг» — задача ИИ/выходов rule, а не кнопки.
  4. Публикация не затрагивает запуски, уже находящиеся в работе. Enrollments остаются привязанными к ревизии, на которой они начались — выпуск с исправлением багов достигает только новых запусков. Чтобы переместить существующих пользователей, вызовите POST /storylines/{id}/enrollments/migrate (reset обратно к входу или move к узлу — оба повторно привязывают запуск к текущей опубликованной ревизии).
  5. Пройдите линию как пользователь перед тем, как передать её. Откройте страницу /s/ из ссылки, войдите в поток, пройдите через каждую кнопку и ветку до конца. Тестовый запуск редактора симулирует граф; только реальная страница проверяет открытия, кнопки, видимость и завершение вместе.

Выходы упорядочены по приоритету (порядок в списке): детерминированные rule / user_choice / callback оцениваются первыми, ai последним. Стройте if/else, циклы повторных попыток и AND-соединения из детерминированных выходов — не играйте в лотерею с LLM. Редактируйте черновик с помощью update_storyline (семантика PUT; держите каждый node_key стабильным, так как выходы и воронка ссылаются на него).

Верните отчёт. Верните ссылку на консоль опубликованного Storyline — https://console.agent4.io/#/storylines/<id> (id из create_storyline) — с однострочным резюме потока и тем, как пользователь входит в него.