Выполнение кода (Программный вызов инструментов)

Инструмент execute_code позволяет агенту писать Python-скрипты, которые программно вызывают инструменты Hermes, сворачивая многошаговые рабочие процессы в один шаг LLM. Скрипт выполняется в дочернем процессе на хосте агента, взаимодействуя с Hermes через RPC по Unix domain socket.

Как это работает

  1. Агент пишет Python-скрипт с использованием from hermes_tools import ...

  2. Hermes генерирует модуль-заглушку hermes_tools.py с RPC-функциями

  3. Hermes открывает Unix domain socket и запускает поток RPC-слушателя

  4. Скрипт выполняется в дочернем процессе — вызовы инструментов передаются через socket обратно в Hermes

  5. Только вывод print() скрипта возвращается в LLM; промежуточные результаты инструментов никогда не попадают в контекстное окно

# The agent can write scripts like:
from hermes_tools import web_search, web_extract

results = web_search("Python 3.13 features", limit=5)
for r in results["data"]["web"]:
    content = web_extract([r["url"]])
    # ... filter and process ...
print(summary)

Доступные инструменты внутри скриптов: web_search, web_extract, read_file, write_file, search_files, patch, terminal (только foreground).

Когда агент использует это

Агент использует execute_code, когда есть:

Ключевое преимущество: промежуточные результаты инструментов никогда не попадают в контекстное окно — возвращается только итоговый вывод print(), что значительно снижает использование токенов.

Практические примеры

Конвейер обработки данных

from hermes_tools import search_files, read_file
import json

# Find all config files and extract database settings
matches = search_files("database", path=".", file_glob="*.yaml", limit=20)
configs = []
for match in matches.get("matches", []):
    content = read_file(match["path"])
    configs.append({"file": match["path"], "preview": content["content"][:200]})

print(json.dumps(configs, indent=2))

Многошаговое веб-исследование

from hermes_tools import web_search, web_extract
import json

# Search, extract, and summarize in one turn
results = web_search("Rust async runtime comparison 2025", limit=5)
summaries = []
for r in results["data"]["web"]:
    page = web_extract([r["url"]])
    for p in page.get("results", []):
        if p.get("content"):
            summaries.append({
                "title": r["title"],
                "url": r["url"],
                "excerpt": p["content"][:500]
            })

print(json.dumps(summaries, indent=2))

Массовый рефакторинг файлов

from hermes_tools import search_files, read_file, patch

# Find all Python files using deprecated API and fix them
matches = search_files("old_api_call", path="src/", file_glob="*.py")
fixed = 0
for match in matches.get("matches", []):
    result = patch(
        path=match["path"],
        old_string="old_api_call(",
        new_string="new_api_call(",
        replace_all=True
    )
    if "error" not in str(result):
        fixed += 1

print(f"Fixed {fixed} files out of {len(matches.get('matches', []))} matches")

Конвейер сборки и тестирования

from hermes_tools import terminal, read_file
import json

# Run tests, parse results, and report
result = terminal("cd /project && python -m pytest --tb=short -q 2>&1", timeout=120)
output = result.get("output", "")

# Parse test output
passed = output.count(" passed")
failed = output.count(" failed")
errors = output.count(" error")

report = {
    "passed": passed,
    "failed": failed,
    "errors": errors,
    "exit_code": result.get("exit_code", -1),
    "summary": output[-500:] if len(output) > 500 else output
}

print(json.dumps(report, indent=2))

Режим выполнения

execute_code имеет два режима выполнения, управляемых параметром code_execution.mode в ~/.hermes/config.yaml:

Режим Рабочий каталог Интерпретатор Python
project (по умолчанию) Рабочий каталог сессии (как в terminal()) Python из активного VIRTUAL_ENV / CONDA_PREFIX, с понижением до собственного Python Hermes
strict Временный промежуточный каталог, изолированный от проекта пользователя sys.executable (собственный Python Hermes)

Когда оставлять режим project: вы хотите, чтобы import pandas, from my_project import foo или относительные пути, такие как open(".env"), работали так же, как в terminal(). Это практически всегда то, что нужно.

Когда переключаться на strict: вам нужна максимальная воспроизводимость — вы хотите использовать один и тот же интерпретатор в каждой сессии независимо от того, какое виртуальное окружение активировал пользователь, и хотите изолировать скрипты от дерева проекта (без риска случайного чтения файлов проекта через относительный путь).

