Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

A B Testing Models

BSecurity

Use when comparing two models, providers, or ferry lanes on speed, cost, or behavior before switching a lane — e.g. "is X faster than Y", "compare GLM vs Gemini", "what is this lane costing", "benchmark the flash lane". Covers the thinking-token traps that make naive benchmarks lie (reasoning eats max_tokens, mandatory-reasoning surfaces, ignored thinking budgets, chunk-burst decode rates), the streaming harness at scripts/bench-models.py, real-traffic A/B from the observability stack, and th...

2 stars
0 votes
0 copies
0 views
Added 9/19/2026
ai-agentspythonrustgobashtestingapi

Works with

api

Security Analysis

B84/100
mediumUses curl or wget to download content
criticalDownloads and executes remote scripts — classic supply chain attack

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add sblattj/llm-ferry --skill a-b-testing-models --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of A B Testing Models?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for A B Testing Models
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/sblattj-a-b-testing-models/badge)](https://www.skillsdirectory.com/skills/sblattj-a-b-testing-models)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: a-b-testing-models
description: Use when comparing two models, providers, or ferry lanes on speed, cost, or behavior before switching a lane — e.g. "is X faster than Y", "compare GLM vs Gemini", "what is this lane costing", "benchmark the flash lane". Covers the thinking-token traps that make naive benchmarks lie (reasoning eats max_tokens, mandatory-reasoning surfaces, ignored thinking budgets, chunk-burst decode rates), the streaming harness at scripts/bench-models.py, real-traffic A/B from the observability stack, and the price x volume cost model.
---

# A/B testing models for a ferry lane

Deciding a lane swap (model, provider, or surface) needs three independent
answers: **speed** (synthetic bench), **cost** (real token volume x price), and
**behavior** (thinking policy, tool calls). Naive pings lie for all three on
modern reasoning models. This skill is the checklist that doesn't.

## The traps (each one bit during the 2026-08-26 GLM-vs-Gemini comparison)

1. **Reasoning tokens eat `max_tokens`.** A `max_tokens: 20` ping on a thinking
   model burns the whole budget on thoughts: `finish_reason: "length"`,
   `content: null` — looks like a dead lane, isn't. Any probe of a thinking
   model needs **>= 600 tokens** of budget, and you must read
   `completion_tokens_details.reasoning_tokens` to know what you measured.
2. **Streams omit `usage` by default.** Without
   `"stream_options": {"include_usage": true}` every streamed benchmark reports
   zero tokens and tok/s of 0. (Non-streaming calls always carry usage.)
3. **Some surfaces make reasoning MANDATORY.** OpenRouter rejects every disable
   spelling for these models (`reasoning.enabled: false`,
   `reasoning.effort: "none"` -> 400 "Reasoning is mandatory for this endpoint");
   a top-level `effort: "none"` is *accepted but ignored* — the model still
   thinks. The direct APIs differ: Z.ai takes `thinking: {"type": "disabled"}`
   (works, verified); Gemini takes `thinkingConfig.thinkingBudget: 0` — which is
   **honored inconsistently** (0 thoughts on a one-liner, 732-980 thoughts on a
   paragraph prompt despite budget 0).
4. **Verify thoughts in the RESPONSE, never trust the request knob.** Check
   `usageMetadata.thoughtsTokenCount` (Gemini) / `reasoning_tokens` (OpenAI-ish)
   on every run. A "thinking disabled" arm that thought 900 tokens is not the
   arm you configured.
5. **TTFT is ambiguous on thinking models.** Time-to-first-delta is
   time-to-first-*thought* when thinking is on. Record text-TTFT and
   reasoning-TTFT separately; they answer different questions (worker latency =
   text-TTFT + decode; user-perceived first sign of life = reasoning-TTFT).
6. **Decode tok/s from SSE chunk timing requires per-token deltas.** Gemini's
   native SSE bursts multi-token chunks (a 181-token answer measured "717
   tok/s" — a chunk-burst artifact). When deltas aren't per-token, report
   **e2e tok/s** (tokens / total wall time) instead and say so.
7. **Compare the config you will actually run.** Thinking-on-via-OpenRouter and
   thinking-off-direct are different products; a bench of one doesn't transfer
   to the other. Bench the exact surface the lane will use (through the ferry
   lane name when it exists).
8. **Pin the generation.** `temperature: 0` + `seed: 0`, >= 3 reps, interleave
   arms (round-robin, alternate order per round), report **medians**.
9. **Agentic lanes need a tool-call probe too.** Speed on prose != tool-call
   correctness. One `get_weather`-style forced tool call must round-trip
   (name + JSON args) before a lane swap.
10. **OpenRouter `/api/v1/stats` returns HTML now** — do not script against it.
    `/api/v1/models` is still JSON and is the pricing source of truth
    (`pricing.prompt` / `pricing.completion` / `pricing.input_cache_read`, USD
    per token — multiply by 1e6 for per-M).

## Speed: the harness

`scripts/bench-models.py` (stdlib only) implements all of the above. Arms are
OpenAI-compatible endpoints (a ferry lane, OpenRouter direct, Z.ai direct) or
Gemini native SSE. It prints per-rep lines and per-arm medians for text-TTFT,
reasoning-TTFT, e2e, text/reasoning token split, and decode/e2e tok/s.

```bash
python3 scripts/bench-models.py            # default arms: the flash lane's surfaces
python3 scripts/bench-models.py --rounds 5 # more reps
```

Read its ARMS dict before relying on it — edit arms to the two configurations
being compared (model ids, api bases, thinking knobs, keys from
`~/.config/ferry/secrets.env`). Keys are read from the environment, never
hardcoded.

## Cost: real volume x price, not list-price intuition

1. Pull the lane's actual token history from VictoriaMetrics (`:8429`, 12-day
   retention) — real cache-hit rates dominate flash-lane cost:

```bash
for m in input output input_cached; do
  curl -s http://127.0.0.1:8429/api/v1/query \
    --data-urlencode "query=sum by (model) (increase(litellm_${m}_tokens_metric_total[12d]))" \
  | python3 -c "import json,sys; [print(r['metric']['model'], f\"{float(r['value'][1]):,.0f}\") for r in json.load(sys.stdin)['data']['result']]"
done
```

2. Split into uncached-input, cached-input, output. Multiply each by the
   provider's per-M price (OpenRouter `/api/v1/models`; Gemini list at
   ai.google.dev/gemini-api/docs/pricing — note OpenRouter's Gemini price is
   often HALF Google direct list, and Google's list DOUBLES at announced dates,
   e.g. 2027-01-01 for 3.7 Flash).
