The Guardrail Edit: Enforcing Diagnostic Preconditions in an Autonomous Agent's Tool Descriptions

Message in Context

Message 11: "Now update the stop_instance tool description: [edit] /tmp/czk/cmd/vast-manager/agent/vast_agent.py Edit applied successfully."

At first glance, this message appears trivial — a single edit operation, five words of commentary, a confirmation. It is the fifth in a sequence of six edits that together implement a new diagnose_instance tool for an autonomous GPU cluster management agent. But this particular edit, changing the description string of the stop_instance tool, is the architectural keystone of the entire feature. It is the moment where the system transitions from having a diagnostic capability to being forced to use it. Without this edit, the diagnose_instance tool would be optional, advisory, easily bypassed. With it, the agent's behavior is structurally constrained: it cannot destroy an instance without first gathering evidence.

The Motivation: Why a Description Matters

The user's original request ([msg 0]) was explicit about the need for a guardrail. The specification for diagnose_instance included the requirement that the stop_instance tool description be updated to state that the API "will reject the stop if you haven't diagnosed the instance in the last 10 minutes." This is not cosmetic documentation. In an LLM-driven agent system, tool descriptions are the primary mechanism for steering model behavior. The agent does not have access to the source code of the tools it calls — it only sees the function definitions, parameter schemas, and descriptions that are injected into its system prompt. The description is the contract.

The reasoning behind this edit is rooted in a fundamental challenge of autonomous agent design: how do you prevent an LLM from making destructive decisions based on speculation? The agent operates on a 5-minute timer, managing dozens of GPU instances running Filecoin SNARK proofs. Each instance costs money, and destroying a healthy instance mid-proof wastes both compute time and the proving work already completed. The system already had a stop_instance tool, but nothing prevented the agent from calling it based on a hunch — a process list that looked wrong, a log line that seemed alarming, a GPU utilization that appeared too low. The diagnose_instance tool was designed to ground the agent in facts, but it would only work if the agent actually used it. The description change creates a hard institutional constraint: the agent is told, in the tool definition itself, that the API will enforce a diagnostic precondition. Whether or not the API actually implements this enforcement on the backend is almost irrelevant — the description shapes the model's behavior at inference time.

The Decision: A Single String Change with Outsize Impact

The edit itself is minimal. The assistant replaces the existing stop_instance description with a new one that reads:

"Stop and destroy a running GPU instance. REQUIRES a prior diagnose_instance() call — the API will reject the stop if you haven't diagnosed the instance in the last 10 minutes. Never stop based on speculation about instance health."

The decision to make this change was straightforward — the user specified the exact text, and the assistant applied it. But the choice to include this requirement in the original specification reveals a sophisticated understanding of how LLM agents work. The user could have implemented the diagnostic precondition as server-side validation alone, silently rejecting unauthorized stop requests. Instead, they chose to surface the constraint in the tool description, making it visible to the model during reasoning. This dual approach — both telling the model the rule and enforcing it on the backend — is a defense-in-depth strategy for agent safety.

The assistant's role in this decision was purely executional. The message shows no hesitation, no analysis, no alternative proposals. The assistant simply confirms the edit was applied. This is appropriate: the design decision was already made by the user, and the assistant's job was to implement it faithfully. The thinking visible in the broader conversation ([msg 6]) shows the assistant planning all six edits in advance, working through a checklist. Message 11 is step five of six.

Assumptions Embedded in the Edit

Several assumptions underpin this edit. First, the assistant assumes that the stop_instance tool description is the correct place to enforce this constraint — that the model will actually read and respect tool descriptions during its reasoning process. This is a well-supported assumption in current LLM agent architectures, but it is not guaranteed. Models can hallucinate tool behavior, ignore descriptions under token pressure, or be prompted into bypassing safety instructions.

Second, the edit assumes that the diagnostic precondition is universally appropriate. The description says the API "will reject the stop if you haven't diagnosed the instance in the last 10 minutes." This implies that every stop must be preceded by a diagnosis, even in clear-cut emergency scenarios. If an instance is on fire (literally or metaphorically), waiting for a diagnostic sub-agent to analyze logs before stopping it could be harmful. The assumption is that the cost of false negatives (destroying a healthy instance) outweighs the cost of false positives (delaying a necessary destruction).

