llm-caching – Agents & Automation skill for AI agents
Implement multi-layer LLM caching with exact match, semantic similarity, and provider-side prompt caching.
Install the llm-caching skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/llm-caching-66cf8.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /llm-caching – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/llm-caching-66cf8.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $llm-caching – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/llm-caching-66cf8.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /llm-caching – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/llm-caching-66cf8.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /llm-caching – then describe the task you want done.
SKILL.md content
LLM Caching
Cut LLM costs and latency with exact match, semantic, and provider-side caching layers.
When to Use This Skill
Use this skill when:
- The same or similar queries are asked repeatedly (FAQ bots, support tools)
- LLM API costs are growing and you need immediate savings
- Serving high request volumes where repeated queries cause bottlenecks
- Implementing prompt caching for long system prompts (Anthropic/OpenAI)
- Building offline-capable AI features that need response persistence
Caching Layers
Cache examples are incomplete until authorization/data boundaries are included. Partition by tenant/user access scope and include model revision, system/developer prompts, conversation/tool context, parameters, locale and source/policy version in keys or eligibility filters. Never serve a privileged cached answer across users merely because queries are similar. Hashing prompts is not encryption: restrict/encrypt stored queries/results, honor deletion/retention and invalidate when permissions/data change. Avoid semantic reuse for sensitive or time-dependent answers unless evaluated false-hit risk is acceptable. Similarity thresholds/latencies/savings are examples, not calibrated guarantees. Cache warming is paid generation: bound concurrency, requests and budget before running.
Request → Exact Cache → Semantic Cache → Provider Cache → LLM API
↓ hit ↓ hit ↓ hit
instant ~5ms 50-80% cheaperLayer 1: Exact Match Cache (Redis)
import hashlib
import json
import redis
from openai import OpenAI
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
client = OpenAI()
def build_cache_key(model: str, messages: list, temperature: float) -> str:
"""Deterministic key from request parameters."""
payload = json.dumps({
"model": model,
"messages": messages,
"temperature": temperature,
}, sort_keys=True)
return f"llm:exact:{hashlib.sha256(payload.encode()).hexdigest()}"
def cached_completion(model: str, messages: list, temperature: float = 0.0,
ttl: int = 3600) -> dict:
key = build_cache_key(model, messages, temperature)
# Check cache
if cached := r.get(key):
return json.loads(cached)
# Call API
response = client.chat.completions.create(
model=model, messages=messages, temperature=temperature
)
result = response.model_dump()
# Cache only eligible responses; temperature 0 does not guarantee determinism.
if temperature == 0.0:
r.setex(key, ttl, json.dumps(result))
return resultLayer 2: Semantic Cache (GPTCache)
from gptcache import cache, Config
from gptcache.adapter import openai
from gptcache.embedding import Onnx
from gptcache.manager import CacheBase, VectorBase, get_data_manager
from gptcache.similarity_evaluation.distance import SearchDistanceEvaluation
# Configure GPTCache with Qdrant backend
def init_gptcache(cache_obj, llm: str):
onnx = Onnx() # local embedding model
data_manager = get_data_manager(
CacheBase("redis"), # metadata store
VectorBase("qdrant",
host="localhost",
port=6333,
collection_name=f"llm-cache-{llm}",
dimension=onnx.dimension),
)
cache_obj.init(
embedding_func=onnx.to_embeddings,
data_manager=data_manager,
similarity_evaluation=SearchDistanceEvaluation(),
config=Config(similarity_threshold=0.80), # 80% similarity = cache hit
)
cache.set_openai_key()
init_gptcache(cache, "gpt-4o-mini")
# Now openai calls are automatically cached
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "What is machine learning?"}],
)
# Second call with similar question ("Explain machine learning") → cache hitCustom Semantic Cache (Illustrative)
from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, Range
import numpy as np
import uuid
import time
embed_model = SentenceTransformer("BAAI/bge-small-en-v1.5") # fast, 33M params
qdrant = QdrantClient("http://localhost:6333")
CACHE_COLLECTION = "semantic-cache"
SIMILARITY_THRESHOLD = 0.88
CACHE_TTL_SECONDS = 86400 # 24h
# Create collection once
qdrant.create_collection(
collection_name=CACHE_COLLECTION,
vectors_config=VectorParams(size=384, distance=Distance.COSINE),
on_disk_payload=True,
)
def semantic_cache_lookup(query: str, model: str) -> str | None:
embedding = embed_model.encode(query).tolist()
results = qdrant.query_points(
collection_name=CACHE_COLLECTION,
query=embedding,
query_filter=Filter(must=[
FieldCondition(key="model", match={"value": model}),
FieldCondition(key="expires_at", range=Range(gte=time.time())),
]),
limit=1,
score_threshold=SIMILARITY_THRESHOLD,
)
if results.points:
return results.points[0].payload["response"]
return None
def semantic_cache_store(query: str, response: str, model: str):
embedding = embed_model.encode(query).tolist()
qdrant.upsert(
collection_name=CACHE_COLLECTION,
points=[PointStruct(
id=str(uuid.uuid4()),
vector=embedding,
payload={
"query": query,
"response": response,
"model": model,
"created_at": time.time(),
"expires_at": time.time() + CACHE_TTL_SECONDS,
},
)],
)
def smart_llm_call(query: str, model: str = "gpt-4o-mini") -> dict:
# 1. Semantic lookup
if cached_response := semantic_cache_lookup(query, model):
return {"response": cached_response, "source": "semantic_cache", "cost": 0}
# 2. LLM call
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": query}],
)
text = response.choices[0].message.content
cost = litellm.completion_cost(response)
# 3. Store in cache
semantic_cache_store(query, text, model)
return {"response": text, "source": "llm_api", "cost": cost}Gateway-Level Semantic Cache
If LLM traffic already goes through a gateway (see llm-gateway), the semantic cache can live there instead of in each application. LiteLLM's cache: true setting is one option; Bifrost's semantic_cache plugin is another. Bifrost checks an exact-match hash first and runs the vector similarity search only on a miss. It needs a vector store (RediSearch-compatible Redis/Valkey, Weaviate, Qdrant or Pinecone) and an embedding model for the semantic path, and it only caches requests that carry a cache key (the x-bf-cache-key header, or a configured default_cache_key).
{
"vector_store": { "enabled": true, "type": "redis", "config": { "addr": "redis:6379" } },
"plugins": [{
"enabled": true,
"name": "semantic_cache",
"config": {
"provider": "openai",
"embedding_model": "text-embedding-3-small",
"dimension": 1536,
"threshold": 0.85,
"ttl": "1h"
}
}]
}Layer 3: Provider-Side Prompt Caching
# Anthropic — cache long system prompts (saves 90% on cached input tokens)
import anthropic
anthropic_client = anthropic.Anthropic()
# Long system prompt — mark for caching
SYSTEM_PROMPT = open("knowledge-base.txt").read() # e.g., 50k tokens
def call_with_prompt_cache(user_question: str) -> str:
response = anthropic_client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=[
{"type": "text", "text": "You are a helpful assistant."},
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"}, # cache this block
}
],
messages=[{"role": "user", "content": user_question}],
)
# Log cache efficiency
usage = response.usage
cache_savings = usage.cache_read_input_tokens * 0.9 # 90% discount on cached
print(f"Cache hits: {usage.cache_read_input_tokens} tokens "
f"(saved ~${cache_savings * 3.0 / 1_000_000:.4f})")
return response.content[0].text
# OpenAI — automatic for repeated prefixes (≥1,024 tokens)
# No code change needed; cached tokens appear in usage.prompt_tokens_details
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": LONG_SYSTEM_PROMPT}, # auto-cached
{"role": "user", "content": user_question},
]
)
cached = response.usage.prompt_tokens_details.cached_tokens
print(f"OpenAI cached {cached} tokens")Cache Warming
async def warm_cache(common_queries: list[… (full content is in the download package)
Related skills
- llm-ops – LLM Operations -- RAG, embeddings, vector databases, fine-tuning, prompt engineering…
- llm-prompt-optimizer – Use when improving prompts for any LLM. Applies proven prompt engineering techniques to…
- loki-mode – Historical 2.35.x multi-agent workflow for turning a scoped PRD into reviewed…
- longbridge-derivatives – Curated upstream guidance for Longbridge Derivatives; use when the workflow matches the…
- longbridge-quant – Curated upstream guidance for Longbridge Quant; use when the workflow matches the user…
- lore – Markdown project memory for AI agents. Use for decisions, architecture, conventions…
- m365-agents-dotnet – Microsoft 365 Agents SDK for .NET. Build multichannel agents for Teams/M365/Copilot…
- m365-agents-py – Microsoft 365 Agents SDK for Python. Build multichannel agents for Teams/M365/Copilot…