Skip to content
Эта страница создана и переведена с помощью ИИ. Если вы заметили неточности, помогите нам улучшить её. Редактировать на GitHub

Хуки ​

Хуки PRX предоставляют событийную систему расширений, позволяющую реагировать на события жизненного цикла во время выполнения агента. Каждый значимый момент в цикле агента -- начало хода, вызов LLM, вызов инструмента, возникновение ошибки -- генерирует событие хука. Действия прикрепляются к этим событиям через конфигурационный файл hooks.json, манифесты WASM-плагинов или HTTP API.

Хуки работают по принципу "отправил и забыл". Они никогда не блокируют цикл агента, никогда не изменяют поток выполнения и никогда не внедряют данные обратно в разговор. Это делает их идеальными для аудит-логирования, сбора метрик, внешних уведомлений и автоматизации побочных эффектов без внесения задержек или точек отказа в основной конвейер агента.

Существует три бэкенда выполнения хуков:

  • Shell-хуки -- Запускают внешнюю команду с передачей полезной нагрузки события через переменную окружения, временный файл или stdin. Настраиваются в hooks.json.
  • Хуки WASM-плагинов -- Вызывают функцию on-event, экспортируемую WASM-плагином. Объявляются в манифесте plugin.toml плагина.
  • Хуки шины событий -- Публикуют во внутреннюю шину событий по теме prx.lifecycle.<event>. Всегда активны; настройка не требуется.

События хуков ​

PRX генерирует 8 событий жизненного цикла. Каждое событие несёт JSON-полезную нагрузку с контекстно-специфичными полями.

СобытиеКогда генерируетсяПоля полезной нагрузки
agent_startЦикл агента начинает новый ходagent (string), session (string)
agent_endЦикл агента завершает ходsuccess (bool), messages_count (number)
llm_requestПеред отправкой запроса к LLM-провайдеруprovider (string), model (string), messages_count (number)
llm_responseПосле получения ответа LLMprovider (string), model (string), duration_ms (number), success (bool)
tool_call_startПеред началом выполнения инструментаtool (string), arguments (object)
tool_callПосле завершения выполнения инструментаtool (string), success (bool), output (string)
turn_completeПолный ход завершён (все инструменты обработаны)(пустой объект)
errorЛюбая ошибка во время выполненияcomponent (string), message (string)

Схемы полезной нагрузки ​

Все полезные нагрузки являются JSON-объектами. Структура верхнего уровня оборачивает событийно-специфичные поля:

json
{
  "event": "llm_response",
  "timestamp": "2026-03-21T08:15:30.123Z",
  "session_id": "sess_abc123",
  "payload": {
    "provider": "openai",
    "model": "gpt-4o",
    "duration_ms": 1842,
    "success": true
  }
}

Поля event, timestamp и session_id присутствуют в каждом событии хука. Объект payload варьируется в зависимости от типа события, как описано в таблице выше.

Конфигурация ​

Shell-хуки настраиваются в файле hooks.json, размещённом в каталоге рабочего пространства (том же каталоге, что и config.toml). PRX следит за изменениями этого файла и горячо перезагружает конфигурацию без необходимости перезапуска.

Базовая структура ​

json
{
  "hooks": {
    "<event_name>": [
      {
        "command": "/path/to/script",
        "args": ["--flag", "value"],
        "env": {
          "CUSTOM_VAR": "value"
        },
        "cwd": "/working/directory",
        "timeout_ms": 5000,
        "stdin_json": true
      }
    ]
  }
}

Каждое имя события отображается на массив действий хука. К одному событию можно прикрепить несколько действий; они выполняются конкурентно и независимо.

Полный пример ​

json
{
  "hooks": {
    "agent_start": [
      {
        "command": "/usr/local/bin/notify",
        "args": ["--channel", "ops", "--title", "Agent Started"],
        "timeout_ms": 3000
      }
    ],
    "llm_response": [
      {
        "command": "python3",
        "args": ["/opt/hooks/log_latency.py"],
        "stdin_json": true,
        "timeout_ms": 2000
      }
    ],
    "tool_call": [
      {
        "command": "/opt/hooks/audit_tool_usage.sh",
        "env": {
          "LOG_DIR": "/var/log/prx/audit"
        },
        "timeout_ms": 5000
      }
    ],
    "error": [
      {
        "command": "curl",
        "args": [
          "-X", "POST",
          "-H", "Content-Type: application/json",
          "-d", "@-",
          "https://hooks.slack.com/services/T00/B00/xxxxx"
        ],
        "stdin_json": true,
        "timeout_ms": 10000
      }
    ]
  }
}

Поля действий хуков ​

Каждый объект действия хука поддерживает следующие поля:

