sidebar_position: 9 title: "Tools Runtime" description: "Runtime behavior of the tool registry, toolsets, dispatch, and terminal environments" lang: ru
Инструменты
Инструменты Hermes представляют собой саморегистрирующиеся функции, сгруппированные в наборы инструментов и выполняемые через центральную систему регистрации/диспетчеризации.
Первичные файлы:
tools/registry.pymodel_tools.pytoolsets.pytools/terminal_tool.pytools/environments/*
Модель регистрации инструмента
Каждый инструментальный модуль вызывает registry.register(...) во время импорта.
model_tools.py отвечает за импорт/обнаружение инструментальных модулей и создание списка схем, используемых моделью.
Как работает registry.register()
Каждый файл инструмента в tools/ вызывает registry.register() на уровне модуля, чтобы объявить о себе. Сигнатура функции:
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
)
Каждый вызов создает ToolEntry, хранящийся в одноэлементном словаре ToolRegistry._tools с ключом по имени инструмента. Если конфликт имен происходит между наборами инструментов, регистрируется предупреждение, и более поздняя регистрация выигрывает.
Открытие: discover_builtin_tools()
При импорте model_tools.py он вызывает discover_builtin_tools() из tools/registry.py. Эта функция сканирует каждый файл tools/*.py, используя анализ AST, чтобы найти модули, содержащие вызовы registry.register() верхнего уровня, а затем импортирует их:
# 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}")
Это автоматическое обнаружение означает, что новые файлы инструментов подбираются автоматически — не нужно составлять список вручную. Проверка AST соответствует только вызовам registry.register() верхнего уровня (не вызовам внутри функций), поэтому вспомогательные модули в tools/ не импортируются.
Каждый импорт запускает вызовы registry.register() модуля. Ошибки в дополнительных инструментах (например, отсутствие fal_client для создания изображения) фиксируются и протоколируются — они не мешают загрузке других инструментов.
После обнаружения основного инструмента также обнаруживаются инструменты MCP и плагины:
- Инструменты MCP —
tools.mcp_tool.discover_mcp_tools()считывает конфигурацию сервера MCP и регистрирует инструменты с внешних серверов. - Инструменты плагинов —
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() каждого инструмента:
# 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
Ключевые модели поведения:
- Результаты проверки кэшируются для каждого вызова — если несколько инструментов используют один и тот же check_fn, он запускается только один раз.
- Исключения в check_fn() рассматриваются как «недоступные» (отказоустойчивые).
- Метод is_toolset_available() проверяет, проходит ли набор инструментов check_fn, используемый для отображения пользовательского интерфейса и разрешения набора инструментов.
Разрешение набора инструментов
Наборы инструментов — это именованные пакеты инструментов. Гермес решает их посредством:
- явные списки включенных/отключенных наборов инструментов
- пресеты платформы (
hermes-cli,hermes-telegramи т.д.) - динамические наборы инструментов MCP
- специально подобранные наборы специального назначения, такие как
hermes-acp
Как get_tool_definitions() фильтрует инструменты
Основная точка входа — model_tools.get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode):
-
Если указан
enabled_toolsets— включены только инструменты из этих наборов инструментов. Имя каждого набора инструментов разрешается с помощьюresolve_toolset(), который расширяет составные наборы инструментов до отдельных имен инструментов. -
Если указан
disabled_toolsets— начните со ВСЕХ наборов инструментов, затем вычтите отключенные. -
Если ни то, ни другое — включите все известные наборы инструментов.
-
Фильтрация реестра — разрешенный набор имен инструментов передается
registry.get_definitions(), который применяет фильтрациюcheck_fnи возвращает схемы в формате OpenAI. -
Динамическое исправление схемы — после фильтрации схемы
execute_codeиbrowser_navigateдинамически настраиваются так, чтобы ссылаться только на инструменты, которые фактически прошли фильтрацию (предотвращает галлюцинацию модели о недоступных инструментах).
Названия устаревших наборов инструментов
Имена старых наборов инструментов с суффиксами _tools (например, web_tools, terminal_tools) сопоставляются с современными именами инструментов через _LEGACY_TOOLSET_MAP для обеспечения обратной совместимости.
Отправка
Во время выполнения инструменты передаются через центральный реестр с исключениями цикла агента для некоторых инструментов уровня агента, таких как обработка памяти/задач/поиска сеанса.
Поток отправки: модельtool_call → выполнение обработчика
Когда модель возвращает tool_call, порядок действий следующий:
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", ...)
Ошибка переноса
Все выполнение инструмента связано с обработкой ошибок на двух уровнях:
-
registry.dispatch()— перехватывает любое исключение из обработчика и возвращает{"error": "Tool execution failed: ExceptionType: message"}в формате JSON. -
handle_function_call()— переносит всю отправку во вторичную попытку, за исключением того, что возвращается{"error": "Error executing tool_name: message"}.
Это гарантирует, что модель всегда получит правильно сформированную строку JSON, а не необработанное исключение.
Инструменты цикла агента
Перед отправкой реестра перехватываются четыре инструмента, поскольку им требуется состояние уровня агента (TodoStore, MemoryStore и т. д.):
todo— планирование/отслеживание задачmemory— запись в постоянную памятьsession_search— межсессионный отзывdelegate_task— порождает сеансы субагента.
Схемы этих инструментов все еще зарегистрированы в реестре (для get_tool_definitions), но их обработчики возвращают ошибку-заглушку, если диспетчеризация каким-то образом достигает их напрямую.
Асинхронный мост
Когда обработчик инструмента является асинхронным, _run_async() соединяет его с путем синхронизации:
- Путь CLI (без текущего цикла) — использует постоянный цикл событий для поддержания активности кэшированных асинхронных клиентов.
- Путь шлюза (выполняющийся цикл) — запускает одноразовый поток с
asyncio.run() - Рабочие потоки (параллельные инструменты) — используются постоянные циклы для каждого потока, хранящиеся в локальном хранилище потока.
Порядок утверждения DANGEROUS_PATTERNS
В терминальном инструменте интегрирована система одобрения опасных команд, определенная в tools/approval.py:
- Обнаружение шаблонов —
DANGEROUS_PATTERNS— это список кортежей(regex, description), охватывающих деструктивные операции: - Рекурсивное удаление (
rm -rf) - Форматирование файловой системы (
mkfs,dd) - Деструктивные операции SQL (
DROP TABLE,DELETE FROMбезWHERE) - Конфигурация системы перезаписывается (
> /etc/) - Сервисные манипуляции (
systemctl stop) - Удаленное выполнение кода (
curl | sh) -
Вилочные бомбы, технологические убийства и т. д.
-
Обнаружение — перед выполнением любой команды терминала
detect_dangerous_command(command)проверяет все шаблоны. -
Запрос на одобрение — если совпадение найдено:
- Режим CLI — интерактивный запрос предлагает пользователю одобрить, отклонить или разрешить навсегда.
- Режим шлюза — обратный вызов асинхронного утверждения отправляет запрос на платформу обмена сообщениями.
-
Интеллектуальное утверждение — дополнительно вспомогательный модуль LLM может автоматически утверждать команды с низким уровнем риска, соответствующие шаблонам (например,
rm -rf node_modules/безопасно, но соответствует «рекурсивному удалению»). -
Состояние сеанса — утверждения отслеживаются для каждого сеанса. После того как вы разрешите «рекурсивное удаление» сеанса, последующие команды
rm -rfне будут выдавать повторные запросы. -
Постоянный список разрешенных — опция «разрешить навсегда» записывает шаблон в
command_allowlistconfig.yaml, сохраняя его на протяжении всех сеансов.
Терминал/среда выполнения
Терминальная система поддерживает несколько бэкэндов:
- местный
- докер
- сш
- сингулярность
- модальный
- Дайтона
- vercel_sandbox
Он также поддерживает:
- переопределение cwd для каждой задачи
- управление фоновыми процессами
- Режим PTY
- обратные вызовы подтверждения для опасных команд
Параллелизм
Вызовы инструментов могут выполняться последовательно или одновременно в зависимости от набора инструментов и требований к взаимодействию.