У вас есть ИИ-агент, и вы хотите запустить его в безопасной среде, где у него есть доступ к нужным ресурсам — например, к файловой системе — и возможность выполнять команды (например, shell-команды) без риска «что-нибудь сломать». Какие есть варианты?
Лучший подход — предоставить вашему ИИ-агенту песочницу (sandbox). Внутри песочницы агент может безопасно взаимодействовать с Linux-машиной, работать с файловой системой и выполнять определённые команды, оставаясь ограниченным от потенциально опасных операций.
С такой настройкой мы можем создавать мощные приложения. Например, агента для программирования, который умеет:
- Создавать и редактировать файлы с кодом в файловой системе
- Выполнять команды вроде git, python или node
- Взаимодействовать с разработчиками, запуская и тестируя код прямо в среде
В этой статье мы шаг за шагом создадим такого агента для программирования. Мы будем использовать LLM Novita с поддержкой вызова функций в сочетании с Novita agent sandbox как безопасной средой. В завершение создадим удобный интерфейс на Gradio и развернём его на Hugging Face Spaces.
Прежде чем выбирать модель для такой настройки, ознакомьтесь со статьёй Варианты использования больших языковых моделей для агентов программирования — там описаны практические сценарии: Q&A по репозиторию, триаж тестов, рефакторинг и рабочие процессы с маршрутизацией через инструменты.
Давайте начнём!
Agent Sandbox
Agent Sandbox от Novita — это среда выполнения, созданная специально для ИИ-агентов. Она предоставляет безопасное и изолированное облачное окружение, которое работает как виртуальный компьютер. Внутри такой среды агенты могут безопасно выполнять сгенерированный код, не подвергая риску основную систему.
Ключевые возможности Novita Agent Sandbox
- Безопасность: песочница полностью изолирована, поэтому агент имеет доступ только к собственным ресурсам.
- Быстрый запуск: новые среды создаются менее чем за 200 мс.
- Виртуальная машина: поскольку песочница ведёт себя как ВМ, агенты могут запускать код на любом языке программирования.
- Пауза и возобновление: вы можете поставить песочницу на паузу в любой момент и продолжить работу позже.
- Фоновые задачи: агенты могут выполнять задачи в фоновом режиме и получать результаты асинхронно.
Установка SDK
Для использования Novita Sandbox понадобится SDK, поддерживающий Python и TypeScript/JavaScript. В этом руководстве мы используем Python SDK:
pip install novita-sandbox
После установки задайте ваш API-ключ Novita как переменную окружения:
export NOVITA_API_KEY=your_api_key_here
Проверка песочницы
Когда всё настроено, давайте создадим песочницу и выполним несколько базовых операций:
from novita_sandbox.code_interpreter import Sandbox
sandbox = Sandbox.create()
files = sandbox.files.list("/")
for file in files:
print(file.name)
result = sandbox.commands.run('pwd')
print(result)
sandbox.kill()
Этот пример показывает, как:
- Создать экземпляр песочницы
- Получить доступ к файловой системе через объект files
- Выполнять команды с помощью метода commands.run
- Освободить ресурсы с помощью kill после завершения работы
Теперь, когда мы разобрались с основами доступа к файловой системе и выполнения команд, можно приступить к созданию нашего агента для программирования, который будет использовать возможности песочницы как инструменты.
Создание агента для программирования
Чтобы создать такого агента, нам нужна LLM, которая поддерживает вызов функций. Novita предоставляет несколько таких моделей. Чтобы агент вёл себя как помощник программиста, он должен обладать правильным набором функций.
Подумаем, что делает человек-программист. Обычно он пишет, читает и выполняет код. Значит, наш агент должен уметь:
- Записывать данные в файл
- Читать данные из файла
- Выполнять команды
- Записывать сразу несколько файлов
Настройка агента
Поскольку модели Novita совместимы с OpenAI, для взаимодействия с ними можно использовать OpenAI SDK. Установим его:
pip install openai
После установки задайте ваш API-ключ Novita как переменную окружения, как мы делали раньше. Когда это готово, можем приступить к коду, добавив импорты:
from openai import OpenAI
import os
import json
from novita_sandbox.code_interpreter import Sandbox
Теперь создадим экземпляр клиента OpenAI:
client = OpenAI(
base_url="https://api.novita.ai/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
Здесь мы направляем клиент на базовый URL Novita вместо OpenAI и используем наш API-ключ Novita для аутентификации.
Далее создадим экземпляр песочницы, которую будет использовать агент:
sandbox = Sandbox.create(timeout=1200)
Параметр timeout определяет, как долго песочница остаётся активной. В данном случае мы установили 10 минут.
Определения функций
Теперь определим функции, которые будет использовать агент.
1. Чтение файла
Эта функция принимает путь к файлу и читает его содержимое через объект files песочницы.
def read_file(path: str):
print(f"[DEBUG] read_file called with path: {path}")
try:
content = sandbox.files.read(path)
print(f"[DEBUG] read_file result: {content}")
return content # returns string content
except Exception as e:
print(f"[DEBUG] read_file error: {e}")
return f"Error reading file: {e}"
2. Запись файла
Эта функция записывает данные по указанному пути.
def write_file(path: str, data: str):
print(f"[DEBUG] write_file called with path: {path}")
try:
sandbox.files.write(path, data)
msg = f"File created successfully at {path}"
print(f"[DEBUG] {msg}")
return msg
except Exception as e:
print(f"[DEBUG] write_file error: {e}")
return f"Error writing file: {e}"
3. Запись нескольких файлов
Эта функция работает так же, как write_file, но обрабатывает сразу несколько файлов.
def write_files(files: list):
print(f"[DEBUG] write_files called with {len(files)} files")
try:
sandbox.files.write_files(files)
msg = f"{len(files)} file(s) created successfully"
print(f"[DEBUG] {msg}")
return msg
except Exception as e:
print(f"[DEBUG] write_files error: {e}")
return f"Error writing multiple files: {e}"
4. Выполнение команд
Эта функция выполняет shell-команды внутри песочницы и возвращает стандартный вывод.
def run_commands(command: str):
print(f"[DEBUG] run_commands called with commands: {command}")
try:
result = sandbox.commands.run(command)
print(f"[DEBUG] run_commands result: {result}")
return result.stdout # returns CommandResult object
except Exception as e:
print(f"[DEBUG] run_commands error: {e}")
return f"Error running commands: {e}"
Регистрация инструментов
Теперь, когда все функции готовы, зарегистрируем их как инструменты, которые LLM сможет вызывать по мере необходимости. Каждое описание инструмента включает имя функции, описание и схему параметров.
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read contents of a file inside the sandbox",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "File path in the sandbox"}
},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write_file",
"description": "Write a single file inside the sandbox",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "File path in the sandbox"},
"data": {"type": "string", "description": "Content to write"},
},
"required": ["path", "data"],
},
},
},
{
"type": "function",
"function": {
"name": "write_files",
"description": "Write multiple files inside the sandbox",
"parameters": {
"type": "object",
"properties": {
"files": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": {"type": "string"},
"data": {"type": "string"},
},
"required": ["path", "data"],
},
}
},
"required": ["files"],
},
},
},
{
"type": "function",
"function": {
"name": "run_commands",
"description": "Run a single shell command inside the sandbox working directory",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The shell command to run, e.g. 'ls' or 'python main.py'",
}
},
"required": ["command"],
},
},
}
]
С инструментами зарегистрированными перейдём к созданию чат-цикла, который будет использовать нашего агента и все определённые нами инструменты.
Чат-цикл
Теперь создадим простой чат-цикл, позволяющий пользователю взаимодействовать с агентом для программирования. Цикл будет хранить список сообщений и обрабатывать вызовы функций, когда агент их запрашивает.
messages = []
print("💬 Enter your queries (type 'exit' to quit):")
while True:
user_input = input("You: ")
if user_input.lower() == "exit":
break
# Append user message
messages.append({"role": "user", "content": user_input})
# Send to model
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
assistant_msg = response.choices[0].message
messages.append(assistant_msg)
if assistant_msg.tool_calls:
print(f"[DEBUG] Assistant requested {len(assistant_msg.tool_calls)} tool call(s).")
for tool_call in assistant_msg.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"[DEBUG] Tool call detected: {fn_name} with args {fn_args}")
if fn_name == "read_file":
fn_result = read_file(**fn_args)
elif fn_name == "write_file":
fn_result = write_file(**fn_args)
elif fn_name == "write_files":
fn_result = write_files(**fn_args)
elif fn_name == "run_commands":
fn_result = run_commands(**fn_args)
else:
fn_result = f"Error: Unknown tool {fn_name}"
print(f"[DEBUG] Unknown tool requested: {fn_name}")
# Append result back
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": str(fn_result),
})
# Get model's final answer with tool results
follow_up = client.chat.completions.create(
model=model,
messages=messages,
)
final_answer = follow_up.choices[0].message
messages.append(final_answer)
print("Assistant:", final_answer.content)
else:
print("Assistant:", assistant_msg.content)
sandbox.kill()
print("[DEBUG] Sandbox terminated. 👋")
Этот чат-цикл поддерживает интерактивное взаимодействие, позволяет агенту вызывать любой из зарегистрированных инструментов по мере необходимости и завершает работу песочницы, когда пользователь выходит.
Создание интерфейса с помощью Gradio
Теперь у нас есть полноценный агент для программирования, с которым можно общаться, но взаимодействие через REPL — не самое интересное. Давайте сделаем опыт приятнее, добавив агенту простой интерфейс Gradio.
Создать UI на Gradio просто. Мы используем gr.ChatInterface для управления чат-взаимодействием и свяжем его с логикой, которую создали ранее. Кроме того, добавим интерфейс команд для выполнения shell-команд внутри песочницы, а также выпадающий список для выбора модели.
Чтобы адаптировать предыдущий код под Gradio, заменим чат-цикл и две последние строки следующим кодом:
# --- Persistent chat messages ---
messages = []
# --- Global model setter ---
def set_model(selected_model):
global model
model = selected_model
print(f"[DEBUG] Model switched to: {model}")
return f"✅ Model switched to **{model}**"
def chat_fn(user_message, history):
global messages, model
messages.append({"role": "user", "content": user_message})
# Send to model
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
assistant_msg = response.choices[0].message
messages.append(assistant_msg)
output_text = ""
if assistant_msg.tool_calls:
print(f"[DEBUG] Assistant requested {len(assistant_msg.tool_calls)} tool call(s).")
for tool_call in assistant_msg.tool_calls:
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
print(f"[DEBUG] Tool call detected: {fn_name} with args {fn_args}")
if fn_name == "read_file":
fn_result = read_file(**fn_args)
elif fn_name == "write_file":
fn_result = write_file(**fn_args)
elif fn_name == "write_files":
fn_result = write_files(**fn_args)
elif fn_name == "run_commands":
fn_result = run_commands(**fn_args)
else:
fn_result = f"Error: Unknown tool {fn_name}"
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": str(fn_result),
})
follow_up = client.chat.completions.create(
model=model,
messages=messages,
)
final_answer = follow_up.choices[0].message
messages.append(final_answer)
output_text = final_answer.content
else:
output_text = assistant_msg.content
return output_text
# --- Command Interface function ---
def execute_command(command):
if not command.strip():
return "⚠️ Please enter a command."
print(f"[DEBUG] Executing command from interface: {command}")
output = run_commands(command)
return f"```bash\
{output}\
```" if output else "✅ Command executed (no output)."
# --- Gradio UI ---
with gr.Blocks(title="Novita Sandbox App") as demo:
gr.Markdown("## 🧠 Novita Sandbox Agent")
gr.Markdown(
"This app is an AI-powered **code agent** that lets you chat with intelligent assistants backed by **Novita AI LLMs**. These agents can write, read, and execute code safely inside a **Novita sandbox**, providing a secure environment for running commands, testing scripts, and managing files, all through an intuitive chat interface with model selection and command execution built right in."
)
with gr.Row(equal_height=True):
# Left: Chat Interface
with gr.Column(scale=2):
gr.Markdown("### 💬 Chat Interface")
gr.ChatInterface(chat_fn)
# Right: Command Interface
with gr.Column(scale=1):
gr.Markdown("### 💻 Command Interface")
# Model selector
model_selector = gr.Dropdown(
label="Select Model",
choices=[
"meta-llama/llama-3.3-70b-instruct",
"deepseek/deepseek-v3.2-exp",
"qwen/qwen3-coder-30b-a3b-instruct",
"openai/gpt-oss-120b",
"moonshotai/kimi-k2-instruct",
],
value=model,
interactive=True,
)
model_status = gr.Markdown(f"✅ Current model: **{model}**")
model_selector.change(set_model, inputs=model_selector, outputs=model_status)
command_input = gr.Textbox(
label="Command",
placeholder="e.g., ls, python main.py",
lines=1,
)
with gr.Row():
run_btn = gr.Button("Run", variant="primary", scale=0)
command_output = gr.Markdown("Command output will appear here...")
run_btn.click(execute_command, inputs=command_input, outputs=command_output)
# --- Cleanup on exit ---
atexit.register(lambda: (sandbox.kill(), print("[DEBUG] Sandbox terminated. 👋")))
if __name__ == "__main__":
demo.launch()
В этой версии функция chat_fn обрабатывает каждый обмен сообщениями между пользователем и агентом. gr.ChatInterface принимает эту функцию и автоматически управляет взаимодействиями в интерфейсе.
Когда приложение Gradio запускается, агент работает прямо в браузере, предоставляя пользователю чистый интерактивный чат-интерфейс. Наконец, мы регистрируем процедуру очистки через atexit, чтобы гарантировать корректное завершение работы песочницы при остановке приложения.
Теперь у нас есть ИИ-агент для программирования, работающий в безопасной песочнице, с дружелюбным чат-интерфейсом Gradio.
Полный код можно найти на GitHub.
Тестирование агента для программирования
Чтобы использовать агента, запустим наш код Gradio как скрипт.
python gradio_chat.py
После этого наше приложение Gradio будет работать на localhost. Мы сможем общаться с агентом для программирования, и агент будет выполнять все наши действия внутри песочницы.
В приложении Gradio мы видим только сообщения от агента, но если заглянуть в терминал, можно увидеть отладочный вывод: какие функции и команды агент вызывал для выполнения запроса пользователя.
Благодаря доступу к инструментам работы с файлами и командами мы можем запрограммировать почти всё. Но вместо прямого написания кода мы отдаём инструкции агенту, а он пишет и выполняет код за нас.
Деплой на Hugging Face Spaces
Сейчас наш агент для программирования запущен локально на компьютере. Давайте сделаем его доступным для всего мира, развернув на Hugging Face Spaces. Hugging Face Spaces позволяет разместить код и приложение в одном месте. Приступим.
Создание Space
Перейдите на Hugging Face и создайте новый Space для вашего агента, задав ему уникальное имя.

