Design good tools
The model decides when to call a tool, which one, and with what arguments, based only on the names, descriptions and schemas you send in tools. Well-designed definitions are the difference between an assistant that calls the right function first time and one that guesses, loops or asks the user for things it could look up. This page is about writing those definitions. The mechanics of the round trip are on Tool calling.
Name tools after what they do
- Use a verb and an object:
search_orders,get_invoice_pdf,create_refund_request. - Keep names unique and unambiguous.
get_customerandget_customer_infoside by side guarantee confusion. - Stick to letters, digits, underscores and hyphens.
- Group related tools with a shared prefix when you have many:
crm_search_contacts,crm_update_contact.
Write descriptions for a newcomer
The description is the model’s only documentation. Say:
- What the tool does and returns, in one or two sentences.
- The situations that call for it, and the ones that do not, if a neighbouring tool could be confused with it.
- What the arguments mean, including formats and units.
- Limits and side effects: “returns at most 20 results”, “sends an email to the customer”.
| Weak | Strong |
|---|---|
"Gets weather." | "Current conditions for one city: temperature in Celsius, wind in km/h and a one-word sky description. Use for today only; use get_forecast for future days." |
"Search." | "Full-text search over help-centre articles. Returns up to 10 titles with URLs and a 200-character snippet each. Use before answering any how-to question." |
Three to five sentences per tool is normal for anything non-trivial. Descriptions are sent with every request but sit in the stable part of the prompt, so they are served from cache after the first call.
Make the schema do the validation work
The parameters object is a JSON Schema. Use it to make wrong calls hard:
{
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "Order number as printed on the receipt, e.g. FL-12345",
"pattern": "^FL-[0-9]{5}$"
},
"reason": {
"type": "string",
"enum": ["damaged", "wrong_item", "late", "other"],
"description": "Main reason for the return"
},
"notes": { "type": "string", "maxLength": 500 }
},
"required": ["order_number", "reason"]
}enumfor fixed choices: the model picks from the list instead of inventing values.descriptionon each property, with an example value.requiredfor what the function cannot work without.- Formats and patterns for identifiers, dates (
"format": "date") and amounts.
Arguments are not yet validated against your schema on the server, and they arrive as a JSON string that may not parse. Validate them in your code before acting, and return validation errors to the model as the tool result so it can correct itself. Server-side enforcement is specified on Strict tool arguments.
Choose the right granularity
| Too fine | About right | Too coarse |
|---|---|---|
get_order_status, get_order_items, get_order_address, get_order_tracking called one after another | get_order(order_number) returning status, items, address and tracking | do_anything(action, payload) |
Fewer, well-shaped tools mean fewer round trips and fewer wrong choices. A single “do anything” tool gives the model no guidance at all and is hard to secure.
Return results the model can use
- Return compact JSON with the fields the model needs, not the full database row.
- Include identifiers the model may need for a follow-up call (
order_number,ticket_id). - On failure, return a clear error message rather than throwing:
{"error": "No order FL-12346 for this customer. Check the number with the customer."}. The model reads it and recovers. - Say when results were truncated:
{"results": [...], "truncated": true, "total": 214}.
More on keeping results small is in Tool results and context.
Keep the tool list focused
Every tool in the list competes for the model’s attention. Offer the tools that fit the current task:
- A support assistant does not need the admin tools during an ordinary chat.
- Swap tool sets by conversation stage when stages are clearly different (identify the customer, then handle the request).
- Keep the list stable within one conversation, so the start of the prompt stays cacheable.
When you have many tools, the planned tool search feature loads definitions on demand.
Steer with tool_choice
| Value | Use it when |
|---|---|
"auto" (default) | The model should decide. |
"none" | You want a text answer now, but must keep tools in the request for caching. |
{"type": "function", "function": {"name": "..."}} | The next step must be that tool, for example structured extraction. |
Test tool selection
Add cases to your test set that check which tool is called and with what arguments, not just the final answer: “Where’s my order FL-20391?” should call get_order with that number; “What’s your returns policy?” should call no tool. See Troubleshoot tool calls when they do not.