3. Cross-check against litellm's own tracker:
   `increase(litellm_spend_metric_total[12d])` (it uses its price table; a
   large divergence means a price table is stale — investigate, don't average).
4. For subscription surfaces (Z.ai coding plan), price in **credits**:
   per-model multipliers differ ~3x within one plan (GLM-5.3 is
   6.9/1.7/24 per 1M in/cached/out; GLM-5.3-Flash exactly 1/3: 2.3/0.56/8;
   off-peak Mon-Fri 14:00-18:00 SGT = 50% credits). Weekly allowance (Lite 10k /
   Pro 60k / Max 140k) is the budget, not dollars.

## Speed: real-traffic A/B when history exists

Synthetic benches miss queueing, cache state, and fan-out concurrency. The
observability stack already recorded 12 days of live traffic — use it before
(re)running anything synthetic:

```bash
curl -s http://127.0.0.1:8429/api/v1/query \
  --data-urlencode "query=sum by (model) (litellm_request_total_latency_metric_sum) / sum by (model) (litellm_request_total_latency_metric_count)"
```

Same for `litellm_llm_api_time_to_first_token_metric_*` (TTFT). If the old lane
has history, its real numbers beat a fresh synthetic run of it.

## Recording the verdict

Write the comparison INTO the lane's comment block in
`~/.config/ferry/litellm.yaml` (see the `flash` header for the format: date,
price basis, measured tok/s, why the winner won) so the next session inherits
the decision instead of re-deriving it. If the swap changes committed guidance
(example yaml / README / skills), bump VERSION and say what changed.

## Worked example (2026-08-26, flash lane, 3 reps each, temp 0)

| Surface | Thinking | text-TTFT | decode | e2e |
|---|---|---|---|---|
| Gemini 3.7 Flash via OpenRouter | mandatory | ~2.2s | ~256 tok/s | 4.5s / 600 tok |
| GLM 5.3 Flash via OpenRouter | mandatory | ~2.4s | ~80 tok/s | 9.7s / 600 tok |
| GLM 5.3 Flash direct (Z.ai coding, disabled) | off (0 thoughts) | ~0.9s | ~54 tok/s | 4.3s / 184 tok |
| Gemini 3.7 Flash direct (budget 0) | **budget ignored** (732-980 thoughts) | ~3.3s (text) | chunk-burst, unmeasurable | 3.6s / 181 tok |

Verdict recorded: Gemini is ~3x faster at decode; GLM flash is ~7x cheaper at
observed volume ($2.81 vs $20.20 / 12d). Lane went to GLM on cost, with Gemini
as fallback hops — and thinking stays ON in practice everywhere, because the
mandatory-reasoning surfaces are the ones the lane actually uses.

Attribution

sblattjsblattj
View sourceMore from sblattj →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Caveman

Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.

1023331 votes

Hyperplan

Adversarial multi-agent planning skill. Self-orchestrates 5 hostile category members (unspecified-low, unspecified-high, deep, ultrabrain, artistry) via team-mode for ruthless cross-critique debate, distills only the defensible insights, then MANDATORILY hands the distilled insight bundle to the `plan` agent for executable plan formalization. Use when planning needs maximum rigor and surfacing of weak assumptions, blind spots, and over-engineering. Triggers: 'hyperplan', 'hpp', '/hyperplan', ...

686011 votes

Mcp Code Execution

Routes multi-tool workflows through MCP servers for large datasets and pipelines. Use when Bash tool overhead is limiting throughput on data-heavy tasks.

3331 votes

catchup

Recovers prior coding-agent session context by running `catchup <agent> --since-compact`, which extracts a clean summary of a previous Codex, Claude Code, Antigravity, OpenCode, or Pi Agent session. Use when the user says "catch up", "what did the last session do", "get me up to speed", "I switched agents", or asks to recover/summarize a previous session before continuing. Do NOT use for the current conversation, git history, or any non-agent log.

611 votes

math-skill

A comprehensive mathematical reasoning skill for AI assistants — handles arithmetic to research-level problems with rigorous step-by-step reasoning, systematic verification, and transparent uncertainty handling

381 votes
View all in ai-agents →