Python-скрипты

Research Agent можно расширять своими скриптами на Python: они лежат обычными файлами в папке исследований и подключаются к конвейеру — вместо поискового провайдера, вместо метода скачивания, вместо модели, а также для обработки Markdown и результатов извлечения. Пересобирать и перезапускать приложение не нужно: положили файл — новая возможность уже работает. Скрипт выручает там, где готовых средств не хватает: подключить сервис, для которого нет готового провайдера, забрать страницу своим способом, вычистить разметку перед сохранением или отфильтровать строки извлечения перед записью.

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

Где лежат скрипты

Все определения живут в папке /project/apps/research/scripts/ — в том же постоянном томе, что отчёты и настройки сервисов. Один скрипт — один файл настроек, имя которого и есть ключ скрипта (например, clean-markdown.yml):

title: Чистка Markdown
notes: Убирает меню и служебные блоки перед сохранением
enabled: true
timeoutSec: 30
code: |
  import json, sys
  ctx = json.load(sys.stdin)
  print(json.dumps({"markdown": ctx["payload"]["markdown"]}))
  • код — либо прямо в файле настроек, либо отдельным файлом .py рядом с ним: как удобнее. Отдельный файл читается с диска на каждом запуске, поэтому правка в нём видна сразу;
  • название и заметки — чтобы через полгода было понятно, что это и зачем;
  • включение — выключенный скрипт не обслуживает подключённые к нему места;
  • предельное время — сколько секунд ждать ответа (по умолчанию 60, потолок — 10 минут);
  • переменные окружения — постоянные значения для запуска: адреса, ключи доступа. Можно также указать, какие переменные окружения сервера передавать скрипту;
  • аргументы — готовые значения на каждый запуск; их можно переопределить в конкретном месте подключения.

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

Библиотеки, которые нужны скриптам, перечисляются в одном общем файле scripts/requirements.txt. Приложение доустанавливает их само перед следующим запуском, отдельно окружение настраивать не нужно.

Три точки подключения сервисом

Скрипт подключается в разделе Services как обычный сервис — с приоритетом, параллелизмом, лимитом запросов и запасными вариантами, как у любого провайдера:

  • Python script (search) — свой источник поиска: скрипт получает запрос и возвращает найденные страницы. Подойдёт любой сервис, у которого нет готового провайдера (подробнее — Поиск);
  • Python script (fetch) — свой способ забрать страницу: скрипт получает адрес и возвращает её содержимое (подробнее — Скачивание страниц);
  • Python script (LLM) — свой путь к модели: скрипт получает запрос к модели и возвращает ответ. Так подключается шлюз с нестандартным протоколом (подробнее — Извлечение данных).

В настройках такого сервиса два поля: «Ключ скрипта» — имя файла без расширения, обязательно; и «Аргументы» — необязательный набор значений, который скрипт получит при запуске. Если скрипт не отвечает или отвечает ошибкой, конвейер ведёт себя как с любым другим сервисом: переходит к следующему по приоритету.

Два преобразования на весь продукт

Кроме сервисов есть два места, куда подключается один скрипт на весь продукт. Они задаются в файле настроек settings.yml (секция scripts:) — не в интерфейсе:

  • Обработка Markdown (onMarkdown) — выполняется после того, как страница скачана и переведена в Markdown, но до сохранения: скрипт получает текст и возвращает исправленный. Сохраняется уже обработанный текст, а сырая страница остаётся нетронутой;
  • Обработка извлечения (onExtract) — выполняется после разбора извлечения и до записи строк: скрипт может отфильтровать, переименовать или дополнить записи.

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

Как проходит запуск

  • каждый запуск — отдельный процесс Python: он запускается, отдаёт ответ и завершается;
  • на вход приходит один документ JSON: точка подключения, имя скрипта, откуда запуск, аргументы, данные. Читается привычным json.load(sys.stdin);
  • ответ — тоже JSON: последний цельный объект, который скрипт напечатал. Сообщения о ходе работы печатать можно, они не мешают;
  • у каждого скрипта есть своя рабочая папка, которая сохраняется между запусками: там удобно держать файлы, курсоры и промежуточные результаты;
  • размер данных ограничен: если данных много, передавайте не сами данные, а ссылку или файл в рабочей папке;
  • процесс не получает секретов приложения: ему доступны только те переменные, которые вы сами перечислили в его настройках.

Правила выполнения

Общие правила задаются в том же файле настроек, в секции scripts:, и применяются на лету:

  • общий выключатель — одно переключение запрещает все запуски; каждое подключённое место честно сообщит, что скрипты отключены, а не сделает вид, что всё в порядке;
  • предельное время — по умолчанию 60 секунд на запуск, потолок — 10 минут; по истечении процесс останавливается;
  • одновременные запуски — по умолчанию не больше двух процессов сразу, чтобы скрипты не перегружали сервер;
  • предел вывода — 1 МиБ на запуск; данные большего размера остаются в рабочей папке, а скрипт возвращает ссылку на файл.

Раздел «Скрипты»

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

  • состояние среды сверху: готов ли Python, собран ли набор библиотек, сколько запусков и сбоев было за сутки, что случилось в прошлый раз;
  • список скриптов: название, откуда берётся код, к чему подключён, когда запускался в последний раз;
  • журнал запусков: время, точка подключения, откуда запуск, объект, длительность, результат или понятная причина сбоя. Фильтры — по состоянию и точке подключения;
  • тестовый запуск: задали аргументы и данные, нажали «Тестировать скрипт» — результат тут же появился в журнале, как от обычного запуска;
  • журнал хранится 30 дней и чистится сам.

Причины сбоя названы по-русски и понятны без расшифровки: «Среда выполнения недоступна» (нет рабочего Python или не собрались библиотеки), «Таймаут», «Неверный вывод» (скрипт не напечатал цельный JSON), «Слишком большой», «Отменён», «Отключён». Сбой скрипта никогда не подменяет данные молчанием: у подключённого сервиса он выглядит как ошибка сервиса, у преобразования — как предупреждение в журнале.

Граница доверия

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

Читайте также: Сервисы и модели, Скачивание страниц, Извлечение данных, Интеграция с Pastukhov Agent.

← Оглавление документации