Tool Loop

2026-07-31 05:33 UTC · 2f67443

About this benchmark

Twenty-nine tools, five requests, and no request any one tool can answer. The calls are answered from a lookup table beside each case, so the loop runs for real: the model calls, reads a result, and calls again with what the result told it.

Every chain is locked by an identifier the model cannot know in advance. Places are found by area, so coordinates have to be resolved first; a session is ranked by exchange code and priced by ticker; a fare needs two station ids. A call whose identifier the world does not recognise is refused rather than answered loosely, so guessing costs a turn and shows up in `rejected`.

The answer is a gate, so a right chain ending in a wrong number scores zero while the per-field detail still says which hop broke. Three cases leave the arithmetic to the model. The distractors are close rather than absent: council parking beside a venue's own car park, an e-hailing estimate beside a rail fare, and all four ranking measures populated, so the wrong one returns a plausible company rather than an error.

Cases

  • dividend-yield: No tool returns a yield: it is 0.63 declared against a last price of 9.84, divided by the model. `get_company_profile` answers for the same ticker with everything except that.
  • index-close: Two calls are enough. Everything else the world answers here is real and adjacent, so a model that keeps going lands on constituents and a market status.
  • mall-parking: `find_places` takes an area and not coordinates, so the geocode is unavoidable. The nearest mall is 0.4 km out and the famous one 2.9 km, and free bays are a subtraction the model does itself.
  • most-traded-stock: Shares changing hands is `volume`, and all four measures are populated, so the wrong one returns a different company at a plausible price rather than an error. The volume list is unsorted and the leader sits third.
  • station-fare: The two middle hops are independent, so grading matches on tool name rather than position. The nearest station is not the one sharing the area's name, and both routes to KL Sentral are priced.

Results

14/14 targets scored · 5 trials each · $0.85 total

Cost vs latencylower-left is better
Latency & costshorter is better
TargetScoreStddevLatencyTok/sCostErrors
OpenAIgpt-5.6-luna@openai100.00.09.5s19.3best$0.00240/5
bestQwenqwen3.7-flash@alibaba100.00.06.8s120.9$0.00240/5
OpenAIgpt-5-mini@openai100.00.022.7s46.2$0.01460/5
MoonshotAIkimi-k2.5@moonshotai100.00.017.3s20.2$0.01850/5
OpenAIgpt-5.6-terra@openai100.00.07s22.1$0.02290/5
Z.aiglm-5.2@z-ai100.00.017.6s14.5$0.04660/5
DeepSeekdeepseek-v4-pro@together100.00.019.6s20.4$0.05130/5
Grokgrok-4.5@xai100.00.07.1s33.6$0.06140/5
Claudeclaude-haiku-4.5@anthropic100.00.06.7s48.4$0.08890/5
Geminigemini-3.6-flash@google-ai-studio100.00.07.6s109.3$0.13040/5
Z.aiglm-4.7@cerebras100.00.0best3.1s134.6$0.17440/5
Claudeclaude-sonnet-5@anthropic100.00.09.1s33.4$0.19970/5
Geminigemini-2.5-flash@google-ai-studio4 field missesstation-fare: calls (1/1)station-fare: calls.#(name=="get_transit_fare").args.from_station_id (1/1)station-fare: content (1/1)station-fare: rejected (1/1)80.040.04.3s25.0$0.01460/5
Geminigemini-3.5-flash-lite@google-ai-studio2 field missesstation-fare: calls.#(name=="get_transit_fare").args.from_station_id (1/1)station-fare: content (1/1)80.040.03.4s32.5$0.02110/5

Note

  • First run on this branch's fixture identifiers and match-query assertions, so the scores are not directly comparable with the run of 31 July at 04:01 even though the roster and the trial count are the same.
  • No target skipped a graded call. The three that supplied an identifier out of their own knowledge on 31 July, Sonnet 5 and Haiku 4.5 among them, called the whole chain this time.
  • gemini-2.5-flash misspelt the same argument key on two successive calls, was refused both times, and then reported a fare that no tool in this run returned. Its first attempt carried the right station id, so the refusal cost it the answer rather than the lookup.
  • gemini-3.5-flash-lite made four well-formed calls with nothing rejected and answered for a station it was not asked about. Its miss is a wrong answer, not a broken shape, and the two 80s on the page do not mean the same thing.
  • deepseek-v4-pro made exactly the calls each case expects with none refused, after the previous run left it unscored on one case and spending a whole step budget on another. Nothing in this run exceeded five calls or two refusals.
  • One trial per case, so the 40.0 in the stddev column is spread across the five cases and not flakiness across repeats. Nothing here separates a target that cannot do the work from one that did not do it once.
  • 345,370 of 904,375 prompt tokens were served from cache, and unevenly: gpt-5.6-luna read 78% of its station-fare prompt from cache against qwen3.7-flash's 36%. The cost and latency columns flatter the warm targets.

Conclusion

Twelve targets tie at 100, so cost and latency decide, and they point at qwen3.7-flash at $0.000486 a task. gpt-5.6-luna is $0.000007 a task cheaper and gives back 2730ms for it, at 19.3 tok/s against 120.9, and its edge is the softer of the two numbers because more of its prompt came from cache. Where latency is the whole requirement, glm-4.7@cerebras answers in 3089ms for seventy-two times the money.

Both targets below 100 lost the same case, for reasons that do not deserve the same reading. gemini-3.5-flash-lite returned a clean, well-formed loop and picked the wrong subject, which is a single visible field and the kind of miss a sharper prompt can move. gemini-2.5-flash malformed an argument and then stated a number it had never been given, which is the failure that matters here: the answer gate caught it this once, and at one trial per case there is no way to tell whether that is habitual. Run trials against that target before trusting its 80, and treat the fabricated figure, not the score, as the finding.