Skip to content

feat: role-aware sub-agent prompting (built-in contract + per-tier appends) - #151

Merged
hallerite merged 3 commits into
mainfrom
feat/subagent-append-prompt
Sep 1, 2026
Merged

feat: role-aware sub-agent prompting (built-in contract + per-tier appends)#151
hallerite merged 3 commits into
mainfrom
feat/subagent-append-prompt

Conversation

@hallerite

@hallerite hallerite commented Aug 27, 2026

Copy link
Copy Markdown
Member

Recursive sessions currently prompt every engine identically: a depth-3 leaf gets the same system prompt as the root, and a single append_to_system_prompt applies at every depth. This PR makes prompting role-aware, in two layers.

What this adds

1. A built-in sub-agent framing, gated on depth (zero config). build_system_prompt now takes depth; any depth>0 engine gets its sub-agent identity woven into the prompt's two opening lines instead of the root's plain ones:

You are a coding agent, spawned as a sub-agent: your caller delegated a single task to you and sees none of your work. Do exactly that task; don't widen the scope. [+ tool sentences]

When the task is done, stop calling tools and state your final answer. It is the only thing your caller receives, so make it a complete, self-contained result — the answer plus the evidence needed to trust it (sources, file paths, values).

2. Per-tier append fields (config). The ACP runtime-v1 contract and RuntimeConfig gain subagent_append_to_system_prompt and leaf_append_to_system_prompt alongside the existing append_to_system_prompt. RuntimeConfig.resolved_append_to_system_prompt picks by role, with fallback leaf → subagent → root, so unset fields preserve today's single-append behavior. Children inherit config via model_copy, so every descendant resolves its own tier.

The resulting tiers:

tier condition sub-agent framing rlm hint append field
root depth == 0 ✓ (if max_depth > 0) append_to_system_prompt
node 1 ≤ depth < max_depth subagent_append_to_system_prompt
leaf depth == max_depth leaf_append_to_system_prompt

Rationale

The design splits sub-agent prompting into three concerns, each handled where it belongs:

  • Contract — built-in, shared by node and leaf. "Your caller sees only your final message; make it self-contained; stay on the delegated task" is true identically for every sub-agent, so it ships as the default rather than something every host must remember to configure. It lives in the role and final-answer lines (not a separate mid-prompt block) so there's exactly one "You are …" statement and the framing is the first thing the model reads — weaker models overweight the prompt opening.
  • Capability — built-in, differs by tier. Nodes get the rlm recursion hint; leaves get no mention of rlm at all. Deliberately no "you cannot spawn sub-agents" line: advertising a missing capability is what caused depth-thrash before this PR — leaves calling rlm(), hitting [depth limit … reached], and burning turns retrying.
  • Strategy — config, per tier. How aggressively a node should decompose vs. a leaf should execute-and-report is model- and taskset-dependent, so it stays out of the harness and goes in the appends. That's also the fix for the motivating bug: a delegation-oriented root append used to be injected verbatim into leaves that couldn't act on it. In a search-taskset eval, tiered appends took depth-thrash from 13–27 occurrences to 0 while keeping heavy, load-bearing delegation.

Before/after on SWE-bench Pro

32 stratified tasks (3 per repo across all 11 repos; identical task list in every arm), k=1, three models via Prime Inference, stock harness settings except max_depth=1 and a 1h rollout wall clock. before = main (7eaff71), after = #151 + #166 stacked (c886994). Zero harness errors across ~190 rollouts.

model arm n mean reward solved turns/rollout tokens/rollout
z-ai/glm-5.3 before 32 0.562 18/32 82.8 143k
z-ai/glm-5.3 after 32 0.656 21/32 78.2 141k
deepseek-v4-flash before 32 0.438 14/32 74.3 123k
deepseek-v4-flash after 32 0.438 14/32 72.5 111k
kimi-k3 before 31 0.581 18/31 59.3 165k
kimi-k3 after 32 0.594 19/32 57.5 157k

Paired on shared tasks: glm-5.3 3W/0L/29T (sign p=0.25), deepseek 2W/2L/28T, kimi-k3 2W/1L/28T. The after arm is never worse on reward and slightly cheaper on every model (fewer turns and tokens). Individual deltas are within noise at n=32/k=1; the uniform direction across three models is the signal.

One honest caveat for THIS PR: on SWE tasks no model delegates unprompted — exactly one rlm() call occurred across all ~190 rollouts — so the built-in sub-agent contract almost never rendered and this eval is primarily a no-regression check for it. The contract's behavioral evidence is the append-driven redsearcher experiment above (depth-thrash 13–27 → 0).


Note

Medium Risk
Changes affect every agent’s system prompt and runtime contract fields; misconfigured tier appends could alter delegation behavior, though fallbacks preserve prior single-append semantics when new fields are unset.

