@hicaru/pi-rlm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/README.ru.md +200 -0
- package/README.zh-CN.md +224 -0
- package/package.json +54 -0
- package/src/bridge/fallback-todo.ts +137 -0
- package/src/bridge/interactive.ts +65 -0
- package/src/bridge/llm-query.ts +124 -0
- package/src/bridge/model.ts +97 -0
- package/src/bridge/pi-interactive.ts +86 -0
- package/src/bridge/rlm-query.ts +78 -0
- package/src/commands/rlm-config.ts +42 -0
- package/src/commands/rlm.ts +165 -0
- package/src/config/defaults.ts +38 -0
- package/src/config/settings.ts +185 -0
- package/src/context/repomix-context.ts +253 -0
- package/src/core/answer.ts +97 -0
- package/src/core/compaction.ts +64 -0
- package/src/core/engine.ts +408 -0
- package/src/core/history.ts +13 -0
- package/src/core/iteration.ts +45 -0
- package/src/core/limits.ts +90 -0
- package/src/core/pipeline.ts +100 -0
- package/src/core/resource-limits.ts +14 -0
- package/src/core/types.ts +131 -0
- package/src/index.ts +165 -0
- package/src/mode/input-router.ts +23 -0
- package/src/mode/rlm-mode.ts +149 -0
- package/src/patch/apply.ts +148 -0
- package/src/patch/index.ts +37 -0
- package/src/prompts/system.ts +278 -0
- package/src/prompts/user.ts +21 -0
- package/src/sandbox/protocol.ts +191 -0
- package/src/sandbox/sandbox-manager.ts +143 -0
- package/src/sandbox/sandbox.ts +362 -0
- package/src/sandbox/worker.py +457 -0
- package/src/state/events.ts +22 -0
- package/src/state/index.ts +23 -0
- package/src/state/internal.ts +46 -0
- package/src/state/paths.ts +42 -0
- package/src/state/reads.ts +96 -0
- package/src/state/resume.ts +154 -0
- package/src/state/rows.ts +117 -0
- package/src/state/writes.ts +56 -0
- package/src/telemetry/dispatcher.ts +116 -0
- package/src/telemetry/index.ts +14 -0
- package/src/telemetry/mlflow-config.ts +15 -0
- package/src/telemetry/mlflow-sink.ts +136 -0
- package/src/telemetry/mlflow.ts +99 -0
- package/src/telemetry/sink.ts +8 -0
- package/src/text/edits.ts +16 -0
- package/src/text/parsing.ts +35 -0
- package/src/text/preview.ts +18 -0
- package/src/text/tokens.ts +64 -0
- package/src/tool/apply-diff-tool.ts +125 -0
- package/src/tool/emitter-listener.ts +24 -0
- package/src/tool/repl-details.ts +23 -0
- package/src/tool/repl-tool.ts +528 -0
- package/src/tool/rlm-aggregator.ts +115 -0
- package/src/tool/rlm-details.ts +53 -0
- package/src/tool/rlm-events.ts +215 -0
- package/src/tool/rlm-tool.ts +199 -0
- package/src/tool/subcall-render.ts +129 -0
- package/src/tool/subcall-store.ts +90 -0
- package/src/tool/tool-utils.ts +73 -0
- package/src/ui/config-panel.ts +92 -0
- package/src/ui/intro.ts +23 -0
- package/src/ui/model-picker.ts +139 -0
- package/src/ui/status.ts +26 -0
- package/src/ui/theme.ts +47 -0
- package/src/util/concurrency.ts +15 -0
- package/src/util/errors.ts +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hicaru contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="../../assets/hero.png" alt="pi-rlm">
|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+
<sub>
|
|
10
|
+
**English** · <a href="README.zh-CN.md">中文</a> · <a href="README.ru.md">Русский</a>
|
|
11
|
+
</sub>
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# pi-rlm — Recursive Language Models for the [Pi](https://github.com/earendil-works) Coding Agent
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
|
|
21
|
+
**Recursive Language Models (RLMs)**, implemented natively as a Pi extension —
|
|
22
|
+
no extra servers, no Docker, no sockets.
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
A **Recursive Language Model (RLM)** is a task-agnostic inference paradigm where a
|
|
29
|
+
root language model orchestrates over near-infinite context by *programmatically*
|
|
30
|
+
examining, decomposing, and **recursively calling itself** over its input. RLMs
|
|
31
|
+
replace the canonical `llm.completion(prompt, model)` call with an
|
|
32
|
+
`rlm.completion(prompt, model)` call: the prompt/context is offloaded as a variable
|
|
33
|
+
in a REPL environment that the model interacts with, and the model can launch
|
|
34
|
+
sub-LLM and sub-RLM calls as ordinary functions in code.
|
|
35
|
+
|
|
36
|
+
This is a bet on a [CodeAct](https://arxiv.org/abs/2402.01030)-style harness — every
|
|
37
|
+
language model gets access to a code environment, sub-(R)LM calls are functions, and
|
|
38
|
+
context/prompts are objects in code — moving away from the JSON tool-calling standard.
|
|
39
|
+
A system built this way is *itself* a language model that relies on recursive
|
|
40
|
+
sub-LLM calls, hence the name.
|
|
41
|
+
|
|
42
|
+
`pi-rlm` brings that paradigm **natively into Pi**:
|
|
43
|
+
|
|
44
|
+
- A **root orchestrator** model drives a **persistent Python REPL** turn-by-turn.
|
|
45
|
+
- Long-context work is **delegated** to cheap worker models via `llm_query` / `llm_query_batched`.
|
|
46
|
+
- Hard sub-problems **recurse** into child RLMs via `rlm_query` (depth-capped).
|
|
47
|
+
- Everything runs **in-process** — the only external process is one local `python3` worker.
|
|
48
|
+
|
|
49
|
+
> This is a Pi-plugin reimplementation of the RLM method (see the [RLM paper](https://arxiv.org/abs/2512.24601)
|
|
50
|
+
> and the [Python `rlm` library](https://github.com/alexzhang13/rlm-minimal)). It is **not** the Python library.
|
|
51
|
+
|
|
52
|
+
## How it works
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
pi process (TypeScript)
|
|
56
|
+
├─ /rlm ──► engine drives the SMART (root) model turn-by-turn (writes ```repl``` Python)
|
|
57
|
+
│ │ each turn: parse repl blocks ──► run in sandbox ──► feed stdout back
|
|
58
|
+
│ ▼
|
|
59
|
+
├─ bridge ── llm_query / llm_query_batched ──► WORKER model (serverless, in-process)
|
|
60
|
+
│ rlm_query ──► recursive child RLM (own sandbox), depth-capped
|
|
61
|
+
├─ AgentTree ──► live agent/subagent tree above the editor (roles, depth, cost, tokens)
|
|
62
|
+
└─ PythonSandbox ── `python3 worker.py` ──[JSONL over stdio, bidirectional]── persistent REPL
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- **No servers, no sockets, no Docker.** The only external process is one local `python3` sandbox.
|
|
66
|
+
When sandbox code calls `llm_query`, the worker writes a request on stdout and blocks on stdin;
|
|
67
|
+
Pi services it in-process and writes the reply back. **Provider API keys never enter the sandbox.**
|
|
68
|
+
- The sandbox exposes `context`, `llm_query`, `llm_query_batched`, `rlm_query`,
|
|
69
|
+
`rlm_query_batched`, `SHOW_VARS()`, `todo()`, `ask_user_question()`, and an `answer` dict.
|
|
70
|
+
The model submits its final result by setting `answer["ready"] = True`.
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
`pi-rlm` is a Pi package. Pi provides the `@earendil-works/pi-*` and `typebox` peer
|
|
75
|
+
dependencies; do **not** install a separate copy of them into this package. Requires
|
|
76
|
+
`python3` on `PATH` (standard library only).
|
|
77
|
+
|
|
78
|
+
Recommended local install while developing:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pi install /path/to/this-repo/pi-plugin/rlm
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Published npm package install:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm publish # e.g. as @<you>/pi-rlm
|
|
88
|
+
pi install npm:@<you>/pi-rlm
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> **Git installs** require the package manifest to live at the installed repository root.
|
|
92
|
+
> For monorepo subdirectories like this one, prefer the local-path or npm flow above.
|
|
93
|
+
|
|
94
|
+
If you previously copied the extension folder directly, remove it so it does not shadow the package:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
rm -rf ~/.pi/agent/extensions/rlm
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Then run `/reload` or restart Pi. Verify with `pi list` that the package appears in
|
|
101
|
+
`settings.packages`, and check that `/rlm`, `/rlm-config`, and `/rlm-stop` appear under **[Extensions]**.
|
|
102
|
+
|
|
103
|
+
## Commands
|
|
104
|
+
|
|
105
|
+
| Command | Shortcut | Description |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `/rlm` | `Ctrl+Shift+R` | Toggle persistent RLM mode (route plain prompts through the RLM engine) |
|
|
108
|
+
| `/rlm-stop` | | Abort an in-progress run |
|
|
109
|
+
| `/rlm-config` | | Pick smart + worker models and tune run settings |
|
|
110
|
+
| `/rlm-resume` | | Resume an interrupted run (default `@latest`) |
|
|
111
|
+
| `/rlm-runs` | | List recent runs |
|
|
112
|
+
| `/rlm-help` | | Show the startup guide & cheatsheet |
|
|
113
|
+
|
|
114
|
+
While a run is active, a **live tree** shows the root orchestrator and every sub-LLM /
|
|
115
|
+
recursive child with status, model, cost, tokens, and duration. The final answer is posted
|
|
116
|
+
to the chat as markdown; any code edits are collected as diffs and reviewed via a popup
|
|
117
|
+
(unless `yolo` is on).
|
|
118
|
+
|
|
119
|
+
## Sandbox API
|
|
120
|
+
|
|
121
|
+
These functions are injected into the model's Python namespace inside the REPL:
|
|
122
|
+
|
|
123
|
+
| Function | Signature | Description |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `context` | `list[dict]` | Repository packed as `[{"path","content","tokens"}, ...]` — the full codebase |
|
|
126
|
+
| `llm_query` | `(prompt, model=None) -> str` | One-shot sub-LLM call (worker model) |
|
|
127
|
+
| `llm_query_batched` | `(prompts, model=None) -> list[str]` | Concurrent sub-LLM calls (pool-bounded) |
|
|
128
|
+
| `rlm_query` | `(prompt, model=None) -> str` | Recursive child RLM with its own sandbox (depth-capped) |
|
|
129
|
+
| `rlm_query_batched` | `(prompts, model=None) -> list[str]` | Concurrent recursive child RLMs |
|
|
130
|
+
| `todo` | `(action, **kwargs) -> str` | Task list: `create`/`update`/`list`/`get`/`delete`/`clear` |
|
|
131
|
+
| `ask_user_question` | `(questions) -> list[dict]` | Ask the user structured questions (depth 0 only) |
|
|
132
|
+
| `SHOW_VARS` | `() -> str` | List currently defined variables & their types |
|
|
133
|
+
| `answer` | `dict` | Set `answer["content"]=...; answer["ready"]=True` to finalize |
|
|
134
|
+
|
|
135
|
+
## Settings (`/rlm-config`)
|
|
136
|
+
|
|
137
|
+
| Setting | Default | Meaning |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| Smart model | Pi's active model | the root orchestrator |
|
|
140
|
+
| Worker model | cheapest available | answers `llm_query` |
|
|
141
|
+
| Max recursion depth | `4` | `rlm_query` past this falls back to `llm_query` |
|
|
142
|
+
| Max iterations | `30` | turns before the engine finalizes |
|
|
143
|
+
| Budget ceiling | none | stops the whole tree when USD spend exceeds this |
|
|
144
|
+
| Max consecutive errors | `5` | stops after N consecutive error turns |
|
|
145
|
+
| REPL block timeout | `120s` | per-`repl`-block wall-clock (SIGALRM in the worker) |
|
|
146
|
+
| Max concurrent sub-calls | `4` | pool size for `*_batched` |
|
|
147
|
+
| Orchestrator addendum | on | "delegate, don't solve" guidance |
|
|
148
|
+
| Trajectory compaction | on (0.85) | summarize history when it nears the context window |
|
|
149
|
+
| `yolo` | off | apply proposed edits immediately, skipping the review popup |
|
|
150
|
+
| `askUserQuestion` | on | expose `ask_user_question()` to the model |
|
|
151
|
+
| `todo` | on | expose `todo()` to the model |
|
|
152
|
+
|
|
153
|
+
> **Concurrency note:** each `rlm_query` child spawns its own `python3` worker (~50–150 ms
|
|
154
|
+
> cold start). Worst-case concurrent interpreters ≈ `maxConcurrentSubcalls`^(depth−1); at
|
|
155
|
+
> defaults (depth 4, conc 4) that's 4³ = 64 in the pathological case. Budget and error
|
|
156
|
+
> caps (above) bound total spend regardless of fan-out.
|
|
157
|
+
|
|
158
|
+
## Telemetry & run logs
|
|
159
|
+
|
|
160
|
+
- **Run logs** (`runLog`): always-on by default. Each run writes a JSONL trail to `.rlm/runs/`
|
|
161
|
+
(default), capped at `maxRuns` (50). Supports **snapshots** (`sandbox.pkl`) and **resume**
|
|
162
|
+
of interrupted runs via `/rlm-resume`. Snapshots are protected by a per-session `nonce`
|
|
163
|
+
to prevent cross-session replay.
|
|
164
|
+
- **MLflow tracing** (`telemetry`): optional. Set `MLFLOW_TRACKING_URI` or configure
|
|
165
|
+
`trackingUri` / `experimentId` in `/rlm-config`. The root run is tagged as an MLflow span
|
|
166
|
+
for trace correlation on resume. The Bearer token comes from the `MLFLOW_TRACKING_TOKEN`
|
|
167
|
+
env var and is **never persisted** to `rlm.json`.
|
|
168
|
+
|
|
169
|
+
## Security
|
|
170
|
+
|
|
171
|
+
- **Key isolation**: provider keys live only in TypeScript (`AuthStorage`); the sandbox
|
|
172
|
+
receives prompts and returns text — never keys.
|
|
173
|
+
- **Environment sanitization**: sensitive env vars (API keys, tokens) are stripped before the
|
|
174
|
+
worker spawns. The worker cannot read provider credentials from `os.environ`.
|
|
175
|
+
- **NOT a security sandbox**: the Python worker exposes `__import__` and `open`. Model-authored
|
|
176
|
+
code can import networking modules, read/write local files, and write protocol-shaped JSON to
|
|
177
|
+
stdout. This tier trusts the root model's code; the stdio protocol isolates provider keys and
|
|
178
|
+
process lifecycle, **not** adversarial code containment. A stronger sandbox (Docker, seccomp)
|
|
179
|
+
can be added later behind a setting without protocol changes.
|
|
180
|
+
- **Restricted builtins**: no `eval`/`exec`/`compile`/`input`/`globals`/`locals`; per-block
|
|
181
|
+
SIGALRM timeout + parent watchdog (SIGKILL on hang); budget / token / timeout /
|
|
182
|
+
consecutive-error caps.
|
|
183
|
+
- **Trust**: project-local install requires Pi project trust.
|
|
184
|
+
|
|
185
|
+
## Project layout
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
src/
|
|
189
|
+
sandbox/ worker.py + JSONL stdio driver (PythonSandbox) · protocol.ts · sandbox-manager.ts
|
|
190
|
+
bridge/ model.ts (one-shot completion) · llm-query.ts · rlm-query.ts (recursion)
|
|
191
|
+
core/ engine.ts (the loop) · iteration · limits · answer · compaction · pipeline · types
|
|
192
|
+
prompts/ system + per-turn prompts (ported from the Python reference)
|
|
193
|
+
text/ parsing (repl blocks) · tokens · preview · edits
|
|
194
|
+
state/ agent-tree · events · reads/writes · resume · paths · rows
|
|
195
|
+
tool/ repl-tool · rlm-events · aggregator · propose-edits · emitter-listener
|
|
196
|
+
config/ defaults · settings (rlm.json persistence + validation)
|
|
197
|
+
context/ repomix-based repository packing + caching
|
|
198
|
+
telemetry/ MLflow sink · dispatcher · mlflow-config
|
|
199
|
+
ui/ tree-widget · status · model-picker · config-panel · intro · theme
|
|
200
|
+
commands/ rlm · rlm-config
|
|
201
|
+
mode/ rlm-mode (controller) · input-router
|
|
202
|
+
patch/ apply · popup · index
|
|
203
|
+
util/ errors · concurrency
|
|
204
|
+
test/ phase1–phase9 · native-smoke · native-mode · helpers
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Tests
|
|
208
|
+
|
|
209
|
+
Runtime is **Bun** (`bun install`, `bun run …` — never npm/pnpm/yarn).
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
bun run test/phase1.ts # sandbox: exec, persistence, key isolation, timeout kill
|
|
213
|
+
bun run test/phase4.ts # recursion depth-cap logic (no tokens)
|
|
214
|
+
bun run test/phase5.ts # live agent tree rendering (no tokens)
|
|
215
|
+
RLM_TEST_LIVE=1 bun run test/phase2.ts # real llm_query through the sandbox
|
|
216
|
+
RLM_TEST_LIVE=1 bun run test/phase3.ts # real end-to-end /rlm over a file context
|
|
217
|
+
RLM_TEST_LIVE=1 bun run test/phase4.ts # engine solves a 20-doc needle-in-haystack
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Background
|
|
221
|
+
|
|
222
|
+
Modeled on the Python reference [`rlm`](https://github.com/alexzhang13/rlm-minimal) and the
|
|
223
|
+
method in the [RLM paper](https://arxiv.org/abs/2512.24601), reimplemented natively for Pi.
|
|
224
|
+
|
|
225
|
+
If you use this in your research, please cite the original RLM work:
|
|
226
|
+
|
|
227
|
+
```bibtex
|
|
228
|
+
@misc{zhang2026recursivelanguagemodels,
|
|
229
|
+
title={Recursive Language Models},
|
|
230
|
+
author={Alex L. Zhang and Tim Kraska and Omar Khattab},
|
|
231
|
+
year={2026},
|
|
232
|
+
eprint={2512.24601},
|
|
233
|
+
archivePrefix={arXiv},
|
|
234
|
+
primaryClass={cs.AI},
|
|
235
|
+
url={https://arxiv.org/abs/2512.24601},
|
|
236
|
+
}
|
|
237
|
+
```
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="../../assets/hero.png" alt="pi-rlm">
|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+
<sub>
|
|
10
|
+
<a href="README.md">English</a> · <a href="README.zh-CN.md">中文</a> · **Русский**
|
|
11
|
+
</sub>
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# pi-rlm — рекурсивные языковые модели для кодинг-агента [Pi](https://github.com/earendil-works)
|
|
18
|
+
|
|
19
|
+
<div align="center">
|
|
20
|
+
|
|
21
|
+
**Рекурсивные языковые модели (RLMs)**, реализованные нативно как расширение Pi —
|
|
22
|
+
без дополнительных серверов, Docker или сокетов.
|
|
23
|
+
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
**Рекурсивная языковая модель (RLM)** — это универсальная (task-agnostic) парадигма инференса, в которой корневая языковая модель управляет почти бесконечным контекстом, *программно* исследуя, декомпозируя и **рекурсивно вызывая саму себя** для обработки входных данных. RLM заменяют канонический вызов `llm.completion(prompt, model)` на вызов `rlm.completion(prompt, model)`: промпт/контекст передается как переменная в среде REPL, с которой взаимодействует модель, а модель может запускать вызовы sub-LLM и sub-RLM как обычные функции в коде.
|
|
29
|
+
|
|
30
|
+
Это ставка на архитектуру в стиле [CodeAct](https://arxiv.org/abs/2402.01030): каждая языковая модель получает доступ к среде выполнения кода, вызовы sub-(R)LM являются функциями, а контекст/промпты — объектами в коде, что является уходом от стандарта вызова инструментов через JSON. Система, построенная таким образом, *сама по себе* является языковой моделью, которая полагается на рекурсивные вызовы sub-LLM, отсюда и название.
|
|
31
|
+
|
|
32
|
+
`pi-rlm` переносит эту парадигму **нативно в Pi**:
|
|
33
|
+
|
|
34
|
+
- **Модель-оркестратор** управляет постоянным Python REPL пошагово.
|
|
35
|
+
- Работа с длинным контекстом **делегируется** дешевым worker-моделям через `llm_query` / `llm_query_batched`.
|
|
36
|
+
- Сложные подзадачи **рекурсивно** передаются в дочерние RLM через `rlm_query` (с ограничением глубины).
|
|
37
|
+
- Все работает **in-process** — единственным внешним процессом является локальный worker `python3`.
|
|
38
|
+
|
|
39
|
+
> This is a Pi-plugin reimplementation of the RLM method (see the [RLM paper](https://arxiv.org/abs/2512.24601)
|
|
40
|
+
> and the [Python `rlm` library](https://github.com/alexzhang13/rlm-minimal)). It is **not** the Python library.
|
|
41
|
+
|
|
42
|
+
## Как это работает
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pi process (TypeScript)
|
|
46
|
+
├─ /rlm ──► движок управляет SMART (корневой) моделью пошагово (пишет ```repl``` Python)
|
|
47
|
+
│ │ каждый шаг: парсинг repl-блоков ──► запуск в песочнице ──► возврат stdout
|
|
48
|
+
│ ▼
|
|
49
|
+
├─ bridge ── llm_query / llm_query_batched ──► WORKER модель (serverless, in-process)
|
|
50
|
+
│ rlm_query ──► рекурсивный дочерний RLM (с собственной песочницей), с ограничением глубины
|
|
51
|
+
├─ AgentTree ──► живое дерево агентов/субагентов над редактором (роли, глубина, стоимость, токены)
|
|
52
|
+
└─ PythonSandbox ── `python3 worker.py` ──[JSONL over stdio, bidirectional]── постоянный REPL
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **Никаких серверов, сокетов или Docker.** Единственным внешним процессом является локальная песочница `python3`. Когда код в песочнице вызывает `llm_query`, worker пишет запрос в stdout и блокируется на stdin; Pi обрабатывает его внутри своего процесса и записывает ответ обратно. **API-ключи провайдеров никогда не попадают в песочницу.**
|
|
56
|
+
- Песочница предоставляет `context`, `llm_query`, `llm_query_batched`, `rlm_query`,
|
|
57
|
+
`rlm_query_batched`, `SHOW_VARS()`, `todo()`, `ask_user_question()` и словарь `answer`.
|
|
58
|
+
Модель отправляет окончательный результат, устанавливая `answer["ready"] = True`.
|
|
59
|
+
|
|
60
|
+
## Установка
|
|
61
|
+
|
|
62
|
+
`pi-rlm` — это пакет Pi. Pi предоставляет peer-зависимости `@earendil-works/pi-*` и `typebox`; **не** устанавливайте их отдельную копию в этот пакет. Требуется `python3` в `PATH` (только стандартная библиотека).
|
|
63
|
+
|
|
64
|
+
Рекомендуемая локальная установка при разработке:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pi install /path/to/this-repo/pi-plugin/rlm
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Установка опубликованного npm-пакета:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm publish # например, как @<you>/pi-rlm
|
|
74
|
+
pi install npm:@<you>/pi-rlm
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> **Установка через Git** требует, чтобы манифест пакета находился в корне устанавливаемого репозитория. Для поддиректорий монорепозитория, таких как эта, предпочтительнее использовать локальный путь или npm, как указано выше.
|
|
78
|
+
|
|
79
|
+
Если вы ранее копировали папку расширения напрямую, удалите ее, чтобы она не перекрывала пакет:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
rm -rf ~/.pi/agent/extensions/rlm
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Затем выполните `/reload` или перезапустите Pi. Убедитесь с помощью `pi list`, что пакет появился в `settings.packages`, и проверьте, что `/rlm`, `/rlm-config` и `/rlm-stop` отображаются в разделе **[Extensions]**.
|
|
86
|
+
|
|
87
|
+
## Команды
|
|
88
|
+
|
|
89
|
+
| Команда | Горячая клавиша | Описание |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `/rlm` | `Ctrl+Shift+R` | Переключить постоянный режим RLM (направлять обычные промпты через движок RLM) |
|
|
92
|
+
| `/rlm-stop` | | Прервать текущий запуск |
|
|
93
|
+
| `/rlm-config` | | Выбрать smart- и worker-модели и настроить параметры запуска |
|
|
94
|
+
| `/rlm-resume` | | Возобновить прерванный запуск (по умолчанию `@latest`) |
|
|
95
|
+
| `/rlm-runs` | | Список последних запусков |
|
|
96
|
+
| `/rlm-help` | | Показать руководство по запуску и шпаргалку |
|
|
97
|
+
|
|
98
|
+
Пока запуск активен, **живое дерево** отображает корневой оркестратор и каждый sub-LLM / рекурсивный дочерний элемент со статусом, моделью, стоимостью, токенами и длительностью. Окончательный ответ публикуется в чате в формате markdown; любые правки кода собираются в виде диффов и проверяются через всплывающее окно (если не включен `yolo`).
|
|
99
|
+
|
|
100
|
+
## Sandbox API
|
|
101
|
+
|
|
102
|
+
Эти функции внедряются в пространство имен Python модели внутри REPL:
|
|
103
|
+
|
|
104
|
+
| Функция | Сигнатура | Описание |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `context` | `list[dict]` | Репозиторий, упакованный как `[{"path","content","tokens"}, ...]` — вся кодовая база |
|
|
107
|
+
| `llm_query` | `(prompt, model=None) -> str` | Одноразовый вызов sub-LLM (worker-модель) |
|
|
108
|
+
| `llm_query_batched` | `(prompts, model=None) -> list[str]` | Параллельные вызовы sub-LLM (с ограничением пула) |
|
|
109
|
+
| `rlm_query` | `(prompt, model=None) -> str` | Рекурсивный дочерний RLM со своей песочницей (с ограничением глубины) |
|
|
110
|
+
| `rlm_query_batched` | `(prompts, model=None) -> list[str]` | Параллельные рекурсивные дочерние RLM |
|
|
111
|
+
| `todo` | `(action, **kwargs) -> str` | Список задач: `create`/`update`/`list`/`get`/`delete`/`clear` |
|
|
112
|
+
| `ask_user_question` | `(questions) -> list[dict]` | Задать пользователю структурированные вопросы (только на глубине 0) |
|
|
113
|
+
| `SHOW_VARS` | `() -> str` | Список текущих переменных и их типов |
|
|
114
|
+
| `answer` | `dict` | Установите `answer["content"]=...; answer["ready"]=True` для завершения |
|
|
115
|
+
|
|
116
|
+
## Настройки (`/rlm-config`)
|
|
117
|
+
|
|
118
|
+
| Настройка | По умолчанию | Значение |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| Smart model | Активная модель Pi | корневой оркестратор |
|
|
121
|
+
| Worker model | Самая дешевая доступная | отвечает на `llm_query` |
|
|
122
|
+
| Max recursion depth | `4` | при превышении этой глубины `rlm_query` переключается на `llm_query` |
|
|
123
|
+
| Max iterations | `30` | количество шагов до завершения работы движка |
|
|
124
|
+
| Budget ceiling | нет | остановка всего дерева, когда затраты в USD превышают этот лимит |
|
|
125
|
+
| Max consecutive errors | `5` | остановка после N последовательных шагов с ошибками |
|
|
126
|
+
| REPL block timeout | `120s` | реальное время на один `repl`-блок (SIGALRM в worker) |
|
|
127
|
+
| Max concurrent sub-calls | `4` | размер пула для `*_batched` |
|
|
128
|
+
| Orchestrator addendum | вкл | инструкция «делегируй, а не решай сам» |
|
|
129
|
+
| Trajectory compaction | вкл (0.85) | суммаризация истории при приближении к лимиту окна контекста |
|
|
130
|
+
| `yolo` | выкл | применять предлагаемые правки немедленно, пропуская окно подтверждения |
|
|
131
|
+
| `askUserQuestion` | вкл | предоставить доступ к `ask_user_question()` для модели |
|
|
132
|
+
| `todo` | вкл | предоставить доступ к `todo()` для модели |
|
|
133
|
+
|
|
134
|
+
> **Примечание по параллелизму:** каждый дочерний `rlm_query` запускает собственного worker `python3` (~50–150 мс «холодного старта»). В худшем случае количество параллельных интерпретаторов ≈ `maxConcurrentSubcalls`^(depth−1); при настройках по умолчанию (глубина 4, параллелизм 4) это 4³ = 64 в патологическом случае. Лимиты бюджета и ошибок (см. выше) ограничивают общие затраты независимо от степени разветвления.
|
|
135
|
+
|
|
136
|
+
## Телеметрия и логи запусков
|
|
137
|
+
|
|
138
|
+
- **Логи запусков** (`runLog`): включены по умолчанию. Каждый запуск записывает след в формате JSONL в `.rlm/runs/` (по умолчанию) с ограничением `maxRuns` (50). Поддерживает **снимки** (`sandbox.pkl`) и **возобновление** прерванных запусков через `/rlm-resume`. Снимки защищены сессионным `nonce` для предотвращения повторов между сессиями.
|
|
139
|
+
- **Трассировка MLflow** (`telemetry`): опционально. Установите `MLFLOW_TRACKING_URI` или настройте `trackingUri` / `experimentId` в `/rlm-config`. Корневой запуск помечается как span MLflow для корреляции трасс при возобновлении. Bearer-токен берется из переменной окружения `MLFLOW_TRACKING_TOKEN` и **никогда не сохраняется** в `rlm.json`.
|
|
140
|
+
|
|
141
|
+
## Безопасность
|
|
142
|
+
|
|
143
|
+
- **Изоляция ключей**: ключи провайдеров хранятся только в TypeScript (`AuthStorage`); песочница получает промпты и возвращает текст, но никогда не получает ключи.
|
|
144
|
+
- **Очистка окружения**: чувствительные переменные окружения (API-ключи, токены) удаляются перед запуском worker. Worker не может прочитать учетные данные провайдеров из `os.environ`.
|
|
145
|
+
- **НЕ является защищенной песочницей**: Python-worker предоставляет доступ к `__import__` и `open`. Код, написанный моделью, может импортировать сетевые модули, читать/записывать локальные файлы и писать JSON-данные протокола в stdout. Этот уровень доверяет коду корневой модели; протокол stdio изолирует ключи провайдеров и жизненный цикл процесса, а **не** ограничивает вредоносный код. Более строгая песочница (Docker, seccomp) может быть добавлена позже через настройки без изменения протокола.
|
|
146
|
+
- **Ограниченные встроенные функции**: запрещены `eval`/`exec`/`compile`/`input`/`globals`/`locals`; тайм-аут SIGALRM для каждого блока + родительский watchdog (SIGKILL при зависании); лимиты по бюджету / токенам / времени / количеству последовательных ошибок.
|
|
147
|
+
- **Доверие**: локальная установка в проект требует доверия к проекту Pi.
|
|
148
|
+
|
|
149
|
+
## Структура проекта
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
src/
|
|
153
|
+
sandbox/ worker.py + JSONL stdio driver (PythonSandbox) · protocol.ts · sandbox-manager.ts
|
|
154
|
+
bridge/ model.ts (одноразовое завершение) · llm-query.ts · rlm-query.ts (рекурсия)
|
|
155
|
+
core/ engine.ts (цикл) · iteration · limits · answer · compaction · pipeline · types
|
|
156
|
+
prompts/ системные промпты и промпты для каждого шага (перенесены из Python-референса)
|
|
157
|
+
text/ парсинг (repl-блоки) · токены · превью · правки
|
|
158
|
+
state/ дерево-агентов · события · чтения/записи · возобновление · пути · строки
|
|
159
|
+
tool/ repl-tool · rlm-events · агрегатор · предложение-правок · emitter-listener
|
|
160
|
+
config/ значения по умолчанию · настройки (сохранение и валидация rlm.json)
|
|
161
|
+
context/ упаковка репозитория на базе repomix + кеширование
|
|
162
|
+
telemetry/ MLflow sink · диспетчер · mlflow-config
|
|
163
|
+
ui/ виджет-дерева · статус · выбор-модели · панель-конфигурации · вступление · тема
|
|
164
|
+
commands/ rlm · rlm-config
|
|
165
|
+
mode/ rlm-mode (контроллер) · маршрутизатор-ввода
|
|
166
|
+
patch/ применение · всплывающее-окно · индекс
|
|
167
|
+
util/ ошибки · параллелизм
|
|
168
|
+
test/ фазы 1–9 · native-smoke · native-mode · помощники
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Тесты
|
|
172
|
+
|
|
173
|
+
Среда выполнения — **Bun** (`bun install`, `bun run …` — никогда не используйте npm/pnpm/yarn).
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
bun run test/phase1.ts # sandbox: exec, persistence, key isolation, timeout kill
|
|
177
|
+
bun run test/phase4.ts # recursion depth-cap logic (no tokens)
|
|
178
|
+
bun run test/phase5.ts # live agent tree rendering (no tokens)
|
|
179
|
+
RLM_TEST_LIVE=1 bun run test/phase2.ts # real llm_query through the sandbox
|
|
180
|
+
RLM_TEST_LIVE=1 bun run test/phase3.ts # real end-to-end /rlm over a file context
|
|
181
|
+
RLM_TEST_LIVE=1 bun run test/phase4.ts # engine solves a 20-doc needle-in-haystack
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Общая информация
|
|
185
|
+
|
|
186
|
+
Реализовано на основе эталонного проекта [`rlm`](https://github.com/alexzhang13/rlm-minimal) на Python и метода из [статьи RLM](https://arxiv.org/abs/2512.24601), с нативной переработкой для Pi.
|
|
187
|
+
|
|
188
|
+
Если вы используете этот проект в своих исследованиях, пожалуйста, сошлитесь на оригинальную работу RLM:
|
|
189
|
+
|
|
190
|
+
```bibtex
|
|
191
|
+
@misc{zhang2026recursivelanguagemodels,
|
|
192
|
+
title={Recursive Language Models},
|
|
193
|
+
author={Alex L. Zhang and Tim Kraska and Omar Khattab},
|
|
194
|
+
year={2026},
|
|
195
|
+
eprint={2512.24601},
|
|
196
|
+
archivePrefix={arXiv},
|
|
197
|
+
primaryClass={cs.AI},
|
|
198
|
+
url={https://arxiv.org/abs/2512.24601},
|
|
199
|
+
}
|
|
200
|
+
```
|