Skip to main content
DSPy is a Python framework for programming language models. Its dspy.RLM module, a Recursive Language Model, answers questions over inputs that are too large for one prompt. The model gets the inputs as variables in a Python REPL, writes code to explore them, and calls sub-LLMs from that code. By default, the REPL runs in a local Deno and Pyodide process. dspy.RLM accepts any interpreter that follows DSPy’s CodeInterpreter protocol. This page builds one that runs each REPL in a Superserve sandbox. The model’s code then runs in an isolated VM with a full Python and the packages from your template. Sub-LLM calls still run in your app, so the model API key never enters the sandbox.

Setup

DSPy needs Python 3.10 or later. It reads the provider key from the environment; to use another provider, change the model string. See Language Models in the DSPy docs, and API keys to create a Superserve API key.

How it works

dspy.RLM calls the interpreter factory once for each call, runs every step through the interpreter, and shuts it down when the call ends. The interpreter on this page:
  1. Creates a sandbox and starts a small Python worker in it with commands.spawn. The worker is a long-lived process, and its stdin and stdout stay open for the whole call.
  2. Sends each step’s code and the input variables to the worker as one JSON line. The worker runs the code in a namespace that persists between steps, and replies with the printed output, the error, or the value given to SUBMIT.
  3. Answers tool calls while a step runs. When the model’s code calls llm_query, the worker writes a request line and waits. The interpreter runs the tool in your app and writes the result back.

Add the sandbox worker

Save this file as rlm_worker.py. It runs inside the sandbox and uses only the Python standard library.
rlm_worker.py
The worker keeps its protocol on a private copy of stdout. Output that the model’s code writes directly to file descriptor 1, for example from a subprocess, is discarded instead of corrupting the protocol.

Add the interpreter

Save this file as superserve_interpreter.py, next to rlm_worker.py. The highlighted lines are the Superserve calls: create the sandbox, start the worker, write to its stdin, and clean up.
superserve_interpreter.py
The Superserve session API is async, but dspy.RLM calls the interpreter synchronously. The interpreter runs the sandbox I/O on a private event loop in a background thread, and waits for each result.

Run an RLM

Pass the class as interpreter_factory. DSPy reads execution_instructions from the factory and adds it to the model’s instructions.
run_rlm.py
Output from a run (the model’s wording varies):
The model never sees the 800 KB of logs in its prompt. It reads them with code in the sandbox, and sub_lm answers the llm_query calls that the code makes.

Use a template

The sandbox starts from the default template unless you pass from_template. To give the model’s code more packages, subclass the interpreter. Use a subclass instead of a lambda or functools.partial, because DSPy reads execution_instructions from the factory itself.
superserve/code-interpreter comes with pandas, numpy, matplotlib, and other data packages. See Templates for the full list, and to build your own.

Good to know

  • Each dspy.RLM call creates one sandbox and kills it in shutdown(), also when the call fails. As a backstop for a crashed app, the interpreter sets timeout_seconds and auto_delete_seconds. See Auto-delete.
  • The model can create sub-agents in its code, such as dspy.Predict or a nested dspy.RLM. They run in your app and use your API key. A nested dspy.RLM uses the same interpreter factory, so it gets its own sandbox.
  • DSPy sends the input variables again with every step. Inputs of several megabytes work, but each step uploads them again.
  • If a step, including the host tools it calls, does not finish within step_timeout seconds (600 by default), the interpreter raises CodeInterpreterError. The call stops and the sandbox is killed. A host tool that is still running is not stopped, but the step no longer waits for it.
  • dspy.RLM is marked experimental in DSPy. This page was tested with DSPy 3.4.1.

Resources

DSPy RLM docs

Parameters, built-in tools, and custom interpreters for dspy.RLM.

Command sessions

Long-lived processes with streaming output and stdin, through commands.spawn.