Overview
This PR makes system prompts depend on where an engine sits in the recursion tree, instead of treating every depth the same.

Built-in sub-agent framing (no config): build_system_prompt now takes depth and session_dir. Engines with depth > 0 get a sub-agent role and a stricter “final answer” line (caller-only, self-contained result with evidence). The conversation log path uses the real session directory when available. Kernel guidance is consolidated into KERNEL_PACKAGES_PROMPT (!uv pip install in cells); skill help text is slightly tightened.

Per-tier config appends: The ACP runtime-v1 contract and RuntimeConfig add subagent_append_to_system_prompt and leaf_append_to_system_prompt. resolved_append_to_system_prompt selects root vs node vs leaf append with fallback leaf → subagent → root, so unset tiers keep today’s single-append_to_system_prompt behavior. RLMEngine applies the resolved append when loading the system prompt.

Kernel bootstrap: IPython startup pre-imports asyncio so parallel rlm examples in the prompt work without extra setup.

README documents the new append fields and tier semantics.

Reviewed by Cursor Bugbot for commit abc4d2d. Bugbot is set up for automated code reviews on this repo. Configure here.

@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from 38966d2 to 203b511 Compare August 27, 2026 10:06
@hallerite hallerite changed the title feat: separate sub-agent system-prompt append (RLM_SUBAGENT_APPEND_TO_SYSTEM_PROMPT) feat: role-specific system-prompt appends (root / node / leaf) Aug 27, 2026
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from 203b511 to c37465e Compare August 27, 2026 10:22
@hallerite
hallerite marked this pull request as ready for review August 27, 2026 10:50
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from c37465e to 90658c1 Compare August 27, 2026 10:51
@hallerite hallerite changed the title feat: role-specific system-prompt appends (root / node / leaf) feat: role-aware sub-agent prompting (built-in defaults + override knobs) Aug 27, 2026
Comment thread src/rlm/engine.py
Comment thread src/rlm/engine.py
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from 8d89117 to 50aaef0 Compare August 27, 2026 15:58
Comment thread src/rlm/supervisor.py Outdated
Comment thread src/rlm/engine.py Outdated
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from c08032f to 9059b73 Compare August 28, 2026 12:59

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9059b73. Configure here.

Comment thread src/rlm/acp.py
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch 2 times, most recently from 7d97749 to ec8b5dd Compare August 28, 2026 14:08
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch from ec8b5dd to 09e24c9 Compare August 30, 2026 13:51
@hallerite
hallerite changed the base branch from main to feat/execution-guardrails August 30, 2026 13:52
@hallerite
hallerite force-pushed the feat/execution-guardrails branch 5 times, most recently from f693220 to 546b915 Compare September 1, 2026 17:46
Base automatically changed from feat/execution-guardrails to main September 1, 2026 18:36
@hallerite
hallerite force-pushed the feat/subagent-append-prompt branch 2 times, most recently from 00abb75 to b23e26a Compare September 1, 2026 20:31
@hallerite hallerite changed the title feat: role-aware sub-agent prompting (built-in defaults + override knobs) feat: role-aware sub-agent prompting (built-in contract + per-tier appends) Sep 1, 2026
RuntimeConfig gains optional subagent_append_to_system_prompt (nodes that can still
recurse) and leaf_append_to_system_prompt (depth == max_depth), resolved by depth in
resolved_append_to_system_prompt with each tier falling back to the next-more-general
one (leaf -> subagent -> root). Wired through the ACP runtime contract; children inherit
via model_copy and resolve their own tier.
…pth-gated)

Any depth>0 engine opens with its sub-agent identity in the role line
(single delegated task, caller sees none of the work) and carries the
final-message contract in the final-answer line (self-contained result
plus evidence), instead of a separate mid-prompt block that gave the
prompt two competing "You are" statements.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@mikasenghaas mikasenghaas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@hallerite
hallerite merged commit 240090d into main Sep 1, 2026
9 checks passed
@hallerite
hallerite deleted the feat/subagent-append-prompt branch September 1, 2026 23:00
samsja added a commit that referenced this pull request Sep 3, 2026
…ation off)

Deploy tip (not for merge), rebuilt on current main (post #147/#151/#158/#160/#168 —
main takes precedence everywhere):
- default system prompt = the validated glm_minimal profile (system_prompt_path and
  append_to_system_prompt from the ACP contract still take precedence / apply on top;
  the composed builder remains available as _load_system_prompt_built)
- tool-output truncation off unless policy.max_tool_output_bytes is set
- prompts/glm_minimal.txt + README (validation numbers)

Pin this commit for the GLM-5.3 syngen configuration; everything else is stock main
(skills tooling default, compaction + guardrails per #158/#168).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants