Перейти к содержанию

Runtime

На этой странице Инструменты Hermes — это саморегистрирующиеся функции, сгруппированные в наборы инструментов и выполняемые через центральную систему реестра/диспетчеризации. Основные файлы: * tools/registry.py * model_tools.py * toolsets.py * tools/terminal_tool.py * tools/environments/*

Модель регистрации инструментов

Каждый модуль инструментов вызывает registry.register(...) во время импорта. model_tools.py отвечает за импорт/обнаружение модулей инструментов и построение списка схем, используемых моделью.

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

Каждый файл инструментов в tools/ вызывает registry.register() на уровне модуля, чтобы зарегистрировать себя. Сигнатура функции: [code] registry.register(
name="terminal", # Unique tool name (used in API schemas)
toolset="terminal", # Toolset this tool belongs to
schema={...}, # OpenAI function-calling schema (description, parameters)
handler=handle_terminal, # The function that executes when the tool is called
check_fn=check_terminal, # Optional: returns True/False for availability
requires_env=["SOME_VAR"], # Optional: env vars needed (for UI display)
is_async=False, # Whether the handler is an async coroutine
description="Run commands", # Human-readable description
emoji="💻", # Emoji for spinner/progress display
)

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

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

При импорте model_tools.py вызывается discover_builtin_tools() из tools/registry.py. Эта функция сканирует все файлы tools/*.py с помощью AST-парсинга для поиска модулей, содержащих вызовы registry.register() верхнего уровня, затем импортирует их: [code] # tools/registry.py (simplified)
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 check for top-level registry.register()
importlib.import_module(f"tools.{path.stem}")

[/code] Это автообнаружение означает, что новые файлы инструментов подхватываются автоматически — не нужно поддерживать ручной список. 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 в противном случае. Типичные проверки включают: * Наличие API-ключа — например, lambda: bool(os.environ.get("SERP_API_KEY")) для веб-поиска * Запущен ли сервис — например, проверка настроен ли сервер Honcho * Установлен ли бинарник — например, проверка доступности playwright для инструментов браузера

Когда registry.get_definitions() строит список схем для модели, он запускает check_fn() каждого инструмента: [code] # Simplified from registry.py
if entry.check_fn:
try:
available = bool(entry.check_fn())
except Exception:
available = False # Exceptions = unavailable
if not available:
continue # Skip this tool entirely

[/code] Ключевые особенности: * Результаты проверки кэшируются на один вызов — если несколько инструментов используют одинаковый check_fn, он выполняется только один раз. * Исключения в check_fn() трактуются как «недоступен» (отказобезопасное поведение). * Метод is_toolset_available() проверяет, проходит ли check_fn набора инструментов; используется для отображения в UI и разрешения наборов инструментов.

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

Наборы инструментов — это именованные группы инструментов. Hermes разрешает их через: * явные списки включённых/отключённых наборов инструментов * предустановки платформы (hermes-cli, hermes-telegram и т.д.) * динамические MCP-наборы * специальные наборы для конкретных целей, такие как hermes-acp

Как 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 для обратной совместимости.

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

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

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

Когда модель возвращает tool_call, поток выглядит следующим образом: [code] Model response with tool_call

run_agent.py agent loop

model_tools.handle_function_call(name, args, task_id, user_task)

[Agent-loop tools?] → handled directly by agent loop (todo, memory, session_search, delegate_task)

[Plugin pre-hook] → invoke_hook("pre_tool_call", ...)

registry.dispatch(name, args, **kwargs)

Look up ToolEntry by name

[Async handler?] → bridge via _run_async()
[Sync handler?] → call directly

Return result string (or JSON error)

[Plugin post-hook] → invoke_hook("post_tool_call", ...)

[/code]

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

Всё выполнение инструментов обёрнуто в обработку ошибок на двух уровнях: 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 и т.д.): * todo — планирование/отслеживание задач * memory — запись в постоянную память * session_search — поиск по сессиям * delegate_task — запуск дочерних сессий агента

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

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

Когда обработчик инструмента асинхронный, _run_async() соединяет его с синхронным путём диспетчеризации: * CLI-путь (нет запущенного цикла) — использует постоянный цикл событий для поддержания кэшированных асинхронных клиентов * Gateway-путь (есть запущенный цикл) — запускает одноразовый поток с asyncio.run() * Рабочие потоки (параллельные инструменты) — использует постоянные циклы для каждого потока, хранящиеся в thread-local storage

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

Терминальный инструмент включает систему подтверждения опасных команд, определённую в tools/approval.py: 1. Обнаружение шаблоновDANGEROUS_PATTERNS — это список кортежей (regex, description), охватывающих деструктивные операции: * Рекурсивное удаление (rm -rf) * Форматирование файловой системы (mkfs, dd) * Деструктивные операции SQL (DROP TABLE, DELETE FROM без WHERE) * Перезапись системных конфигов (> /etc/) * Управление сервисами (systemctl stop) * Удалённое выполнение кода (curl | sh) * Fork bomb, завершение процессов и т.д. 2. Обнаружение — перед выполнением любой терминальной команды detect_dangerous_command(command) проверяет её по всем шаблонам. 3. Запрос подтверждения — при совпадении: * CLI-режим — интерактивный запрос предлагает пользователю подтвердить, отклонить или разрешить навсегда * Gateway-режим — асинхронный callback подтверждения отправляет запрос на платформу обмена сообщениями * Умное подтверждение — опционально, вспомогательная LLM может автоматически подтверждать низкорисковые команды, совпадающие с шаблонами (например, rm -rf node_modules/ безопасно, но совпадает с «рекурсивное удаление») 4. Состояние сессии — подтверждения отслеживаются в рамках сессии. После подтверждения «рекурсивное удаление» для сессии последующие команды rm -rf не запрашивают повторного подтверждения. 5. Постоянный белый список — опция «разрешить навсегда» записывает шаблон в config.yaml в command_allowlist, сохраняя его между сессиями.

Терминальные/исполнительные окружения

Система терминала поддерживает несколько бэкендов: * local * docker * ssh * singularity * modal * daytona * vercel_sandbox

Она также поддерживает: * переопределение рабочей директории для каждой задачи * управление фоновыми процессами * режим PTY * callback-запросы подтверждения для опасных команд

Конкурентность

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

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