Prompt Caching and Cost skill for Codex

Lower the LLM bill without lowering quality. Codex starts by logging usage and cost per feature, then reorders prompts so static instructions, tools and schemas form a stable cached prefix while dates and user data move to the end. It sets explicit output and reasoning budgets, routes easy requests to cheaper tiers with escalation, moves offline jobs to Batch, and gates every saving on evals.

  • AI & LLM Engineering
  • $prompt-caching-cost
  • 139 lines
  • 2 files
  • ~1,600 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- prompt-caching-cost

Installs to ~/.agents/skills/prompt-caching-cost/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $prompt-caching-cost, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Cuts LLM spend and latency without losing quality — prompt layout for OpenAI prompt caching (static prefix first, prompt_cache_key, retention settings, cached_tokens monitoring), token budgets for input, output and reasoning, history trimming and compaction, model routing and escalation, and Batch for offline work. Use when the OpenAI bill grows faster than traffic, cached_tokens stays near zero, responses are slow on long prompts, or when deciding which model tier each request should use.

Use it when

  • LLM spend is growing faster than traffic
  • Long repeated prompts show few cached tokens
  • Responses are slow because prompts are large
  • Choosing model tiers or reasoning effort per request

Use something else when

How to check the result

A per-feature before/after table shows cost per request, cached-token ratio and latency, and eval pass rates for every changed feature are unchanged within noise.

The mistake it prevents

Putting the current date or the user's name at the top of the system prompt, which breaks the cached prefix for every request.

~/.agents/skills/prompt-caching-cost/SKILL.md

--- name: prompt-caching-cost description: Cuts LLM spend and latency without losing quality — prompt layout for OpenAI prompt caching (static prefix first, prompt_cache_key, retention… license: MIT ---

Done well means: cost per request is measured from usage, prompts are laid out so the cache hits, every call has explicit output and reasoning budgets, cheap models handle the requests they can (proven by evals), and offline work runs at batch prices.

Measure first

Log per request: feature, model, usage.input_tokens, usage.input_tokens_details.cached_tokens, usage.output_tokens, usage.output_tokens_details.reasoning_tokens, latency, prompt version.

def cost_usd(u, price):  # price per 1M tokens, from the pricing page, in config
    cached = u.input_tokens_details.cached_tokens
    return ((u.input_tokens - cached) * price.input
            + cached * price.cached_input
            + u.output_tokens * price.output) / 1_000_000

Reasoning tokens are billed as output tokens. Keep prices in config, not code — they change. Rank features by monthly cost before optimizing anything.

Workflow

  • 1. Read AGENTS.md; find the LLM wrapper and where usage is (or is not) logged
  • 2. Add usage logging and a cost dashboard query
  • 3. Fix prompt layout for caching
  • 4. Set budgets: max output, reasoning effort, history length
  • 5. Route by difficulty; move offline jobs to Batch
  • 6. Prove quality held with evals; report cost delta

3. Prompt layout for caching

OpenAI caches automatically for prompts above a minimum length (1,024 tokens on current models; varies on older ones) and only on an exact prefix match. Anything that differs early invalidates everything after it.

[1] instructions / developer prompt      static, versioned
[2] tool definitions                     static order, static text
[3] output schema (text.format)          static
[4] long reference material              static per tenant/document
[5] conversation history                 append-only
[6] current user message + per-request data (date, user name, retrieved chunks)

Rules:

  • No timestamps, request ids, user names or shuffled retrieval results in [1]–[4].
  • Keep tools identical across turns; narrow with allowed_tools / tool_choice instead of adding or removing tools. Changing model, tools, parallel_tool_calls, text.format or reasoning.effort breaks reuse from the changed point on.
  • Append to history; do not rewrite or re-summarize earlier turns on every request.
  • Set prompt_cache_key to a stable value for requests sharing a prefix (e.g. a tenant or document id). On high-traffic prefixes (above roughly 15 requests per minute per prefix-and-key), spread load across a few keys, since overflow lands on machines without the cache.
  • Retention: newer models expose a cache TTL option and charge for cache writes; older models use prompt_cache_retention (in_memory or 24h). Check the prompt caching guide for the configured model before setting either; longer retention costs more to write and only pays off for prefixes reused across that window.

Check: cached_tokens / input_tokens per feature. Near zero on a long, repeated prompt means something in the prefix varies — diff two rendered prompts to find it.

4. Budgets

LeverSettingNotes
Output lengthmax_output_tokens + a word limit in the promptTreat status: "incomplete" as an error path, not success
Reasoningreasoning={"effort": "low"} or lower where evals allowOften the biggest cost on reasoning models
Verbositytext={"verbosity": "low"} where supportedShorter answers, same content
HistoryKeep last N turns + summary, or server-side compactionMeasure the quality impact on long-conversation cases
RetrievalFewer, better chunks (rerank to top 5–8)Cheaper than a larger context
Tool outputsSummarize or paginate large resultsRaw HTML and full tables are expensive
ImagesDownscale and use lower detail when fine print does not matterImage tokens scale with resolution

5. Routing and escalation

def answer(req):
    tier = route(req)  # rules first: intent, length, customer plan; classifier later
    r = call(model=MODELS[tier], req=req)
    if tier == "small" and (r.parsed is None or r.parsed.confidence == "low"):
        r = call(model=MODELS["large"], req=req)   # escalate, log the escalation
    return r
  • Start with rule-based routing on signals you already have; add a small classifier only if rules plateau.
  • The escalation rate is a metric: if most requests escalate, the small tier costs more than it saves.
  • Validate each tier on the eval set for the traffic slice it serves.
  • Keep model names in config (MODELS = {"small": ..., "large": ...}) and check the models and pricing pages when choosing tiers.

Offline or non-interactive work (backfills, nightly classification, evals) → Batch API at half price (see openai-batch-api). Identical repeated questions → exact-match response cache keyed on normalized input + prompt version; for similarity-based reuse see semantic-caching.

Gotchas

  • Optimizing cost without an eval gate: a 40% saving that drops answer quality 10% is usually a loss.
  • Inserting the current date at the top of the system prompt disables caching for the whole prompt.
  • Per-user personalization in the first message (name, plan) has the same effect — move it to the end.
  • Retry storms double spend; cap retries and honor Retry-After.
  • Streaming does not reduce cost; cancelling abandoned streams does.
  • Cached tokens still count against the context window and still consume some rate limit; they are cheaper, not free.

Verification

  1. Before/after table per feature: requests, cost per request, cached-token ratio, p50/p95 latency.
  2. Render two consecutive prompts for the same feature and diff them; the first differing byte should be in section [5] or [6].
  3. Eval pass rate unchanged (within noise) for every changed feature — see llm-evals.

Output contract

| Feature | Cost/req before | after | Cached ratio | p95 latency | Eval pass |
|---|---|---|---|---|---|
| support-reply | $0.0142 | $0.0061 | 3% → 71% | 3.4 s → 2.2 s | 91% → 91% |
| ticket-labels | $0.0021 | $0.0009 | n/a (Batch) | n/a | 96% → 95% |

**Changes:** moved date and user name to final message; froze tool order; effort low;
routed short intents to small tier (escalation rate 7%); labels moved to Batch.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Prompt Caching and Cost"
  short_description: "Lower the LLM bill without lowering quality"
  default_prompt: "$prompt-caching-cost Our OpenAI bill doubled this month — find where the tokens go and cut it"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem