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.