Tienes un agente de IA y quieres que se ejecute en un entorno seguro donde tenga acceso a los recursos adecuados, como un sistema de archivos, y a la capacidad de ejecutar comandos (p. ej., comandos de shell) sin el riesgo de “romper nada”. Entonces, ¿cuáles son tus opciones?
El mejor enfoque es darle a tu agente de IA un sandbox. Dentro de un sandbox, el agente puede interactuar de forma segura con una máquina Linux, trabajar con el sistema de archivos y ejecutar comandos específicos mientras se le restringen las operaciones potencialmente dañinas.
Con esta configuración, podemos crear aplicaciones potentes. Por ejemplo, un agente de codificación que pueda:
- Crear y editar archivos de código en el sistema de archivos
- Ejecutar comandos como git, python o node
- Colaborar con desarrolladores ejecutando y probando código directamente en el entorno
En este artículo, veremos cómo construir un agente de codificación de este tipo. Usaremos los LLM de Novita con function calling, junto con el Agent Sandbox de Novita como nuestro entorno seguro. Para completarlo, crearemos una interfaz fácil de usar con Gradio y lo desplegaremos en Hugging Face Spaces.
Antes de elegir un modelo para esta configuración, revisa Casos de uso de modelos de lenguaje para agentes de codificación para ver patrones prácticos como Q&A sobre repositorios, triaje de pruebas, refactorización y flujos de trabajo con enrutamiento de herramientas.
¡Manos a la obra!
Agent Sandbox
El Agent Sandbox de Novita es un entorno de ejecución diseñado específicamente para agentes de IA. Proporciona una configuración en la nube segura y aislada que funciona como una computadora virtual. Dentro de este entorno, los agentes pueden ejecutar código generado de forma segura sin arriesgar el sistema subyacente.
Características principales del Agent Sandbox de Novita
- Seguro: El sandbox está completamente aislado, por lo que el agente solo tiene acceso a sus propios recursos.
- Inicio rápido: Los nuevos entornos se levantan en menos de 200 ms.
- Máquina virtual: Como el sandbox se comporta como una VM, los agentes pueden ejecutar código en cualquier lenguaje de programación.
- Pausar y reanudar: Puedes pausar un sandbox en cualquier momento y reanudarlo más tarde.
- Tareas en segundo plano: Los agentes pueden ejecutar tareas en segundo plano y obtener resultados de forma asíncrona.
Instalación del SDK
Para usar el Novita Sandbox, necesitarás el SDK, que es compatible tanto con Python como con TypeScript/JavaScript. Para este tutorial, usaremos el SDK de Python:
pip install novita-sandbox
Después de instalarlo, define tu clave de API de Novita como variable de entorno:
export NOVITA_API_KEY=your_api_key_here
Probando el Sandbox
Con todo listo, creemos un sandbox y ejecutemos algunas operaciones básicas:
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()
Este ejemplo muestra cómo:
- Crear una instancia de sandbox
- Acceder al sistema de archivos con el objeto files
- Ejecutar comandos con el método commands.run
- Liberar recursos con kill cuando hayas terminado
Ahora que hemos explorado los conceptos básicos de acceso al sistema de archivos y ejecución de comandos, estamos listos para construir nuestro agente de codificación que usa estas capacidades del sandbox como herramientas.
Construyendo un agente de codificación
Para construir nuestro agente de codificación, necesitamos un LLM que admita function calling. Novita ofrece varios modelos que pueden hacerlo. Para que nuestro agente se comporte como un asistente de codificación, debe tener el conjunto adecuado de funciones.
Pensemos en lo que hace un programador humano. Normalmente escribe, lee y ejecuta código. Así que nuestro agente debería poder:
- Escribir en un archivo
- Leer un archivo
- Ejecutar comandos
- Escribir en varios archivos a la vez
Configuración del agente
Como los modelos de Novita son compatibles con OpenAI, podemos usar el OpenAI SDK para interactuar con ellos. Instalémoslo:
pip install openai
Después de instalarlo, define tu clave de API de Novita como variable de entorno, igual que hicimos antes. Una vez hecho esto, podemos empezar a programar añadiendo nuestros imports:
from openai import OpenAI
import os
import json
from novita_sandbox.code_interpreter import Sandbox
Ahora, creemos nuestra instancia del cliente OpenAI:
client = OpenAI(
base_url="https://api.novita.ai/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
Aquí estamos apuntando el cliente a la URL base de Novita en lugar de la de OpenAI y usando nuestra clave de API de Novita para la autenticación.
A continuación, crearemos la instancia del sandbox que usará nuestro agente:
sandbox = Sandbox.create(timeout=1200)
El parámetro timeout especifica cuánto tiempo debe permanecer activo el sandbox. En este caso, lo hemos configurado a 10 minutos.
Definición de funciones
Ahora podemos definir las funciones que usará nuestro agente.
1. Leer archivo
Esta función toma una ruta de archivo y lee su contenido con el objeto files del sandbox.
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. Escribir archivo
Esta función escribe datos en una ruta de archivo especificada.
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. Escribir varios archivos
Esta función funciona igual que write_file, pero maneja varios archivos a la vez.
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. Ejecutar comandos
Esta función ejecuta comandos de shell dentro del sandbox y devuelve la salida estándar.
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}"
Registro de herramientas
Ahora que tenemos todas nuestras funciones, las registraremos como herramientas que el LLM puede invocar cuando sea necesario. Cada definición de herramienta incluye el nombre de la función, la descripción y el esquema de parámetros.
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"],
},
},
}
]
Con las herramientas registradas, pasemos a crear un bucle de chat que utilice nuestro agente y todas las herramientas que hemos definido.
Bucle de chat
Ahora crearemos un bucle de chat simple que permita al usuario interactuar con el agente de codificación. El bucle mantendrá una lista de mensajes y gestionará las llamadas a función cada vez que el agente lo solicite.
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. 👋")
Este bucle de chat mantiene la interacción activa, permite al agente llamar a cualquiera de las herramientas registradas cuando lo necesite y limpia el sandbox cuando el usuario sale.
Creación de una interfaz con Gradio
Ahora tenemos un agente de codificación totalmente funcional que puede chatear con nosotros, pero interactuar a través de un REPL no es exactamente emocionante. Hagamos la experiencia más atractiva dándole a nuestro agente una interfaz simple con Gradio.
Crear una interfaz de Gradio es sencillo. Usaremos gr.ChatInterface para gestionar nuestras interacciones de chat y enlazarla con la lógica que construimos antes. Junto a eso, incluiremos una interfaz de comandos para ejecutar comandos de shell dentro del sandbox, así como un menú desplegable que nos permita elegir qué modelo usar.
Para actualizar nuestro código anterior y que admita Gradio, reemplazaremos el bucle de chat y las dos últimas líneas con lo siguiente:
# --- 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()
En esta versión, la función chat_fn se encarga de cada intercambio de mensajes entre el usuario y el agente. El gr.ChatInterface recibe esta función como entrada y gestiona automáticamente las interacciones de la interfaz.
Cuando la aplicación de Gradio se inicia, ejecuta tu agente en el navegador dándole al usuario una interfaz de chat limpia e interactiva. Por último, registramos una rutina de limpieza con atexit para asegurarnos de que el sandbox se termine correctamente cuando la aplicación se detenga.
Ahora tenemos un agente de codificación impulsado por IA ejecutándose de forma segura en un sandbox, con una interfaz de chat amigable en Gradio.
Puedes encontrar el código completo en GitHub.
Probando el agente de codificación
Para usar el agente, necesitamos ejecutar nuestro código de Gradio como un script.
python gradio_chat.py
Al hacerlo, tendremos nuestra aplicación de Gradio ejecutándose en localhost. Con esto podemos mantener conversaciones con nuestro agente de codificación y el agente ejecutará todas nuestras acciones dentro del sandbox.
Desde la aplicación de Gradio solo vemos los chats de nuestro agente, pero si vamos a la terminal también podemos ver las salidas de depuración: qué comando o función llamó el agente para ayudar en la solicitud del usuario.
El hecho de tener acceso a una herramienta de archivos y a una herramienta de comandos significa que casi no hay nada que no podamos codificar; pero en lugar de codificar directamente, le damos instrucciones a nuestro agente y él se encarga de escribir y ejecutar el código por nosotros.
Despliegue en Hugging Face Spaces
Actualmente tenemos nuestro agente de codificación ejecutándose localmente en nuestra computadora. Ahora, hagámoslo accesible para el resto del mundo desplegándolo en Hugging Face Spaces. Hugging Face Spaces nos permite alojar tanto nuestro código como la aplicación en un solo lugar. Empecemos.
Crear el Space
Ve a Hugging Face y crea un nuevo Space para tu agente de codificación dándole un nombre único.

