From 46ead72b1f81fabefa09e72b5deeef4c23b89dbd Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Fri, 5 Jun 2026 00:19:32 +0000 Subject: [PATCH 1/4] feat(prompt): add ipython usage + project-venv notes Spell out two things the ipython tool's environment didn't make obvious: - ipython treats `!cmd` per-line; multi-line shell needs `%%bash`. - the rlm tool's uv env is NOT the project venv, so `import ` requires sys.path tweak / running under the project interpreter / editable install. Both have cost agents wasted turns on swebench-pro. --- src/rlm/prompt.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index c9f627a..eaa2e7f 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -28,6 +28,17 @@ ) SHELL_TOOL_NAMES = frozenset({"bash", "ipython"}) + +IPYTHON_USAGE_PROMPT = ( + "The `ipython` tool runs each call as one IPython cell. `!cmd` shells out a " + "single line — for multi-line shell, use the `%%bash` cell magic on its own " + "first line and put the script underneath. Your shell runs inside this tool's " + "uv-managed environment, which is NOT the project's venv. Importing the " + "project under test directly (e.g. `import `) won't work unless you " + "prepend the source path to `sys.path`, run the code under the project's " + "interpreter (e.g. `!/usr/local/bin/python3 ...` or `%%bash` calling it), or " + "install the project into this environment with `uv pip install -e `." +) GIT_HISTORY_GUARD_PROMPT = ( "Do not cheat by using online solutions or hints specific to this task, or " "by copying or inferring solutions from other branches, tags, remotes, " @@ -92,6 +103,9 @@ def build_system_prompt( ] ) + if any(tool.name == "ipython" for tool in active_tools): + parts.extend(["", IPYTHON_USAGE_PROMPT]) + if _should_include_git_history_guard(active_tools): parts.extend(["", GIT_HISTORY_GUARD_PROMPT]) From e2222dda177c073990e8873d2f7864f8cf8fcf23 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Fri, 5 Jun 2026 03:40:00 +0000 Subject: [PATCH 2/4] feat(prompt): scratchpad framing + ipython/shell antipatterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframe the ipython usage block: - Lead with 'ipython is your scratchpad' (Python state, notes, context). - Make 'cells default to Python' explicit, since the model's prior keeps pushing bare `find ...`/`grep ...` into cells and getting SyntaxError. - List concrete BAD/GOOD antipatterns instead of prose so the rule is easy to pattern-match. - Move the pre-installed package list into this block, drop the 'uv pip install ' line — that line was misleading on the kernel's uv-tool environment (no .venv -> 'no virtual environment found'). - Drop the 'project venv' commentary; let env-specific append handle it. Also nudge 'breaking down into sub-tasks' in the opening sentence. Validated on rlm-swe SWE-bench Verified n=95 glm-5.1: 64.2 % solve (+6.0 pp vs prior prompt at the same model + dataset). --- src/rlm/prompt.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index eaa2e7f..f53d232 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -29,16 +29,26 @@ SHELL_TOOL_NAMES = frozenset({"bash", "ipython"}) -IPYTHON_USAGE_PROMPT = ( - "The `ipython` tool runs each call as one IPython cell. `!cmd` shells out a " - "single line — for multi-line shell, use the `%%bash` cell magic on its own " - "first line and put the script underneath. Your shell runs inside this tool's " - "uv-managed environment, which is NOT the project's venv. Importing the " - "project under test directly (e.g. `import `) won't work unless you " - "prepend the source path to `sys.path`, run the code under the project's " - "interpreter (e.g. `!/usr/local/bin/python3 ...` or `%%bash` calling it), or " - "install the project into this environment with `uv pip install -e `." -) + +def _ipython_usage_prompt() -> str: + return ( + "- Treat `ipython` as your scratchpad: hold Python state, jot intermediate " + "results, keep notes between turns, manage context. Each call is one IPython " + "cell.\n" + "- Cells default to Python. To run a shell command, prefix with `!` " + "(single line) or `%%bash` (multi-line, must be the cell's first line). " + "Prefer `%%bash`; use `!` only for one-liners.\n" + "- Antipatterns (each raises `SyntaxError` — the cell is parsed as Python):\n" + " BAD: find /app -name '*.py' GOOD: !find /app -name '*.py'\n" + " BAD: grep -rn 'foo' /app GOOD: !grep -rn 'foo' /app\n" + " BAD: cat > /tmp/x.py <` (this is a uv-managed venv with no pip module).", ] skill_lines: list[str] = [] @@ -104,7 +112,7 @@ def build_system_prompt( ) if any(tool.name == "ipython" for tool in active_tools): - parts.extend(["", IPYTHON_USAGE_PROMPT]) + parts.extend(["", _ipython_usage_prompt()]) if _should_include_git_history_guard(active_tools): parts.extend(["", GIT_HISTORY_GUARD_PROMPT]) From 61a5f40c3c46ca97b39442d6161b5252e702601b Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Fri, 5 Jun 2026 03:44:56 +0000 Subject: [PATCH 3/4] feat(prompt): drop 'Conversation log' line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversation log path is internal plumbing — the agent doesn't need to know it, and a system-prompt line per rollout is just per-request token overhead. messages_path is still threaded through the function signature for callers that may use it programmatically. --- src/rlm/prompt.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index f53d232..09d2f04 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -80,7 +80,6 @@ def build_system_prompt( "When you are done, stop calling tools and state your final answer.", "", f"Working directory: {cwd}", - f"Conversation log: {messages_path}", ] skill_lines: list[str] = [] From 3ceaf6533fddda2cdae710ddfa0e18bd0515f332 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Fri, 5 Jun 2026 04:36:41 +0000 Subject: [PATCH 4/4] feat(prompt): rewrite ipython block as assertive prose Drop the bulleted list + BAD/GOOD antipattern table in favor of a single assertive paragraph: frame ipython as the primary working surface / persistent scratchpad, state that cells are Python by default, and fold the shell-escape rule (! / %%bash, bare commands fail) into prose. Keeps the pre-installed-imports note. --- src/rlm/prompt.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 09d2f04..cfb5fd6 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -32,20 +32,17 @@ def _ipython_usage_prompt() -> str: return ( - "- Treat `ipython` as your scratchpad: hold Python state, jot intermediate " - "results, keep notes between turns, manage context. Each call is one IPython " - "cell.\n" - "- Cells default to Python. To run a shell command, prefix with `!` " - "(single line) or `%%bash` (multi-line, must be the cell's first line). " - "Prefer `%%bash`; use `!` only for one-liners.\n" - "- Antipatterns (each raises `SyntaxError` — the cell is parsed as Python):\n" - " BAD: find /app -name '*.py' GOOD: !find /app -name '*.py'\n" - " BAD: grep -rn 'foo' /app GOOD: !grep -rn 'foo' /app\n" - " BAD: cat > /tmp/x.py <