FinOps Review
Purpose
Look at the dispatch-log with a finance hat on. Answer:
- Where is the money going? Per feature, per agent, per model.
- What fraction of input tokens are hitting cache? (Anything below
~70% on a stable system prompt is a smell.)
- Is the routing sensible? Are opus calls doing work that haiku /
gpt-5-nano / gemini-flash could do for 1/30th the cost?
- Are there workloads on the realtime API that should be on the
batch API (50% discount, 24h SLA)?
The output is a list of opportunities, ranked by estimated monthly
savings, not a blame report. Owned by the ai-finops agent.
Scope
- Reads
~/.yakos-state/dispatch-log*.ndjson (current + rotated).
- Joins with the agent registry (model alias per agent) and the
runtime billing snapshot to compute per-call cost.
- Computes:
- Spend by feature. Tag-based: each dispatch carries a
feature_tag (set by the lead or inferred from the calling
agent's domain).
- Cache hit rate per system prompt. Grouped by
system_prompt_hash.
Low hit rates point at unstable prompts (date-stamped headers,
shuffled examples, etc.).
- Model routing audit. For each agent, the distribution of
model choices. Opus on a
cheap-eligible agent is flagged.
- Batch-eligible candidates. Workloads with high volume + low
latency-sensitivity (offline rubric scoring, summarization
backfills, etc.) that are running on the realtime API.
- Output is a markdown report with three sections: top spend,
optimization opportunities, and recommended next actions.
When to use
- Monthly finops review, before the spend report goes to the budget
owner.
- After a usage spike, to find the cause.
- Before a pricing renegotiation with a provider — bring real
numbers to the meeting.
- When a feature flag rolls out and you want to know its cost
fingerprint before going to GA.
- As input to the quarterly model-routing review (which agents
should be downgraded / upgraded).
When NOT to use
- For real-time per-call cost lookup —
yakos cost --tail does that.
- As a substitute for the runtime's billing dashboard. yakOS
estimates are best-effort; the provider invoice is authoritative.
This skill finds patterns the dashboard doesn't surface.
- For projects with <100 dispatches in the window — the noise floor
is too high to draw conclusions.
Automated pass
Pull raw dispatch data.
SINCE="${SINCE:-$(date -u -v-30d +%Y-%m-%d 2>/dev/null || date -u -d '30 days ago' +%Y-%m-%d)}"
yakos cost --since "$SINCE" --json --raw > /tmp/dispatches.jsonl
Spend by feature. Group by feature_tag, sum cost. Top N
features get listed; tail is "other."
jq -s 'group_by(.feature_tag) | map({feature: .[0].feature_tag, cost: map(.cost_usd) | add, calls: length}) | sort_by(-.cost)' \
/tmp/dispatches.jsonl > /tmp/by-feature.json
Cache hit rate per system prompt. Pull
usage.cache_read_input_tokens vs.
usage.cache_creation_input_tokens vs. usage.input_tokens per
system_prompt_hash. Flag prompts where
cache_read / (cache_read + uncached) < 0.7.
Routing audit. For each agent_id, list the models actually
used and the model declared in its agent file.
agent.model: cheap but actual = claude-opus-4-7 → flag
"model override" (lead manually upgraded; check rationale).
agent.model: opus but task prompt is <500 tokens and output is
<100 tokens → flag "best-when-cheap-would-do."
Batch eligibility. Heuristic: an agent's calls are batch-
eligible if (a) volume > 100/day, (b) p99 user-facing latency
tolerance > 1h (declared in agent frontmatter), (c) calls are
independent (no chaining). The skill emits a candidate list.
Pending routing candidates.
Read ~/.yakos-state/model-routing-candidates.ndjson and surface
any pending model-routing opportunities as part of the review.
# List pending candidates, ranked by estimated monthly savings.
MR_CANDS="${HOME}/.yakos-state/model-routing-candidates.ndjson"
if [ -s "$MR_CANDS" ]; then
echo "### Pending model-routing candidates"
jq -rs '
group_by(.agent) |
map(sort_by(.generated_at) | last) |
sort_by(-.estimated_monthly_savings_usd) |
.[] |
" \(.agent): \(.current_model) -> \(.suggested_model)" +
" est. savings=~$\(.estimated_monthly_savings_usd)/mo" +
" n=\(.evidence.n_cases) run=\(.evidence.eval_run_id)"
' "$MR_CANDS"
echo
echo " Promote via: yakos model-routing promote <agent-id>"
echo " Reject via: yakos model-routing reject <agent-id> [--note \"reason\"]"
fi
Each candidate entry includes estimated_monthly_savings_usd (from
the eval run), the evidence n_cases, and the eval run id so the
operator can cross-reference the eval log. List ranked by savings
desc; tail roll into "and N more" for long lists (> 10).
Compose the report.
- Headline: total spend, vs prior period delta.
- Top features by spend: table, with "% of total" column.
- Optimization opportunities: ranked by est. monthly savings.
Each item: what to change, why, est. $/mo saved, est. effort
(hours). Include pending model-routing candidates from step 6
in this ranking.
- Routing audit: agents whose actual model differs from
declared; agents that should be downgraded.
- Cache health: prompts under 70% hit rate, with the
suspected cause (volatile prefix, low call volume, etc.).
- Batch candidates: list with current realtime cost vs.
batch-equivalent.
- Pin block: window, dispatch count, source log version.
Optionally post to $YAKOS_FINOPS_WEBHOOK if --post is set.
Manual pass
# 1. Top features by spend
yakos cost --since 2026-04-01 --by feature --json | jq 'sort_by(-.cost_usd) | .[0:10]'
# 2. Cache hit rate (claude only — others lack the field as of v0.6)
yakos cost --since 2026-04-01 --raw | \
jq -s 'group_by(.system_prompt_hash) | map({hash: .[0].system_prompt_hash, hit_rate: ((map(.cache_read) | add) / ((map(.cache_read) | add) + (map(.input_uncached) | add)))})'
# 3. Eyeball routing
yakos cost --since 2026-04-01 --by agent --by model
Skim for the obvious wins — usually one feature accounts for 60%+
of spend, and within that feature, one agent or one prompt is the
hot spot.
Known gotchas
- Estimate vs. actual. Costs are computed from
usage fields if
present, otherwise from chars/4 estimates. Mixing the two in one
report is misleading. The skill marks each row source = actual
or estimate and reports them separately when the mix is large.
- Cache fields are runtime-specific. Claude reports
cache_read_input_tokens since v0.5; codex/gemini have different
shapes (or none). The skill normalizes via the runtime adapter;
agents on a runtime without cache reporting are listed as
"cache-unknown" not "cache-cold."
- Feature tagging discipline.
feature_tag is only as good as
the leads who set it. If 40% of dispatches are tagged
untagged, the top-features view is useless. Recommend the
project enforce tagging via a pre-dispatch hook (separate skill).
- Batch eligibility false positives. Marking work batch-eligible
doesn't mean the API supports it for that runtime + workload. Some
tools / multi-turn flows aren't batch-able. The skill produces a
candidate list; the human confirms eligibility per workload.
- Best-when-cheap-would-do detection. The heuristic (small
prompt, small output) misses cases where opus is genuinely needed
for reasoning quality. Treat the flag as "investigate," not
"downgrade now." Pair with prompt-eval to confirm haiku doesn't
regress before downgrading.
- PII in feature tags. Some leads embed customer ids in tags.
Don't post the report to a shared webhook without scrubbing —
same caveat as
cost-summary.
- Multi-machine. dispatch-log is per-machine. For org-wide
finops, ship logs to a central host before running this skill.
References
lib/agents/ai-finops.md — owns this skill.
lib/skills/cost-summary/SKILL.md — daily/weekly summary; pair
with finops-review for the deeper cut.
cli/lib/cost.sh — underlying cost command.
docs/runtime-matrix.md — which runtimes report cache and real
token counts.
docs/batch-api.md — batch-eligibility heuristics in detail.
1---2name: finops-review3description: Analyze the dispatch-log for per-feature spend, cache hit rate, and model routing, surfacing optimization opportunities. Use when costs look high, before a budget review, or when hunting for ways to cut LLM spend.4---56# FinOps Review78## Purpose910Look at the dispatch-log with a finance hat on. Answer:1112- Where is the money going? Per feature, per agent, per model.13- What fraction of input tokens are hitting cache? (Anything below14 ~70% on a stable system prompt is a smell.)15- Is the routing sensible? Are opus calls doing work that haiku /16 gpt-5-nano / gemini-flash could do for 1/30th the cost?17- Are there workloads on the realtime API that should be on the18 batch API (50% discount, 24h SLA)?1920The output is a list of *opportunities*, ranked by estimated monthly21savings, not a blame report. Owned by the `ai-finops` agent.2223## Scope2425- Reads `~/.yakos-state/dispatch-log*.ndjson` (current + rotated).26- Joins with the agent registry (model alias per agent) and the27 runtime billing snapshot to compute per-call cost.28- Computes:29 - **Spend by feature.** Tag-based: each dispatch carries a30 `feature_tag` (set by the lead or inferred from the calling31 agent's domain).32 - **Cache hit rate per system prompt.** Grouped by `system_prompt_hash`.33 Low hit rates point at unstable prompts (date-stamped headers,34 shuffled examples, etc.).35 - **Model routing audit.** For each agent, the distribution of36 model choices. Opus on a `cheap`-eligible agent is flagged.37 - **Batch-eligible candidates.** Workloads with high volume + low38 latency-sensitivity (offline rubric scoring, summarization39 backfills, etc.) that are running on the realtime API.40- Output is a markdown report with three sections: top spend,41 optimization opportunities, and recommended next actions.4243## When to use4445- Monthly finops review, before the spend report goes to the budget46 owner.47- After a usage spike, to find the cause.48- Before a pricing renegotiation with a provider — bring real49 numbers to the meeting.50- When a feature flag rolls out and you want to know its cost51 fingerprint before going to GA.52- As input to the quarterly model-routing review (which agents53 should be downgraded / upgraded).5455## When NOT to use5657- For real-time per-call cost lookup — `yakos cost --tail` does that.58- As a substitute for the runtime's billing dashboard. yakOS59 estimates are best-effort; the provider invoice is authoritative.60 This skill finds *patterns* the dashboard doesn't surface.61- For projects with <100 dispatches in the window — the noise floor62 is too high to draw conclusions.6364## Automated pass65661. **Pull raw dispatch data.**67 ```sh68 SINCE="${SINCE:-$(date -u -v-30d +%Y-%m-%d 2>/dev/null || date -u -d '30 days ago' +%Y-%m-%d)}"69 yakos cost --since "$SINCE" --json --raw > /tmp/dispatches.jsonl70 ```71722. **Spend by feature.** Group by `feature_tag`, sum cost. Top N73 features get listed; tail is "other."74 ```sh75 jq -s 'group_by(.feature_tag) | map({feature: .[0].feature_tag, cost: map(.cost_usd) | add, calls: length}) | sort_by(-.cost)' \76 /tmp/dispatches.jsonl > /tmp/by-feature.json77 ```78793. **Cache hit rate per system prompt.** Pull80 `usage.cache_read_input_tokens` vs.81 `usage.cache_creation_input_tokens` vs. `usage.input_tokens` per82 `system_prompt_hash`. Flag prompts where83 `cache_read / (cache_read + uncached) < 0.7`.84854. **Routing audit.** For each agent_id, list the models actually86 used and the model declared in its agent file.87 - `agent.model: cheap` but actual = `claude-opus-4-7` → flag88 "model override" (lead manually upgraded; check rationale).89 - `agent.model: opus` but task prompt is <500 tokens and output is90 <100 tokens → flag "best-when-cheap-would-do."91925. **Batch eligibility.** Heuristic: an agent's calls are batch-93 eligible if (a) volume > 100/day, (b) p99 user-facing latency94 tolerance > 1h (declared in agent frontmatter), (c) calls are95 independent (no chaining). The skill emits a candidate list.96976. **Pending routing candidates.**98 Read `~/.yakos-state/model-routing-candidates.ndjson` and surface99 any pending model-routing opportunities as part of the review.100 ```sh101 # List pending candidates, ranked by estimated monthly savings.102 MR_CANDS="${HOME}/.yakos-state/model-routing-candidates.ndjson"103 if [ -s "$MR_CANDS" ]; then104 echo "### Pending model-routing candidates"105 jq -rs '106 group_by(.agent) |107 map(sort_by(.generated_at) | last) |108 sort_by(-.estimated_monthly_savings_usd) |109 .[] |110 " \(.agent): \(.current_model) -> \(.suggested_model)" +111 " est. savings=~$\(.estimated_monthly_savings_usd)/mo" +112 " n=\(.evidence.n_cases) run=\(.evidence.eval_run_id)"113 ' "$MR_CANDS"114 echo115 echo " Promote via: yakos model-routing promote <agent-id>"116 echo " Reject via: yakos model-routing reject <agent-id> [--note \"reason\"]"117 fi118 ```119 Each candidate entry includes `estimated_monthly_savings_usd` (from120 the eval run), the evidence `n_cases`, and the eval run id so the121 operator can cross-reference the eval log. List ranked by savings122 desc; tail roll into "and N more" for long lists (> 10).1231247. **Compose the report.**125 - **Headline:** total spend, vs prior period delta.126 - **Top features by spend:** table, with "% of total" column.127 - **Optimization opportunities:** ranked by est. monthly savings.128 Each item: what to change, why, est. $/mo saved, est. effort129 (hours). Include pending model-routing candidates from step 6130 in this ranking.131 - **Routing audit:** agents whose actual model differs from132 declared; agents that should be downgraded.133 - **Cache health:** prompts under 70% hit rate, with the134 suspected cause (volatile prefix, low call volume, etc.).135 - **Batch candidates:** list with current realtime cost vs.136 batch-equivalent.137 - Pin block: window, dispatch count, source log version.1381398. Optionally post to `$YAKOS_FINOPS_WEBHOOK` if `--post` is set.140141## Manual pass142143```sh144# 1. Top features by spend145yakos cost --since 2026-04-01 --by feature --json | jq 'sort_by(-.cost_usd) | .[0:10]'146147# 2. Cache hit rate (claude only — others lack the field as of v0.6)148yakos cost --since 2026-04-01 --raw | \149 jq -s 'group_by(.system_prompt_hash) | map({hash: .[0].system_prompt_hash, hit_rate: ((map(.cache_read) | add) / ((map(.cache_read) | add) + (map(.input_uncached) | add)))})'150151# 3. Eyeball routing152yakos cost --since 2026-04-01 --by agent --by model153```154155Skim for the obvious wins — usually one feature accounts for 60%+156of spend, and within that feature, one agent or one prompt is the157hot spot.158159## Known gotchas160161- **Estimate vs. actual.** Costs are computed from `usage` fields if162 present, otherwise from chars/4 estimates. Mixing the two in one163 report is misleading. The skill marks each row source = `actual`164 or `estimate` and reports them separately when the mix is large.165- **Cache fields are runtime-specific.** Claude reports166 `cache_read_input_tokens` since v0.5; codex/gemini have different167 shapes (or none). The skill normalizes via the runtime adapter;168 agents on a runtime without cache reporting are listed as169 "cache-unknown" not "cache-cold."170- **Feature tagging discipline.** `feature_tag` is only as good as171 the leads who set it. If 40% of dispatches are tagged172 `untagged`, the top-features view is useless. Recommend the173 project enforce tagging via a pre-dispatch hook (separate skill).174- **Batch eligibility false positives.** Marking work batch-eligible175 doesn't mean the API supports it for that runtime + workload. Some176 tools / multi-turn flows aren't batch-able. The skill produces a177 candidate list; the human confirms eligibility per workload.178- **Best-when-cheap-would-do detection.** The heuristic (small179 prompt, small output) misses cases where opus is genuinely needed180 for reasoning quality. Treat the flag as "investigate," not181 "downgrade now." Pair with prompt-eval to confirm haiku doesn't182 regress before downgrading.183- **PII in feature tags.** Some leads embed customer ids in tags.184 Don't post the report to a shared webhook without scrubbing —185 same caveat as `cost-summary`.186- **Multi-machine.** dispatch-log is per-machine. For org-wide187 finops, ship logs to a central host before running this skill.188189## References190191- `lib/agents/ai-finops.md` — owns this skill.192- `lib/skills/cost-summary/SKILL.md` — daily/weekly summary; pair193 with finops-review for the deeper cut.194- `cli/lib/cost.sh` — underlying cost command.195- `docs/runtime-matrix.md` — which runtimes report cache and real196 token counts.197- `docs/batch-api.md` — batch-eligibility heuristics in detail.