Skip to content

Commit 4fdeeeb

Browse files
committed
[argus] lead_agent/prompt: add <file_editing> guidance block
The stock system prompt has no edit-vs-rewrite guidance, so models default to bash heredocs for every file write — including fixes to files they just wrote in the same conversation. That roughly doubles wall-time on iterative coding tasks: instead of a 5-line str_replace, the model emits the entire file again, which has to round-trip through the LLM and gets persisted in conversation history forever. The <file_editing> block sits inside <working_directory> (so it's adjacent to the workspace guidance) and tells the model: - Prefer targeted edits (str_replace) over rewrites (write_file) over bash heredocs (cat <<EOF). - read_file before editing files you wrote earlier in the same conversation — context drift makes "expected old content" stale. - For deliverables in /mnt/user-data/outputs, write once with write_file then str_replace to fix in place; never rewrite. Adds a test asserting block presence, contents, and placement inside <working_directory>. PR-candidate: maybe Upstream-issue: none Reason: Opinionated phrasing — upstream may want a more general sentence rather than the three-pillar block. Worth proposing in a follow-up issue once we have data on whether it actually speeds up coding tasks in practice.
1 parent a18dc1b commit 4fdeeeb

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

backend/packages/harness/deerflow/agents/lead_agent/prompt.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,18 @@ def _build_subagent_section(max_concurrent: int) -> str:
422422
- Avoid hardcoding `/mnt/user-data/...` inside generated scripts when a relative path from the workspace is enough
423423
- Final deliverables must be copied to `/mnt/user-data/outputs` and presented using `present_file` tool
424424
{acp_section}
425+
<file_editing>
426+
**Prefer targeted edits over full rewrites.** When modifying a file you wrote earlier in this conversation, the cost of rewriting is real: every byte of the rewritten file has to be re-emitted as model output and lives in conversation history forever. A 5-line fix in a 500-line file should be 5 lines, not 500.
427+
428+
**Tool ordering when changing files:**
429+
1. `str_replace` — for targeted modifications. Use this whenever you can describe the change as "replace X with Y" in an existing file. Faster than rewriting because only the diff goes through the LLM.
430+
2. `write_file` — for *new* files, or when the change covers more than ~80% of the file's contents.
431+
3. `bash` heredocs (`cat <<EOF > file`) — avoid for any file you might edit later. The full content gets baked into conversation history. Reserve heredocs for one-off scripts the agent itself will execute and discard.
432+
433+
**Before editing a file you wrote earlier in the same conversation, call `read_file` first.** Don't rely on memory of what you wrote — context can drift. `read_file` is cheap; `str_replace` failing because of a stale "expected old content" is expensive.
434+
435+
**For deliverables in `/mnt/user-data/outputs`:** write the file once with `write_file`. If you find a bug after writing, use `str_replace` to fix in place. Do not re-run a HEREDOC `cat > ...` to rewrite the whole file.
436+
</file_editing>
425437
</working_directory>
426438
427439
<response_style>

backend/tests/test_lead_agent_prompt.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,38 @@ def test_apply_prompt_template_includes_relative_path_guidance(monkeypatch):
6868
assert "`hello.txt`, `../uploads/data.csv`, and `../outputs/report.md`" in prompt
6969

7070

71+
def test_apply_prompt_template_includes_file_editing_block(monkeypatch):
72+
"""The <file_editing> block must be present in the rendered system prompt
73+
so the model gets edit-vs-rewrite guidance. Without it, models default
74+
to bash heredocs for every file write — including fixes to files they
75+
just wrote — which roughly doubles wall-time on iterative coding tasks.
76+
"""
77+
config = SimpleNamespace(
78+
sandbox=SimpleNamespace(mounts=[]),
79+
skills=SimpleNamespace(container_path="/mnt/skills"),
80+
)
81+
monkeypatch.setattr("deerflow.config.get_app_config", lambda: config)
82+
monkeypatch.setattr(prompt_module, "_get_enabled_skills", lambda: [])
83+
monkeypatch.setattr(prompt_module, "get_deferred_tools_prompt_section", lambda: "")
84+
monkeypatch.setattr(prompt_module, "_build_acp_section", lambda: "")
85+
monkeypatch.setattr(prompt_module, "_get_memory_context", lambda agent_name=None: "")
86+
monkeypatch.setattr(prompt_module, "get_agent_soul", lambda agent_name=None: "")
87+
88+
prompt = prompt_module.apply_prompt_template()
89+
90+
# Block delimiters and the three pillar guidances must all be present
91+
assert "<file_editing>" in prompt
92+
assert "</file_editing>" in prompt
93+
assert "Prefer targeted edits over full rewrites" in prompt
94+
assert "`str_replace`" in prompt
95+
assert "Before editing a file you wrote earlier in the same conversation" in prompt
96+
# The block must live inside <working_directory>...</working_directory>
97+
wd_open = prompt.index("<working_directory")
98+
wd_close = prompt.index("</working_directory>")
99+
fe_open = prompt.index("<file_editing>")
100+
assert wd_open < fe_open < wd_close
101+
102+
71103
def test_refresh_skills_system_prompt_cache_async_reloads_immediately(monkeypatch, tmp_path):
72104
def make_skill(name: str) -> Skill:
73105
skill_dir = tmp_path / name

0 commit comments

Comments
 (0)