Средства выполнения инструментов

Инструменты Hermes — это самостоятельные функции, сгруппированные в наборы инструментов и выполняемые через центральную систему реестра/диспетчеризации.

Основные файлы:

Инструменты регистрации моделей

Каждый модульный инструмент предоставляет registry.register(...) во время импорта.

model_tools.py отвечает за импорт/обнаружение модулей инструментов и построение списка схем, внедрение модели.

Как работает registry.register()

Каждый файл-инструмент в tools/ вызывает registry.register() на уровне модуля, чтобы объявить себя. Сигнатурная функция:

registry.register(
    name="terminal",               # Уникальное имя инструмента (используется в схемах API)
    toolset="terminal",            # Набор инструментов, к которому принадлежит этот инструмент
    schema={...},                  # Схема вызова функции OpenAI (описание, параметры)
    handler=handle_terminal,       # Функция, которая выполняется при вызове инструмента
    check_fn=check_terminal,       # Опционально: возвращает True/False для доступности
    requires_env=["SOME_VAR"],     # Опционально: необходимые переменные окружения (для отображения в UI)
    is_async=False,                # Является ли обработчик асинхронной корутиной
    description="Run commands",    # Человекочитаемое описание
    emoji="💻",                    # Эмодзи для отображения спиннера/прогресса
)

Каждый вызов вызывает ToolEntry, хранящийся в словаре ToolRegistry._tools (синглтон) с ключом — именем инструмента. Если возникает коллизия имен между наборами инструментов, регистрируется предупреждение, и более поздняя регистрация перезаписывает предыдущую.

Обнаружение: discover_builtin_tools()