Third, the assistant assumes that the file structure is as expected — that the stop_instance tool definition exists in TOOL_DEFINITIONS and that the description field is a simple string that can be replaced. The earlier edits in the conversation ([msg 6], [msg 7], [msg 9], [msg 10]) had already modified the file successfully, so this assumption was well-grounded.

Potential Mistakes and Oversights

The most significant potential mistake in this edit is the lack of corresponding server-side validation. The description tells the agent that the API will enforce a diagnostic precondition, but the user's specification did not include code to implement that enforcement on the backend. The stop_instance API endpoint, as described in the conversation, does not appear to have been modified to check for a recent diagnose_instance call. This creates a gap: the model may believe it cannot stop without diagnosing, but if a sufficiently aggressive prompt override or jailbreak occurs, the backend would happily destroy the instance anyway. The description is a soft constraint, not a hard one.

Another subtle issue: the description says the API will reject the stop if the instance hasn't been diagnosed "in the last 10 minutes." This introduces a time-based validity window that is not defined anywhere in the tool definitions or the system prompt. The agent has no way to know what constitutes a "valid" diagnosis — does a diagnosis from 9 minutes ago still count? What about 11 minutes? The ambiguity could lead to the agent re-diagnosing unnecessarily, or incorrectly believing a stale diagnosis is sufficient.

Input Knowledge Required

To understand this message, one must understand the architecture of the agent system. The vast_agent.py file implements an autonomous LLM agent that manages GPU instances on vast.ai for Filecoin SNARK proving. It uses a persistent rolling conversation, a set of tool definitions injected into the system prompt, and an execute_tool dispatcher. The stop_instance tool is one of several tools the agent can call to manage instances. The diagnose_instance tool being added in parallel edits collects raw diagnostic data (logs, processes, memory, GPU status) and sends it to a sub-agent LLM for structured analysis.

One must also understand the operational context: instances take 1-2 hours to start up, go through a well-defined lifecycle (memcheck, params download, benchmark, SRS loading, production running), and are expensive to destroy prematurely. The normal startup sequence includes periods where the instance appears idle (GPU 0%, cuzk process not running) but is actually progressing normally.

Output Knowledge Created

This edit creates a documented dependency between two tools. The stop_instance tool now explicitly requires a prior diagnose_instance call. This dependency is visible to the agent during reasoning, to developers reading the code, and to anyone inspecting the tool definitions. It establishes a protocol: diagnosis before destruction. It also creates an implicit expectation that the backend API enforces this constraint, even though no such enforcement code is present in the conversation.

The Thinking Process

The assistant's thinking in this message is minimal but purposeful. The message opens with "Now update the stop_instance tool description" — a transitional phrase that signals the assistant is working through a planned sequence. The "now" indicates that the assistant is aware of the ordering: the diagnostic tool must exist (edits 1-4) before the stop_instance description can reference it. The edit is applied, confirmed, and the assistant moves on to the final edit (the UI panel update in [msg 12]).

The broader thinking visible in [msg 6] shows the assistant planning all six edits as a coherent unit: "Let me apply all edits: 1. Add diagnose_instance to TOOL_DEFINITIONS 2. Add to execute_tool 3. Add the DIAG_SYSTEM_PROMPT and run_instance_diagnosis function 4. Update system prompt CRITICAL RULES 5. Update stop_instance description 6. Update UI tools panel." This planning reveals a methodical, checklist-driven approach. The assistant is not making architectural decisions — it is executing a well-specified design.

Conclusion

Message 11 is a small edit with large implications. It transforms diagnose_instance from a useful but optional tool into a mandatory prerequisite for destruction. It encodes a safety policy into the tool definition layer, where it shapes model behavior at inference time. It represents a defense-in-depth approach to agent safety: constrain the model through descriptions, prompt rules, and (ideally) backend enforcement. The edit itself is trivial — a string replacement — but the reasoning behind it reflects a deep understanding of how to build reliable autonomous agents. The guardrail is not in the code that runs; it is in the words the model reads.