ПолеТипОбязательноеПо умолчаниюОписание
commandstringДа--Абсолютный путь к исполняемому файлу или имя команды из очищенного PATH
argsstring[]Нет[]Аргументы, передаваемые команде
envobjectНет{}Дополнительные переменные окружения, объединяемые с очищенной средой выполнения
cwdstringНеткаталог рабочего пространстваРабочий каталог для порождаемого процесса
timeout_msnumberНет30000Максимальное время выполнения в миллисекундах. Процесс убивается (SIGKILL) при превышении
stdin_jsonboolНетfalseПри true полная JSON полезная нагрузка события передаётся процессу через stdin

Замечания по command ​

Поле command подвергается проверке безопасности перед выполнением. Оно не должно содержать метасимволов shell (;, |, &, `, $()) -- они отклоняются для предотвращения shell-инъекций. Если вам нужны возможности shell, оберните их в файл скрипта и укажите command на этот скрипт.

Относительные пути разрешаются относительно каталога рабочего пространства. Однако для предсказуемости рекомендуется использовать абсолютные пути.

Доставка полезной нагрузки ​

Действия хуков получают полезную нагрузку события через три канала одновременно. Эта избыточность гарантирует, что скрипты на любом языке могут получить доступ к данным через наиболее удобный метод.

1. Переменная окружения (ZERO_HOOK_PAYLOAD) ​

JSON-строка полезной нагрузки устанавливается как переменная окружения ZERO_HOOK_PAYLOAD. Это самый простой метод доступа для shell-скриптов:

bash
#!/bin/bash
# Чтение полезной нагрузки из переменной окружения
echo "$ZERO_HOOK_PAYLOAD" | jq '.payload.tool'

Ограничение размера: 8 КБ. Если сериализованная полезная нагрузка превышает 8 КБ, переменная окружения не устанавливается, и полезная нагрузка доступна только через временный файл и каналы stdin.

2. Временный файл (ZERO_HOOK_PAYLOAD_FILE) ​

Полезная нагрузка записывается во временный файл, и путь к файлу устанавливается в переменной окружения ZERO_HOOK_PAYLOAD_FILE. Временный файл автоматически удаляется после завершения процесса хука.

python
import os, json

payload_file = os.environ["ZERO_HOOK_PAYLOAD_FILE"]
with open(payload_file) as f:
    data = json.load(f)
print(f"Tool: {data['payload']['tool']}, Success: {data['payload']['success']}")

Этот канал не имеет ограничения размера и является рекомендуемым методом для полезных нагрузок, которые могут быть большими (напр., tool_call с подробным выводом).

3. Стандартный ввод (stdin) ​

Когда stdin_json установлен в true в действии хука, JSON полезной нагрузки передаётся процессу через stdin. Это полезно для команд, которые нативно читают из stdin, таких как curl -d @- или jq.

bash
#!/bin/bash
# Чтение из stdin (требуется stdin_json: true в конфигурации хука)
read -r payload
echo "$payload" | jq -r '.payload.message'

Переменные окружения ​

Каждый процесс хука получает следующие переменные окружения, помимо ZERO_HOOK_PAYLOAD и ZERO_HOOK_PAYLOAD_FILE:

ПеременнаяОписаниеПример
ZERO_HOOK_EVENTИмя события, вызвавшего этот хукtool_call
ZERO_HOOK_SESSIONИдентификатор текущей сессииsess_abc123
ZERO_HOOK_TIMESTAMPВременная метка события в формате ISO 86012026-03-21T08:15:30.123Z
ZERO_HOOK_PAYLOADПолная полезная нагрузка как JSON-строка (не задаётся если >8 КБ){"event":"tool_call",...}
ZERO_HOOK_PAYLOAD_FILEПуть к временному файлу с полезной нагрузкой/tmp/prx-hook-a1b2c3.json

Среда выполнения очищается перед запуском процесса хука. Конфиденциальные и опасные переменные окружения удаляются (см. Безопасность ниже), и доступны только перечисленные выше переменные плюс переопределения env из действия хука.

Хуки WASM-плагинов ​

WASM-плагины могут подписываться на события хуков, экспортируя функцию on-event, определённую в WIT (WebAssembly Interface Types) интерфейсе PRX.

WIT-интерфейс ​

wit
interface hooks {
    /// Вызывается при срабатывании подписанного события.
    /// Возвращает Ok(()) при успехе, Err(message) при неудаче.
    on-event: func(event: string, payload-json: string) -> result<_, string>;
}

Параметр event -- это имя события (напр., "tool_call"), а payload-json -- полная полезная нагрузка, сериализованная как JSON-строка, идентичная тому, что получают shell-хуки.

Паттерны подписки на события ​

Плагины объявляют, какие события они хотят получать, в своём манифесте plugin.toml с использованием сопоставления паттернов:

ПаттернСовпаденияПример
Точное совпадениеОдно конкретное событие"tool_call"
Суффикс с подстановкойВсе события, совпадающие с префиксом"prx.lifecycle.*"
УниверсальныйКаждое событие"*"

Пример манифеста плагина ​

toml
[plugin]
name = "audit-logger"
version = "0.1.0"
description = "Logs all lifecycle events to an audit trail"

[[capabilities]]
type = "hook"
events = ["agent_start", "agent_end", "error"]

[[capabilities]]
type = "hook"
events = ["prx.lifecycle.*"]

Один плагин может объявить несколько блоков [[capabilities]] с разными паттернами событий. Объединение всех совпавших событий определяет, какие события получает плагин.

Модель выполнения ​

Хуки WASM-плагинов выполняются внутри WASM-песочницы с теми же ограничениями ресурсов, что и другие функции плагинов. На них распространяются:

  • Лимит памяти: Определяется в конфигурации ресурсов плагина (по умолчанию 64 МБ)
  • Таймаут выполнения: Такой же, как timeout_ms для shell-хуков (по умолчанию 30 секунд)
  • Без доступа к файловой системе: Если не предоставлен явно через WASI-возможности
  • Без сетевого доступа: Если не предоставлен явно через флаги возможностей

Если WASM-хук возвращает Err(message), ошибка логируется, но не влияет на цикл агента. Хуки всегда работают по принципу "отправил и забыл".

Интеграция с шиной событий ​

Каждое событие хука автоматически публикуется во внутреннюю шину событий по теме prx.lifecycle.<event>. Это происходит независимо от того, настроены ли shell- или WASM-хуки.

Формат тем ​

prx.lifecycle.agent_start
prx.lifecycle.agent_end
prx.lifecycle.llm_request
prx.lifecycle.llm_response
prx.lifecycle.tool_call_start
prx.lifecycle.tool_call
prx.lifecycle.turn_complete
prx.lifecycle.error

Типы подписок ​

Внутренние компоненты и плагины могут подписываться на темы шины событий тремя способами:

  • Точная: prx.lifecycle.tool_call -- получает только события tool_call
  • С подстановкой: prx.lifecycle.* -- получает все события жизненного цикла
  • Иерархическая: prx.* -- получает все события домена PRX (жизненный цикл, метрики и т.д.)

Ограничения полезной нагрузки ​

ОграничениеЗначение
Максимальный размер полезной нагрузки64 КБ
Максимальная глубина рекурсии8 уровней
Модель диспетчеризации"Отправил и забыл" (async)
Гарантия доставкиНе более одного раза

Если событие хука вызывает другое событие хука (напр., скрипт хука вызывает инструмент, который генерирует tool_call), счётчик рекурсии увеличивается. На глубине 8 уровней дальнейшие генерации событий тихо отбрасываются для предотвращения бесконечных циклов.

HTTP API ​

Хуками можно управлять программно через HTTP API. Все эндпоинты требуют аутентификации и возвращают JSON-ответы.

Список всех хуков ​

GET /api/hooks

Ответ:

json
{
  "hooks": [
    {
      "id": "hook_01",
      "event": "error",
      "action": {
        "command": "/opt/hooks/notify_error.sh",
        "args": [],
        "timeout_ms": 5000,
        "stdin_json": false
      },
      "enabled": true,
      "created_at": "2026-03-20T10:00:00Z",
      "updated_at": "2026-03-20T10:00:00Z"
    }
  ]
}

Создание хука ​

POST /api/hooks
Content-Type: application/json

{
  "event": "llm_response",
  "action": {
    "command": "python3",
    "args": ["/opt/hooks/track_latency.py"],
    "stdin_json": true,
    "timeout_ms": 3000
  },
  "enabled": true
}

Ответ (201 Created):

json
{
  "id": "hook_02",
  "event": "llm_response",
  "action": {
    "command": "python3",
    "args": ["/opt/hooks/track_latency.py"],
    "stdin_json": true,
    "timeout_ms": 3000
  },
  "enabled": true,
  "created_at": "2026-03-21T08:00:00Z",
  "updated_at": "2026-03-21T08:00:00Z"
}

Обновление хука ​

PUT /api/hooks/hook_02
Content-Type: application/json

{
  "event": "llm_response",
  "action": {
    "command": "python3",
    "args": ["/opt/hooks/track_latency_v2.py"],
    "stdin_json": true,
    "timeout_ms": 5000
  },
  "enabled": true
}

Ответ (200 OK): Возвращает обновлённый объект хука.

Удаление хука ​

DELETE /api/hooks/hook_02

Ответ (204 No Content): Пустое тело при успехе.

Переключение хука ​

PATCH /api/hooks/hook_01/toggle

Ответ (200 OK):

json
{
  "id": "hook_01",
  "enabled": false
}

Этот эндпоинт переключает состояние enabled. Отключённые хуки остаются в конфигурации, но не выполняются при срабатывании их события.

Безопасность ​

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

Заблокированные переменные окружения ​

Следующие переменные окружения удаляются из среды выполнения хуков и не могут быть переопределены через поле env в действиях хуков:

ПеременнаяПричина
LD_PRELOADВектор атаки через внедрение библиотек
LD_LIBRARY_PATHМанипуляция путём поиска библиотек
DYLD_INSERT_LIBRARIESВнедрение библиотек на macOS
DYLD_LIBRARY_PATHМанипуляция путём библиотек на macOS
PATHПредотвращение перехвата PATH; предоставляется минимальный безопасный PATH
HOMEПредотвращение подмены домашнего каталога

Валидация ввода ​

  • Отклонение нулевых байтов: Любое значение command, args, ключа env или значения env, содержащее нулевой байт (\0), отклоняется. Это предотвращает атаки внедрения нулевых байтов, которые могут обрезать строки на уровне ОС.
  • Отклонение метасимволов shell: Поле command не должно содержать ;, |, &, `, $( или другие метасимволы shell. Это предотвращает shell-инъекции, даже если команда случайно передана через shell.
  • Обход путей: Поле cwd проверяется на отсутствие выхода за пределы каталога рабочего пространства через компоненты ...

Применение таймаутов ​

Каждый процесс хука подчиняется настроенному timeout_ms (по умолчанию 30 секунд). При превышении лимита:

  1. Процессу отправляется SIGTERM
  2. После 5-секундного льготного периода отправляется SIGKILL
  3. Хук помечается как превысивший таймаут во внутренних метриках
  4. Цикл агента не затрагивается

Изоляция ресурсов ​

Процессы хуков наследуют те же ограничения cgroup и пространства имён, что и выполнение инструмента shell, когда активен бэкенд песочницы. В режиме песочницы Docker хуки выполняются в отдельном контейнере без сетевого доступа по умолчанию.

Примеры ​

Хук аудит-логирования ​

Логирование каждого вызова инструмента в файл для аудита соответствия:

json
{
  "hooks": {
    "tool_call": [
      {
        "command": "/opt/hooks/audit_log.sh",
        "env": {
          "AUDIT_LOG": "/var/log/prx/tool_audit.jsonl"
        },
        "timeout_ms": 2000
      }
    ]
  }
}

/opt/hooks/audit_log.sh:

bash
#!/bin/bash
echo "$ZERO_HOOK_PAYLOAD" >> "$AUDIT_LOG"

Хук уведомления об ошибках ​

Отправка событий ошибок в канал Slack:

json
{
  "hooks": {
    "error": [
      {
        "command": "curl",
        "args": [
          "-s", "-X", "POST",
          "-H", "Content-Type: application/json",
          "-d", "@-",
          "https://hooks.slack.com/services/T00/B00/xxxxx"
        ],
        "stdin_json": true,
        "timeout_ms": 10000
      }
    ]
  }
}

Хук метрик задержки LLM ​

Отслеживание времени ответа LLM для мониторинговых панелей:

json
{
  "hooks": {
    "llm_response": [
      {
        "command": "python3",
        "args": ["/opt/hooks/metrics.py"],
        "stdin_json": true,
        "timeout_ms": 3000
      }
    ]
  }
}

/opt/hooks/metrics.py:

python
import sys, json

data = json.load(sys.stdin)
payload = data["payload"]
provider = payload["provider"]
model = payload["model"]
duration = payload["duration_ms"]
success = payload["success"]

# Отправка в StatsD, Prometheus pushgateway или любой бэкенд метрик
print(f"prx.llm.duration,provider={provider},model={model} {duration}")
print(f"prx.llm.success,provider={provider},model={model} {1 if success else 0}")

Отслеживание жизненного цикла сессий ​

Отслеживание начала и завершения сессий агента для аналитики использования:

json
{
  "hooks": {
    "agent_start": [
      {
        "command": "/opt/hooks/session_tracker.sh",
        "args": ["start"],
        "timeout_ms": 2000
      }
    ],
    "agent_end": [
      {
        "command": "/opt/hooks/session_tracker.sh",
        "args": ["end"],
        "timeout_ms": 2000
      }
    ]
  }
}

Связанные разделы ​

  • Выполнение shell -- инструмент shell, который часто оборачивают хуки
  • Интеграция MCP -- протокол внешних инструментов, генерирующий события tool_call
  • Плагины -- система WASM-плагинов, включая возможности хуков
  • Наблюдаемость -- метрики и трассировка, дополняющие хуки
  • Безопасность -- песочница и движок политик, управляющие выполнением хуков

Released under the Apache-2.0 License.