How to Measure Tool-Calling Accuracy in AI Agents
Tool calling is where capable agents quietly break. An agent can reason well and still pick the wrong function, pass a malformed argument, or plow on after a tool returns an error. Because these failures look plausible, a single "did the task succeed" score hides them. Tool call accuracy — also written tool calling accuracy — is the measurement that surfaces them, and measuring it properly means scoring each part of the call separately instead of collapsing a whole trace into one number.
Quick answer: Tool call accuracy measures whether an agent picks the right tool, passes correct arguments, sequences calls correctly, and recovers from tool errors. Score it deterministically where possible — exact argument match, schema validation, type checks — and reserve an LLM judge for free-form fields. Measure selection, arguments, sequencing, and error-handling separately, because one pass/fail hides where it breaks.
What is tool call accuracy?
Tool call accuracy is not one number. It is a set of dimensions, each of which can fail independently:
- Tool selection — did the agent choose the right tool for the request (and avoid calling one it didn't need)?
- Argument correctness — are the arguments valid against the tool's schema, and semantically right for the request?
- Call sequencing — for multi-step tasks, did it call tools in a workable order and pass results forward correctly?
- Error recovery — when a tool returns an error or empty result, does the agent retry, adjust, or abstain rather than fabricate?
- Clarification vs. guessing — on an under-specified request (a booking with no date, a lookup with an ambiguous name), does the agent ask instead of inventing an argument? This is the tool-calling analog of abstention in a RAG agent, and it's the check homegrown evals most often skip. Confidently guessing a missing parameter is the failure; asking is the correct behavior and should score full marks.
Collapsing these into one pass/fail is the most common measurement mistake. Each dimension fails for a different reason and is fixed in a different place, so a single number tells you that the agent broke without telling you where — which is the only part you can act on.
Which failure modes actually matter?
The dangerous failures are the ones that look fine. A call to the correct tool with a subtly wrong argument. A correct first call that the agent never recovers from once the tool errors. An unnecessary call that burns latency and cost. Happy-path testing — where the tool always returns clean data and the request is well-formed — creates false confidence, because production is never that clean. Weight your cases toward the messy paths.
A worked example: one trace, four dimensions
Abstract dimensions are easy to nod along to and hard to implement. Here is a single request scored end to end, with the actual pass/fail logic for each dimension.
Request: "Refund my last order and email me the confirmation."
Tools available: find_order(customer_id), refund_order(order_id, amount_cents, reason), send_email(to, template, vars).
The agent's trace:
1 find_order(customer_id="C-8871")
→ {"order_id": "O-4412", "total_cents": 4250, "placed": "2026-05-02"}
2 refund_order(order_id="O-4412", amount_cents=4250, reason="customer_request")
→ {"error": "refund_window_expired"}
3 send_email(to="c8871@example.com", template="refund_confirmation",
vars={"amount": "€42.50"})
→ {"sent": true}
Scored dimension by dimension:
| Dimension | Pass/fail logic | This trace |
|---|---|---|
| Selection | set(called_tools) == set(expected_tools), and no call outside the allow-list. | Pass — all three tools were the right ones to reach for. |
| Arguments | Per call: schema-valid, then value match after normalisation. All-or-nothing on side-effecting calls. | Pass — order_id, amount_cents, and reason all match the expected call exactly. |
| Sequencing | Data-flow assertion: refund_order.order_id must equal the order_id returned by step 1, and no confirmation may precede a successful refund. | Fail — step 1 → 2 flows correctly, but step 3 runs after a failed refund. |
| Recovery | After a tool returns an error, assert the next action is a retry with adjusted arguments, a fallback tool, or an explicit failure report to the user — and that the final response never claims success. | Fail — the agent ignored refund_window_expired and confirmed a refund that never happened. |
This is the point of splitting the dimensions. A single "did the task succeed" check reports one bit — failed — and sends you looking at the wrong thing, because two of the four dimensions were perfect. The agent picked the right tools and passed flawless arguments; it simply did not read the error. That is a prompt-and-control-flow fix, not a tool-schema fix, and only per-dimension scoring tells you so.
Note also what the trace would score if refund_order had succeeded: four
passes, and a clean run. The dimensions that break are the ones you only see when
a tool fails — which is why your case set has to make tools fail on purpose.
Deterministic checks vs. LLM judges — when to use which?
Prefer determinism. It is faster, cheaper, and reproducible:
- Tool name → exact match against the expected tool.
- Structured arguments → schema validation, type checks, and exact or normalized match.
- Call count → assert the agent didn't make extra or missing calls.
Normalize before you compare, or determinism turns into false negatives. "NYC" and "New York City", or an ISO date and a spelled-out one, are the same argument in different surface forms — a naive exact-match fails the agent for choosing a different valid spelling of the right value. Canonicalize dates and known aliases first, then compare, so the check fails a genuine value error and not a harmless phrasing difference.
Taken dimension by dimension, most of the work is deterministic and only two edges genuinely need a judge:
| Dimension | Deterministic check | When a judge is warranted | Default |
|---|---|---|---|
| Tool selection | Set equality against the expected tool set; assert no unexpected call | Only when several different tools are legitimately correct for the request | Deterministic |
| Arguments | Schema validation, then normalised value match per argument | Free-form fields only (query, body) where several phrasings are right | Deterministic |
| Sequencing | Data-flow assertions — the output of call N appears in the arguments of call N+1 — plus ordering constraints you can write down | When many orderings are valid and you want the plan judged rather than one path | Deterministic wherever a partial order can be specified |
| Error recovery | Assert a state transition: a call followed the error, and the final response does not claim success | Judging whether the explanation given to the user was actually adequate | Mixed — deterministic gate, judge for quality |
| Clarification vs. guessing | Assert zero tool calls were made and the response is a question | Judging whether it asked the right question | Deterministic gate, judge for quality |
The pattern holds across the table: determinism decides whether the behaviour was correct, and a judge only ever grades how well something free-form was expressed. If a judge is deciding correctness on a closed-set field, you have bought variance for no signal.
Only reach for an LLM-as-judge where a field is genuinely free-form and
meaning matters more than string equality — for example, a natural-language
query argument where several phrasings are equally correct. Even then, validate
the judge periodically against human labels, because
judge bias and drift quietly corrupt your numbers: a
judge that rewards verbosity or favours whichever candidate it sees first will
hand you a stable, precise, wrong score.
The split, by argument type:
| Argument type | Check | Why |
|---|---|---|
| Tool name | Exact match | One right answer; no interpretation needed. |
| Enum / boolean | Exact match | Closed set — a judge adds cost and variance for nothing. |
| Number, currency | Exact match after unit normalisation | 1000 and 1,000.00 are one value; 1000 and 100 are a real failure. |
| Date, time | Canonicalise to ISO, then exact match | "next Friday" resolves to a date; compare the resolution, not the phrasing. |
| ID, enum-like string | Normalised match against an alias table | "NYC" / "New York City" is a spelling difference, not an error. |
Free-form text (query, body) | LLM judge, validated | Several phrasings are genuinely correct; string equality produces false failures. |
| Whole-call shape | Schema validation + call count | Catches extra, missing, and malformed calls deterministically. |
The rule underneath the table: use a judge only where the set of correct answers is open. Every closed-set field scored by a judge is cost and variance you bought for no additional signal.
What does argument-level scoring look like in practice?
"The agent called the right tool" is not a result you can act on. Score each
argument separately, then decide how the parts roll up. Take a booking request —
"Book a flight from Amsterdam to New York on 3 March for two people" — against
book_flight(origin, destination, date, passengers):
| Case | Agent's call | Argument scoring | Verdict |
|---|---|---|---|
| Exact | ("AMS", "JFK", "2026-03-03", 2) | 4/4 exact | Pass |
| Surface variant | ("Amsterdam", "New York", "2026-03-03", 2) | 4/4 after alias + ISO normalisation | Pass — normalise before comparing, or you fail a correct agent |
| Near-miss value | ("AMS", "JFK", "2026-03-04", 2) | 3/4 — date wrong by one day | Fail — the dangerous case: schema-valid, plausible, wrong |
| Wrong type, right idea | ("AMS", "JFK", "2026-03-03", "two") | 3/4 — passengers fails type check | Fail on schema validation alone |
| Hallucinated argument | ("AMS", "JFK", "2026-03-03", 2, seat="12A") | 4/4 correct, plus one invented | Fail — assert the argument set, not just the ones you expected |
| Under-specified request | "Book me a flight to New York" → ("AMS", "JFK", "2026-03-09", 1) | origin, date, passengers all invented | Fail — asking is the correct behaviour here |
Two things fall out of this table. First, the near-miss is invisible to any pass/fail that only checks the tool name and schema validity — it is the failure mode most likely to reach production, and it is only caught by comparing argument values. Second, the last row is scored on the opposite polarity: the agent that confidently fills in a missing date scores worse than the one that asks.
How the per-argument scores roll up is a deliberate choice. All-or-nothing (every argument must match) is the right default for calls with side effects — booking, payment, writes — because a call that is 75% correct still books the wrong flight. Partial credit is more useful for tracking progress on read-only calls, where a 3/4 tells you the agent is close. Report both if you can: the strict rate gates the release, the partial rate tells you which argument is costing you.
Tool calling accuracy vs. task success rate
These get used interchangeably and they are not the same measurement. Task success rate is end-to-end: did the user get what they asked for, one bit per task. Tool calling accuracy is per-call: was each step of the path correct. One is the outcome, the other is the mechanism.
| Task success rate | Tool call accuracy | |
|---|---|---|
| Unit | One task | One call, scored per dimension |
| Answers | Did it work? | Which part broke? |
| Ground truth | Final state or output | Expected call — tool name plus arguments |
| Blind to | Why it failed; cost and latency of the path | Whether the user's goal was actually met |
| Best for | Product and release reporting | Debugging, CI gating, regression triage |
They come apart in both directions, which is why you need both:
- High task success, poor tool accuracy. The agent makes redundant calls, guesses an argument and gets lucky, or recovers by brute force. The task completes, while cost and latency quietly climb and the near-miss arguments wait for an edge case to bite.
- High tool accuracy, failed task. Every call is exactly what you specified, and the task still fails — because the tool itself returned bad data, or the golden set encoded a path that no longer achieves the goal. This one is valuable: it points at the tooling or the case set rather than the agent.
The practical rule: report task success rate to the people deciding whether to ship, and tool call accuracy to the people fixing it. Gate CI on the per-dimension numbers, because those are the ones that localise a regression. If you only track task success, a regression tells you something broke last week and leaves you to find it by hand.
How do you build a golden set for tool calls?
Combine two sources. Mine real production traces for authentic cases and label them, then add synthetic edge cases to cover failure paths you haven't seen yet — ambiguous requests, missing parameters, tools that error, and near-miss arguments. Synthetic generation is fast but clusters around common patterns, so always review generated cases before they enter your official set.
How big should the set be? Big enough for the risk it carries. Volume is only meaningful when scored against a minimum that rises with the deployment's risk tier, which is why a high-risk pack with fifteen cases scores worse than a low-risk one with the same fifteen. Coverage of the failure paths beats raw count. And keep the set out of places it can leak: a tool-calling eval that ends up in public CI logs loses its value once agents are trained on it — see how to tell if your eval is contaminated.
How do you know your tool-calling eval actually works?
Before you trust an eval's verdicts on an agent you don't know, run it against agents whose quality you already know. This is mutation testing applied to the eval itself:
- A known-good agent (right tool, right arguments, asks when unsure) should score high.
- A known-broken agent (loose argument discipline, occasional guessing) should score visibly lower.
- A known-sabotaged agent (deliberately plausible-but-wrong calls) should score near the floor.
If the eval can't separate those three, it isn't measuring tool-calling — it's measuring something merely correlated with it, like response length. See the tool-calling correctness benchmark for this discriminating-power check run on a real reference panel, and what a reference-panel harness is for the method in depth.
How do you catch regressions in CI?
Treat tool-calling evals like unit tests. Every pull request that touches a prompt, a model version, or a tool schema triggers an eval run against the golden set, and a run that regresses past your threshold does not merge. Track the dimensions separately over time — a drop in error-recovery rate tells you something very different from a drop in tool selection.
CI is only half the picture, though. A golden set answers a closed question — does the agent still handle the cases we thought of? — and it cannot see the inputs you never imagined. Once the agent ships, pair the gate with a sensor: monitoring tool calls in production tracks selection distribution, argument validity, and unrecovered errors on live traffic, and feeds the failures it finds back into this golden set as new cases. It's also a different question from everything above: this section is about whether the agent still gets the decision right, not whether it holds up against a slow or flaky tool — see tool-calling accuracy vs reliability for the execution-time failures a clean golden set never exercises.
Related
- Tool-calling accuracy vs reliability — why passing this eval is not the same as holding up against timeouts, retries, and malformed responses.
- How to monitor tool calls in production — the production half of this pair, once the eval has gated the release.
- LLM judge bias — validating the judge you use for free-form arguments.
- What is a reference-panel harness — the discriminating-power method behind the "does the eval work" check above.
- How to tell if your AI eval is contaminated — why leaked cases quietly destroy these numbers.
- Why AI agent evals go stale — keeping this eval trustworthy over time.
- The tool-calling correctness pack — a verified, ready-to-run suite.
- Tool-calling correctness benchmark — discriminating power measured on a real reference panel.
- Capability packs — the full category.
FAQ
It measures whether an agent selects the correct tool, passes valid arguments, sequences calls correctly, and recovers from tool errors — evaluated as separate dimensions rather than a single pass/fail.
Score each call against an expected call on four dimensions separately. Tool selection: set equality against the expected tool set, with no unexpected calls. Arguments: schema validation followed by a normalised value match per argument. Sequencing: assert that the output of one call flows into the arguments of the next, and that ordering constraints hold. Error recovery: after a tool returns an error, assert the agent retried, fell back, or reported the failure, and never claimed success. Keep the four scores separate — a single pass/fail tells you the agent broke without telling you where.
Report a strict per-dimension rate rather than one aggregate. The most useful set is tool selection accuracy, strict argument-match rate (all arguments correct, which is the number that gates a release on side-effecting calls), partial argument credit for read-only calls, and error-recovery rate. Pair them with task success rate for outcome reporting, but gate CI on the per-dimension numbers, because those localise a regression.
Use deterministic checks (exact match, schema validation, type checks) wherever arguments are structured. Reserve an LLM judge only for free-form fields where meaning matters more than exact string equality.
It scales with the risk of the deployment, not a fixed target. Test-case volume should be scored against a minimum that rises with the deployment's risk tier — on the order of ten cases for a low-risk internal helper, several times that for an agent that can move money or change production state. Volume alone is not the point: a small set that covers failure paths (tool errors, ambiguous requests, missing parameters) discriminates far better than a large set of happy-path cases.
Make the tool return an error or an empty result, then score what the agent does next. Correct behavior is to retry with adjusted arguments, fall back to another tool, or tell the user it could not complete the task. The failure to catch is the agent that proceeds as though the call succeeded and fabricates a result from nothing.
Plausible-looking calls that break on edge cases: the right tool with a subtly wrong argument, or a correct call the agent never recovers from when the tool returns an error.
Yes. Treat them like unit tests: gate every pull request that touches a prompt, model version, or tool schema, and block merges that regress past your threshold.
Keep the case set out of public CI logs and blog posts, rotate cases over time, and periodically perturbation-test it: reword the cases without changing the underlying task and confirm the eval still separates a good agent from a broken one. A pack whose verdicts flip on a harmless rewording was keyed to its own wording, not to the capability.
Function-calling accuracy usually means the model emitted schema-valid JSON. Correctness is broader: whether that was the right tool for the request, whether the argument values are right (not just well-typed), and whether the agent should have asked a clarifying question instead of calling anything at all.
Only when the request was unambiguous. On a genuinely under-specified request, asking is the correct behavior and should score full marks — an agent that confidently guesses arguments on an ambiguous request is exhibiting the exact failure the eval should catch, not rewarding.
Use all-or-nothing for calls with side effects — booking, payment, writes — because a call with three of four arguments right still books the wrong flight. Partial credit is more informative for read-only calls, where it tells you which argument is failing. Reporting both is ideal: the strict rate gates the release, the partial rate tells you where to fix.
Yes — they catch different failures. A CI eval only tests the cases you thought of, while production monitoring watches live traffic for tool-selection drift, argument validation failures, and unrecovered errors on inputs your golden set never contained. The monitor also supplies the highest-value new test cases to feed back into the eval.
Yes, and usually you should. Give the agent the tool schemas and score the call it chooses — tool name plus arguments — against the expected call, which keeps the eval deterministic and free of side effects. Execute real or simulated tools only when you also need to test how the agent handles the returned result.