Skip to main content
Journal
AIAgentTool

Notes on Writing Effective Tools for AI Agents

A tool is an external capability an AI agent can call to perform actions or access information beyond what the model can do by itself. Designing an effective tool means making it clear when to use it, how to call it, and what the agent can learn from its response.

LLM-oriented design

In traditional software, developers usually define which APIs and functions a program calls. With an agent, the model selects tools at runtime: it may call a tool, skip it, or choose the wrong one. Tool design therefore needs to account for how the model understands the available actions and decides between them.

Traditional API design

Tool design

For instance, if a user asks:

Please check whether Roger is available tomorrow afternoon; if yes, schedule a meeting.

A conventional API might expose these functions:

get_user()
list_calendar_events()
get_availability()
create_event()

Exposing all these endpoints separately can leave the agent coordinating steps that are usually performed together. A schedule_event() tool could instead find an available slot and create the event in one call.

This works best when the workflow has clear inputs and a well-defined outcome. In this example, the tool still needs an unambiguous attendee, a meeting duration, and a timezone. Missing details should be resolved through clarification or documented defaults before an event is created. If no suitable slot exists, the tool should report that outcome without creating an event.

Return meaningful context

A tool does more than execute actions; its response also shapes the agent’s context. Returning too much information can waste tokens and make relevant evidence harder to find.

For example, a log-search tool can filter records before returning them to the agent:

# ❌
read_logs() # it may return a bunch of content

# ✅
search_logs(
    query="payment failed",
    time_range="last_24h"
) # return relevant records with enough context to interpret them

The goal is to return enough information for the next decision. Matching log entries may need timestamps, service names, error messages, and surrounding lines. Useful identifiers should remain available when the agent needs them for follow-up calls. If results are truncated, the response should say so and explain how to retrieve more.

Tool description and spec

A clear tool description helps the agent understand when and how to use it. I find it useful to think about how I would explain the tool to a new teammate: what it does, what it needs, and what to expect.

A poor example:

"""
Search things
"""
search(query)

This leaves several questions unanswered: what does it search, when should the agent call it, and what will it return? A clearer description would be:

"""
Search customer support tickets by keywords.

Use this tool when you need to find support tickets related to
a specific customer, product, error, or issue.

Args:
    query: Keywords describing what to search for,
           e.g. "payment failed" or "customer@example.com".

Returns:
    Up to 10 matching tickets, including ticket ID,
    title, status, and a short excerpt.
    An empty list means the search succeeded but found no matches.

Errors:
    Invalid queries return guidance for correcting the input.
    Service failures indicate whether retrying is appropriate.
"""
search_support_tickets(query)

These docstrings illustrate the description content, not a complete tool registration. The runtime must expose the description and parameter schema to the model.

A useful tool specification should answer:

  1. What does it do?
  2. When should the agent use it?
  3. What should each input contain?
  4. What will it return?
  5. What happens when there are no results, invalid inputs, or service failures?

For example, a successful search with no matches could return:

{
  "ok": true,
  "tickets": []
}

A temporary service failure should be distinguishable from an empty result:

{
  "ok": false,
  "error": {
    "code": "SERVICE_UNAVAILABLE",
    "message": "The ticket service is temporarily unavailable. Try again later.",
    "retryable": true
  }
}

This is one possible response format. What matters is giving the agent enough information to decide whether to continue, correct its input, or retry.

Evaluation

Once a prototype works, I would evaluate it against a set of realistic tasks instead of assuming that a clear-looking interface will work well for an agent. Start with representative cases and expand to cover ambiguous inputs, empty results, and failures.

Task 1
Help me find the PRs that Roger opened recently.

Task 2
Find the reason for yesterday's payment failure.

Task 3
Schedule the next meeting with Roger and attach the notes from the last meeting.

Each task needs a verifiable outcome. For a scheduling test with a known attendee, duration, timezone, and calendar state, success means creating exactly one event with the correct details in an available slot. If no suitable slot exists, no event should be created. If required details are missing and no defaults apply, the agent should ask for clarification.

Then run the tasks and inspect both the outcomes and the tool-call traces:

  1. Does the agent select the correct tools?
  2. Are the parameters correct?
  3. How many calls and tokens does it use, and how long does it take?
  4. Does it handle empty results and errors appropriately?
  5. Does the final result satisfy the task’s success criteria?

When results fall short, use the traces to identify whether the problem lies in the tool’s name, description, schema, response, or implementation. An LLM can help review these transcripts and suggest changes, but those changes still need to be evaluated.

Keep a separate set of tasks for final testing. Otherwise, repeated improvements may only make the tools better at the examples used during development. Also allow different valid tool sequences when they achieve the same correct outcome.

So writing an effective tool is actually a loop:

Conclusion

The idea I want to carry forward is to treat a tool as an interface for the agent’s decisions. I would use these principles when designing one:

  1. Design around meaningful tasks, with clear responsibilities and explicit boundaries.
  2. Consolidate common workflows when doing so reduces ambiguity and unnecessary calls.
  3. Treat tool responses as part of context engineering: return relevant evidence and enough information for the next step.
  4. Treat names, descriptions, and schemas as instructions the agent relies on.
  5. Verify improvements through realistic tasks with measurable outcomes.

Sources

Based on: Writing effective tools for agents with agents