One Tool With Twelve Parameters or Four With Three? Let the Agent's Error Log Decide
Merging tools trades wrong-tool mistakes for wrong-argument mistakes. Your traces already show which one your agent makes more, and an overloaded tool leaves a signature you can grep for.
A support agent has one tool called manage_ticket. It takes twelve parameters and an action field that can be create, update, assign or close. Most days it works. Then someone reads the traces and sees the agent closing a ticket with priority: "high" and assignee_id filled in, neither of which close reads. Twice it sent resolution_note on an update, and the tool silently dropped the text.
Nothing about the model changed. The schema was the problem. Whether one wide tool or several narrow ones is the better shape comes down to which kind of mistake your agent makes more often, and your logs can tell you that.
The tradeoff you are actually making
Splitting and merging don't remove errors. They move them from one place to another.
- One wide tool leaves almost no choice about which tool to call. The hard part moves into the arguments: which fields matter for this action, which ones to leave out, and which combinations are valid.
- Several narrow tools make each argument list easy to fill in. The hard part moves into selection: picking
update_ticketwhen the user meantassign_ticket.
So "fewer tools is simpler" and "small tools are cleaner" are both incomplete. The question to ask is where your agent's errors are piling up today.
🔍 The signature of an overloaded tool
An overloaded tool is a set of separate operations sharing one entry point. You can usually spot it in the schema before you open a single trace:
- A mode switch. A parameter named
action,operation,modeortypewhose value changes what the other parameters mean. - Conditional descriptions. Parameter docs that say "only used when action is X" or "required if mode is Y". Every one of those sentences is a rule the model has to apply correctly on every call.
- Parameters that never appear together. If you sort the parameters by which actions read them and get groups that barely overlap, you are looking at separate tools.
- Mostly optional fields. Twelve parameters with one required usually means the real requirements live in prose, where the JSON schema can't enforce them.
The traces show the same thing from the behavior side:
- Cross-mode arguments. The agent fills in fields that belong to a different action, as in the
closecall above. - Silent drops. The tool ignores a field the agent clearly meant to use, and the agent then tells the user the work is done.
- Mode-hopping retries. After a validation error, the agent changes
actionrather than fixing the argument, which is a sign it never had a clear picture of what each mode needs.
Measure it before you refactor
You don't need an eval suite to start. Tag each tool call in your existing traces with one of these outcomes:
ok correct tool, valid arguments, intended effect
wrong_tool valid call, but a different tool was the right one
invalid_args rejected by validation
cross_mode_args accepted, but carried fields the chosen action ignores
missing_required omitted a field the action needed (often only caught downstream)
Then group by tool, and for a wide tool, by action too. Two patterns decide it.
Argument errors cluster around mode boundaries. Say, for illustration, that 200 manage_ticket calls give you 18 errors, and 15 of them are cross_mode_args or missing_required where the fields belong to a different action. The tool is overloaded. Split it along the action values.
Selection errors dominate instead. If a set of narrow tools shows mostly wrong_tool, with the agent swapping update_ticket and assign_ticket, splitting was too aggressive. Either merge the two that get confused or rewrite their descriptions so each one says when not to use it.
If both counts are low, leave the design alone. Refactoring a tool that works is a good way to create a regression.
When one wide tool is the right call
Not every tool with a lot of parameters is overloaded. A search_tickets tool with status, assignee, tag, created_after, created_before, customer_id and limit can have twelve parameters and be perfectly healthy. Here's the test:
✅ Keep it wide when the parameters are independent modifiers. Any subset is valid, every combination means something, and leaving a field out has an obvious default ("no filter").
❌ Split it when the parameters are mutually exclusive by mode. Some combinations are meaningless, and the right set of fields depends on an earlier choice.
A search filter narrows one operation. A mode field picks between operations. The first kind of complexity is fine for a model. The second is a decision tree written in prose, and the model has to walk it again on every call.
What the split looks like
Before:
{
"name": "manage_ticket",
"parameters": {
"action": {"enum": ["create", "update", "assign", "close"]},
"ticket_id": {"description": "Required unless action is create"},
"subject": {"description": "Only for create"},
"body": {"description": "For create or update"},
"priority": {"description": "For create or update"},
"assignee_id": {"description": "Only for assign"},
"resolution_note": {"description": "Required when action is close"},
"notify_customer": {"description": "For update or close"}
}
}
After:
{"name": "create_ticket", "required": ["subject", "body"], "optional": ["priority"]}
{"name": "update_ticket", "required": ["ticket_id"], "optional": ["body", "priority", "notify_customer"]}
{"name": "assign_ticket", "required": ["ticket_id", "assignee_id"]}
{"name": "close_ticket", "required": ["ticket_id", "resolution_note"], "optional": ["notify_customer"]}
Every "only for" and "required when" sentence is gone. Those rules now live in required lists, which the schema validator enforces and the model can see at a glance. A close_ticket call with no resolution_note now fails loudly at validation. Before, it could slip through and leave a ticket closed with no explanation.
Two details keep the split from creating selection errors:
- Name tools verb-first with a shared noun (
create_ticket,close_ticket) so they sort together and read like a family. - Add a disambiguating line to the pair most likely to be confused. For example, "Use
assign_ticketto change the owner;update_ticketcannot change the assignee."
⚠️ Don't split past the point of confusion
The opposite failure is real too. Four narrow tools are fine. Turning update_ticket into update_ticket_priority, update_ticket_body and update_ticket_notify gives the model three near-identical descriptions to tell apart, and a request like "bump it to high and add a note" now takes two calls. Once wrong_tool becomes the top error for a family of tools, merge them back.
The rule to take away
Put the complexity where the machinery can check it. Rules about which fields go together belong in separate schemas with required lists, because the validator enforces those. Choices between free-form filters belong in one tool's optional parameters, because every combination is valid anyway. Your traces will tell you which of those two problems you have. Count first, then refactor.