# ~/.hermes/config.yaml
code_execution:
  mode: project   # or "strict"

Поведение при понижении в режиме project: если VIRTUAL_ENV / CONDA_PREFIX не установлен, повреждён или указывает на Python старше 3.8, резолвер корректно понижается до sys.executable — агент никогда не остаётся без работающего интерпретатора.

Критически важные для безопасности инварианты идентичны в обоих режимах:

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

Лимиты ресурсов

Ресурс Лимит Примечания
Тайм-аут 5 минут (300 с) Скрипт завершается через SIGTERM, затем SIGKILL после 5-секундной задержки
Stdout 50 КБ Вывод обрезается с уведомлением [output truncated at 50KB]
Stderr 10 КБ Включается в вывод при ненулевом коде возврата для отладки
Вызовы инструментов 50 на одно выполнение Возвращается ошибка при достижении лимита

Все лимиты настраиваются через config.yaml:

# In ~/.hermes/config.yaml
code_execution:
  mode: project      # project (default) | strict
  timeout: 300       # Max seconds per script (default: 300)
  max_tool_calls: 50 # Max tool calls per execution (default: 50)

Как работают вызовы инструментов внутри скриптов

Когда ваш скрипт вызывает функцию, например web_search("query"):

  1. Вызов сериализуется в JSON и отправляется через Unix domain socket родительскому процессу

  2. Родительский процесс диспетчеризирует вызов через стандартный обработчик handle_function_call

  3. Результат отправляется обратно через socket

  4. Функция возвращает разобранный результат

Это означает, что вызовы инструментов внутри скриптов ведут себя идентично обычным вызовам инструментов — те же лимиты скорости, та же обработка ошибок, те же возможности. Единственное ограничение — terminal() доступен только в режиме foreground (без параметров background или pty).

Обработка ошибок

При сбое скрипта агент получает структурированную информацию об ошибке:

Ответ всегда включает status (success/error/timeout/interrupted), output, tool_calls_made и duration_seconds.

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

danger Модель безопасности Дочерний процесс выполняется с минимальным окружением. Ключи API, токены и учётные данные удаляются по умолчанию. Скрипт получает доступ к инструментам исключительно через RPC-канал — он не может читать секреты из переменных окружения, если это не разрешено явно.

Переменные окружения, содержащие KEY, TOKEN, SECRET, PASSWORD, CREDENTIAL, PASSWD или AUTH в своих названиях, исключаются. Передаются только безопасные системные переменные (PATH, HOME, LANG, SHELL, PYTHONPATH, VIRTUAL_ENV и т.д.).

Проброс переменных окружения навыков

Когда навык объявляет required_environment_variables в своём frontmatter, эти переменные автоматически пробрасываются в дочерние процессы execute_code и terminal после загрузки навыка. Это позволяет навыкам использовать свои объявленные ключи API без ослабления политики безопасности для произвольного кода.

Для случаев, не связанных с навыками, вы можете явно добавить переменные в белый список в config.yaml:

terminal:
  env_passthrough:
    - MY_CUSTOM_KEY
    - ANOTHER_TOKEN

Подробнее см. в Руководстве по безопасности.

Hermes всегда записывает скрипт и автоматически сгенерированную RPC-заглушку hermes_tools.py во временный промежуточный каталог, который очищается после выполнения. В режиме strict скрипт также выполняется там; в режиме project он выполняется в рабочем каталоге сессии (промежуточный каталог остаётся в PYTHONPATH, чтобы импорты по-прежнему работали). Дочерний процесс работает в своей собственной группе процессов, поэтому его можно корректно завершить по тайм-ауту или прерыванию.

execute_code vs terminal

Сценарий использования execute_code terminal
Многошаговые рабочие процессы с вызовами инструментов между ними
Простая shell-команда
Фильтрация/обработка больших выводов инструментов
Запуск сборки или набора тестов
Циклы по результатам поиска
Интерактивные/фоновые процессы
Требуются ключи API в окружении ⚠️ Через проброс ✅ (большинство пробрасывается)

Эмпирическое правило: Используйте execute_code, когда нужно программно вызывать инструменты Hermes с логикой между вызовами. Используйте terminal для запуска shell-команд, сборок и процессов.

Поддержка платформ

Выполнение кода требует Unix domain sockets и доступно только на Linux и macOS. Оно автоматически отключается на Windows — агент возвращается к обычным последовательным вызовам инструментов.