Guides

AI agents & tool use

Let an LLM write code and run it safely — in an isolated sandbox, not on your server.

LLMs are great at writing code, but you can't trust what they produce — a hallucinated os.system("rm -rf /") is one token away. The safe pattern is to give the model a tool that executes code in an isolated, ephemeral QuickTane sandbox, and feed the result back. The model gets a real interpreter; your infrastructure never runs untrusted code.

The whole integration is one function — run code, return output:

python
from quicktane import QuickTane

qt = QuickTane()   # reads QUICKTANE_API_KEY

def run_python(code: str) -> str:
    """Execute Python in a secure sandbox and return its output."""
    run = qt.run_and_wait(code, language="python")
    return run.output or f"(no output, exit code {run.exit_code})"

OpenAI function calling

Expose run_python as a tool and run the standard tool-call loop:

python
import json
from openai import OpenAI
from quicktane import QuickTane

client = OpenAI()
qt = QuickTane()

def run_python(code: str) -> str:
    run = qt.run_and_wait(code, language="python")
    return run.output or f"(no output, exit code {run.exit_code})"

tools = [{
    "type": "function",
    "function": {
        "name": "run_python",
        "description": "Execute Python code in a secure sandbox and return whatever it "
                       "prints. Your code MUST print() any value you want to see back.",
        "parameters": {
            "type": "object",
            "properties": {"code": {"type": "string", "description": "Python code to run."}},
            "required": ["code"],
        },
    },
}]

messages = [
    {"role": "system", "content": "To compute anything, write Python and call run_python. "
                                  "Always print() the final result, then report it."},
    {"role": "user", "content": "What is the 20th Fibonacci number? Use code."},
]

while True:
    resp = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
    msg = resp.choices[0].message
    messages.append(msg)

    if not msg.tool_calls:
        print(msg.content)          # final answer
        break

    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        output = run_python(args["code"])
        messages.append({"role": "tool", "tool_call_id": call.id, "content": output})

The model writes the Fibonacci code, QuickTane runs it in a sandbox, and the model reads the result back to answer — all without your process ever executing model-generated code.

LangChain / LangGraph

Wrap the same function with the @tool decorator and hand it to a prebuilt agent:

python
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from quicktane import QuickTane

qt = QuickTane()

@tool
def run_python(code: str) -> str:
    """Execute Python code in a secure sandbox and return its output."""
    return qt.run_and_wait(code, language="python").output or ""

agent = create_react_agent(ChatOpenAI(model="gpt-4o"), [run_python])

result = agent.invoke({
    "messages": [("user", "Compute the 20th Fibonacci number with code.")]
})
print(result["messages"][-1].content)

Multi-step agents: use a session

For agents that build up state over many steps — install a package, write files, run several commands — give them a live session instead of one-shot runs, so the environment persists between tool calls:

python
sbx = qt.create_sandbox("python")

@tool
def run_in_sandbox(command: str) -> str:
    """Run a shell command in the agent's persistent sandbox."""
    return sbx.exec(command=command).stdout

# ... give run_in_sandbox to your agent; state persists across calls ...
# sbx.exec("pip install pandas"); later calls can import pandas.

sbx.kill()   # when the agent is done

Node/TypeScript? The same pattern works with @quicktane-sdk/client and the Vercel AI SDK or OpenAI Node SDK — define a tool whose handler calls qt.runAndWait(...).

Why run it in QuickTane

  • Hardware isolation — every run is a fresh gVisor sandbox, destroyed after. Model output can't touch your host.
  • No infra — no containers to manage, no cleanup, no escape surface to audit.
  • Filtered network — egress goes through a proxy; the sandbox can't reach your internal services.
  • Pay per use — metered by the second, so idle agents cost nothing.

Get an API key from your dashboard and start with the Quickstart.