Skip to content

feat(plugins): toolscale sweep of tool count, similarity, query cue, and filler density - #324

Merged
SeraphimSerapis merged 5 commits into
mainfrom
feat/toolscale-319
Oct 27, 2026
Merged

SeraphimSerapis merged 5 commits into
mainfrom
feat/toolscale-319

Conversation

@SeraphimSerapis

Copy link
Copy Markdown
Owner

Problem

Closes #319.

tool-eval-bench could not show how tool-selection accuracy changes as the number of offered tools grows, or separate that from how similar the tool descriptions are. Category L measures one point (a fixed 52-tool set), TC-70 tests near-duplicates at a fixed count, and published results usually confound count with similarity, because adding tools adds near-duplicates.

Change

A new toolscale plugin (--toolscale, --toolscale-only, plugin toolscale). Each item is one streamed request offering a target tool, 3 neighbours from one similarity tier, and unrelated fillers up to N tools. Only the first tool call's name is graded.

Four independent, paired axes:

  • Count: 1 (target-only control) up to 64 with sparse fillers, or 248 with crowded fillers.
  • Similarity: near (same family), related (same domain), or distinct neighbours, defined by the structure of a new 272-tool catalog (17 domains x 4 families x 4 variants) and confirmed by a dependency-free TF-IDF check.
  • Cue: a literal query names the target's distinguishing qualifier, an implied one only implies it, on the identical tool list.
  • Filler density (opt-in): sparse (one tool per unrelated family) or crowded (whole near-duplicate families).

Fillers are shared across tiers and nested across N, neighbours keep their slots, and the target keeps its relative position, so every contrast is paired by target with a bootstrap interval.

The headline is two numbers, not one: accuracy when a tool was called (tool confusion) and call rate (whether the model acted at all). In calibration a mid-sized model scored 83% blended while never picking a wrong tool; every miss was a clarifying question. The blended accuracy is kept as the stored score so runs still sort.

--toolscale-catalog PATH runs a private catalog with the same schema; the report, stored results, logs, and errors withhold its tool names and queries.

Category L and all existing scores are unchanged. The plugin adds eight --toolscale* flags; the compatibility snapshots change only by those additions.

Calibration

Two local models ran the grids, about 10,000 live requests in total:

Model Accuracy when called (literal / implied) Call rate (literal / implied)
DeepSeek V4.1 Flash 100.0% / 99.7% 97.8% / 98.7%
Gemma 4 100.0% / 99.9% 83.2% / 86.4%

Neither model showed meaningful tool confusion on any axis: not with near-duplicates present, not when the query only implied the target, not in crowded catalogs, and not at 248 tools (DeepSeek 537/537, Gemma 483/486 correct when called). What separated the models was the call rate. Calibration also fixed one ambiguous literal query, one ambiguous implied query, and replaced a production-deploy variant that the strong model refused even as the only tool offered. docs/toolscale.md records the results; docs/decisions.md records why a larger catalog and real-world filler tools were deferred.

Regression coverage

tests/test_toolscale.py (97 tests) covers catalog validation and lint (including implied-query rules), every grid invariant (tier membership, shared and nested fillers, slot and position pairing, crowded families, per-density count limits, empty-grid rejection), grading outcomes, statistics, aggregation on partial grids, the two-number headline and call-rate denominator, report rendering (including pipes and carriage returns in model output), private-catalog withholding in details, reports, errors, and logs, and CLI wiring for flat flags and the plugin subcommand. An independent review also fuzzed the grid builder with 150 random catalogs against the same invariants.

Updated expectations in test_cli_dispatch_golden.py, test_json_output_contract.py, and the bench --help length budget in test_cli_parser.py reflect the new plugin and flags.

Changelog and documentation

  • changelog.d/319.added.md
  • docs/toolscale.md (new), README.md, docs/cli-reference.md, docs/methodology.md, docs/decisions.md, docs/adding-a-plugin.md

