Перейти к основному содержимому

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

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

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

  1. Агент пишет Python-скрипт, используя from vibeos_tools import ...
  2. VibeOS генерирует заглушечный модуль vibeos_tools.py с RPC-функциями
  3. VibeOS открывает доменный сокет Unix и запускает поток-слушатель RPC
  4. Скрипт выполняется в дочернем процессе — вызовы инструментов передаются через сокет обратно в VibeOS
  5. LLM возвращается только вывод print() из скрипта; промежуточные результаты работы инструментов никогда не попадают в контекстное окно
# Агент может писать скрипты вроде:
from vibeos_tools import web_search, web_extract

results = web_search("Возможности Python 3.13", limit=5)
for r in results["data"]["web"]:
content = web_extract([r["url"]])
# ... фильтрация и обработка ...
print(summary)

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

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

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

  • 3+ вызова инструментов с логикой обработки между ними
  • Пакетная фильтрация данных или условное ветвление
  • Циклы по результатам

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

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

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

from vibeos_tools import search_files, read_file
import json

# Найти все конфигурационные файлы и извлечь настройки базы данных
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 vibeos_tools import web_search, web_extract
import json

# Поиск, извлечение и обобщение за один шаг
results = web_search("Сравнение асинхронных рантаймов Rust 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 vibeos_tools import search_files, read_file, patch

# Найти все Python-файлы, использующие устаревший API, и исправить их
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} файлов из {len(matches.get('matches', []))} совпадений")

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

from vibeos_tools import terminal, read_file
import json

# Запустить тесты, разобрать результаты и составить отчёт
result = terminal("cd /project && python -m pytest --tb=short -q 2>&1", timeout=120)
output = result.get("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 в ~/.vibeos/config.yaml:

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

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

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

# ~/.vibeos/config.yaml
code_execution:
mode: project # или "strict"

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

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

  • очистка окружения (ключи API, токены, учётные данные удаляются)
  • белый список инструментов (скрипты не могут рекурсивно вызывать execute_code, delegate_task или MCP-инструменты)
  • ограничения ресурсов (тайм-аут, лимит stdout, лимит вызовов инструментов)

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

Ограничения ресурсов​

РесурсЛимитПримечания
Тайм-аут5 минут (300 с)Скрипт завершается сигналом SIGTERM, затем SIGKILL через 5 с льготного периода
Stdout50 КБВывод усекается с уведомлением [вывод усечён на 50 КБ]
Stderr10 КБВключается в вывод при ненулевом коде завершения для отладки
Вызовы инструментов50 на выполнениеВозвращается ошибка при достижении лимита

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

# В ~/.vibeos/config.yaml
code_execution:
mode: project # project (по умолчанию) | strict
timeout: 300 # Макс. секунд на скрипт (по умолчанию: 300)
max_tool_calls: 50 # Макс. вызовов инструментов на выполнение (по умолчанию: 50)

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

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

  1. Вызов сериализуется в JSON и отправляется через доменный сокет Unix родительскому процессу
  2. Родительский процесс диспетчеризует его через стандартный обработчик handle_function_call
  3. Результат отправляется обратно через сокет
  4. Функция возвращает разобранный результат

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

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

Когда скрипт завершается с ошибкой, агент получает структурированную информацию об ошибке:

  • Ненулевой код завершения: stderr включается в вывод, чтобы агент видел полную трассировку
  • Тайм-аут: Скрипт завершается, и агент видит сообщение «Скрипт превысил тайм-аут 300 с и был завершён.»
  • Прерывание: Если пользователь отправляет новое сообщение во время выполнения, скрипт завершается, и агент видит «[выполнение прервано — пользователь отправил новое сообщение]»
  • Лимит вызовов инструментов: При достижении лимита в 50 вызовов последующие вызовы инструментов возвращают сообщение об ошибке

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

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

Модель безопасности

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

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

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

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

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

terminal:
env_passthrough:
- MY_CUSTOM_KEY
- ANOTHER_TOKEN

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

Переменные VIBEOS_* в дочернем процессе​

Дочерний процесс получает только небольшой фиксированный набор рабочих переменных VIBEOS_* по точному имени:

  • VIBEOS_HOME
  • VIBEOS_PROFILE
  • VIBEOS_CONFIG
  • VIBEOS_ENV

(плюс VIBEOS_RPC_DIR / VIBEOS_RPC_SOCKET / TZ / HOME, которые VibeOS явно внедряет для работы RPC-канала).

Изменение поведения

В более ранних версиях в дочерний процесс передавалась любая переменная, имя которой начиналось с VIBEOS_. Этот широкий префикс был удалён для усиления безопасности: он мог пропускать конфигурацию с именем VIBEOS_*, не соответствующую подстроке секрета (например, VIBEOS_BASE_URL, VIBEOS_KANBAN_DB или конечную точку VIBEOS_*_WEBHOOK), в произвольный изолированный код.

Если скрипт execute_code — или импортируемый им модуль репозитория/плагина во время импорта — полагался на переменную VIBEOS_* вне четырёх указанных выше рабочих имён, теперь он обнаружит, что эта переменная не установлена в дочернем процессе. Это изменение намеренное, а не ошибка.

Обходной путь — явно разрешите переменную обратно. Оба способа пропускают переменную через дочерние процессы execute_code и terminal, и ни один из них не ослабляет гарантию удаления секретов (учётные данные провайдера, управляемые VibeOS, никогда не могут быть повторно разрешены таким образом):

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

    terminal:
    env_passthrough:
    - VIBEOS_KANBAN_DB
    - VIBEOS_BASE_URL
  2. Для навыка, в метаданных навыка — объявите её, чтобы она регистрировалась автоматически при загрузке этого навыка:

    required_environment_variables:
    - VIBEOS_KANBAN_DB

Диагностика. Когда дочерний процесс отбрасывает одну или несколько неразрешённых переменных VIBEOS_*, VibeOS выводит однострочное сообщение уровня debug с их именами и указанием на механизм env_passthrough. Запустите с отладочным логированием (vibeos logs --level DEBUG или проверьте ~/.vibeos/logs/agent.log) и ищите сообщение execute_code: dropped N non-allowlisted VIBEOS_* var(s), если скрипт ведёт себя так, будто переменная VIBEOS_* отсутствует.

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

execute_code vs terminal​

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

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

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

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