При импорте model_tools.py возникает discover_builtin_tools() из tools/registry.py. Эта функция сканирует все файлы tools/*.py с помощью AST-парсинга, чтобы найти модули, содержащие вызовы registry.register() на верхнем уровне, а затем импортирует их:

# tools/registry.py (упрощенно)
def discover_builtin_tools(tools_dir=None):
    tools_path = Path(tools_dir) if tools_dir else Path(__file__).parent
    for path in sorted(tools_path.glob("*.py")):
        if path.name in {"__init__.py", "registry.py", "mcp_tool.py"}:
            continue
        if _module_registers_tools(path):  # AST-проверка на registry.register() на верхнем уровне
            importlib.import_module(f"tools.{path.stem}")

Это обнаружение означает, что новые инструменты захватываются автоматически — не нужно вести ручной список. AST-проверка поддерживает только вызовы registry.register() на верхнем уровне (не вызывают вызовы внутри функций), поэтому вспомогательные модули в tools/ не импортируются.

Каждый импорт запускает вызовы модуля registry.register(). Ошибки в дополнительных инструментах (например, отсутствие fal_client для генерации изображений) перехватываются и регистрируются — они не включают в себя другие инструменты.

После обнаружения основных инструментов также обнаруживаются MCP-инструменты и подключаемые инструменты:

  1. MCP-инструментыtools.mcp_tool.discover_mcp_tools() контролирует конфигурацию MCP-сервера и регистрирует инструменты из внешних серверов.
  2. Плагинные инструментыhermes_cli.plugins.discover_plugins() загружают пользовательские/проектные/pip-плагины, которые можно регистрировать дополнительные инструменты.

Проверка доступности инструментов (check_fn)

Каждый инструмент может опционально оплатить check_fn — вызываемый объект, возвращающий True, если инструмент доступен, и False в случае его отмены. Типичные проверки включают в себя:

Когда registry.get_definitions() строит схему списка для модели, он запускает check_fn() каждым инструментом:

# Упрощенно из registry.py
if entry.check_fn:
    try:
        available = bool(entry.check_fn())
    except Exception:
        available = False   # Исключения = недоступен
    if not available:
        continue            # Пропустить этот инструмент полностью

Ключевые особенности: - Результаты проверок кэшируются за один вызов — если несколько инструментов используют один и тот же check_fn, результат будет только один раз. - Исключения в check_fn() как "недоступен" (отказоустойчивость). - Метод is_toolset_available() затем проходит через набор инструментов check_fn; используется для отображения в наборах инструментов пользовательского интерфейса и разрешения.

разрешения наборов инструментов

Наборы инструментов — это именованные группы инструментов. Гермес разрешает их через:

Как get_tool_definitions() фильтрует инструменты

Основная точка входа — model_tools.get_tool_definitions(enabled_toolsets, Disabled_toolsets, quiet_mode):

  1. Если указано enabled_toolsets — включаются только инструменты из этих наборов. Каждое имя набора разрешается через resolve_toolset(), который раскрывает составные наборы в различных инструментах производителей.

  2. Если указано disabled_toolsets — запустите со ВСЕХ наборов, затем вычесть отключенные.

  3. Если не указано ни то, ни другое — включите все уникальные наборы.

  4. Фильтрация реестра — разрешенный набор имен инструментов в registry.get_definitions(), который применяет фильтрацию check_fn и загружает схемы в формате OpenAI.

  5. Динамическое патчирование схем — после фильтрации схемы execute_code и browser_navigate активируются, чтобы ссылаться только на те инструменты, которые прошли фильтрацию (предотвращает галлюцинацию моделей о внешних инструментах).

Устаревшие названия наборов инструментов

Старые наборы имен с суффиксом _tools (например, web_tools, terminal_tools) сопоставляются с современными именами через _LEGACY_TOOLSET_MAP для обеспечения обратной совместимости.

Диспетчеризация

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

Поток диспетчеризации: модельtool_call → Выполнение обработчика

Когда модель возвращает tool_call, поток выглядит так:

Ответ модели с tool_call
    
Цикл агента run_agent.py
    
model_tools.handle_function_call(name, args, task_id, user_task)
    
[Инструменты цикла агента?]  обрабатываются напрямую циклом агента (todo, memory, session_search, delegate_task)
    
[Хук плагина pre]  invoke_hook("pre_tool_call",...)
    
registry.dispatch(name, args, **kwargs)
    
Поиск ToolEntry по имени
    
[Асинхронный обработчик?]  мост через _run_async()
[Синхронный обработчик?]   прямой вызов
    
Возврат строки результата (или JSON-ошибки)
    
[Хук плагина post]  invoke_hook("post_tool_call",...)

Оборачивание ошибок

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

  1. registry.dispatch() — перехватывает любое исключение из обработчика и возвращает {"error": "Tool execution failed: ExceptionType: message"} в формате JSON.

  2. handle_function_call() — оборачивает всю диспетчеризацию во вторичный try/except, который возвращает {"error": "Error executing tool_name: message"}.

Это гарантирует, что модель всегда получает корректную JSON-строку, а не необработанное исключение.

Инструменты цикла агента

Четыре инструмента перехватываются до диспетчеризации в реестре, поскольку им требуется состояние уровня агента (TodoStore, MemoryStore и т.д.):

Схемы этих инструментов по-прежнему регистрируются в реестре (для get_tool_definitions), но их обработчики возвращают заглушку с ошибкой, если диспетчеризация каким-то образом достигает их напрямую.

Асинхронный мост

Когда обработчик инструмента асинхронный, _run_async() создает мост к синхронному пути диспетчеризации:

Процесс утверждения DANGEROUS_PATTERNS

Инструмент терминала интегрирует систему утверждения опасных команд, определенную в tools/approval.py:

  1. Обнаружение шаблоновDANGEROUS_PATTERNS — это список кортежей (regex, description), охватывающих деструктивные операции:
  2. Рекурсивное удаление (rm -rf)
  3. Форматирование файловой системы (mkfs, dd)
  4. Деструктивные операции SQL (DROP TABLE, DELETE FROM без WHERE)
  5. Перезапись системных конфигов (> /etc/)
  6. Манипуляции с сервисами (systemctl stop)
  7. Удаленное выполнение кода (curl | sh)
  8. Форк-бомбы, завершение процессов и т.д.

  9. Обнаружение — перед выполнением любой команды терминала detect_dangerous_command(command) проверяет все шаблоны.

  10. Запрос утверждения — если найдено совпадение:

  11. Режим CLI — интерактивный запрос предлагает пользователю утвердить, отклонить или разрешить навсегда
  12. Режим шлюза — асинхронный обратный вызов утверждения отправляет запрос в платформу обмена сообщениями
  13. Умное утверждение — опционально вспомогательная LLM может автоматически утверждать низкорисковые команды, соответствующие шаблонам (например, rm -rf node_modules/ безопасно, но соответствует "рекурсивному удалению")

  14. Состояние сессии — утверждения отслеживаются для каждой сессии. После утверждения "рекурсивного удаления" для сессии последующие команды rm -rf не запрашивают подтверждение повторно.

  15. Постоянный белый список — опция "разрешить навсегда" записывает шаблон в config.yaml в раздел command_allowlist, сохраняясь между сессиями.

Среды выполнения терминала

Система терминала поддерживает несколько бэкендов:

Она также поддерживает:

Параллельность

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

Связанные документы