n4nAI

Tool descriptions that actually improve agent accuracy

Practical patterns for writing tool descriptions for AI agents that reduce ambiguity, improve tool selection, and raise agent accuracy in production.

n4n Team4 min read836 words

Audio narration

Coming soon — every post will get a voice note here.

Poorly written tool descriptions for AI agents are the silent killer of agent reliability. The model treats your description as the only contract for what a tool does, and ambiguity there propagates directly into wrong calls, retries, and stalled workflows. This guide gives an ordered path to writing descriptions that constrain model behavior instead of hoping it guesses.

1. Treat the description as a strict interface

The fastest way to improve agent accuracy is to stop writing prose and start writing specs. A tool description is not a docstring for a human; it is the sole input the model uses to decide whether to call your function and how. If the description omits a constraint, the model will violate it.

Define the tool with a JSON schema that mirrors your actual implementation. The description field should state the function’s purpose in one or two sentences, then defer detailed parameter constraints to the parameters block.

{
  "name": "get_invoice",
  "description": "Retrieves a PDF invoice by ID. Returns base64 content. Use only for invoices owned by the authenticated tenant.",
  "parameters": {
    "type": "object",
    "properties": {
      "invoice_id": {
        "type": "string",
        "pattern": "^inv_[0-9a-f]{12}$",
        "description": "Invoice ID formatted as inv_ followed by 12 hex chars."
      }
    },
    "required": ["invoice_id"]
  }
}

When you revise tool descriptions for AI agents, run a diff against the handler code. Any branch in the code that returns an error must be represented either in the description or in the parameter schema.

2. Lead with the verb and the boundary

Models attend heavily to the first clause. Start the description with the action and immediately state the scope limit. “Fetches X for Y” beats “This function is used to fetch X which might be useful when Y”.

Bad:

"description": "This tool can be used to get the weather if the user asks about it."

Good:

"description": "Fetches current temperature for a US zip code. Use only for US locations; return error otherwise."

The boundary clause (“Use only for US locations”) is not optional polish. It reduces cross-tool confusion when another tool handles international queries.

3. Encode constraints as explicit negatives

Positive descriptions tell the model what to do; negative constraints tell it when not to. Agents fail most often at the edges: calling a write tool without confirmation, or passing an ID from the wrong namespace.

Write negatives directly:

{
  "name": "delete_user",
  "description": "Permanently removes a user record. SIDE EFFECT: irreversible. Do NOT call unless user_id was provided by the user in the current session and the user typed 'confirm delete'.",
  "parameters": {
    "type": "object",
    "properties": {
      "user_id": {"type": "string", "description": "UUID of target user."}
    },
    "required": ["user_id"]
  }
}

The phrase “Do NOT call unless” is a hard guardrail. Test it with adversarial prompts during eval. If the model still calls, tighten the wording or move the constraint into a required parameter with an enum.

4. Separate side effects from pure reads

A core tradeoff in tool descriptions for AI agents is signaling mutation. Models default to calling tools they think are safe. If a tool writes state, say so in the first sentence.

tools = [
  {
    "type": "function",
    "function": {
      "name": "send_email",
      "description": "Sends an email via the org SMTP relay. SIDE EFFECT: delivers external message. Only call after the user approved the draft.",
      "parameters": {
        "type": "object",
        "properties": {
          "to": {"type": "string"},
          "body": {"type": "string"}
        },
        "required": ["to", "body"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "draft_email",
      "description": "Pure function: returns a markdown draft without sending. Use this to preview before send_email.",
      "parameters": {
        "type": "object",
        "properties": {"topic": {"type": "string"}},
        "required": ["topic"]
      }
    }
  }
]

Marking draft_email as a pure function lets the agent iterate without triggering external actions. This pattern cuts accidental sends by an order of magnitude in our tests.

5. Provide one concrete example call

Models benefit from a single illustrative invocation inside the description or as a separate examples extension if your framework supports it. Keep it minimal and realistic.

"description": "Converts currency. Example: {'amount': 100, 'from': 'USD', 'to': 'EUR'} returns float. Does not fetch live rates; uses daily close."

The example anchors the expected shape and flags stale data. Do not include multiple examples; they dilute the signal and increase prompt length.

6. Version descriptions like code

Tool descriptions for AI agents drift as backends change. Treat the description string as part of your API contract. Tag it with a version comment outside the schema, and run evals when you change it.

# tool: get_weather v2.1 - added US-only boundary

Set up a canary: deploy new description to 5% of agent sessions, compare tool-selection error rate against baseline. If errors rise, revert. This is cheaper than a full model fine-tune and often yields larger accuracy gains.

7. Common pitfalls and tradeoffs

Overloading a single tool. Engineers love a do_everything endpoint. The model hates it. Split tools by side effect and domain. Five narrow tools beat one Swiss Army knife.

Description length. Beyond ~60 words, accuracy drops as attention spreads. Put verbose detail in parameter schemas, not the top-level description. If you must explain complex logic, link to a retrieved doc rather than inlining.

Hidden rate limits. If a tool is flaky under load, say “May return 429; retry once after 1s.” The agent can handle declared failure modes; undeclared ones become stuck loops.

Assuming model memory. The description is re-read every call. Do not reference “the previous tool” or “the user’s earlier choice” inside the description. State invariants globally.

Ignoring parameter descriptions. A missing pattern or enum forces the model to guess formats. Always constrain strings with regex or enums where possible.

8. Evaluation loop that closes the gap

Write descriptions, then break them. Construct a test set of 50 tasks where the wrong tool is tempting. Run the agent and log which description clause would have prevented the mistake. Edit the description to add that clause. Repeat.

A minimal eval harness:

for task in tasks:
    resp = agent.run(task, tools=tools)
    if resp.tool_calls[0].name != task.expected_tool:
        print(f"FAIL: {task.id} called {resp.tool_calls[0].name}")
        # inspect tool description for missing constraint

Over three iterations on a support bot, this loop reduced wrong-tool calls from 22% to 3% without changing the model.

9. When to move constraints out of text

Text descriptions are not enforceable. For high-stakes actions, back the description with a runtime validator. The description says “only after confirm”; the validator rejects the call if no confirmation flag exists in state. The description guides; the validator guarantees.

Tool descriptions for AI agents are the highest-leverage artifact in your agent stack. They are cheaper to iterate than prompts or models, and they compound: better descriptions mean fewer retries, lower token spend, and happier users. Write them like the interface they are.

Tagsai-agentstool-useprompt-engineeringaccuracy

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All ai agent tool use design patterns posts →