Contributor checklist

  • This PR contains one focused, logically related change.
  • Regression tests were added or a maintainer applied tests-not-needed.
  • A changelog.d/ fragment was added or updated, or a maintainer applied skip-changelog.
  • Fork PRs enable Allow edits from maintainers.
  • No credentials, live endpoints, generated run artifacts, or unrelated formatting changes are included.

Written with AI assistance; reviewed before opening.

Add a toolscale plugin that measures tool-selection accuracy as the
number of offered tools and the similarity of the target's nearest
neighbours vary independently. Each item offers the target, three
neighbours from one tier (near, related, distinct), and unrelated
fillers up to N tools, and grades the first tool call's name.

Fillers are shared across tiers and nested across N, and the target
keeps its relative position, so every contrast is paired by target.
Wrong picks are split into neighbour and filler picks, and accuracy
among items with a call separates abstention from confusion.

A 272-tool catalog (17 domains x 4 families x 4 variants) defines the
tiers. A TF-IDF check confirms near > related > distinct for every tool,
and lint rules keep every query unambiguous. The catalog was calibrated
against a live model: one ambiguous query was rewritten and one variant
the model refused even alone was replaced.

--toolscale-catalog runs a private catalog whose items the report and
stored results withhold. Category L is unchanged.

Closes #319
Calibration showed two models making almost no wrong tool picks, with
every miss in the blended accuracy being a clarifying question. Report
accuracy when a tool was called apart from the call rate, keeping the
blended accuracy only as the stored score so runs still sort.

Add a cue axis: every catalog tool now has a query_implied that implies
its qualifier without naming it, sent with the identical tool list as
the literal query so the cue effect is paired. Lint rejects implied
queries that name their own or a sibling's qualifier. A query check
reports how much each cue overlaps its target versus its closest
sibling. --toolscale-cues selects cues (default: literal,implied).

Calibration on two models found neither cue produces measurable
confusion up to 64 tools; docs record the results and point the next
difficulty axes at description quality, crowded fillers, and larger N.
One ambiguous implied query and the flight-cabin descriptions were
fixed. The console grid now fits 80 columns.
Add --toolscale-fillers sparse,crowded. Crowded fillers draw whole
4-variant families instead of one tool per family, walking the same
family order, so the target's neighbours, slot layout, and target
position are unchanged and sparse minus crowded is paired by target.
Crowded skips the control and N=4, which have no fillers, and adding
it leaves the sparse items untouched.

The report adds a density effect, a headline by filler density, density
slices in every grid and effect table, and a filler-crowding measure
(each filler's similarity to its closest other filler) that confirms
crowded lists are denser: 0.605 against 0.174 on the built-in catalog.

Calibration on two models found no density effect; docs record it.
Give each filler density its own maximum count. Sparse lists still stop
at the number of unrelated families (65 built-in); crowded lists reach
the variant count left after reserving the three largest outside
families (248 built-in). Counts above the sparse maximum build crowded
cells only. Long lists stretch the request timeout, and runs above 128
tools warn about provider tool caps.

Fixes from an adversarial review:
- an empty or partly empty grid (crowded-only at N=4) now fails with a
  clear error instead of an IndexError or silently dropped counts;
- a control-only run scores the control, and the final tally counts the
  same items as the score;
- the call rate leaves request errors out of its denominator;
- the failed-items table escapes pipes and carriage returns in model
  and catalog text, and shows why a request failed;
- private-catalog tool names stay out of grid errors and log lines;
- the flat-nearest similarity check reads sparse lists, filler crowding
  is measured on each density's own largest list, and the console picks
  effects from the measured counts;
- docs now state the real count limits, the timeout stretch, the call
  rate and score denominators, and the provider cap.
@SeraphimSerapis
SeraphimSerapis merged commit 0fa98ab into main Oct 27, 2026
10 checks passed
@SeraphimSerapis
SeraphimSerapis deleted the feat/toolscale-319 branch October 27, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tool-count sweep: selection accuracy vs number of tools, with description similarity as a separate axis

1 participant