Skip to main content

ToolSelectionAccuracy

Measures whether the agent selected the correct tools to accomplish the task. Compares actual tool calls against expected tool calls by name.

PropertyValue
Default threshold0.8
Requires LLM judgeNo
Required fieldstoolCalls, expectedToolCalls
Available sinceP0

How It Works

The metric computes the intersection over union between the set of actual tool names called and the set of expected tool names:

score = |actual ∩ expected| / |actual ∪ expected|

Order does not matter by default (configurable).

Example

var testCase = AgentTestCase.builder()
.input("Cancel order #12345 and issue a refund.")
.toolCalls(List.of(
ToolCall.of("GetOrder", Map.of("orderId", "12345")),
ToolCall.of("CancelOrder", Map.of("orderId", "12345")),
ToolCall.of("IssueRefund", Map.of("orderId", "12345"))
))
.expectedToolCalls(List.of(
ToolCall.of("GetOrder", Map.of("orderId", "12345")),
ToolCall.of("CancelOrder", Map.of("orderId", "12345")),
ToolCall.of("IssueRefund", Map.of("orderId", "12345"))
))
.build();

EvalScore score = new ToolSelectionAccuracy(0.8).evaluate(testCase);
// score.value() → 1.0
// score.passed() → true

Missing tool

var testCase = AgentTestCase.builder()
.toolCalls(List.of(
ToolCall.of("GetOrder", Map.of("orderId", "12345")),
ToolCall.of("CancelOrder", Map.of("orderId", "12345"))
// missing: IssueRefund
))
.expectedToolCalls(List.of(
ToolCall.of("GetOrder", ...),
ToolCall.of("CancelOrder", ...),
ToolCall.of("IssueRefund", ...)
))
.build();

EvalScore score = new ToolSelectionAccuracy(0.8).evaluate(testCase);
// score.value() → 0.67
// score.passed() → false
// score.reason() → "Missing expected tool: IssueRefund"

In JUnit 5

@Test
@AgentTest
@Metric(value = ToolSelectionAccuracy.class, threshold = 0.9)
void agentShouldSelectCorrectTools() {
var testCase = AgentTestCase.builder()
.input("Cancel order #12345 and refund it.")
.actualOutput(agent.run("Cancel order #12345 and refund it."))
.toolCalls(agent.getLastToolCalls())
.expectedToolCalls(List.of(
ToolCall.of("GetOrder", Map.of("orderId", "12345")),
ToolCall.of("CancelOrder", Map.of("orderId", "12345")),
ToolCall.of("IssueRefund", Map.of("orderId", "12345"))
))
.build();

AgentAssertions.assertThat(testCase)
.calledTool("GetOrder")
.calledTool("CancelOrder")
.calledTool("IssueRefund")
.neverCalledTool("DeleteOrder")
.meetsMetric(new ToolSelectionAccuracy(0.9));
}

Configuration

OptionTypeDefaultDescription
thresholddouble0.8Minimum score to pass
orderMattersbooleanfalseRequire tools called in the expected order
new ToolSelectionAccuracy(0.9, true)   // order must match