The Anatomy of a Tool Handler: Wiring Machine Notes into an Autonomous Agent

In the lifecycle of building autonomous LLM-driven systems, few moments are as deceptively simple as the one captured in message [msg 4532] of this opencode session. The assistant writes: "Now add the handler in execute_tool:" and then reads a portion of a Python file. On the surface, this is a mundane act — a developer reading source code before making an edit. But within the broader arc of constructing a production-grade fleet management agent for GPU proving infrastructure, this message represents a critical juncture where architectural design meets concrete implementation. It is the moment a new capability — machine notes — transitions from abstract concept to executable reality.

The Context: A Machine Notes System Takes Shape

To understand why this message exists, we must trace back to [msg 4507], where the user asked a simple question: "Expose machine notes?" This query landed in the middle of a feverish development sprint. The assistant had just fixed a critical agent over-provisioning bug ([msg 4506]), where the LLM-driven fleet manager was launching instances without accounting for machines still in their loading phase. The agent had been redesigned around a projected_proofs_hr metric — combining currently running capacity with the estimated capacity of loading instances — and the system was finally making sane scaling decisions.

The user's question about machine notes was deceptively open-ended. The assistant interpreted it as a request for a persistent annotation system where operators (and the agent itself) could attach notes to specific vast.ai machines. This was not an unreasonable interpretation: the fleet had grown to encompass dozens of instances across multiple GPU types (RTX 4090s, RTX 5090s, RTX PRO 4000s), and operators needed a way to record operational knowledge — "this machine had OOM issues," "good performer, prefer this one," "has the budget-integrated pinned pool deployed."

Over the course of messages [msg 4508] through [msg 4531], the assistant built the complete machine notes infrastructure. This included:

The Subject Message: Reading Before Writing

Message [msg 4532] is the bridge between definition and execution. The assistant states its intent — "Now add the handler in execute_tool:" — and then issues a read tool call to inspect the current state of the file. The returned content shows lines 505–513 of vast_agent.py, specifically the stop_instance handler:

elif name == "stop_instance":
    vast_id = arguments.get("vast_id")
    reason = arguments.get("reason", "")
    if vast_id is None:
        result = {"error": "vast_id is required"}
    else:
        result = call_api("POST", "/api/agent/stop", {
            "vast_id": int(vast_id),

This is not random browsing. The assistant is reading the file to find the exact insertion point and to understand the established pattern for tool handlers. Every tool in the agent follows the same convention: an elif branch in the execute_tool function that extracts parameters from arguments, validates them, calls the appropriate API endpoint via call_api, and returns a result dictionary. The stop_instance handler, being the last tool handler before the else fallback (as shown in the subsequent read at [msg 4533]), is the natural place to insert the new handler.

The Reasoning: Why This Pattern Matters

The assistant's approach reveals several important design assumptions and decisions.

First, the assumption of structural consistency. The assistant assumes that the add_note handler should follow the exact same pattern as stop_instance and every other tool handler. This is a reasonable architectural choice: consistency reduces bugs, makes the code easier to maintain, and ensures the LLM gets predictable responses regardless of which tool it calls. The handler will extract machine_id, note, and author from arguments, validate that machine_id is present, and then POST to /api/machine-notes/{machine_id}.

Second, the assumption about the API contract. The assistant assumes that the POST /api/machine-notes/{machine_id} endpoint (already implemented in the Go backend at [msg 4524]) accepts the same parameter structure that the agent will send. This is a safe assumption because the assistant built both the API endpoint and the tool definition — they are designed to be compatible.

Third, the decision to read rather than blindly edit. The assistant could have used a regex-based edit or assumed the line numbers. Instead, it reads the file to see the exact current state. This is a defensive programming practice: file contents can drift due to parallel edits, and reading ensures the edit targets the right location. The read reveals that the stop_instance handler is at line 505, and the else fallback is somewhere after line 513 (truncated). The subsequent edit at [msg 4534] will insert the add_note handler between stop_instance and the else clause.

The Knowledge Flow: Inputs and Outputs

This message consumes specific input knowledge and produces specific output knowledge.

Input knowledge required to understand this message includes:

The Thinking Process: Methodical Construction

The assistant's thinking process, visible in the sequence of messages, reveals a methodical, layered approach to building the machine notes feature:

  1. Interpret the request ([msg 4508]): The assistant considers what "machine notes" means, reviews existing infrastructure (host_perf, bad_hosts, agent_actions tables), and designs a lightweight annotation system.
  2. Build the data layer ([msg 4509]): Add the machine_notes SQLite table schema.
  3. Build the API layer ([msg 4512]-[msg 4514]): Add GET and POST endpoints.
  4. Build the UI layer ([msg 4517]-[msg 4522]): Add the Notes tab, inline notes in offers, and the add-note form.
  5. Integrate with agent context ([msg 4527]): Include notes in the fleet-performance.md file so the LLM can read them.
  6. Define the agent tool ([msg 4531]): Add the add_note tool to the LLM's tool schema.
  7. Wire the handler ([msg 4532], [msg 4534]): Add the execution handler in execute_tool. This is classic bottom-up feature construction: data model → API → UI → agent integration. Each layer depends on the one before it. The tool handler is the final piece because it requires the API to exist (the handler calls it) and the tool definition to exist (the handler implements it).

What This Message Reveals About the System

This small message illuminates several truths about the agent architecture being built.

The agent is not monolithic. It's a layered system: a Go backend provides REST APIs and SQLite persistence, a Python script orchestrates LLM interactions and tool execution, and a web UI renders state for human operators. The execute_tool function is the nervous system connecting the LLM's decisions to the backend's capabilities.

Tool definitions and handlers are decoupled. The LLM sees only the tool schema (name, description, parameters). The handler is invisible to the LLM — it's pure implementation. This separation means the tool schema serves as a contract between the LLM and the system, while the handler can contain arbitrary complexity (validation, error handling, API calls).

The pattern is extensible. Adding a new capability requires exactly three things: a backend endpoint, a tool definition, and a handler. The consistency of this pattern means new tools can be added rapidly without architectural changes. By message [msg 4532], the assistant had added multiple tools across the session — launch_instance, stop_instance, send_alert, get_perf, diagnose_instance, remember, and now add_note — each following the same template.

Conclusion

Message [msg 4532] is a small but essential step in a larger engineering narrative. It is the moment when a feature crosses the threshold from "defined" to "implemented" — when the machine notes capability becomes not just something the LLM can talk about, but something it can do. The assistant's decision to read the file before editing, its adherence to established patterns, and its methodical layering of data model, API, UI, and agent integration all reflect a disciplined approach to building autonomous systems. In the world of LLM-driven agents, where the line between prompt engineering and software engineering blurs, this message stands as a reminder that reliable agents are built on solid code, not just clever prompts.