Once you know how schemas work, the next question is bigger: which tools should the model get at all? It is easy to keep adding tools whenever something is missing. This guide explains why that slowly makes an agent worse, and how to design a menu the model can use well.
04
Principles for each tool
One tool, one job
A tool should be easy to describe in a single sentence that does not contain the word "and". If it is hard to say what a tool is for, the model will find it hard to decide when to use it.
Names say what it does
Prefer plain verbs and nouns: search_tickets, get_notes. Avoid names that could mean several things, such as lookup, fetch, handle, or process. Use one naming pattern across the set, so that the model can predict it. If one tool says ticket_id and another says id for the same thing, you invite mistakes.
Descriptions separate the neighbors
Where two tools are close, say how they differ and when to prefer each. "Use get_ticket when you already have an id. Use search_tickets when you only have words." That one sentence prevents most confusion.
Arguments are few and obvious
Prefer a couple of well-named arguments to many optional ones. If you find yourself adding a fifth or sixth argument, ask whether this is really two tools.
Results look the same everywhere
When tools in a set return results in a consistent format, one line per item with the same labeled fields, the model learns it once and reads every result easily. Mixed formats make it work harder.
05
Wrappers: hide the mess, keep the power
You often cannot change the underlying functions. They may come from a library, or a legacy system, or a teammate. But the model does not have to see them. You can put a wrapper between the model and the real functions.
A wrapper is a small function you write that you present to the model in place of the underlying ones. It can:
- have a better name and description,
- accept arguments in a friendlier shape,
- fill in safe defaults,
- call whichever underlying function is right for the request,
- and return results in a clean, consistent format.
1def search_tickets(text: str, customer: str | None = None) -> str:2 """One entry point that hides two messy back-end calls."""3 if customer:4 rows = backend.tickets_for_customer(customer)5 rows = [r for r in rows if text.lower() in r.lower()]6 else:7 rows = backend.lookup(text)8 return "\n".join(rows[:20]) or "No matching tickets."Behind the wrapper you can keep all the original tools. The model only sees the one you designed. The menu stays small and the capabilities do not shrink.
You also need a dispatcher: a function that takes the name and arguments the model sent and routes them to the right wrapper. Keep it simple, and make it return a clear error message for a name it does not recognize.
07
A note on limits
Some systems cap the number of tools you may expose, and that is a useful discipline. A hard cap forces the question you should be asking anyway: which of these tools actually earn their place?
Be sure to know how your platform treats the cap. If exceeding it fails the entire submission, check the count first, in code, before anything else runs.
08
Where this shows up
Problem 07 hands you a pile of overlapping, awkwardly named functions and a limit on how many the model may see. You will not be rewarded for a cleverer prompt over the same mess. You will be rewarded for deciding what the model is allowed to call, naming it so that it is hard to misuse, and doing the plumbing behind the scenes.