.pptx files and the
finance skill that builds decks both read as PowerPoint tools. Ask for changes to an
existing deck and the agent may load the one that builds new ones. On a turn where no skill
fits at all, it may still load one anyway, because a list of names invites a guess.
We use progressive disclosure, reading all 194 skills cheaply and then reading three of
them in detail. Two TypeSafe requests go in front of the decision on which skill to load,
if any. The first ranks every skill in the roster against the user’s turn and answers
whether the turn needs a skill at all. The second re-reads only the top three, now with
each skill’s full description and the opening of its instructions, and is free to reject
all of them.
The winner’s name goes into one extra line of the agent’s system prompt for that turn:
claude-haiku-4-5-20251001, using skills
from the Hermes roster:
The third row is the floor: an agent handed the right skill still does not always load it,
and no selection method gets below that.
You end up with a
suggest() function that returns at most one skill name, a
suggestion_block() that wraps it for the system prompt, and the harness that produced the
table above, ready to point at your own roster.
Setup
- Install the TypeSafe client, the Anthropic client, and the shared cookbook helpers.
- Set a TypeSafe API key, and an Anthropic key for the agent being measured.
Note: this page’s code blocks form one script. To run it, put them in one file in the order they appear.
Caching results
hermes_roster.json, requests.json and the answer cache json_cache.json are in
typesafe-public-examples.
Put them in the folder you run the script from.
We cache each call’s result in json_cache.json, keyed on its inputs, so re-running
replays the numbers below instead of calling either API. Delete json_cache.json to run
live. The published run used jev-1.14.0 and claude-haiku-4-5-20251001, rendered
2026-10-30.
Step 1: load the roster
hermes_roster.json holds the 194 skills of
NousResearch/hermes-agent at one pinned
commit. Each record holds a skill’s name and category, the description as the index shows
it, the full description, and the opening of its SKILL.md. The skills’ licences are in
DATA_LICENSE.md.
The index below, and the instructions above it in the prompt, are copied from Hermes.
Step 2: score the agent on its own
requests.json holds 452 single-turn requests, 280 of them covered by exactly one skill
and the other 172 covered by nothing.
The covered requests were written by Claude Sonnet 5 from each skill’s own SKILL.md, so
the labels are trustworthy and the requests are easier than the ones users send.
The 172 uncovered ones were all written to punish guessing: 84 everyday requests, 42
technical questions no skill serves (explain what a monad is), and 46 that ask for
something specific the roster has no skill for, like post this to Mastodon on a roster
that covers X and nothing else.
Scoring reads the agent’s first response only. Both numbers are error rates, so lower is
better on each:
- wrong load: of the covered requests, the share where the first
skill_viewcall was not the covering skill. A turn that loaded nothing at all counts as a miss. - needless load: of the uncovered requests, the share where the agent called
skill_viewat all.
skill_view to load a skill using a
free-text name. The name must match the skill exactly for a correct load.
Step 3: rank the whole roster
One request carries two kinds of question:whichis aChoicequestion over all 194 skill names, with the index description as each option’s criteria (the same text the agent itself gets). Its probabilities are the ranking.- three
Noulquestions about the request, each asking in a different way whether the request wants an action taken rather than an explanation given. We average the three, flippingprose_sufficesbecause a yes there means no skill is needed. - one
Noulquestion,named: does the request ask for a specific named tool, format, template, or method?
named measures the second, so it opens the
gate on its own instead of joining the average, where the three action answers would
outvote it. We suggest nothing only when both reasons come back low.
One Choice question holds a roster this size comfortably. A few times larger, you would
split it into chunks, rank each chunk, and then rerank the chunk winners as in step 4.
named opens the gate. Its top option is the meme skill.
That leaves the deck. Both leaders are .pptx skills, and with only 60 characters of
description each, the Choice question over all 194 skills puts the deck-building skill
ahead of the Office skill, for a request to edit an existing deck.
Step 4: rerank the top three
Three options leave room for the full description plus the opening of each skill’s ownSKILL.md, so the second request puts the same question to better evidence:
whichis aChoicequestion over the shortlist, with that longer text as each option’s criteria.fits::{name}is oneNoulquestion per candidate: does this skill do the specific thing the request asks for? Each is answered on its own, so they can all come back low, and when they do the shortlist is dropped.
.pptx skills separate once each one brings its own text: the deck request flips
to the Office skill, which edits existing files.
The fits nouls score both .pptx skills almost the same, because both can do something
with a deck. They decide a different thing from the Choice question, which settles
which skill, while the nouls settle whether to say anything at all. The
Line-by-line search cookbook pairs a Choice question and a
Noul question the same way, to tell whether a document answers a query at all.
The Mastodon request stops at the second pass. None of its three near-misses does what it
asks, so even its best fits noul comes back under the threshold and nothing is suggested.
The whole recipe is two requests and two thresholds, and it returns at most one skill name.
To point it at your own roster, replace hermes_roster.json. Every question above reads
name, description, description_full, and body out of that file, and nothing else
in suggest() is specific to Hermes.
Step 5: measure the suggestion
Each of the 452 requests goes to the agent three times, one measured turn each. The runs differ only in what the agent is told:
No real system knows the covering skill in advance, so the third arm sets the floor the
other two are measured against.
The wording of that suggestion is doing two jobs. It says the suggestion can be ignored,
because pushing harder wins compliance on wrong suggestions too, and a wrong one is worse
than none. And a turn with nothing to suggest still sends a sentence saying so; sending
nothing at all would leave the roster’s own “err on the side of loading” instruction
unopposed.

What the results show
- Wrong loads fell from 15.7% to 7.9%, closing more than half the gap between guessing from a truncated index and being handed the answer. Needless loads fell from 14.0% to 5.2%, closing nearly all of theirs.
- Some requests the agent had right on its own come back wrong once a suggestion is attached: of the 280 covered requests, the suggestion broke 7 and fixed 29.

