Connect an LLM¶
Give a model a sandbox as a code-execution tool: it writes Python, your app runs it in an isolated microVM, you send stdout/stderr back. That is a coding agent's inner loop.
you ──prompt──▶ LLM ──tool: run_python(code)──▶ your app
└── sb.run("python3 /tmp/run.py") in a microVM
└── tool_result (stdout/stderr)
└── LLM ──final answer──▶ you
This page is a complete script, not a sketch. Copy it, set two env vars, run it.
You need:
python3 -m venv .venv && source .venv/bin/activate
pip install sistemo anthropic # or: pip install sistemo openai
export SISTEMO_API_KEY=sk_live_... # dashboard → API Keys → full scope
export ANTHROPIC_API_KEY=... # or OPENAI_API_KEY for the OpenAI section
The model will emit multiline Python. Do not stuff that into python3 -c. Write a file in the guest, then python3 /tmp/run.py.
import shlex
from sistemo import Sandbox
def run_python(sb: Sandbox, code: str) -> str:
"""Execute LLM-generated Python in the guest. Multiline and quotes survive."""
w = sb.run(f"printf %s {shlex.quote(code)} > /tmp/run.py")
if not w.ok:
return w.stderr or f"write failed ({w.exit_code})"
r = sb.run("python3 /tmp/run.py", timeout=30)
if r.ok:
return r.stdout
return r.stderr or f"exit {r.exit_code}"
With Claude (Anthropic)¶
Save as connect_claude.py. Needs ANTHROPIC_API_KEY and SISTEMO_API_KEY.
import shlex
import anthropic
from sistemo import Sandbox
def run_python(sb: Sandbox, code: str) -> str:
w = sb.run(f"printf %s {shlex.quote(code)} > /tmp/run.py")
if not w.ok:
return w.stderr or f"write failed ({w.exit_code})"
r = sb.run("python3 /tmp/run.py", timeout=30)
if r.ok:
return r.stdout
return r.stderr or f"exit {r.exit_code}"
client = anthropic.Anthropic() # ANTHROPIC_API_KEY
tools = [{
"name": "run_python",
"description": "Run Python in an isolated microVM. Return stdout, or stderr on failure.",
"input_schema": {
"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"],
},
}]
messages = [{"role": "user", "content": "Compute the 30th Fibonacci number. Use the tool."}]
with Sandbox(stack="python") as sb: # one VM for the whole session
for _ in range(8):
msg = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=16000,
tools=tools,
messages=messages,
)
if msg.stop_reason in ("refusal", "max_tokens"):
print(f"stopped early: {msg.stop_reason}")
break
if msg.stop_reason != "tool_use":
text = "".join(getattr(b, "text", "") or "" for b in msg.content)
print(text)
break
results = []
for block in msg.content:
if block.type == "tool_use" and block.name == "run_python":
out = run_python(sb, block.input["code"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": out,
})
messages.append({"role": "assistant", "content": msg.content})
messages.append({"role": "user", "content": results})
else:
print("stopped after 8 turns")
You should see the model call run_python, the microVM print 832040, then a short final answer. Each Sandbox() bills CPU/RAM while it runs and the disk while it exists; with destroys the VM when the script ends.
With OpenAI¶
Save as connect_openai.py. Needs OPENAI_API_KEY and SISTEMO_API_KEY.
import json
import shlex
from openai import OpenAI
from sistemo import Sandbox
def run_python(sb: Sandbox, code: str) -> str:
w = sb.run(f"printf %s {shlex.quote(code)} > /tmp/run.py")
if not w.ok:
return w.stderr or f"write failed ({w.exit_code})"
r = sb.run("python3 /tmp/run.py", timeout=30)
if r.ok:
return r.stdout
return r.stderr or f"exit {r.exit_code}"
client = OpenAI()
tools = [{
"type": "function",
"function": {
"name": "run_python",
"description": "Run Python in an isolated microVM. Return stdout, or stderr on failure.",
"parameters": {
"type": "object",
"properties": {"code": {"type": "string"}},
"required": ["code"],
},
},
}]
messages = [{"role": "user", "content": "What is 17 factorial? Use the tool."}]
with Sandbox(stack="python") as sb:
for _ in range(8):
resp = client.chat.completions.create(
model="gpt-4o",
tools=tools,
messages=messages,
)
choice = resp.choices[0].message
if not choice.tool_calls:
print(choice.content)
break
messages.append(choice)
for call in choice.tool_calls:
if call.function.name != "run_python":
continue
code = json.loads(call.function.arguments)["code"]
out = run_python(sb, code)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": out,
})
else:
print("stopped after 8 turns")
What this is not¶
- Not a Jupyter kernel. There is no
run_code()— only shellrun(), so we write/tmp/run.py. - Not a hosted agent. Your process is the loop: LLM → tool → microVM → tool_result → LLM.
- The LLM keys are yours. Sistemo never sees
ANTHROPIC_API_KEY/OPENAI_API_KEY.
Tips¶
- One sandbox per session so installs and files survive across tool calls.
- Cap
timeout— models write infinite loops. Default is 30 s; raise it only when the work is actually long. - Return stderr — the model corrects itself if it sees the traceback.
with Sandbox()/try/finally+close()so a crash does not leave a billed VM.
Next: Install packages if the model needs pip first.