The Art of the Subtle Prompt: How a Single Rule Fix Silenced an Autonomous Agent's Duplicate Responses
The Message
`` [assistant] Add rule for tool-call steps no narrative. [apply_patch] {"patchText":"*** Begin Patch\n*** Update File: /tmp/czk/cmd/vast-manager/agent/vast_agent.py\n@@\n CRITICAL RULES (highest priority):\n - If workers_dead=true: this is an EMERGENCY. Do NOT stop instances. Scale UP with launch_priority=\"emergency\".\n - NEVER stop instances when demand_queued=true.... Success. Updated the following files: M cmd/vast-manager/agent/vast_agent.py ``
At first glance, this message appears almost trivial — a single-line patch to a Python file, adding a rule to a system prompt. But within the broader context of building a fully autonomous LLM-driven fleet management agent for GPU proving infrastructure, this tiny edit represents a critical insight into the behavioral dynamics of large language models when they are given agency over real-world systems. The message is the culmination of a deep investigation into a subtle and frustrating bug: the agent was producing duplicate responses, polluting its conversation history, wasting context window tokens, and confusing both the operator and the context management system.
The Problem: An Agent That Talks Too Much
To understand why this message was written, we must step back and examine the system it was designed to fix. The agent in question is an autonomous LLM-powered fleet manager that runs on a 5-minute systemd timer (and is also triggered by state-change events via a systemd.path unit). It monitors a cluster of GPU instances running CuZK proofs for Filecoin, making decisions about scaling up, scaling down, launching new instances, and stopping unproductive ones. The agent's "brain" is a Qwen3.5-122b language model, and its "body" is a Python script that orchestrates tool calls to a Go-based management API.
The user had reported a persistent and confusing problem: duplicate responses. Each agent run appeared to produce two assistant messages in the conversation history — an intermediate narrative response (something like "I am analyzing the situation and will take action") followed by the actual final response with tool calls. This duplication had several harmful effects:
- Context window bloat: Every duplicate message consumed precious tokens in the LLM's context window, reducing the amount of useful history the agent could reference.
- Confused operator: The UI displayed two responses per run, making it hard to understand what the agent actually decided.
- Compaction interference: The context management system, which prunes old messages to stay within a 30k token budget, could not distinguish between genuine responses and duplicates, potentially pruning useful history instead.
- Potential for contradictory signals: If the intermediate narrative suggested one course of action and the final response took another, the LLM might become confused when reviewing its own history.
The Investigation: Tracing the Root Cause
The assistant's investigation into this issue is visible across several preceding messages ([msg 4960] through [msg 4967]). The reasoning process reveals a systematic approach to debugging an LLM agent's behavior — a fundamentally different challenge from debugging traditional software.
The assistant first examined systemd logs to check for overlapping runs ([msg 4961]). The concern was that the path unit (triggered by state-change events) and the timer (running every 5 minutes) might be launching simultaneous agent instances, each producing a response. However, a file lock had already been implemented to prevent parallel execution, so this was ruled out.
Next, the assistant inspected the conversation history for duplicate observation prefixes ([msg 4963]), checking whether the same state-change event was being processed multiple times across different runs. The logs showed runs with overlapping timestamps and repeated "pending notifications" — the same instance state transitions were being fed to the agent multiple times.
The breakthrough came in [msg 4964], where the assistant's reasoning reveals the core insight:
"I need to focus on providing explanations only in my final response after the tools are finished."
This is the key realization: the LLM agent was producing narrative text during its tool-calling process. When an LLM is given a set of tools and asked to accomplish a task, it often follows a pattern: first it "thinks aloud" by producing an assistant message with reasoning, then it makes tool calls. But in this agent architecture, the system prompt was not explicitly forbidding this intermediate narration. The LLM would produce something like:
"I see there are 5 pending notifications about instance state changes. Let me investigate further by checking the conversation history."
...followed by a read tool call. Then, after receiving the tool results, it would produce another narrative:
"Based on my analysis of the conversation, I see that instances are transitioning from loading to destroyed state. I should scale up to compensate."
...followed by a launch_instance tool call. Finally, after all tool calls completed, it would produce a final summary:
"I have launched 2 new instances to compensate for the lost capacity. The fleet now has sufficient capacity to handle the pending demand."
Each of these intermediate narratives was being stored as a separate assistant message in the conversation history, creating the duplicate responses the user observed.
The Fix: A Single Rule in the System Prompt
The fix, implemented in this message, is deceptively simple. The assistant adds a rule to the "CRITICAL RULES" section of the system prompt — the highest-priority instructions that appear at the end of the prompt (leveraging recency bias for maximum compliance). The exact text of the rule can be inferred from the patch context: it tells the agent to produce no narrative text during tool-calling steps, reserving all explanatory text for the final response after all tools have completed.
The patch targets the file /tmp/czk/cmd/vast-manager/agent/vast_agent.py, specifically the section around line 396-398 where the CRITICAL RULES block lives. The patch context shows the existing rules:
CRITICAL RULES (highest priority):
- If workers_dead=true: this is an EMERGENCY. Do NOT stop instances. Scale UP with launch_priority="emergency".
- NEVER stop instances when demand_queued=true....
The new rule is appended to this block. While the exact text is truncated in the message display, the assistant's stated intent — "Add rule for tool-call steps no narrative" — combined with the todo item from [msg 4964] — "Prompt agent to avoid narrative text on tool-calling steps" — makes the content unambiguous.
Why This Works: The Psychology of LLM Prompts
This fix is elegant because it works with the LLM's natural behavior rather than against it. Large language models are fundamentally text-completion engines. When given a prompt that describes a task, they tend to narrate their reasoning process because that's what the training data does — humans naturally think aloud when solving problems. The system prompt, however, is the lever through which we can shape this behavior.
By adding a specific, imperative rule — "no narrative text during tool-calling steps" — the assistant is effectively telling the model: "Your reasoning should happen silently. Only speak when you have a complete answer." This is a form of behavioral shaping that leverages the LLM's strong instruction-following capability, especially when the rule is placed in the "highest priority" section at the end of the prompt (a technique known as recency bias, where models pay more attention to instructions near the end of the prompt).
Assumptions and Potential Pitfalls
The fix makes several assumptions that deserve examination:
- The LLM will comply with the rule. This is the central assumption. While Qwen3.5-122b is a capable model, prompt adherence is never guaranteed. The rule is competing with the model's inherent tendency to narrate its reasoning. If the model ignores or forgets the rule, duplicates will persist.
- The rule doesn't degrade reasoning quality. There's a risk that suppressing intermediate narration could reduce the quality of the agent's decisions. Many LLM architectures (like ReAct and Chain-of-Thought) explicitly encourage step-by-step reasoning. The assumption here is that the reasoning still happens internally — it's just not emitted as separate messages.
- The final response will contain sufficient explanation. If the agent compresses all its reasoning into a single final message, that message might become overly long or omit important context that the operator needs to understand its decisions.
- The rule is unambiguous. "No narrative text during tool-calling steps" could be interpreted differently by different models. Does it mean no text at all before tools? Does it allow brief summaries? The rule may need refinement based on observed behavior.
Input Knowledge Required
To understand this message, one needs:
- Knowledge of the agent architecture: The agent is a Python script that calls an LLM API, receives tool call responses, and iterates through a reasoning loop. Each iteration can produce assistant messages and tool calls.
- Understanding of the conversation context system: The agent maintains a rolling conversation log in SQLite, with a 30k token window. Messages are filtered and compacted before being sent to the LLM.
- Awareness of the duplicate response bug: The user reported seeing two assistant responses per run, which confused the UI and wasted context.
- Knowledge of prompt engineering techniques: Specifically, the use of recency bias (placing critical rules at the end of the prompt) and imperative behavioral directives.
- Context about the CRITICAL RULES block: This is a section of the system prompt that contains the highest-priority behavioral constraints, previously used for safety rules like "never stop instances when demand_queued=true."
Output Knowledge Created
This message produces:
- A modified system prompt in
vast_agent.pythat includes a new behavioral rule for the agent. - A documented fix for the duplicate response problem, visible in the conversation history for future debugging.
- A precedent for using prompt-level interventions to shape agent behavior, rather than adding complex post-processing logic to filter duplicates.
- A testable hypothesis: that the agent will produce fewer duplicate responses after this change, which can be verified by monitoring subsequent runs.
The Broader Significance
This message is a microcosm of a larger theme in the development of autonomous LLM agents: the tension between the model's natural conversational behavior and the requirements of reliable, deterministic system operation. LLMs are trained to be helpful, verbose, and transparent in their reasoning. But when they operate as autonomous agents controlling real infrastructure, verbosity becomes a liability — it wastes context, confuses operators, and creates ambiguity.
The solution is not to fight the model's nature but to channel it through careful prompt design. A single rule, placed strategically in the system prompt, can eliminate an entire class of bugs without adding a single line of complex filtering logic. This is the art of prompt engineering at its most effective: understanding the model's behavioral tendencies and shaping them with minimal, precise interventions.
The message also demonstrates the iterative nature of building reliable AI systems. The assistant didn't start with this fix — it investigated overlapping runs, checked for duplicate observations, analyzed conversation history, and only after understanding the full picture did it identify the root cause and apply the targeted fix. This systematic approach, combining traditional debugging with an understanding of LLM behavior, is the emerging methodology for building production-grade autonomous agents.