Evaluating agent skills
A skill is a folder of know-how that an agent loads only when it thinks the skill is relevant. That single design choice creates two separate ways to fail: the skill never fires when it should, or it fires and does not actually help. This module shows how to measure both, following the method in Anthropic's own open-source skill-creator skill, and then goes further: adherence, many skills at once, portability across models, and skills as a security supply chain.
By the end of this module you can
- Explain what a skill is (folder,
SKILL.md, frontmatter, bundled files), how progressive disclosure loads it in three levels, and how it differs from a tool, an MCP server and a system prompt. - Treat triggering as a classification problem: build should-trigger and near-miss should-not-trigger sets, run repeats, and compute trigger rate, precision, recall and a confusion matrix.
- Optimize a skill's description with a train / held-out split and explain why you pick the winner by held-out score.
- Run an uplift eval: with-skill vs baseline runs in parallel, objective assertions, a grader that cites evidence, aggregate stats with mean and standard deviation, and an analyst pass that catches non-discriminating and flaky assertions.
- Check adherence from the trajectory: did the agent read the referenced file, run the bundled script, follow the steps in order?
- Evaluate selection when dozens of skills are installed, portability across models and harnesses, and safety of third-party skills.
- Compute trigger metrics and a paired bootstrap confidence interval for uplift in Python.
What a skill is
Imagine you hire a very capable generalist. They can write, code and analyze data, but they do not know your company's way of doing things: which report template finance expects, which chart colors are on-brand, which validation step always catches the broken export. You could brief them on all of it every morning, but most days they would not need most of it. A better approach is a shelf of labeled binders. The generalist glances at the labels, pulls down the right binder when a task calls for it, and only then reads the details.
An agent skill is one of those binders. Anthropic introduced Agent Skills in October 2025. Concretely, a skill is a folder containing:
- A file called
SKILL.md. It starts with YAML frontmatter (a small block ofkey: valuelines between---markers) holding at least anameand adescription, followed by Markdown instructions (the body). - Optional bundled resources:
scripts/(executable code for deterministic or repetitive steps),references/(longer documents loaded into context only when needed) andassets/(files used in the output, such as templates, icons or fonts).
quarterly-report skill as the running example.Two limits from the spec matter for evals. The name can be at most 64 characters and the description at most 1,024 characters; skill-creator's validator enforces both and its description optimizer aims for roughly 100 to 200 words. That short description is everything the agent knows about the skill before deciding to open it, so it carries almost all of the triggering behavior. Hold on to that thought: it is why trigger evals exist.
Progressive disclosure: three levels of loading
The key design idea is progressive disclosure: show the model a little, and let it ask for more. Skills load in three levels:
- Level 1, metadata. The
nameanddescriptionof every installed skill sit in the context all the time.skill-creatorputs this at about 100 words per skill. - Level 2, the body. When the agent decides a skill is relevant, the whole
SKILL.mdbody is loaded. The guidance is to keep it under about 500 lines, and to split anything bigger into reference files with clear pointers. - Level 3, bundled resources. Reference files are read only when a step needs them. Scripts can be executed without their code ever entering the context: only their output does. This level is effectively unlimited.
Skill vs tool vs MCP server vs system prompt
Beginners often blur these four. They sit in different places around the agent loop and fail in different ways, so they need different evals. This module covers skills; tools and MCP servers get their own treatment in Module 24.
| System prompt | Tool | MCP server | Skill | |
|---|---|---|---|---|
| What it is | Standing instructions | A callable function with a schema | A process exposing tools, resources and prompts over a protocol | A folder of instructions plus optional scripts and files |
| When it is in context | Always | Schema always; result after a call | Its tool schemas, usually always | Label always; body and files on demand |
| Who decides to use it | Nobody: it is just there | Model picks the tool and arguments | Model picks one of its tools | Model decides from the description alone |
| Typical failure | Too long, instructions ignored | Wrong tool, wrong arguments | Same as tools, plus server errors | Never opened, opened wrongly, or opened but not followed |
| Main eval | Instruction following (Module 06) | Tool-call accuracy (Module 06, Module 24) | Tool and server evals (Module 24) | Trigger eval + uplift eval + adherence (this module) |
In December 2025 the format was published as an open standard, with a specification at agentskills.io, and other tools now load the same folders (VS Code documents "Use Agent Skills in VS Code", for example). For evals this matters: a skill is a portable artifact that may run under several models and harnesses you did not test, which is why this module has a section on portability.
Why skills need their own evals
A normal prompt change has one job: make the output better. A skill has two independent jobs, and skill-creator frames its whole method around them:
- Does it trigger at the right times? The agent must decide, from the description alone, to open the skill for relevant requests and to leave it closed for irrelevant ones.
- When it triggers, does it improve the output? A skill that loads perfectly but adds nothing (or makes things worse) is just context bloat.
These are independent. A brilliant body behind a vague description never gets read. A crisp description in front of a muddled body gets read and then ignored. Measuring only the end result mixes the two, and you cannot tell which half to fix.
So a complete skill eval has at least three measurements, plus two situational ones:
A classification test: for each query, should the skill fire, and did it? Metrics: trigger rate, precision, recall.
A paired A/B test: same tasks with and without the skill. Metrics: pass-rate delta, plus time and token cost.
A process check on the trajectory: did the agent do what the skill said, in order, with the bundled files?
Selection among dozens of skills, behavior across models, and the risks of installing someone else's code.
A recipe card in a kitchen binder can fail in three ways. Nobody pulls it out when making that dish (trigger). The cook pulls it out but improvises anyway (adherence). The cook follows it exactly and the dish is no better than their usual version (uplift). A food critic who only tastes the final plate cannot tell you which one happened.
Part 1: Trigger evals
Triggering is a binary classification problem. Each query is a case. The label is "should this skill fire?" and the prediction is "did it fire?". Everything you learned about precision and recall in Module 04 applies directly.
How triggering actually works
Skills appear in the agent's list of available skills with just their name and description, and the model decides whether to consult one based on that description. skill-creator adds two observations that shape how you write trigger evals:
- Claude tends to under-trigger skills, that is, not use them when they would help. Its advice is to make descriptions "a little bit pushy": say what the skill does and list the situations where it should be used, even when the user does not name the skill.
- Simple, one-step queries may not trigger a skill even when the description matches perfectly, because the agent can handle them directly with basic tools. "Read this PDF" is a poor trigger test. Complex, multi-step or specialized queries trigger reliably when the description matches.
The second point is subtle and important: a trigger eval full of short, easy queries will show low recall for every description, and you will waste time "fixing" a description that is fine. Your queries must be substantive enough that a skill would genuinely help.
Building the query set
skill-creator recommends about 20 queries: 8 to 10 that should trigger and 8 to 10 that should not. The format is a plain JSON list:
[
{"query": "ok so finance just dropped q3_sales_FINAL.csv in /data and the CFO wants the usual quarterly writeup by thursday, same format as last quarter pls", "should_trigger": true},
{"query": "Can you prepare the Q2 business review document for EMEA? Revenue by country is in the 'Q2' tab of the attached sheet; flag anything down more than 10% QoQ.", "should_trigger": true},
{"query": "what was our Q3 revenue growth vs Q2? just the percentage, csv is data/q3_sales.csv", "should_trigger": false},
{"query": "write a python job that pulls the quarterly CSV exports from s3 every night and loads them into postgres", "should_trigger": false}
]
The guidance on writing these is specific, and worth following closely:
- Realistic, concrete and detailed. File paths, column names, company context, a little backstory, casual phrasing, even typos. Bad:
"Create a chart". Good: a paragraph that sounds like a real person with a real file. - Should-trigger coverage. Different phrasings of the same intent (formal and casual), cases where the user never names the skill or file type but clearly needs it, uncommon use cases, and cases where this skill competes with another and should win.
- Should-not-trigger = near-misses. The most valuable negatives share keywords or concepts with the skill but actually need something else: adjacent domains, ambiguous phrasing where naive keyword matching would fire. "Write a fibonacci function" as a negative for a report skill is too easy and tests nothing. In the example above, both negatives mention quarterly CSVs, but one only needs a number and the other needs a data pipeline.
Repeats and the trigger rate
Triggering is stochastic: the same query can fire on one run and not on the next. So each query is run 3 times, and the result per query is a trigger rate (fired runs divided by total runs). In skill-creator's scripts a query "passes" if its trigger rate is at least a threshold (default 0.5) when it should trigger, or below that threshold when it should not.
How does the harness know a skill fired? skill-creator's run_eval.py installs the description temporarily, runs the query headlessly through the Claude Code CLI (claude -p), and watches the streamed events: if the agent's first tool call is a Skill or Read call that names this skill, it counts as triggered; any other first tool call counts as not triggered. That is a precise, cheap, code-based grader (Module 03), which is exactly what you want for a classification eval.
The confusion matrix, worked
Suppose you run 10 should-trigger and 10 should-not-trigger queries, 3 times each: 60 runs. You can count at the run level:
At the query level, with the 0.5 threshold, the same data might look like this: of the 10 positives, 8 fired in at least 2 of 3 runs (pass) and 2 fired in 1 or 0 runs (fail); of the 10 negatives, 9 fired in at most 1 run (pass) and 1 fired in 2 runs (fail). Score: 17 of 20 queries pass. The two views answer different questions. Run-level rates tell you how often a real user would see the behavior; query-level pass counts tell you which phrasings are broken, which is what you need to fix the description.
A query that fires 1 run in 3 is not "a miss". It is sitting on the model's decision boundary, and a tiny change in wording, model version or the other installed skills will flip it. Report the trigger-rate distribution (how many queries at 0/3, 1/3, 2/3, 3/3), not just the pass count. Many queries at 1/3 or 2/3 means your description is ambiguous, even if the pass count looks fine.
What a good description looks like
Because the description is the only thing the agent sees before deciding, description quality is trigger quality. Before running any optimizer, check a description against the rules that skill-creator and its scripts encode:
| Property | Why it matters for triggering | Weak | Stronger |
|---|---|---|---|
| Says what and when | All "when to use" information belongs in the description, not the body, because the body is not visible until after the decision | "Quarterly report helper." | "Build the quarterly sales report from a CSV export. Use whenever the user mentions QBRs, quarterly writeups or finance's quarterly CSV..." |
| A little pushy | Counters the tendency to under-trigger | Lists only the exact skill name | Adds "even if they don't explicitly ask for a report" |
| Short | Hard limit of 1,024 characters (longer is truncated); the optimizer aims for about 100 to 200 words; every word is paid on every request | A 400-word essay | Two or three dense sentences |
| Distinct from neighbors | Overlapping descriptions collide (Part 4) | "Helps with reports and slides" | Names what it does not cover, or the artifact it produces |
A description review like this is cheap and catches the obvious problems, but it is a hypothesis, not a measurement. Only the trigger eval tells you whether the description actually works with a given model.
Try it: move the description's trigger threshold and watch precision and recall trade off.
Optimizing the description without fooling yourself
Once you can score a description, the obvious next step is to improve it automatically: ask a model to rewrite the description based on which queries failed, re-score, repeat. skill-creator has a script for exactly this (run_loop.py), and its design contains a classic machine-learning lesson.
If you tune the description against the same 20 queries you score it on, you will overfit: the description slowly turns into a list of those exact queries' keywords, scores 20 of 20, and then fails on the next real user. So the loop:
- Splits the set into 60% train and 40% held-out test, stratified so both halves contain should-trigger and should-not-trigger queries (with 10 and 10, that is 6 + 6 train and 4 + 4 test).
- Evaluates the current description on both halves, running each query 3 times.
- Asks Claude to propose an improved description based on the train failures only.
- Repeats for up to 5 iterations.
- Returns the
best_description, selected by test score, not train score.
It also runs the eval with the same model that powers the user's session, so the triggering test matches what the user will actually experience.
With 8 held-out queries, one query flipping moves the score by 12.5 points, and the queries themselves are stochastic. Treat the held-out score as a guard against gross overfitting, not a precise measurement. If triggering really matters (a skill used by thousands of people), grow the query set, keep a second untouched set for a final check, and add real queries from production logs where the skill should have fired but did not.
Part 2: Uplift evals
Now the second question: when the skill is used, is the output better? "Better" must mean better than something. The heart of an uplift eval is a paired comparison: the same task run with the skill and against a baseline.
Choosing the baseline
- New skill: the baseline is no skill at all. Same prompt, same model, same tools, no skill path. This answers "is the skill worth having?".
- Improving an existing skill: the baseline is the old version. Snapshot the skill folder before editing and point the baseline run at the snapshot. This answers "is the new version better than the old?".
skill-creator insists on one operational detail: spawn the with-skill and baseline runs in the same turn, at the same time, rather than running all with-skill runs first and baselines later. Running them together means both arms see the same model endpoint, load and conditions, and it avoids the temptation to stop once the with-skill results look good. It is the agent-eval version of running control and treatment together in an experiment.
The test cases: evals.json
Test prompts live in evals/evals.json inside the skill folder. Start with 2 or 3 realistic prompts, and add verifiable statements (the expectations, also called assertions) while the first runs are in progress:
{
"skill_name": "quarterly-report",
"evals": [
{
"id": 1,
"prompt": "finance dropped the Q3 export in data/q3_sales.csv, can you turn it into our usual quarterly report? regions are col B, revenue col E",
"expected_output": "A .docx built from the company template with a revenue-by-region chart and a short executive summary",
"files": ["evals/files/q3_sales.csv"],
"expectations": [
"The report is built from assets/template.docx (same headers and footer)",
"Revenue-by-region totals in the chart match the CSV to the dollar",
"The executive summary names the highest and lowest region by revenue",
"The chart was produced by scripts/make_chart.py"
]
}
]
}
What makes a good assertion, according to skill-creator:
- Objectively verifiable. Someone else (or a script) could check it and agree. "Totals match the CSV to the dollar" is checkable; "the report is professional" is not.
- Descriptively named. Someone glancing at the results should understand what each one checks without opening anything.
- Not forced onto subjective qualities. Writing style or design quality go to human review instead. Forcing a fuzzy judgment into a pass/fail assertion gives you a noisy number you will over-trust.
- Scripted when possible. For assertions a program can check (file exists, totals match, template headers present), write a script instead of eyeballing: faster, more reliable, reusable across iterations. This is the code-grader-first rule of Module 03.
Philipp Schmid's Practical Guide to Evaluating and Testing Agent Skills (2026) reaches a very similar recipe from outside Anthropic. His own summary: define success criteria covering outcome, style and efficiency, then write around 10 to 12 prompts with deterministic checks. The three criteria line up with this module: outcome is the uplift eval, style is where human review or a calibrated judge comes in, and efficiency is the time and token columns. His AI Engineer talk makes the point in its title: "Don't Ship Skills Without Evals".
The pipeline
skill-creator. Paired runs start together, a grader checks every assertion against the transcript and output files and cites evidence, an aggregation script summarizes each configuration, an analyst pass looks for patterns the averages hide, and a human reviews real outputs, not just numbers.The grader: evidence, not vibes
skill-creator's grader (agents/grader.md) is an LLM-as-a-judge (Module 13) with an unusually strict brief. It reads the full transcript and examines the output files itself (it is told not to trust what the transcript says was produced), then marks each assertion pass or fail. A few of its rules are worth memorizing because they apply to any grader:
- Pass requires genuine substance. "A file exists with the right name but wrong or empty content" is a fail. So is meeting an assertion "by coincidence rather than by actually doing the work".
- The burden of proof is on the assertion. When uncertain, fail.
- No partial credit. Each assertion is pass or fail.
- Extract and verify claims beyond the assertions: factual ("the form has 12 fields"), process ("used pypdf") and quality claims, flagging any that cannot be verified.
- Critique the evals themselves. "A passing grade on a weak assertion is worse than useless: it creates false confidence." If an assertion would also pass for a clearly wrong output, or an important outcome has no assertion, the grader says so.
The result is a grading.json per run. Its expectations array must use the field names text, passed and evidence, because the viewer depends on them:
{
"expectations": [
{
"text": "Revenue-by-region totals in the chart match the CSV to the dollar",
"passed": true,
"evidence": "Ran check_totals.py: all 6 regions match (APAC 1,204,310 = CSV sum)"
},
{
"text": "The executive summary names the highest and lowest region by revenue",
"passed": false,
"evidence": "Summary names APAC as highest but calls LATAM lowest; CSV shows MEA is lowest"
}
],
"summary": {"passed": 3, "failed": 1, "total": 4, "pass_rate": 0.75},
"claims": [
{"claim": "All six regions are included", "type": "factual", "verified": true,
"evidence": "Six rows in the chart data table"}
],
"eval_feedback": {
"suggestions": [
{"assertion": "The report is built from assets/template.docx (same headers and footer)",
"reason": "Passes in baseline runs too when the model copies a generic header; check the footer text exactly"}
],
"overall": "Totals are well checked; the template assertion is weak."
}
}
Aggregate, then look past the aggregate
An aggregation script (aggregate_benchmark.py) turns all the grading files into a benchmark.json with, for each configuration, the mean, standard deviation, min and max of pass rate, time and tokens, plus the delta between configurations. Time and tokens come from a timing.json saved for each run as soon as it finishes (the notification that carries total_tokens and duration_ms is the only chance to capture them).
Then comes a step many teams skip: the analyst pass (agents/analyzer.md, "Analyzing Benchmark Results"). Its job is to surface patterns that aggregate numbers hide, not to suggest fixes. For each assertion across all runs, it asks:
- Does it always pass in both configurations? Then it does not differentiate skill value (a non-discriminating assertion).
- Does it always fail in both? It may be broken, or beyond the model's ability.
- Does it always pass with the skill and fail without? That is where the skill clearly adds value.
- Does it always fail with the skill but pass without? The skill may be hurting.
- Is it highly variable? Possibly a flaky assertion or non-deterministic behavior.
It also compares time, tokens and tool calls across configurations (does the skill make runs much slower or more expensive?) and looks for outlier runs that skew averages.
Worked example: the quarterly-report skill
Four test prompts, 3 runs each per configuration (a new skill, so the baseline is no skill). Here is what the benchmark summary and a per-assertion breakdown might look like. These numbers are illustrative, chosen to show the patterns the analyst pass is meant to catch.
| Configuration | Pass rate (mean ± sd) | Time, s | Tokens |
|---|---|---|---|
| with_skill | 0.86 ± 0.10 | 52 ± 12 | 6,100 ± 900 |
| without_skill | 0.44 ± 0.17 | 34 ± 8 | 3,500 ± 600 |
| delta | +0.42 | +18 | +2,600 |
| Assertion (12 runs per arm) | With skill | Without | Analyst reading |
|---|---|---|---|
| Output is a .docx file | 12/12 | 12/12 | Non-discriminating: drop it or strengthen it |
| Built from the company template | 12/12 | 0/12 | Clear skill value (the baseline cannot know the template) |
| Chart totals match the CSV | 11/12 | 7/12 | Real uplift from the bundled script |
| Summary names top and bottom region | 10/12 | 8/12 | Small difference; not significant at this n |
| Chart made by make_chart.py | 11/12 | 0/12 | Process check: measures adherence, not uplift |
The analyst notes might read:
[
"Assertion 'Output is a .docx file' passes 100% in both configurations - may not differentiate skill value",
"Eval 'emea-multi-currency' shows high variance with skill (1.00, 0.40, 1.00) - run 2 converted EUR twice; may be flaky",
"Without-skill runs never use the company template (0/12)",
"Skill adds 18s and ~2,600 tokens per run on average but improves pass rate by 0.42"
]
Three expert lessons hide in this small example:
- Some assertions inflate uplift by construction. "Chart made by make_chart.py" and "built from the company template" can never pass without the skill, because the baseline has no access to those files. They are legitimate checks, but they measure "did the agent use the skill?" (adherence), not "is the result better?". Report them separately, or the headline delta will look bigger than the real quality gain. The honest uplift number comes from outcome assertions both arms could pass (totals correct, summary correct).
- Cost is part of the result. A skill that adds 50% tokens and 18 seconds for a +0.42 pass-rate gain is a great trade. The same cost for a +0.03 gain probably is not. Always read the three columns together.
- A 4-prompt suite is a smoke test.
skill-creatoritself says to expand the test set and try again at larger scale once the skill looks good. To claim uplift with confidence you need enough prompts for a confidence interval, which is the Python section below.
The human review loop and iterations
Numbers alone do not tell you why an output is worse. skill-creator generates a viewer with two tabs: Outputs (one test case at a time: prompt, output files rendered inline, previous iteration's output, formal grades, and a feedback box) and Benchmark (the stats). The reviewer's comments are saved to feedback.json; empty feedback means "looked fine". It even says to generate the viewer before the agent evaluates the outputs itself, so a human sees real outputs as early as possible.
template-q3), not eval-0, so results read clearly.The advice on how to improve is as much eval wisdom as skill-writing wisdom:
- Generalize from the feedback. You are iterating on a handful of examples, but the skill will be used on huge numbers of different prompts. Fiddly, overfitted fixes that only rescue these test cases are useless. This is the uplift-eval twin of the train/test lesson in Part 1.
- Read transcripts, not just outputs. If the skill makes the agent waste time on unproductive steps, remove the part of the skill causing it and see what happens.
- Look for repeated work. If every test run independently wrote a similar helper script, bundle that script in
scripts/once. - Explain the why. ALWAYS and NEVER in capitals are a yellow flag; explaining the reasoning usually works better with capable models.
Deep dive: blind comparison between two skill versions
For a more rigorous "is version 2 really better?", skill-creator offers an optional blind comparison. A comparator agent (agents/comparator.md) receives two outputs labeled A and B without knowing which skill produced which, builds a task-specific rubric with a content dimension (correctness, completeness, accuracy) and a structure dimension (organization, formatting, usability), scores each 1 to 5, and picks a winner, using assertion pass rates only as secondary evidence. Ties should be rare. Then an analyzer (agents/analyzer.md) "unblinds" the result: it reads both skills and both transcripts, scores instruction-following 1 to 10, and writes prioritized improvement suggestions in categories such as instructions, tools, examples and error handling.
This is pairwise LLM judging (Module 13), so the usual cautions apply: swap A and B positions across runs to cancel position bias, and validate the comparator against a few human judgments before trusting it on close calls.
Part 3: Adherence and process
An uplift number tells you the outcome improved. It does not tell you the skill is why. Maybe the agent loaded the skill, skimmed it, and solved the task its own way; the uplift came from something else, and the next model update will erase it. Adherence evals check the trajectory, the sequence of tool calls, against what the skill says to do. This is process grading from Module 05, applied to skills.
Typical adherence checks, most of them simple code over the transcript:
| Check | How to grade it | What a failure usually means |
|---|---|---|
| Skill body was loaded | A Skill or Read call on the skill's SKILL.md | Trigger problem (Part 1) |
| Referenced file was read when needed | Read call on references/style-guide.md before the step that needs it | Pointer in the body is unclear about when to read it |
| Bundled script was run, not rewritten | Bash call containing scripts/make_chart.py; no new file doing the same job | The body does not explain why the script matters, or the script failed and there was no fallback guidance |
| Steps in the prescribed order | Order of matching tool calls (subsequence check) | Order not explained, or not actually important (then drop the requirement) |
| No forbidden actions | No calls matching a deny list (for example editing the input CSV) | Missing guardrail in the body |
| Did not over-read | Count of reference files read vs needed | Body sends the agent to too many files: context waste |
Two cautions. First, do not demand rigid adherence where it does not matter: if the agent reached the right result by a reasonable different path, that may be fine, and over-strict process checks punish good agents (Module 05 discusses this trade-off). Reserve hard process checks for steps that exist for a reason: validation, safety, using the canonical script so numbers are consistent. Second, adherence failures are information about the skill, not only the agent. skill-creator's analyzer scores instruction-following and lists concrete issues ("did not use the skill's formatting template", "invented own approach instead of following step 3") precisely so you can rewrite the step that was misread.
Part 4: Many skills installed
A real agent rarely has one skill. It may have dozens: documents, spreadsheets, slides, brand guidelines, several team-specific workflows. Now the trigger question becomes a multi-class selection question: given this request, which skill (if any) should load? Your skill's triggering, measured alone, can look perfect and still fail in a crowd.
How to evaluate selection
- Build a labeled routing set. Queries labeled with the one skill that should load, or "none". Include queries for every skill you ship, plus near-misses that belong to none.
- Install a realistic set of distractors. Run with the full set of skills your users actually have, not just yours. If you do not know, test at several sizes (for example 5, 20, 50 skills) and plot the curve.
- Measure multi-class metrics. Top-1 selection accuracy, per-skill precision and recall, and a skill-by-skill confusion matrix. The off-diagonal cells show exactly which pairs collide.
- Hunt collisions before users do. Compare descriptions pairwise (even simple embedding similarity works as a first filter) and write disambiguation queries for the most similar pairs: "board slides from Q3 numbers" should go to
board-deck, "the Q3 report doc" toquarterly-report. - Watch for double loads. Sometimes the agent loads two skills whose instructions conflict (two different chart styles). Count runs with more than one skill loaded and check the output against both.
Part 1 recommended "pushy" descriptions to fight under-triggering. In a crowd, a pushy description also steals requests from other skills. Every time you widen a description, re-run the other skills' trigger evals too. Organizations with many skills benefit from a shared routing eval that runs whenever any description changes, like a regression suite for triggers.
Part 5: Portability and regressions
Because the format is an open standard, the same skill folder may run under different models (a large model and a small, cheap one) and different harnesses (a coding CLI, an IDE, a chat app, your own agent built on an SDK). Each combination can change both halves of the funnel: triggering depends on how the harness presents the skill list and on the model's judgment; uplift depends on how capable the model already is without the skill.
| Illustrative results | Large model, harness X | Small model, harness X | Large model, harness Y |
|---|---|---|---|
| Trigger recall | 0.90 | 0.65 | 0.80 |
| False-trigger rate | 0.05 | 0.10 | 0.15 |
| Uplift (pass-rate delta) | +0.20 | +0.45 | +0.18 |
| Adherence (script used) | 95% | 70% | 90% |
Read the table as a pattern, not data: a smaller model might trigger less reliably yet gain more from the skill when it does fire, because it lacked the know-how to begin with. A different harness might present skills differently and produce more false triggers. You only find out by running the same suite on each target, which is why skill-creator says to run trigger evals with the model the user will actually use.
When the model updates
Skills are written against a model's current behavior, and models change. Treat every model update as a reason to rerun the skill suite (Module 15 covers wiring this into CI). Three things can move:
- Triggering drifts. A new model may be more or less eager to consult skills. Rerun the trigger set and compare rates per query, not just the total.
- Uplift shrinks. If the new baseline model already does the task well, the skill's delta may drop toward zero. That is not a regression in quality; it is a signal the skill may now be dead weight (context cost for no gain) and could be slimmed down.
- Uplift turns negative. Instructions written to steer a weaker model ("always do X in exactly this way") can hold back a stronger one. An assertion that "always fails with the skill but passes without" is the analyst's flag for this.
Keep the pinned baseline idea from Part 2: store a snapshot of each released skill version and its benchmark, so a later rerun can separate "the model changed" from "the skill changed".
Part 6: Safety, skills as a supply chain
A skill is instructions plus, often, executable code, and the agent runs that code with whatever permissions it has. Installing a third-party skill is therefore closer to installing a software package than to reading a document. skill-creator states a "Principle of Lack of Surprise": a skill must not contain malware, exploit code or anything that could compromise security, and its contents should not surprise the user in their intent if described. Your evals should verify that, not assume it. Module 17 covers safety evals in general and Module 25 covers agent security evals in depth (prompt injection, tool poisoning, sandbox escape and more). This section only covers what is specific to skills.
How to evaluate skill safety
- Static review. Read every script and reference file, not just
SKILL.md. Flag network calls, reads of credentials or home directories, obfuscated code, instructions that tell the agent to hide actions from the user, and remote content fetched at run time (which can change after review). Re-review every update as a diff. - Sandboxed dynamic runs. Run the skill's own test prompts in an isolated environment seeded with canary secrets (fake keys that should never leave) and log all outbound traffic and file access. Any canary in egress is a hard fail, the same canary idea as in Module 17.
- Adversarial skills. Build a small suite of deliberately malicious test skills (in a sandbox, with harmless placeholder payloads): a skill whose body asks the agent to send a file to an external URL, a skill whose script tries to read environment variables, a skill with an over-broad description. Measure two things: does your review pipeline flag them (detection rate), and does the agent comply, refuse or warn the user when it loads them (compliance rate)? A good agent should treat a skill's instructions as less trusted than the user's when they conflict with the user's intent.
- Trigger hijacking. Run a malicious skill's description through your routing eval (Part 4). A description engineered to load on everything shows up as a very high false-trigger rate.
- Least privilege as a backstop. Evals miss things. Pair them with runtime limits: no production credentials in the agent's environment, network egress allow-lists, confirmation before irreversible actions. Module 25 shows how to measure attack success rates and utility under attack for these defenses.
Security researchers have already examined public skill marketplaces. For example, Snyk's "ToxicSkills" study (2026) reports finding prompt-injection content and malicious payloads in skills published on a public marketplace, and a 2026 survey of agent skills (arXiv 2602.12430) treats security as one of the field's central open problems. Treat any skill you did not write the way you would treat an unreviewed package from the internet.
What research says
Skills are young, so the research base is small. The most relevant benchmark so far is SkillsBench ("Benchmarking How Well Agent Skills Work Across Diverse Tasks", arXiv 2602.12670, February 2026). Its design is exactly the uplift eval of Part 2 at scale: the same tasks, run by agents with and without skills. The paper reports:
- About 84 tasks across 11 domains.
- Curated, human-written skills raised the pass rate by roughly 16 percentage points on average, with large variation by domain: small gains in software engineering, where models already know a lot, and much larger gains in some specialized domains such as healthcare.
- Self-generated skills, where the model writes its own skill before solving the task, gave no reliable benefit on average.
- Focused skills beat long, comprehensive ones.
Each finding maps onto an eval lesson. Variation by domain means an average uplift hides a lot, so report per domain or per task family. The self-generated result means "the model can write its own skills" is a claim to test, not assume; the know-how has to come from somewhere. And the focused-beats-comprehensive result echoes skill-creator's advice to keep the body lean and push detail into reference files: more context is not automatically more help.
A useful mental model: the value a skill adds per request is roughly P(trigger | relevant) x uplift when triggered, minus the cost of false triggers (tokens and occasional derailment on irrelevant requests), minus the always-on label cost. A skill with huge uplift but 30% recall wastes most of its potential; a skill with perfect recall but zero uplift is pure overhead. This is why you need both halves measured separately, and why fixing the cheaper half first (usually the description) is often the fastest win.
The math in Python
Two small functions cover the core numbers of this module. The first computes run-level trigger metrics and query-level pass counts from repeated runs, using the same pass rule as skill-creator (trigger rate compared with a 0.5 threshold). The second computes a paired bootstrap confidence interval for uplift. "Paired" matters: each eval is run in both arms, so you resample whole evals and keep each with-skill score next to its own baseline. That removes the "some evals are just harder" noise both arms share (Module 04 explains why paired differences are so much tighter).
import random
from statistics import mean
# ---- Part 1: trigger metrics from repeated runs -------------------------
# One record per query; "fired" holds one entry per repeat (3 runs each).
trigger_results = [
# should-trigger queries (10)
*[{"should_trigger": True, "fired": f} for f in [
[1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1],
[1,1,0],[1,0,1],[0,1,1],[1,0,0],[0,0,0]]],
# should-not-trigger queries (10), mostly near-misses
*[{"should_trigger": False, "fired": f} for f in [
[1,1,0],[0,1,0],[0,0,1],[0,0,0],[0,0,0],
[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]],
]
def trigger_metrics(results, threshold=0.5):
"""Run-level confusion matrix plus query-level pass count."""
tp = fn = fp = tn = 0
query_pass = 0
for r in results:
fired = [bool(x) for x in r["fired"]]
k, n = sum(fired), len(fired)
if r["should_trigger"]:
tp += k; fn += n - k
query_pass += (k / n) >= threshold # same rule as skill-creator
else:
fp += k; tn += n - k
query_pass += (k / n) < threshold
precision = tp / (tp + fp) if tp + fp else float("nan")
recall = tp / (tp + fn) if tp + fn else float("nan")
f1 = 2 * precision * recall / (precision + recall)
return {"tp": tp, "fn": fn, "fp": fp, "tn": tn,
"precision": round(precision, 3), "recall": round(recall, 3),
"f1": round(f1, 3), "false_trigger_rate": round(fp / (fp + tn), 3),
"queries_passed": f"{query_pass}/{len(results)}"}
print(trigger_metrics(trigger_results))
# ---- Part 2: paired bootstrap CI for uplift -----------------------------
# Mean pass rate per eval (averaged over its 3 runs), same 12 evals in both arms.
with_skill = [0.89, 1.00, 0.78, 0.89, 1.00, 0.67, 0.89, 0.78, 1.00, 0.89, 0.56, 0.89]
baseline = [0.44, 0.67, 0.33, 0.56, 0.78, 0.44, 0.33, 0.56, 0.89, 0.44, 0.56, 0.22]
def paired_bootstrap(a, b, n_boot=10_000, seed=0):
"""95% CI for mean(a - b), resampling whole evals (keeps the pairing)."""
diffs = [x - y for x, y in zip(a, b)]
rng = random.Random(seed)
boots = sorted(
mean(rng.choice(diffs) for _ in diffs) for _ in range(n_boot)
)
lo, hi = boots[int(0.025 * n_boot)], boots[int(0.975 * n_boot) - 1]
return round(mean(diffs), 3), (round(lo, 3), round(hi, 3))
print(paired_bootstrap(with_skill, baseline))
Output (illustrative data):
{'tp': 22, 'fn': 8, 'fp': 4, 'tn': 26, 'precision': 0.846, 'recall': 0.733, 'f1': 0.786, 'false_trigger_rate': 0.133, 'queries_passed': '17/20'}
(0.335, (0.232, 0.438))
The uplift is +0.335 with a 95% interval of roughly +0.23 to +0.44. Because the whole interval is above zero, you can say the skill helps on this suite. Notes for real use:
- Resample evals, not runs. The three runs of one eval are correlated (same prompt, same difficulty). Treating 36 runs as 36 independent points would make the interval falsely narrow. This is the clustering point from Module 04.
- A bootstrap over 3 or 4 evals is meaningless. You need something like a dozen or more evals before an interval says much. Small suites are for iteration; bigger suites are for claims.
- Exclude assertions the baseline cannot pass by construction (Part 2) before computing uplift, or compute two numbers: outcome uplift and adherence.
- Same interval idea for triggering. To compare two descriptions, compute the per-query difference in trigger rate and bootstrap over queries.
A skill eval checklist
| Question | Eval | Minimum setup | Main pitfall |
|---|---|---|---|
| Does it fire when it should, and only then? | Trigger eval | ~20 realistic queries, half near-miss negatives, 3 runs each | Trivial queries that never trigger; easy negatives that test nothing |
| Is the description tuned, not overfitted? | Train / held-out optimization | 60/40 stratified split, pick by held-out score | Selecting by train score |
| Does it make outputs better? | Uplift eval | With-skill and baseline launched together, objective assertions, grader with evidence | Non-discriminating assertions; adherence-only assertions inflating the delta |
| At what cost? | Same benchmark | Time and tokens, mean ± sd, per arm | Reporting pass rate alone |
| Is the skill actually followed? | Adherence | Trajectory checks for reads, script runs, order | Over-strict process checks |
| Does it hold up in a crowd? | Selection | Routing set with realistic distractor skills, skill confusion matrix | Testing each skill alone |
| Does it survive model and harness changes? | Portability / regression | Same suite per target; rerun on every model update | Mistaking a better baseline for a worse skill |
| Is it safe to install? | Supply-chain safety | Static review, sandbox with canaries, adversarial skills | Reviewing SKILL.md but not the scripts |
Key takeaways
- A skill is a folder the agent chooses to open. Its short description is always in context; the body and files load only when needed. That choice is a new failure point that tools and prompts do not have.
- Measure the two halves separately. Triggering (does it fire at the right times?) and uplift (does it help when it fires?) have different causes and different fixes.
- Trigger evals are classification. Realistic, substantive queries; near-miss negatives; 3 runs per query; precision, recall and the trigger-rate distribution.
- Optimize descriptions on train, choose on held-out. A description that aces its training queries has probably memorized them.
- Uplift needs a paired baseline run at the same time, objective assertions, a grader that cites evidence and fails superficial passes, and an analyst pass for non-discriminating, flaky and costly patterns.
- Check the trajectory, not just the output, to confirm the skill is the reason for the gain.
- Test in a crowd, across models, and on every update, and treat third-party skills as a software supply chain: review, sandbox, canaries, adversarial skills.
Check yourself
1. Your trigger eval shows 40% recall. Most should-trigger queries are one-liners like "read report.pdf". What is the most likely problem?
skill-creator notes that simple one-step queries may not trigger a skill even when the description matches perfectly. Rewrite the queries to be substantive and realistic before touching the description.2. Which is the best should-not-trigger query for a quarterly-report skill?
3. During description optimization, iteration 4 scores 12/12 on train and 5/8 on held-out test; iteration 2 scores 11/12 and 7/8. Which do you ship?
skill-creator's loop returns the best description by test score for exactly this reason.4. The assertion "Output is a .docx file" passes 12/12 with the skill and 12/12 without. What should you do?
5. Your uplift delta is +0.50, but most of it comes from "Chart was made by scripts/make_chart.py" and "Built from assets/template.docx". What is the concern?
6. After a model update, your skill's uplift falls from +0.30 to +0.02, while with-skill pass rate stays the same. What most likely happened?
Expert corner: interview questions
- "How would you evaluate a new skill before rolling it out to 5,000 employees?" Separate trigger and uplift. Trigger: 20 or more realistic queries with near-miss negatives, 3 runs each, precision and recall, then a routing eval with the other skills employees actually have installed. Uplift: paired with-skill vs no-skill runs launched together, objective assertions graded with evidence, time and tokens, an analyst pass, human review of real outputs, and a paired bootstrap interval once the suite is big enough. Add adherence checks and a security review (static plus sandbox with canaries) before publishing.
- "Why is description optimization an overfitting risk, and how do you control it?" The optimizer sees which queries failed and rewrites the description toward them, so train score climbs by memorizing. Control it with a stratified train / held-out split, selecting by held-out score, repeated runs per query, and ideally a final untouched set plus fresh production queries.
- "The with-skill pass rate is high, but you suspect the skill is not the reason. How do you check?" Compare against the no-skill baseline on outcome-only assertions; read trajectories for adherence (did it read the reference file, run the bundled script); ablate parts of the skill (remove the script, remove a section) and see which removal drops the score.
- "What changes when 50 skills are installed instead of one?" Triggering becomes multi-class routing; description collisions and double loads appear; always-on label cost grows linearly; a pushy description steals traffic from neighbors. Evaluate with a labeled routing set, realistic distractors, a skill-by-skill confusion matrix, and rerun all trigger evals whenever any description changes.
- "How would you test whether an agent is safe against a malicious skill?" Build an adversarial suite of sandboxed test skills with placeholder payloads (exfiltration instructions in the body, a script reading env vars, an over-broad description). Seed canary secrets, log egress, and measure both detection by the review pipeline and compliance by the agent. Back it with least privilege, because evals will miss some attacks.
- "What does SkillsBench suggest about how to report skill uplift?" The paper reports large variation by domain and no reliable benefit from self-generated skills on average, so report uplift per domain or task family with intervals, compare against both no-skill and self-generated baselines if you claim a skill-writing method works, and prefer focused skills to long comprehensive ones.
Sources and further reading
- Anthropic, anthropics/skills on GitHub, especially
skill-creator:SKILL.md,agents/grader.md,agents/analyzer.md,agents/comparator.md,references/schemas.mdandscripts/run_eval.py/run_loop.py. The primary source for the method in Parts 1 and 2. - Anthropic Engineering, Equipping agents for the real world with Agent Skills (October 2025).
- Claude Docs, Agent Skills overview and Skill authoring best practices.
- Agent Skills open standard: specification and agentskills/agentskills on GitHub.
- Li et al., SkillsBench: Benchmarking How Well Agent Skills Work Across Diverse Tasks (February 2026), and the SkillsBench site.
- Agent Skills for Large Language Models: Architecture, Acquisition, Security, and the Path Forward (survey, 2026).
- Snyk, ToxicSkills: a study of agent skills supply-chain compromise (2026).
- Philipp Schmid, Practical Guide to Evaluating and Testing Agent Skills (2026), and his AI Engineer talk Don't Ship Skills Without Evals.
- Evan Miller, Adding Error Bars to Evals (2024), for paired and clustered intervals.