@hicaru/pi-rlm 0.3.1 → 0.3.3
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/README.md +131 -154
- package/README.ru.md +5 -5
- package/README.zh-CN.md +5 -5
- package/package.json +3 -2
- package/src/bridge/handlers/await.ts +148 -0
- package/src/bridge/handlers/completion.ts +72 -0
- package/src/bridge/handlers/emitting.ts +104 -0
- package/src/bridge/handlers/finish.ts +45 -0
- package/src/bridge/handlers/index.ts +48 -0
- package/src/bridge/handlers/llm-query.ts +130 -0
- package/src/bridge/handlers/rlm-query.ts +227 -0
- package/src/bridge/handlers/task-registry.ts +202 -0
- package/src/bridge/handlers/types.ts +136 -0
- package/src/commands/rlm-config.ts +40 -12
- package/src/config/settings.ts +13 -3
- package/src/context/listing.ts +2 -2
- package/src/context/refresh.ts +141 -0
- package/src/core/engine.ts +16 -18
- package/src/core/types.ts +1 -3
- package/src/index.ts +59 -54
- package/src/mode/native-guards.ts +4 -4
- package/src/mode/rlm-mode.ts +6 -1
- package/src/mode/subagent.ts +1 -1
- package/src/prompts/glossary.ts +71 -74
- package/src/prompts/native.ts +127 -85
- package/src/prompts/system.ts +29 -15
- package/src/sandbox/interrupts.ts +258 -68
- package/src/sandbox/protocol.ts +53 -30
- package/src/sandbox/py/__pycache__/guards.cpython-314.pyc +0 -0
- package/src/sandbox/py/__pycache__/hostio.cpython-314.pyc +0 -0
- package/src/sandbox/py/__pycache__/retrieval.cpython-314.pyc +0 -0
- package/src/sandbox/py/__pycache__/tasks.cpython-314.pyc +0 -0
- package/src/sandbox/py/guards.py +8 -5
- package/src/sandbox/py/retrieval.py +17 -8
- package/src/sandbox/py/tasks.py +1 -1
- package/src/sandbox/py/worker.py +106 -79
- package/src/sandbox/sandbox-manager.ts +26 -1
- package/src/sandbox/sandbox.ts +1 -1
- package/src/tool/background-tasks.ts +1 -1
- package/src/tool/repl-result.ts +2 -2
- package/src/tool/repl-tool.ts +13 -14
- package/src/ui/config-panel.ts +1 -1
- package/src/ui/intro.ts +1 -4
- package/src/ui/model-picker.ts +32 -2
- package/src/util/concurrency.ts +1 -1
- package/src/bridge/subcall-handlers.ts +0 -382
package/src/prompts/native.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
/** Native-mode prompts — the main Pi agent drives the sandbox through the `repl` tool.
|
|
1
|
+
/** Native-mode prompts — the main Pi agent drives the sandbox through the `repl` tool.
|
|
2
|
+
*
|
|
3
|
+
* Structure mirrors rlm_test api_v5_anthropic (best bake-off arm): role → contract → routing →
|
|
4
|
+
* few-shots → anti-patterns → REPL surface. Goal: multi-area work fires rlm_batch / rlm_query
|
|
5
|
+
* as Task (BG), not serial repl+native read.
|
|
6
|
+
*/
|
|
2
7
|
|
|
3
8
|
import {
|
|
4
9
|
CHUNKED_GLOSSARY_LINE_NATIVE,
|
|
@@ -11,65 +16,52 @@ import {
|
|
|
11
16
|
/** Adapts the REPL glossary for native mode — agent calls `repl({code})` instead of writing ```repl``` blocks. */
|
|
12
17
|
function nativeReplGlossary(): string {
|
|
13
18
|
return [
|
|
14
|
-
"##
|
|
15
|
-
"",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"",
|
|
19
|
-
"
|
|
20
|
-
"- `
|
|
21
|
-
"",
|
|
22
|
-
"
|
|
23
|
-
"-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
19
|
+
"## REPL surface (inside `repl({code})` only)",
|
|
20
|
+
"",
|
|
21
|
+
"Persistent Python sandbox. Only `print(...)` returns; variables/`answers`/`plan` persist across repl calls.",
|
|
22
|
+
"",
|
|
23
|
+
"### Free locate (no sub-LLM tokens)",
|
|
24
|
+
"- `search(query, k=10, path_glob=None) -> [{path, line, score, snippet, text}]` — BM25 pointers, not bodies",
|
|
25
|
+
"- `grep_context(pattern, k=50, …) -> {hits, counts, total, truncated}` — regex / lexical needles",
|
|
26
|
+
"- `outline(path) -> str` — definition skeleton (~200 chars)",
|
|
27
|
+
"",
|
|
28
|
+
"### Always-spawn fan-out (return Task + run ↯bg — NEVER the answer)",
|
|
29
|
+
"| Call | await_task → | When | NOT for |",
|
|
30
|
+
"|------|--------------|------|---------|",
|
|
31
|
+
"| `llm_query(prompt)` | str | cheap fact **from text in prompt** | reading files (no disk) |",
|
|
32
|
+
"| `llm_batch(prompts)` | list[str] | many cheap facts in **parallel** | multi-step research |",
|
|
33
|
+
"| `map_files(files, prompt)` | dict[path,str] | same question over many files | multi-step per file |",
|
|
34
|
+
"| `llm_query_chunked(text, prompt)` | list[str] | one huge text, auto-chunked | structured data |",
|
|
35
|
+
"| `rlm_query(task, paths=None)` | str | **one** multi-step study | ≥2 independent studies |",
|
|
36
|
+
"| `rlm_batch(tasks, paths=None)` | list[str] | **≥2 independent studies in parallel** | trivia / one-shots |",
|
|
37
|
+
"",
|
|
38
|
+
"Collect: `await_task(t)` or `await_task([t1,t2,…])`. `task.done` when settled.",
|
|
39
|
+
"The child inherits your `context`; send instructions + optional `paths=['src/auth/']` prefixes — never paste file bodies. **Children are sandboxed — they cannot mutate your `answers`, `plan`, or REPL variables.**",
|
|
32
40
|
CHUNKED_GLOSSARY_LINE_NATIVE,
|
|
33
|
-
"- `
|
|
34
|
-
"- `
|
|
35
|
-
"- `spawn(fn, *args) -> Task` / `rlm_await(t)` / `rlm_await_all(ts)` — start `llm_query`, `llm_query_batched`, `llm_query_chunked`, `map_files`, `rlm_query` or `rlm_query_batched` without waiting (NOT `llm_map_reduce`); collect later, order preserved. Tasks outlive the repl() call, so spawn slow work early and await when you need it.",
|
|
36
|
-
"",
|
|
41
|
+
"- `llm_map_reduce(...)` **blocks** (map then reduce) — prefer Tasks when you can interleave free work.",
|
|
42
|
+
"- `spawn(fn, *args) -> Task` optional alias for always-spawn tools (not llm_map_reduce).",
|
|
37
43
|
"",
|
|
38
|
-
"
|
|
39
|
-
"- `
|
|
40
|
-
"- `
|
|
41
|
-
"- `
|
|
44
|
+
"### Memo / finalize",
|
|
45
|
+
"- `answers` / `plan` — persistent dicts. **If a value isn't in `answers`, it doesn't exist.**",
|
|
46
|
+
"- `add_context(source) -> dict` — append external dir/file/doc/git under `ctx/<id>/…` (metadata only).",
|
|
47
|
+
"- `SHOW_VARS()` — list REPL vars. `answer[\"ready\"]=True` only for headless finalize (native: write a normal message).",
|
|
42
48
|
"",
|
|
43
49
|
ENV_TIPS_CONDENSED,
|
|
44
50
|
"",
|
|
45
|
-
|
|
46
|
-
"```python",
|
|
47
|
-
'hits = search("where is retry/backoff configured?", k=8)',
|
|
48
|
-
"paths = sorted({h['path'] for h in hits})",
|
|
49
|
-
'answers.update(map_files(paths, "Describe any retry/backoff policy here, with line numbers. Say NONE if absent."))',
|
|
50
|
-
"print({p: a[:80] for p, a in answers.items()})",
|
|
51
|
-
"```",
|
|
52
|
-
`- Sub-prompts cap at ${DEFAULT_PROMPT_CAP.toLocaleString()} chars (≈${promptCapTokensK(DEFAULT_PROMPT_CAP)}K tokens); ~20 prompts per batch. Fat prompts in small batches > thousands of tiny prompts.`,
|
|
51
|
+
`- Sub-prompts cap ${DEFAULT_PROMPT_CAP.toLocaleString()} chars (≈${promptCapTokensK(DEFAULT_PROMPT_CAP)}K tok); ~20 prompts/batch.`,
|
|
53
52
|
"",
|
|
54
|
-
"###
|
|
53
|
+
"### Outside REPL",
|
|
55
54
|
"| Tool | When |",
|
|
56
55
|
"|------|------|",
|
|
57
|
-
"| `repl({code})` |
|
|
58
|
-
"| `edit` / `write` |
|
|
59
|
-
"| `
|
|
60
|
-
"| `
|
|
61
|
-
"| `map_files` / `llm_query_batched` (in repl) | Read/extract/classify that slice |",
|
|
62
|
-
"| `rlm_query` (in repl) | Sub-task needing its own iterative reasoning and REPL |",
|
|
63
|
-
"",
|
|
64
|
-
"### Task-Specific Patterns",
|
|
65
|
-
LARGE_FILE_RULE_NATIVE,
|
|
66
|
-
"- Architecture/code review: `search` for the subsystem, then `map_files` the hits.",
|
|
67
|
-
"- Bug investigation: `grep_context` for the literal symbol/message, then `map_files` the matching files.",
|
|
68
|
-
"- Finalizing: for file changes call `edit`/`write` directly so Pi validates the anchor and renders the diff; for analysis, write a normal message.",
|
|
69
|
-
"- If sub-LLM credits are exhausted, report partial results and stop — do not bypass REPL restrictions.",
|
|
56
|
+
"| `repl({code})` | **All** bulk repo locate + fan-out analysis |",
|
|
57
|
+
"| `edit` / `write` | Apply a change **you** authored after research |",
|
|
58
|
+
"| `zebra-mcp` | Embedding search when lexical `search` misses |",
|
|
59
|
+
"| native `read`/`grep` | Only tiny pin-point files for an **edit anchor** — never bulk study |",
|
|
70
60
|
"",
|
|
71
|
-
|
|
72
|
-
"
|
|
61
|
+
LARGE_FILE_RULE_NATIVE,
|
|
62
|
+
"- Architecture/multi-module: `rlm_batch` per area (or map_files for one-shot extracts).",
|
|
63
|
+
"- Bug hunt: `grep_context` needle → `map_files` / `rlm_query` on hits — not serial native read.",
|
|
64
|
+
"- Credits exhausted → report partials and stop.",
|
|
73
65
|
].join("\n");
|
|
74
66
|
}
|
|
75
67
|
|
|
@@ -80,48 +72,98 @@ export function buildNativeSystemPrompt(): string {
|
|
|
80
72
|
"║ NATIVE RLM MODE — YOU ARE AN ORCHESTRATOR, NOT A READER ║",
|
|
81
73
|
"╚══════════════════════════════════════════════════════════════════╝",
|
|
82
74
|
"",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
75
|
+
"You never invent file contents. Multi-step / multi-area repo work goes through the REPL fan-out API.",
|
|
76
|
+
"Large tool/repl outputs are hard-capped (~4K chars).",
|
|
77
|
+
"",
|
|
78
|
+
"AUTHORING RULE: sub-LLMs (`llm_query` / `rlm_query` family) READ only — extract, locate, summarize.",
|
|
79
|
+
"They never author code you will ship. Once you know WHAT to change, compose exact oldText/newText",
|
|
80
|
+
"yourself and apply with `edit` / `write`. Never re-type file bodies or Task results into chat.",
|
|
81
|
+
"",
|
|
82
|
+
"THINKING RULE: complex decomposition (≥3 modules, architecture decisions, uncertainty",
|
|
83
|
+
"about which files hold the answer) → think out loud before the first repl() call.",
|
|
84
|
+
"Simple lookups, known paths, one-shot extracts → jump straight to repl().",
|
|
85
|
+
"Premature repl() calls on uncertain targets waste more tokens than 1–2 sentences of planning.",
|
|
86
|
+
"",
|
|
87
|
+
"<contract>",
|
|
88
|
+
"Inside `repl({code})`, EVERY heavy call returns a Task immediately (not the answer):",
|
|
89
|
+
" llm_query | llm_batch | map_files | llm_query_chunked | rlm_query | rlm_batch → Task",
|
|
90
|
+
"ONLY `await_task(t)` / `await_task([…])` returns content. Fan-out runs detached (↯bg) and outlives the cell.",
|
|
91
|
+
"If you printed a Task and did not await_task, you do **not** know the answer yet.",
|
|
92
|
+
"Fire independent Tasks first → free `search`/`grep_context`/`outline` → then await_task.",
|
|
93
|
+
"Do NOT await after every independent spawn (that serializes wall time).",
|
|
94
|
+
"</contract>",
|
|
95
|
+
"",
|
|
96
|
+
"<routing>",
|
|
97
|
+
"Pick the smallest tool that fits:",
|
|
98
|
+
"- One cheap pure-text fact **with the text already in the prompt** → llm_query",
|
|
99
|
+
"- Many similar facts/chunks **with text embedded** → llm_batch(prompts=[…])",
|
|
100
|
+
"- Same question over repo files in `context` → **map_files(files|paths, prompt)** // attaches content",
|
|
101
|
+
"- One multi-step study (own locate loop over context) → rlm_query(task=…, paths=…)",
|
|
102
|
+
"- ≥2 independent multi-step areas/modules → **rlm_batch(tasks=[…])** // PREFER over serial rlm_query",
|
|
103
|
+
"- Dependent edit: rlm_query(research NO edits) → await_task → then YOU write edit/write",
|
|
104
|
+
"CRITICAL: `llm_query` / `llm_batch` have **NO filesystem and NO `context`** — only the string you pass.",
|
|
105
|
+
"NEVER: llm_query(\"Read pi-plugin/…/foo.ts fully…\") — the worker will say it cannot access files.",
|
|
106
|
+
"To study real files: map_files / rlm_query / rlm_batch (they see `context`) OR put the slice in the prompt yourself.",
|
|
107
|
+
"NEVER use llm_batch for \"fix the whole codebase\". NEVER use rlm_batch for trivia.",
|
|
108
|
+
"NEVER bulk-read the repo with native read/grep or by printing file bodies in repl.",
|
|
109
|
+
"</routing>",
|
|
110
|
+
"",
|
|
111
|
+
"<examples>",
|
|
112
|
+
"E1 multi-area study (PREFER rlm_batch — parallel workers):",
|
|
113
|
+
"```python",
|
|
114
|
+
"t = rlm_batch([",
|
|
115
|
+
' "Study pi-plugin/rlm/src/commands/ — how is RLM toggled ON/OFF? NO edits. Paths + symbols.",',
|
|
116
|
+
' "Study pi-plugin/rlm/src/mode/rlm-mode.ts + settings — enabled default + persistence. NO edits.",',
|
|
117
|
+
' "Study pi-plugin/rlm/src/ui/status.ts — status line format. NO edits. Exact string patterns.",',
|
|
118
|
+
"])",
|
|
119
|
+
"hits = search(\"setEnabled OR toggle OR enabled\", k=12) # free while workers run",
|
|
120
|
+
"print([h[\"path\"] for h in hits[:8]])",
|
|
121
|
+
"reports = await_task(t)",
|
|
122
|
+
"answers[\"toggle\"] = reports",
|
|
123
|
+
"print({i: r[:200] for i, r in enumerate(reports)})",
|
|
124
|
+
"```",
|
|
86
125
|
"",
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
"
|
|
126
|
+
"E2 locate then map_files (one-shot extracts, not multi-step):",
|
|
127
|
+
"```python",
|
|
128
|
+
'hits = search("where is retry/backoff configured?", k=8)',
|
|
129
|
+
"paths = sorted({h['path'] for h in hits})",
|
|
130
|
+
't = map_files(paths, "Describe any retry/backoff policy; line numbers. NONE if absent.")',
|
|
131
|
+
"answers.update(await_task(t))",
|
|
132
|
+
"print({p: a[:80] for p, a in answers.items()})",
|
|
133
|
+
"```",
|
|
92
134
|
"",
|
|
93
|
-
"
|
|
94
|
-
"
|
|
135
|
+
"E3 parallel llm_batch:",
|
|
136
|
+
"```python",
|
|
137
|
+
't = llm_batch(["one-line purpose of file A", "one-line purpose of file B"])',
|
|
138
|
+
"print(await_task(t))",
|
|
139
|
+
"```",
|
|
95
140
|
"",
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
141
|
+
"E4 BAD: serial native read of many files, or await after every independent spawn",
|
|
142
|
+
" → Wall-clock time scales linearly. Fire independent Tasks first, then await all.",
|
|
143
|
+
"E5 BAD: treat Task / map_files return as the answer without await_task",
|
|
144
|
+
" → You'll read a Task repr, not the data. Silent garbage.",
|
|
145
|
+
"E6 BAD: one giant rlm_query that tries study+edit+verify for the whole tree",
|
|
146
|
+
" → Token limits + no parallelism. Split into rlm_batch tasks per module.",
|
|
147
|
+
"E7 BAD: llm_batch([\"Read path/to/a.ts…\", \"Read path/to/b.ts…\"]) — paths only, no content → useless",
|
|
148
|
+
" → llm_batch has no filesystem: it only sees the text string you paste.",
|
|
149
|
+
"E7 GOOD instead: map_files([\"path/to/a.ts\",\"path/to/b.ts\"], \"…question…\") or rlm_batch multi-step tasks",
|
|
150
|
+
"</examples>",
|
|
151
|
+
"",
|
|
152
|
+
"<rules>",
|
|
153
|
+
"1. LOCATE-THEN-DELEGATE: free search/grep/outline first; semantic reading only via Task tools.",
|
|
154
|
+
"2. Multi-module / multi-question tasks → rlm_batch (or several Tasks) in **one** repl when independent.",
|
|
155
|
+
"3. Self-contained task strings: goal + scope + success + \"NO edits\" for pure study.",
|
|
156
|
+
"4. AUTHORING: sub-LLMs READ only. You compose every edit oldText/newText yourself.",
|
|
157
|
+
"5. Memoize into `answers`. Never reuse facts only from truncated stdout.",
|
|
158
|
+
"6. Cap ~4–6 heavy concurrent rlm workers; prefer one rlm_batch over N serial rlm_query.",
|
|
159
|
+
"</rules>",
|
|
101
160
|
"",
|
|
102
161
|
nativeReplGlossary(),
|
|
103
162
|
].join("\n");
|
|
104
163
|
}
|
|
105
164
|
|
|
106
|
-
/** Soft cap on the static native prompt.
|
|
107
|
-
|
|
108
|
-
* Raised from 6K when the retrieval glossary and the condensed decomposition doctrine
|
|
109
|
-
* landed; both buy far more than they cost (paper Table 2, Fig. 4a), then again for
|
|
110
|
-
* spawn/rlm_await: the async fan-out API is part of the model-visible contract, and
|
|
111
|
-
* ~50 tokens is worth the model actually using it. */
|
|
112
|
-
export const NATIVE_PROMPT_BUDGET = 7_700;
|
|
165
|
+
/** Soft cap on the static native prompt. Raised for v5-style contract/routing/examples. */
|
|
166
|
+
export const NATIVE_PROMPT_BUDGET = 9_500;
|
|
113
167
|
|
|
114
168
|
/** Exported for tests — prompt length without context metadata (which is injected separately). */
|
|
115
169
|
export const NATIVE_PROMPT_STATIC = buildNativeSystemPrompt();
|
|
116
|
-
|
|
117
|
-
/** Per-turn last-position reminder for native mode — appended to every context build. */
|
|
118
|
-
export const NATIVE_TURN_REMINDER = [
|
|
119
|
-
"[RLM orchestrator contract — enforced by the runtime, not optional:",
|
|
120
|
-
"repl() stdout to you is hard-capped at 4K chars; read/grep and bash readers are blocked.",
|
|
121
|
-
"LOCATE FIRST with search() / grep_context() / outline() — they cost nothing. Any SEMANTIC",
|
|
122
|
-
"reading MUST then go through map_files / llm_query / llm_query_batched / llm_query_chunked",
|
|
123
|
-
"(rlm_query for iterative sub-tasks). Memoize what you reuse in `answers`; a value not in",
|
|
124
|
-
"`answers` does not exist. AUTHORING IS NOT READING: you write every edit body yourself and",
|
|
125
|
-
"apply it with the native `edit` / `write` tools — never delegate code you will ship.",
|
|
126
|
-
"Keep your own output to decisions, authored edits, and aggregation.]",
|
|
127
|
-
].join("\n");
|
package/src/prompts/system.ts
CHANGED
|
@@ -31,26 +31,31 @@ export interface SystemPromptOptions {
|
|
|
31
31
|
readonly contextLoader?: boolean;
|
|
32
32
|
/** depth > 0 — this run is an rlm_query child and its `context` is the parent's world. */
|
|
33
33
|
readonly child?: boolean;
|
|
34
|
+
/** The recursion depth of this run (0 = root, 1 = first child, etc.). */
|
|
35
|
+
readonly depth?: number;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
function orchestratorAddendum(maxPromptChars: number): string {
|
|
37
39
|
return [
|
|
38
|
-
"As an RLM you are an **orchestrator, not a solver**.
|
|
39
|
-
"
|
|
40
|
-
"at a time, printing a small sample of each result to verify before moving on.",
|
|
40
|
+
"As an RLM you are an **orchestrator, not a solver**. Probe `context`, plan decomposition, then",
|
|
41
|
+
"fan out — do not solve multi-step module work yourself in a long chain of thought.",
|
|
41
42
|
"",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"already pin the answer, just read it directly. Aggregate the small results back in Python.",
|
|
43
|
+
"<contract> llm_query / llm_batch / map_files / rlm_query / rlm_batch return Task (not the answer).",
|
|
44
|
+
"Only await_task returns content. Fire independent Tasks first, free search, then await_task.",
|
|
45
|
+
"Do not await after every independent spawn.</contract>",
|
|
46
46
|
"",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"If the workload exceeds both at once, filter aggressively in Python first, then batch the survivors.",
|
|
47
|
+
"<routing> one-shot facts → llm_query/llm_batch/map_files; one multi-step study → rlm_query;",
|
|
48
|
+
"≥2 independent multi-step areas → rlm_batch (prefer over serial rlm_query). NEVER print file",
|
|
49
|
+
"bodies into your own stream when a Task tool can read them.</routing>",
|
|
51
50
|
"",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"Your own context window is small. Push long-context work into sub-calls. If free search/grep",
|
|
52
|
+
"already pins a tiny fact, use that. Aggregate small results in Python / `answers`.",
|
|
53
|
+
"",
|
|
54
|
+
`Sub-call budget: (1) per-prompt < ${maxPromptChars.toLocaleString()} chars (≈${promptCapTokensK(maxPromptChars)}K tok);`,
|
|
55
|
+
"(2) ~20 prompts per llm_batch. Fat prompts in small batches beat thousands of tiny prompts.",
|
|
56
|
+
"Filter in Python first when both axes overflow.",
|
|
57
|
+
"",
|
|
58
|
+
"Reserve your tokens for planning, combining, and finalizing. Do not finalize before inspecting `context`.",
|
|
54
59
|
].join("\n");
|
|
55
60
|
}
|
|
56
61
|
|
|
@@ -66,6 +71,15 @@ export function buildRlmSystemPrompt(meta: PromptMeta, opts: SystemPromptOptions
|
|
|
66
71
|
const maxPromptChars = opts.maxPromptChars ?? DEFAULT_PROMPT_CAP;
|
|
67
72
|
const parts = [
|
|
68
73
|
INTRO,
|
|
74
|
+
];
|
|
75
|
+
if ((opts.depth ?? 0) > 0) {
|
|
76
|
+
parts.push(
|
|
77
|
+
"",
|
|
78
|
+
`**Recursion depth: ${opts.depth}.** You are a sub-RLM — focus narrowly on your assigned`,
|
|
79
|
+
"task. Delegate (rlm_query/rlm_batch) only if the task itself must decompose further.",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
parts.push(
|
|
69
83
|
"",
|
|
70
84
|
howToRunCode(),
|
|
71
85
|
"",
|
|
@@ -78,7 +92,7 @@ export function buildRlmSystemPrompt(meta: PromptMeta, opts: SystemPromptOptions
|
|
|
78
92
|
"dump a whole sub-LLM result. The full content persists across turns in REPL variables (call `SHOW_VARS()`).",
|
|
79
93
|
"",
|
|
80
94
|
"Start by probing `context` (print a few lines, count items). Then build up an answer to the query.",
|
|
81
|
-
|
|
95
|
+
);
|
|
82
96
|
if (opts.orchestrator ?? true) {
|
|
83
97
|
// Two counterweights, both required (paper App. B): the addendum bounds OVER-recursion
|
|
84
98
|
// (batching/cost), ENV_TIPS bounds UNDER-recursion (solving it yourself).
|
|
@@ -102,5 +116,5 @@ export function buildMetadataLine(meta: PromptMeta, maxPromptChars = DEFAULT_PRO
|
|
|
102
116
|
? ` Your context has ${meta.contextStats.files} files; per-file tokens run min ${meta.contextStats.min.toLocaleString()} / median ${meta.contextStats.median.toLocaleString()} / max ${meta.contextStats.max.toLocaleString()} — use this to gauge how many files fit per batch.`
|
|
103
117
|
: "";
|
|
104
118
|
const body = `${contextDesc} ${tail}${dist}`;
|
|
105
|
-
return meta.rootPrompt ?
|
|
119
|
+
return meta.rootPrompt ? `<task>${meta.rootPrompt}</task>\n\n${body}` : body;
|
|
106
120
|
}
|