Затем выберите SDK для Space — в нашем случае это Gradio. Выберите пустой шаблон (Blank), поскольку код приложения у нас уже есть.

Далее выберите оборудование. Так как агент и песочница работают на базе Novita, нам не нужно специализированное «железо». Варианта Basic CPU достаточно. После этого нажмите Create Space.

Hugging Face создаст Space с файлами README.md и .gitignore.
Есть несколько способов добавить код, но самый простой — нажать Contribute → Add file.

Создайте файл requirements.txt и добавьте следующие зависимости:
openai
novita-sandbox
Добавление переменных окружения
Прежде чем запускать приложение, необходимо задать NOVITA_API_KEY как переменную окружения.
Для этого перейдите в Settings вашего Space, прокрутите до раздела Variables and secrets и добавьте новый секрет с именем NOVITA_API_KEY и вашим API-ключом в качестве значения.
Настройка приложения
Когда переменная окружения задана, пора создать само приложение.
Создайте новый файл app.py и вставьте в него полный код Gradio-агента.

После сохранения файла Hugging Face автоматически начнёт сборку Space.

Когда процесс сборки завершится, ваш агент для программирования станет доступен на Hugging Face Spaces.
Теперь можно общаться с агентом через интерактивный чат-интерфейс.
Кроме того, вы можете следить за логами, чтобы видеть, какие инструменты агент вызывает во время работы.
Вот и всё: у вас есть полноценный агент для программирования, работающий в безопасной песочнице, с интерфейсом Gradio и развёрнутый на Hugging Face Spaces.
Заключение
В этой статье мы разобрали, как использовать возможности Novita Sandbox для создания полноценного агента для программирования, который умеет читать и создавать файлы, выполнять команды и безопасно работать в изолированной среде.
То, что мы построили, — лишь начало. Песочница открывает множество возможностей: от создания ИИ-агентов для визуализации данных до разработки компьютерных агентов, способных интеллектуально взаимодействовать с системами.
Почти всё становится возможным, когда вы объединяете агента с выделенным набором инструментов вроде песочницы.
