---
title: "Python-скрипты"
id: "2099"
type: "page"
slug: "scripts"
published_at: "2026-09-30T04:00:26+00:00"
modified_at: "2026-10-14T20:40:46+00:00"
url: "https://pastukhov.com/agents/research/docs/scripts"
markdown_url: "https://pastukhov.com/agents/research/docs/scripts.md"
excerpt: "Research Agent можно расширять своими скриптами на Python: они лежат обычными файлами в папке исследований…"
---

# Python-скрипты

[https://pastukhov.com/agents/research/docs/scripts.md](https://pastukhov.com/agents/research/docs/scripts.md)

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)** — свой источник поиска: скрипт получает запрос и возвращает найденные страницы. Подойдёт любой сервис, у которого нет готового провайдера (подробнее — [Поиск](/agents/research/docs/search) );
- **Python script (fetch)** — свой способ забрать страницу: скрипт получает адрес и возвращает её содержимое (подробнее — [Скачивание страниц](/agents/research/docs/fetch) );
- **Python script (LLM)** — свой путь к модели: скрипт получает запрос к модели и возвращает ответ. Так подключается шлюз с нестандартным протоколом (подробнее — [Извлечение данных](/agents/research/docs/extract) ).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читайте также: [Сервисы и модели](/agents/research/docs/services)
, [Скачивание страниц](/agents/research/docs/fetch)
, [Извлечение данных](/agents/research/docs/extract)
, [Интеграция с Pastukhov Agent](/agents/research/docs/agent)
.

[← Оглавление документации](/agents/research/docs)
