अध्याय २ · खण्ड III · 22 मिनेट
Caching र खर्च घटाउने
अधिकांश LLM traffic दोहोरिने हुन्छ: उही प्रश्नहरू, उही prompts, उही retrievals। जवाफहरू cache गर्नुहोस् र quality घाटा बिना तपाईंको bill को 20-60% गायब हुन्छ। Techniques — साधारण response caching देखि Anthropic को prompt caching सम्म — र प्रत्येक कहिले payoff हुन्छ।
अधिकांश AI services तिनका designers लाई थाहा भन्दा बढी दोहोरिने हुन्छन्। प्रयोगकर्ताहरूले उही प्रश्नहरू सोध्दछन्। System prompts सबै calls भर उही रहन्छन्। Retrieved contexts भारी रूपमा overlap गर्दछन्। ती दोहोर्याइहरूको हरेक एक cost हो जुन तपाईंले दोस्रो पटक तिर्नुहुन्छ — जबसम्म तपाईंले cache गर्नुहुन्न। यो खण्ड हरेक deployed LLM उत्पादले मूल्यांकन गर्नुपर्ने तीन caching प्रविधिहरू हो: response caching, prompt caching, र embedding caching। एकसाथ, तिनीहरूले सामान्य LLM bills 20-60% ले घटाउँछन्, quality नबिगारेर।
वास्तवमै के दोहोरिन्छ
केही cache गर्नुभन्दा अघि, तपाईंको service ले वास्तवमै के दोहोर्याउँछ थाहा पाउनुहोस्। एक हप्ता queries log गर्नुहोस् र विश्लेषण गर्नुहोस्:
- ठ्याक्कै दोहोर्याइहरू। उही query, उही context। FAQ-style bots मा सामान्य।
- नजिकको-दोहोर्याइहरू। उही प्रश्नको केही फरक phrasings। Customer-service bots मा सामान्य।
- दोहोर्याइएको system prompt। System prompt हरेक request भर समान छ।
- दोहोर्याइएको retrieved chunks। लोकप्रिय documents धेरै queries मा फिर्ता आउँछन्।
फरक दोहोर्याइहरूले फरक caching strategies पाउँछन्। पहिले pattern बुझ्नुहोस्, त्यसपछि सही tool लागू गर्नुहोस्।
Technique 1 — Response caching
सबैभन्दा स्पष्ट: यदि प्रयोगकर्ताले दुई पटक उही प्रश्न सोध्दछ, re-generate नगर्नुहोस्। Cached जवाफ फर्काउनुहोस्।
import hashlib
import redis
import json
r = redis.Redis()
CACHE_TTL = 24 * 3600 # 1 day
def cache_key(question: str, context: str) -> str:
"""Hash the full input to produce a stable cache key."""
payload = f"{question}::{context}"
return "llm_cache:" + hashlib.sha256(payload.encode()).hexdigest()
def get_or_generate(question: str, context: str) -> str:
key = cache_key(question, context)
cached = r.get(key)
if cached:
return json.loads(cached)["answer"]
# Cache miss — call the model
answer = call_model(question, context)
r.setex(key, CACHE_TTL, json.dumps({"answer": answer, "cached_at": time.time()}))
return answer
नेपाली FAQ bot को लागि जहाँ “मैले नागरिकता कसरी नविकरण गर्ने?” शीर्ष query हो र दिनमा 500 पटक सोधिन्छ, response caching ले ती 500 calls लाई 1 मा घटाउँछ। त्यो त्यो query मा 99.8% कमी हो।
Response caching कहाँ राम्रोसँग काम गर्दछ:
- स्थिर corpus माथि FAQ-style Q&A।
- Deterministic कार्यहरू (translation, formatting) जहाँ output मात्र input मा भर पर्दछ।
- Content generation जहाँ “उही request ले उही जवाफ दिनुपर्दछ” अर्थ राख्दछ।
यसले कहाँ राम्रोसँग गर्दैन:
- Personalised प्रतिक्रियाहरू (हरेक प्रयोगकर्ताले tailored जवाफ पाउँछ)।
- Time-dependent जवाफहरू (“आजको मूल्य के हो?”)।
- Conversational context (आदान-प्रदान प्रति प्रयोगकर्ता विकास हुन्छ)।
Cache key design
Cache key ले “उही request” के हो निर्धारण गर्दछ। यसलाई गलत पाउनुहोस् र तपाईंले या त real cache hits छुटाउनुहुन्छ वा गलत जवाफहरू सर्भ गर्नुहुन्छ।
Simple Q&A को लागि: normalised question + retrieved context को hash गर्नुहोस्।
def normalise_question(q: str) -> str:
return q.strip().lower()
def cache_key(question: str, context: str) -> str:
payload = normalise_question(question) + "::" + context
return "llm_cache:" + hashlib.sha256(payload.encode()).hexdigest()
Conversational context को लागि, अन्तिम N turns समावेश गर्नुहोस्:
def cache_key(question: str, history: list) -> str:
recent = json.dumps(history[-4:]) # last 2 exchanges
payload = normalise_question(question) + "::" + recent
return "llm_cache:" + hashlib.sha256(payload.encode()).hexdigest()
Semantic caching
Exact-match भन्दा एक कदम अगाडि: समान पहिलेका प्रश्नहरू भेट्न embeddings प्रयोग गर्नुहोस् र similarity पर्याप्त उच्च हुँदा cached जवाफहरू सर्भ गर्नुहोस्।
# Store each question's embedding alongside its cached answer
def semantic_cache_lookup(question: str, threshold: float = 0.92):
q_vec = embed(question)
hits = vector_store.search(q_vec, top_k=1)
if hits and hits[0]["score"] > threshold:
return hits[0]["answer"]
return None
यदि प्रयोगकर्ताले “मैले नागरिकता कसरी नविकरण गर्ने?” सोध्दछ र cache मा “म कसरी नागरिकता प्रमाणपत्र नविकरण गर्न सक्दछु?” 0.94 similarity मा छ भने, उही जवाफ सर्भ गर्नुहोस्।
सावधान: threshold धेरै कम भएमा semantic caching ले चुपचाप गलत जवाफहरू सर्भ गर्न सक्दछ। कडा रूपमा परीक्षण गर्नुहोस्। नेपाली FAQ डाटाको लागि सुरक्षित threshold प्राय: 0.90-0.94 हो — तर आफ्नो queries मा validate गर्नुहोस्।
Technique 2 — Prompt caching (Anthropic)
Anthropic को API ले prompt caching समर्थन गर्दछ: तपाईंको prompt का भागहरूलाई cacheable रूपमा mark गर्नुहोस्, र त्यसपछिका calls मा तिनीहरूलाई पुन: पठाउन Anthropic ले धेरै कम charge गर्दछ (cached tokens को लागि input side मा लगभग 10× cheaper)।
Use case: तपाईंको system prompt र retrieved documents एक conversation मा 20 calls भर उही छन्। तिनीहरूलाई cacheable रूपमा mark गर्ने मतलब Anthropic ले तिनीहरूलाई पूर्ण रूपमा एकपटक मात्र process गर्दछ।
response = client.messages.create(
model="claude-3-5-sonnet-latest",
max_tokens=500,
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"},
},
{
"type": "text",
"text": f"Documents:\n{retrieved_context}",
"cache_control": {"type": "ephemeral"},
},
],
messages=[{"role": "user", "content": user_question}],
)
Prompt caching कहाँ payoff हुन्छ:
- लामो, static system prompts (Anthropic को threshold को लागि >1,024 tokens)।
- लामो, static retrieved context धेरै calls मा पुन: प्रयोग गरिएको (जस्तै, context को रूपमा पूरै कागजात)।
- Multi-turn conversations जहाँ इतिहास बढ्दै जान्छ।
यसले कहाँ गर्दैन:
- छोटा prompts (caching overhead ले 1,024 tokens तल savings भन्दा बढी हुन्छ)।
- हरेक-call-फरक workloads।
400-token system prompt र 3,000 tokens retrieved context भएको RAG bot को लागि, prompt caching ले cache hits मा input cost 8-10× घटाउँछ। यदि उही context 10-turn conversation भर पुन: प्रयोग गरिन्छ भने, त्यो एक विशाल saving हो।
Technique 3 — Embedding caching
Embeddings सस्ता छन् ($0.02 per million tokens for text-embedding-3-small), तर तिनीहरू योगदान गर्दछन्। यदि तपाईंले दिनमा धेरै पटक उही query embed गर्नुहुन्छ भने, यसलाई cache गर्नुहोस्।
def embed_cached(text: str) -> list[float]:
key = "emb:" + hashlib.sha256(text.encode()).hexdigest()
cached = r.get(key)
if cached:
return json.loads(cached)
v = openai_client.embeddings.create(
model="text-embedding-3-small",
input=text,
).data[0].embedding
r.setex(key, 7 * 86400, json.dumps(v)) # cache 7 days
return v
अझ महत्त्वपूर्ण: आफ्नो document embeddings लाई disk मा, स्थायी रूपमा, cache गर्नुहोस्। तपाईं कागजात ingesting गर्दा एकपटक embed गर्नुहुन्छ, त्यसपछि कहिल्यै फेरि होइन। यदि तपाईंको ingestion pipeline ले हरेक deploy मा उही content re-embed गर्दछ भने, तपाईं उही काममा दुई पटक तिरिरहनुभएको छ।
Caching को economics
एक सामान्य नेपाली policy Q&A bot मा order-of-magnitude cost impact:
| Layer | No caching | With caching | Savings |
|---|---|---|---|
| Model calls (LLM) | $2400 | $960 | $1,440 (60% off) |
| Embedding calls | $50 | $10 | $40 (80% off) |
| Vector store queries | $0 | $0 | 0 |
| Redis cache infra | $0 | $10 | |
| Net monthly cost | $2450 | $980 | $1,470/month |
Rs ~320,000/महिना बेसलाइनमा Rs ~190,000/महिना बचत भयो। Redis cost एक rounding error हो।
Rule of thumb: यदि तपाईंको service ले प्रति दिन केही सयभन्दा बढी queries सम्हाल्दछ र दोहोर्याइको कुनै pattern छ भने, response caching एक्लै ले पहिलो महिना आफैलाई भन्दा बढी तिर्नेछ।
Cache invalidation
गाह्रो भाग। दुई नियमहरू:
-
Time-based expiry (TTL) सधैँ। कुनै cache entry सधैँको लागि रहँदैन। FAQ जवाफहरूमा 24-घण्टा TTL को मतलब हिजोको cached गलत जवाफ आजसम्म गयो। तपाईंको underlying data कति पटक परिवर्तन हुन्छ त्यो match गर्न TTL छनोट गर्नुहोस्।
-
Document updates मा manual invalidation। जब तपाईं underlying documents update गर्नुहुन्छ (एक नयाँ HR policy, एक सच्चाइएको FAQ), तिनीहरूबाट प्राप्त सबै cached जवाफहरूलाई invalidate गर्नुहोस्। यदि तपाईंको cache keys ले document IDs hash गर्दछ भने, यो सीधा हो। यदि होइन भने, “cache version” prefix थप्नुहोस् र updates मा यसलाई bump गर्नुहोस्।
दुबै छोड्नुहोस्। एक stale cache ले confidently गलत जवाफहरू सर्भ गर्दछ, जुन fresh call गर्नुभन्दा खराब छ।
के cache गर्ने vs. के होइन
एक उपयोगी checklist:
खुल्ला रूपमा cache गर्नुहोस्:
- स्थिर कागजातहरू माथि FAQ / Q&A प्रतिक्रियाहरू।
- Deterministic transformations (translation, static content को summarisation, classification)।
- Document embeddings (स्थायी रूपमा, disk मा)।
सावधानीपूर्वक cache गर्नुहोस् (कडा TTLs वा semantic thresholds सँग):
- कागजात डाटालाई थोरै प्रयोगकर्ता context सँग जोड्ने जवाफहरू।
- एक conversation मा लोकप्रिय queries।
Cache नगर्नुहोस्:
- Personalised outputs (प्रयोगकर्ताको account, transactions, portfolio)।
- Time-sensitive जवाफहरू (stock prices, weather, समाचार)।
- Creative content जहाँ हरेक request fresh हुनुपर्दछ।
- कुनै पनि कुरा जुन प्रयोगकर्ताले अर्को प्रयोगकर्ताबाट recycled देख्नु हुँदैन।
Caching audit
महिनामा एकपटक, आफ्नो cache stats हेर्नुहोस्:
- Hit rate। सबै requests मध्ये, कुन अंश cache बाट सर्भ गरिएको थियो? FAQ-style products को लागि 30-60% लक्ष्य राख्नुहोस्।
- Cost saved। Cache hits लाई प्रति call औसत cost सँग गुणा गरेर savings अनुमान गर्नुहोस्।
- Cached responses मा गलत-जवाफ complaints। यी caching bug को सबैभन्दा बलियो signal हुन्।
Cache health एक metric हो जुन watch गर्न लायक हुन्छ, error rates र latency सँगै।
अब के आउँछ
तपाईंले cost squeeze गर्नुभयो। तर अब तपाईंसँग एउटा प्रणाली छ जुन प्राय: silent छ — तपाईंलाई थाहा छैन कहिले यो राम्रोसँग सर्भ गर्दैछ, कहिले असफल हुँदैछ, वा कहिले प्रयोगकर्तालाई खराब समय भइरहेको छ। Chapter 3 तपाईंको प्रणाली देख्ने बारे हो: logs, metrics, र तपाईंको service ले वास्तवमा अहिले के गरिरहेको छ थाहा पाउने discipline।