A continuación, selecciona el SDK para el Space, que en nuestro caso es Gradio. Elige la plantilla Blank, ya que ya tenemos el código de nuestra aplicación.

Después, selecciona el hardware. Como nuestro agente y el sandbox funcionan con Novita, no necesitamos hardware especializado. La opción Basic CPU es suficiente. Una vez hecho esto, haz clic en Create Space.

Hugging Face creará el Space con un README.md y un archivo .gitignore.
Hay varias formas de añadir tu código, pero la más sencilla es hacer clic en Contribute → Add file.

Crea un archivo requirements.txt e incluye las siguientes dependencias:
openai
novita-sandbox
Agregar variables de entorno
Antes de poder ejecutar nuestra aplicación, debemos configurar nuestra NOVITA_API_KEY como variable de entorno.
Para ello, ve a la configuración de tu Space, desplázate a la sección Variables and secrets y agrega un nuevo secreto llamado NOVITA_API_KEY con tu clave de API como valor.
Configurar la aplicación
Con la variable de entorno definida, es hora de crear nuestra aplicación.
Crea un nuevo archivo llamado app.py y pega en él nuestro código completo del agente con Gradio.

Una vez que guardes el archivo, Hugging Face comenzará automáticamente a compilar tu Space.

Después de que el proceso de compilación termine, tu agente de codificación estará en vivo y accesible en Hugging Face Spaces.
Ahora puedes chatear con tu agente a través de la interfaz de chat interactiva.
Además, puedes monitorear los registros para ver las herramientas que tu agente invoca durante la ejecución.
Y eso es todo: ahora tienes un agente de codificación totalmente funcional ejecutándose en un sandbox seguro, equipado con una interfaz de Gradio y desplegado sin problemas en Hugging Face Spaces.
Conclusión
En este artículo, exploramos cómo aprovechar el Sandbox de Novita para construir un agente de codificación totalmente funcional, capaz de leer y crear archivos, ejecutar comandos y operar de forma segura dentro de un entorno protegido.
Lo que hemos construido aquí es solo el comienzo. El sandbox abre la puerta a infinitas posibilidades, desde la creación de agentes de visualización de datos impulsados por IA hasta el desarrollo de agentes de uso de computadoras que puedan interactuar con sistemas de forma inteligente.
Casi cualquier cosa es posible cuando combinas un agente con un conjunto de herramientas dedicado como el sandbox.
