Placement — where does my agent run?
One vocabulary for where the whole workflow, an agent's tools, and its explicit code calls run
Placement decides where work happens: a whole flow, one agent’s tools, and its explicit code calls each answer a different question with a different parameter.
from praisonaiagents import Agent, AgentFlow
writer = Agent(name="writer", instructions="Write /workspace/data.txt")
reader = Agent(name="reader", instructions="Read /workspace/data.txt")
# Every step's tools share one Docker sandbox; thinking stays here
flow = AgentFlow(run_on="docker", steps=[writer, reader])
print(flow.where_does_it_run())Quick Start
- Share one sandbox across a flow or team
Thinking stays on this machine; every step’s shell, file and code tools run in one shared Docker sandbox.
python from praisonaiagents import Agent, AgentFlow writer = Agent(name="writer", instructions="Write /workspace/data.txt") reader = Agent(name="reader", instructions="Read /workspace/data.txt") flow = AgentFlow(run_on="docker", steps=[writer, reader]) - Move one agent's tools
Give a single
LocalAgentits own remote compute provider — thinking stays local, tools run on the provider.python from praisonai import LocalAgent agent = LocalAgent(compute="docker") - Isolate explicit code calls
Agent(sandbox=…)configures the explicitagent.execute_code()API. It does not add tools the model can call.python from praisonaiagents import Agent agent = Agent(name="runner", instructions="Run code", sandbox=True) agent.execute_code_sync("print(6*7)")
How It Works
Each parameter moves a different scope, so the choices never overlap.
| Parameter | What it moves | What it accepts | Where it lives |
|---|---|---|---|
run_on= | every step’s tools in a flow or team, into ONE shared sandbox | compute providers (see below) | AgentFlow, AgentTeam, YAML top-level run_on: |
compute= | one agent’s tools; thinking stays local | compute providers | LocalAgent, LocalManagedAgent |
sandbox= | the explicit agent.execute_code() calls | True / SandboxConfig | Agent(...) |
Which parameter do I want?
The scope you want to move picks the parameter. On a single Agent, run_on= hands the whole loop to any hosted place — see the eight-place table below.
Where run_on= and compute= can point
run_on= and compute= accept the same eight compute providers. As of PR #4071, run_on= hosts the whole agent loop on each of them (previously only anthropic and docker), and all eight are valid for both run_on= and tools_run_on=.
| Provider | Description | run_on= hosts the loop |
|---|---|---|
anthropic | Vendor-hosted managed runtime | ✅ |
docker | A Docker container | ✅ |
e2b | An E2B cloud sandbox | ✅ |
modal | A Modal cloud sandbox | ✅ |
daytona | A Daytona cloud sandbox | ✅ |
flyio | A Fly.io machine | ✅ |
tenki | A Tenki cloud microVM | ✅ |
novita | A Novita cloud sandbox | ✅ |
A typo is rejected at load / call time with the full list of valid names.
run_on= refuses the places that isolate tools or need a host object — they are not runtimes for a loop:
| Place | Why run_on= refuses it |
|---|---|
local | Would just run in your own shell — same as passing nothing |
ssh | Needs a host object (SSHSandbox(host=…)), not a bare name |
subprocess / sandlock | Isolate tools; aren’t runtimes for a loop |
tools_run_on= reaches additional local backends and aliases the resolver understands — every alias in _compute_bridge._ALIASES is accepted at construction as of PR #4070:
| Provider | Description |
|---|---|
subprocess | A separate process on this machine (scrubbed env; not a security boundary) |
sandlock | A locked-down process on this machine (Landlock + seccomp-bpf) |
native | Alias — resolves to sandlock (accepted at construction since PR #4070) |
ssh | A remote machine over SSH |
The explicit Agent(sandbox=…) API accepts additional local sandbox backends — subprocess, sandlock, docker — through SandboxConfig. See Sandbox Backends.
Bringing your own compute place
A compute backend can ship in its own package and register under the praisonai.compute entry-point group — no change to the main repo. Once installed, its name works with run_on=, compute=, and tools_run_on= like a built-in.
# pyproject.toml — in the plugin package
[project.entry-points."praisonai.compute"]
runpod = "praisonai_compute_runpod:RunpodCompute"from praisonaiagents import Agent
# 'runpod' resolves through the entry-point registry
agent = Agent(name="runner", instructions="Run code", tools_run_on="runpod")| Behaviour | What happens |
|---|---|
| Discovery | Providers load on demand from the praisonai.compute entry-point group — a broken plugin fails only for the caller that names it. |
| Friendly phrase | A provider that sets display_name = "a Runpod cloud sandbox" on its class uses that phrase in where_does_it_run() and repr(agent) instead of the bare name. |
managed ps | Derives its list from the registry, so contributed places are listed and stopped automatically — no extra wiring. |
See Compute Provider Plugins for the full protocol and a minimal example.
Inspect placement
where_does_it_run() prints the answer in plain English. It is available on Agent, AgentFlow and AgentTeam, and reports two fields — thinks_on (the model calls) and tools_run_on (the tools).
from praisonaiagents import Agent, AgentFlow
writer = Agent(name="writer", instructions="Write")
reader = Agent(name="reader", instructions="Read")
flow = AgentFlow(run_on="docker", steps=[writer, reader])
print(flow.where_does_it_run())Thinking (the AI model calls) happens on this machine.
Tools run on a Docker container (shared).
Every step shares that same sandbox, so a file written by one step is visible to the next.A bare local agent reports that nothing is isolated:
print(Agent(name="builder", instructions="Build").where_does_it_run())Thinking (the AI model calls) happens on this machine.
Tools run on this machine.
Nothing is isolated: tools you pass run in this program, with your permissions.Migration
Placement parameters at a glance
| Scope | Parameter | Where |
|---|---|---|
| Whole flow / team → one shared sandbox | run_on="docker" | AgentFlow, AgentTeam, YAML run_on: |
| One agent’s tools → a compute provider | compute="docker" | LocalAgent, LocalManagedAgent |
Explicit execute_code() calls | sandbox=True / sandbox=SandboxConfig(...) | Agent |
YAML run_on: maps directly to the Python AgentFlow(run_on=…) kwarg — same name, same providers, validated at load time.
ExecutionConfig.code_sandbox_mode is inert
ExecutionConfig(code_sandbox_mode="sandbox") still exists with a "sandbox" default and survives to_dict() / from_dict(), but no execution path reads it today. It does not isolate anything on its own. To isolate code the model runs, use AgentFlow(run_on="docker") or Agent(sandbox=…). See Execution Systems.
Best Practices
Use sandlock for a real local boundary
subprocess separates a process but does not contain it. sandlock enforces Landlock + seccomp-bpf, the only kernel-level boundary that runs on this machine. Configure it through Agent(sandbox=SandboxConfig.native()).
Share one sandbox with run_on= when steps hand off files
run_on= on a flow or team provisions one sandbox for the whole run, so a file written by one step is visible to the next. Leave it unset for zero-overhead local execution.
sandbox= isolates only explicit execute_code() calls
Agent(sandbox=…) does not add a model-visible tool and does not protect the host execute_command path. For model-driven isolation, run the whole workflow in a real container with AgentFlow(run_on="docker").