Выполнение кода (программный вызов инструментов)
Инструмент execute_code позволяет агенту писать Python-скрипты, которые программно вызывают инструменты VibeOS, сворачивая многошаговые рабочие процессы в один шаг LLM. Скрипт выполняется в дочернем процессе на хосте агента, взаимодействуя с VibeOS через RPC по доменному сокету Unix.
Как это работает
- Агент пишет Python-скрипт, используя
from vibeos_tools import ... - VibeOS генерирует заглушечный модуль
vibeos_tools.pyс RPC-функциями - VibeOS открывает доменный сокет Unix и запускает поток-слушатель RPC
- Скрипт выполняется в дочернем процессе — вызовы инструментов передаются через сокет обратно в VibeOS
- 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 с льготного периода |
| Stdout | 50 КБ | Вывод усекается с уведомлением [вывод усечён на 50 КБ] |
| Stderr | 10 КБ | Включается в вывод при ненулевом коде завершения для отладки |
| Вызовы инструментов | 50 на выполнение | Возвращается ошибка при достижении лимита |
Все лимиты настраиваются через config.yaml:
# В ~/.vibeos/config.yaml
code_execution:
mode: project # project (по умолчанию) | strict
timeout: 300 # Макс. секунд на скрипт (по умолчанию: 300)
max_tool_calls: 50 # Макс. вызовов инструментов на выполнение (по умолчанию: 50)
Как работают вызовы инструментов внутри скриптов
Когда ваш скрипт вызывает функцию вроде web_search("запрос"):
- Вызов сериализуется в JSON и отправляется через доменный сокет Unix родительскому процессу
- Родительский процесс диспетчеризует его через стандартный обработчик
handle_function_call - Результат отправляется обратно через сокет
- Функция возвращает разобранный результат
Это означает, что вызовы инструментов внутри скриптов ведут себя идентично обычным вызовам инструментов — те же ограничения скорости, та же обработка ошибок, те же возможности. Единственное ограничение: 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_HOMEVIBEOS_PROFILEVIBEOS_CONFIGVIBEOS_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, никогда не могут быть повторно разрешены таким образом):
-
На машине, в
config.yaml— добавьте точное имя переменной в список разрешённых для пропуска:terminal:
env_passthrough:
- VIBEOS_KANBAN_DB
- VIBEOS_BASE_URL -
Для навыка, в метаданных навыка — объявите её, чтобы она регистрировалась автоматически при загрузке этого навыка:
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_code | terminal |
|---|---|---|
| Многошаговые рабочие процессы с вызовами инструментов между шагами | ✅ | ❌ |
| Простая команда оболочки | ❌ | ✅ |
| Фильтрация/обработка больших выводов инструментов | ✅ | ❌ |
| Запуск сборки или набора тестов | ❌ | ✅ |
| Циклы по результатам поиска | ✅ | ❌ |
| Интерактивные/фоновые процессы | ❌ | ✅ |
| Требуются ключи API в окружении | ⚠️ Только через пропуск | ✅ (большинство пропускается) |
Эмпирическое правило: Используйте execute_code, когда нужно программно вызывать инструменты VibeOS с логикой между вызовами. Используйте terminal для запуска команд оболочки, сборок и процессов.
Поддержка платформ
Выполнение кода требует доменных сокетов Unix и доступно только на Linux и macOS. Оно автоматически отключается на Windows — агент переключается на обычные последовательные вызовы инструментов.