EmpryoEmpryo.beta
tools

Python kernel

RLM mode: a persistent Python interpreter on the tool belt. State survives between calls, IPython when available, opt-in and off by default.

python_kernel is one Python interpreter, alive for as long as your workspace is. A variable bound in one call is still bound in the next.

That is the whole difference from shell. Every python -c is a fresh process, so a script that loads a dataframe pays for the load again on every question asked about it — and the agent has to re-derive its own context each time. With a kernel the agent loads once and interrogates across a whole turn:

python_kernel(code: "import pandas as pd; df = pd.read_csv('sales.csv'); df.shape")
→ (48211, 17)

python_kernel(code: "df.groupby('region').revenue.sum().sort_values().tail(3)")
→ region
  EMEA     1840221.55
  APAC     2110984.10
  NA       3922015.77

The second call never re-read the file.

Turning it on

Off by default. It executes arbitrary code with your privileges, which is the same power shell has, so it waits to be asked:

"agentFeatures": { "pythonKernel": true }
  • TUI/agent-features → Python Kernel
  • Desktop — Settings → Router → Behaviors → Python kernel
  • One runEMPRYO_PYTHON_KERNEL=1 empryo --headless "…"

Requires python3 on PATH. Without one the tool is not added to the belt at all, so a model never offers to use something that cannot run.

Parameters

FieldTypeWhat it does
codestringPython to run. The last expression's value comes back, like a REPL
resetbooleanClear every variable first (or on its own, to clear and stop)
timeoutMsnumberGive up after this many ms — default 120000, max 600000

Returns stdout, stderr and the value of the last expression. An exception comes back as a failure with the traceback, and the interpreter stays alive.

Behaviour worth knowing

  • IPython when it is importable — rich reprs, display, _. Plain CPython

otherwise; nothing to install either way.

  • One kernel per workspace. Two tabs in the same repo share their globals,

which is the point: the state belongs to the work, not to the chat lane.

  • cwd is the workspace root, so relative paths mean what they look like.
  • A runaway loop is killed at the timeout and the kernel restarts, rather

than the turn hanging. The reply says so — the variables are gone with it.

  • Not a Jupyter client. The protocol is JSON over the child's own stdio, so

there is no jupyter_client, no ZMQ, and nothing to install before the first call works.

When not to use it

Reach for shell for one-shot commands (pytest, ruff, a script you already have) — a kernel buys nothing there and costs you a process that outlives the call. Reach for project for lint/typecheck/test, which already know your toolchain. The kernel earns its place when step N+1 depends on what step N left in memory.