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
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:
- 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. - 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. - 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 asrlm_worker.py. It runs inside the sandbox and uses only the Python standard library.
rlm_worker.py
Add the interpreter
Save this file assuperserve_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
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 asinterpreter_factory. DSPy reads execution_instructions from the factory and adds it to the model’s instructions.
run_rlm.py
sub_lm answers the llm_query calls that the code makes.
Use a template
The sandbox starts from the default template unless you passfrom_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.RLMcall creates one sandbox and kills it inshutdown(), also when the call fails. As a backstop for a crashed app, the interpreter setstimeout_secondsandauto_delete_seconds. See Auto-delete. - The model can create sub-agents in its code, such as
dspy.Predictor a nesteddspy.RLM. They run in your app and use your API key. A nesteddspy.RLMuses 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_timeoutseconds (600 by default), the interpreter raisesCodeInterpreterError. 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.RLMis 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.