llm-caching-patterns

SKILL开发
开发 未标注权限 许可证不明确

Multi-level caching strategies for LLM applications - semantic caching (Redis), prompt caching (Claude/OpenAI native), cache hierarchies, cost optimization, and Langfuse cost tracking with hierarchical trace rollup for 70-95% cost reduction

一句话选型结论

按资料字段整理

llm-caching-patterns是开发类的 Agent Skill,安装方式:GitHub 仓库,权限级别未标注,许可证不明确,没有现成安装命令,需按说明手动放置 SKILL.md。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式GitHub 仓库 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 3 仓库 create-yg-app
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

GitHub 仓库
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    按安装说明或仓库 README,把 SKILL.md 放到客户端的 skills 目录(一般一个技能一个文件夹)。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方yonatangross仓库 create-yg-app · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式GitHub 仓库安装包 yonatangross/create-yg-app
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 未标注
正文体量31.8 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 基础信息齐全,可以先按说明装一个试试。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有现成安装命令,要按安装说明或仓库 README 手动放置。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
llm-caching-patterns当前 未标注权限 GitHub 仓库 未标注 未标注 无
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

31.8 KB
模型实际读到的说明书原文,来自技能仓库

LLM Caching Patterns

Overview

Modern LLM applications can reduce costs by 70-95% through intelligent multi-level caching. This skill covers the multi-tier caching architecture (2025 best practice): combining in-memory LRU, Redis semantic caching, and provider-native prompt caching for maximum efficiency.

Real-World Use Cases:
- High-Volume Chatbot: Semantic cache for FAQ variations (80% cache hit rate)
- Code Review Automation: Prompt cache for system instructions (90% savings)
- Content Moderation: L1/L2 cache for repeat content detection
- Multi-Agent Analysis: Hierarchical cache strategy across agents
- Customer Support: Session-aware caching for context continuity

When to use this skill:
- High-volume LLM applications with repeated queries
- Cost-sensitive AI features
- Similar query patterns (e.g., analyzing similar content types)
- Applications requiring sub-second response times
- Multi-agent systems with redundant LLM calls

Expected Impact:
- L1 (LRU) Cache: 10-20% hit rate, ~1ms latency, 100% cost savings
- L2 (Redis Semantic): 30-50% hit rate, ~5-10ms latency, 100% cost savings
- L3 (Prompt Cache): 80-100% coverage, ~2s latency, 90% token cost savings
- Combined: 70-95% total cost reduction

Core Concepts

Double Caching Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    CACHE HIERARCHY (2025 BEST PRACTICE)         │
└─────────────────────────────────────────────────────────────────┘

Request → L1 (Exact Hash) → L2 (Semantic) → L3 (Prompt) → L4 (Full LLM)
           ↓ Hit: ~1ms      ↓ Hit: ~10ms     ↓ Cached     ↓ Full Cost
         100% savings      100% savings      90% savings   $$$

L1: In-Memory LRU Cache
────────────────────────
• Exact content hash matching
• 1,000-10,000 entry size
• TTL: 5-10 minutes
• Use Case: Duplicate requests within session
• Implementation: Python functools.lru_cache or cachetools

L2: Redis Semantic Cache
─────────────────────────
• Vector similarity search (cosine distance < 0.08)
• Configurable similarity threshold (0.85-0.95)
• TTL: 1-24 hours
• Use Case: Similar but not identical queries
• Implementation: RedisVL SemanticCache + RediSearch

L3: Prompt Caching (Provider Native)
────────────────────────────────────
• Cache identical prompt PREFIXES (system prompts, examples)
• Claude: cache_control ephemeral markers
• GPT: Cached prefix automatically detected
• TTL: 5 minutes (auto-refresh on use)
• Use Case: Same prompts, different user content
• March 2025: Cache reads don't count against rate limits!

L4: Full LLM Call
─────────────────
• No cache hit - full generation required
• Store response in L2 and L1 for future hits
• Full token cost

Cache Decision Flow

async def get_llm_response(query: str, agent_type: str) -> dict:
    """Multi-level cache lookup."""

    # L1: Exact match (in-memory)
    cache_key = hash_content(query)
    if cache_key in lru_cache:
        return lru_cache[cache_key]  # ~1ms, 100% savings

    # L2: Semantic similarity (Redis)
    embedding = await embed_text(query)
    similar = await redis_cache.find_similar(
        embedding=embedding,
        agent_type=agent_type,
        threshold=0.92  # Configurable
    )
    if similar and similar.distance < 0.08:
        lru_cache[cache_key] = similar.response  # Promote to L1
        return similar.response  # ~10ms, 100% savings

    # L3 + L4: Prompt caching + LLM call
    # Prompt cache breakpoints reduce L4 cost by 90%
    response = await llm.generate(
        messages=build_cached_messages(
            system_prompt=AGENT_PROMPT,  # ← Cached
            examples=few_shot_examples,   # ← Cached
            user_content=query            # ← NOT cached
        )
    )

    # Store in L2 and L1
    await redis_cache.set(embedding, response, agent_type)
    lru_cache[cache_key] = response

    return response  # L3: ~2s, 90% savings | L4: ~3s, full cost

Similarity Threshold Tuning

Problem: How similar is "similar enough" to return a cached response?

Threshold Guidelines (cosine similarity):
- 0.98-1.00 (distance 0.00-0.02): Nearly identical - safe to return
- 0.95-0.98 (distance 0.02-0.05): Very similar - usually safe
- 0.92-0.95 (distance 0.05-0.08): Similar - validate with reranking
- 0.85-0.92 (distance 0.08-0.15): Moderately similar - risky
- < 0.85 (distance > 0.15): Different - do not return

Recommended Starting Point: 0.92 (distance < 0.08)

Tuning Process:
1. Start at 0.92 threshold
2. Monitor false positives (wrong cached responses)
3. Monitor false negatives (cache misses that should've hit)
4. Adjust threshold based on precision/recall tradeoff
5. Different thresholds per agent type (security=0.95, general=0.90)

Cache Warming Strategy

Pre-populate cache from golden dataset for instant hit rates:

async def warm_cache_from_golden_dataset(
    cache: SemanticCache,
    min_quality: float = 0.8
) -&gt; int:
    &quot;&quot;&quot;Warm cache with high-quality historical responses.&quot;&quot;&quot;

    # Load golden dataset analyses
    analyses = await db.query(
        &quot;SELECT * FROM analyses WHERE confidence_score &gt;= ?&quot;,
        (min_quality,)
    )

    warmed = 0
    for analysis in analyses:
        # Extract agent findings
        for finding in analysis.findings:
            await cache.set(
                content=analysis.content,
                response=finding.output,
                agent_type=finding.agent_type,
                quality_score=finding.confidence_score
            )
            warmed += 1

    return warmed

Redis Semantic Cache Implementation

Schema Design

# RedisVL Index Schema
CACHE_INDEX_SCHEMA = {
    &quot;index&quot;: {
        &quot;name&quot;: &quot;llm_semantic_cache&quot;,
        &quot;prefix&quot;: &quot;cache:&quot;,
    },
    &quot;fields&quot;: [
        {&quot;name&quot;: &quot;agent_type&quot;, &quot;type&quot;: &quot;tag&quot;},
        {&quot;name&quot;: &quot;content_type&quot;, &quot;type&quot;: &quot;tag&quot;},
        {&quot;name&quot;: &quot;input_hash&quot;, &quot;type&quot;: &quot;tag&quot;},
        {
            &quot;name&quot;: &quot;embedding&quot;,
            &quot;type&quot;: &quot;vector&quot;,
            &quot;attrs&quot;: {
                &quot;dims&quot;: 1536,  # OpenAI text-embedding-3-small
                &quot;distance_metric&quot;: &quot;cosine&quot;,
                &quot;algorithm&quot;: &quot;hnsw&quot;,  # Fast approximate search
            }
        },
        {&quot;name&quot;: &quot;response&quot;, &quot;type&quot;: &quot;text&quot;},
        {&quot;name&quot;: &quot;created_at&quot;, &quot;type&quot;: &quot;numeric&quot;},
        {&quot;name&quot;: &quot;hit_count&quot;, &quot;type&quot;: &quot;numeric&quot;},
        {&quot;name&quot;: &quot;quality_score&quot;, &quot;type&quot;: &quot;numeric&quot;},
    ]
}

Service Class

from redisvl.index import SearchIndex
from redisvl.query import VectorQuery
from redis import Redis

class SemanticCacheService:
    &quot;&quot;&quot;Redis semantic cache for LLM responses.&quot;&quot;&quot;

    def __init__(self, redis_url: str, similarity_threshold: float = 0.92):
        self.client = Redis.from_url(redis_url)
        self.threshold = similarity_threshold
        self.embedding_service = EmbeddingService()

        # Initialize RedisVL index
        schema = IndexSchema.from_dict(CACHE_INDEX_SCHEMA)
        self.index = SearchIndex(schema, self.client)
        self.index.create(overwrite=False)

    async def get(
        self,
        content: str,
        agent_type: str,
        content_type: str | None = None
    ) -&gt; CacheEntry | None:
        &quot;&quot;&quot;Look up cached response by semantic similarity.&quot;&quot;&quot;

        # Generate embedding
        embedding = await self.embedding_service.embed_text(content[:2000])

        # Build query with filters
        filter_expr = f&quot;@agent_type:{{{agent_type}}}&quot;
        if content_type:
            filter_expr += f&quot; @content_type:{{{content_type}}}&quot;

        query = VectorQuery(
            vector=embedding,
            vector_field_name=&quot;embedding&quot;,
            return_fields=[&quot;response&quot;, &quot;quality_score&quot;, &quot;hit_count&quot;],
            num_results=1,
            filter_expression=filter_expr
        )

        results = self.index.query(query)

        if results and len(results) &gt; 0:
            result = results[0]
            distance = float(result.get(&quot;vector_distance&quot;, 1.0))

            # Check similarity threshold
            if distance &lt;= (1 - self.threshold):
                # Increment hit count
                self.client.hincrby(result[&quot;id&quot;], &quot;hit_count&quot;, 1)

                return CacheEntry(
                    response=json.loads(result[&quot;response&quot;]),
                    quality_score=float(result[&quot;quality_score&quot;]),
                    hit_count=int(result[&quot;hit_count&quot;]),
                    distance=distance
                )

        return None

    async def set(
        self,
        content: str,
        response: dict,
        agent_type: str,
        content_type: str | None = None,
        quality_score: float = 1.0
    ) -&gt; None:
        &quot;&quot;&quot;Store response in cache.&quot;&quot;&quot;
        content_preview = content[:2000]
        embedding = await self.embedding_service.embed_text(content_preview)

        key = f&quot;cache:{agent_type}:{hash_content(content_preview)}&quot;

        data = {
            &quot;agent_type&quot;: agent_type,
            &quot;content_type&quot;: content_type or &quot;&quot;,
            &quot;input_hash&quot;: hash_content(content_preview),
            &quot;embedding&quot;: embedding,
            &quot;response&quot;: json.dumps(response),
            &quot;created_at&quot;: time.time(),
            &quot;hit_count&quot;: 0,
            &quot;quality_score&quot;: quality_score,
        }

        self.client.hset(key, mapping=data)
        self.client.expire(key, ttl=86400)  # 24 hours

Prompt Caching (Claude Native)

Cache Breakpoint Strategy

class PromptCacheManager:
    &quot;&quot;&quot;Manage Claude prompt caching with cache breakpoints.&quot;&quot;&quot;

    def build_cached_messages(
        self,
        system_prompt: str,
        few_shot_examples: str | None = None,
        schema_prompt: str | None = None,
        dynamic_content: str = &quot;&quot;
    ) -&gt; list[dict]:
        &quot;&quot;&quot;Build messages with cache breakpoints.

        Cache structure:
        1. System prompt (always cached)
        2. Few-shot examples (cached per content type)
        3. Schema documentation (always cached)
        ──────────────── CACHE BREAKPOINT ────────────────
        4. Dynamic content (NEVER cached)
        &quot;&quot;&quot;

        content_parts = []

        # Breakpoint 1: System prompt
        content_parts.append({
            &quot;type&quot;: &quot;text&quot;,
            &quot;text&quot;: system_prompt,
            &quot;cache_control&quot;: {&quot;type&quot;: &quot;ephemeral&quot;}
        })

        # Breakpoint 2: Few-shot examples (if provided)
        if few_shot_examples:
            content_parts.append({
                &quot;type&quot;: &quot;text&quot;,
                &quot;text&quot;: few_shot_examples,
                &quot;cache_control&quot;: {&quot;type&quot;: &quot;ephemeral&quot;}
            })

        # Breakpoint 3: Schema documentation (if provided)
        if schema_prompt:
            content_parts.append({
                &quot;type&quot;: &quot;text&quot;,
                &quot;text&quot;: schema_prompt,
                &quot;cache_control&quot;: {&quot;type&quot;: &quot;ephemeral&quot;}
            })

        # Dynamic content (NOT cached)
        content_parts.append({
            &quot;type&quot;: &quot;text&quot;,
            &quot;text&quot;: dynamic_content
        })

        return [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: content_parts}]

Cost Calculation

Without Prompt Caching:
─────────────────────────
System prompt:    2,000 tokens @ $3/MTok = $0.006
Few-shot examples: 5,000 tokens @ $3/MTok = $0.015
Schema docs:      1,000 tokens @ $3/MTok = $0.003
User content:    10,000 tokens @ $3/MTok = $0.030
────────────────────────────────────────
Total input:     18,000 tokens           = $0.054 per request

With Prompt Caching (90% hit rate):
───────────────────────────────────
Cached prefix:    8,000 tokens @ $0.30/MTok = $0.0024 (cache read)
User content:    10,000 tokens @ $3/MTok    = $0.0300
────────────────────────────────────────
Total:           18,000 tokens              = $0.0324 per request

Savings: 40% per request

With Semantic Cache (35% hit rate) + Prompt Cache:
──────────────────────────────────────────────────
35% requests: $0.00 (semantic cache hit)
65% requests: $0.0324 (prompt cache benefit)
Average: $0.021 per request

Total Savings: 61% vs no caching

Optimization Techniques

1. LLM Reranking (Optional)

For higher precision, rerank top-k semantic cache candidates:

async def get_with_reranking(
    query: str,
    agent_type: str,
    top_k: int = 3
) -&gt; CacheEntry | None:
    &quot;&quot;&quot;Retrieve with LLM reranking for better precision.&quot;&quot;&quot;

    # Get top-k candidates
    candidates = await semantic_cache.get_topk(query, agent_type, k=top_k)

    if not candidates:
        return None

    # Use lightweight model to rerank
    rerank_prompt = f&quot;&quot;&quot;
    Query: {query}

    Rank these cached responses by relevance (1 = most relevant):
    {format_candidates(candidates)}
    &quot;&quot;&quot;

    ranking = await lightweight_llm.rank(rerank_prompt)
    best_candidate = candidates[ranking[0]]

    if best_candidate.score &gt; 0.8:  # Rerank threshold
        return best_candidate

    return None

2. Metadata Filtering

Filter before vector search to improve precision:

# Good: Filter by agent_type + content_type
query = VectorQuery(
    vector=embedding,
    filter_expression=&quot;@agent_type:{security_auditor} @content_type:{article}&quot;
)

# Better: Add difficulty level
query = VectorQuery(
    vector=embedding,
    filter_expression=&quot;&quot;&quot;
        @agent_type:{security_auditor}
        @content_type:{article}
        @difficulty_level:{advanced}
    &quot;&quot;&quot;
)

3. Quality-Based Eviction

Prioritize keeping high-quality responses:

async def evict_low_quality_entries(cache: SemanticCache, max_size: int):
    &quot;&quot;&quot;Evict low-quality entries when cache is full.&quot;&quot;&quot;

    # Get all entries sorted by quality score
    entries = await cache.get_all_sorted_by_quality()

    if len(entries) &gt; max_size:
        # Keep top N by quality, evict rest
        to_evict = entries[max_size:]
        for entry in to_evict:
            await cache.delete(entry.key)

4. Dynamic Threshold Adjustment

Adjust similarity threshold based on cache hit rate:

class AdaptiveThresholdManager:
    &quot;&quot;&quot;Dynamically adjust threshold based on metrics.&quot;&quot;&quot;

    def __init__(self, target_hit_rate: float = 0.35):
        self.target = target_hit_rate
        self.threshold = 0.92

    async def adjust(self, actual_hit_rate: float):
        &quot;&quot;&quot;Adjust threshold to reach target hit rate.&quot;&quot;&quot;

        if actual_hit_rate &lt; self.target - 0.05:
            # Too many misses, lower threshold (more permissive)
            self.threshold = max(0.85, self.threshold - 0.01)
        elif actual_hit_rate &gt; self.target + 0.05:
            # Too many hits (possibly false positives), raise threshold
            self.threshold = min(0.98, self.threshold + 0.01)

        logger.info(f&quot;Adjusted threshold to {self.threshold}&quot;)

Monitoring & Observability

Key Metrics

@dataclass
class CacheMetrics:
    &quot;&quot;&quot;Track cache performance.&quot;&quot;&quot;

    # Hit rates
    l1_hit_rate: float
    l2_hit_rate: float
    l3_hit_rate: float
    combined_hit_rate: float

    # Latency
    l1_avg_latency_ms: float
    l2_avg_latency_ms: float
    l3_avg_latency_ms: float
    l4_avg_latency_ms: float

    # Cost
    estimated_cost_saved_usd: float
    total_requests: int

    # Quality
    false_positive_rate: float  # Wrong cached responses
    false_negative_rate: float  # Missed valid cache hits

Langfuse Cost Tracking (2025 Best Practice)

Langfuse automatically tracks token usage and costs for all LLM calls. This eliminates manual cost calculation and provides accurate cost attribution.

Automatic Cost Tracking with Custom Trace IDs

from langfuse.decorators import observe, langfuse_context
from uuid import UUID

@observe(as_type=&quot;generation&quot;)
async def call_llm_with_cache(
    prompt: str,
    agent_type: str,
    analysis_id: UUID | None = None
) -&gt; str:
    &quot;&quot;&quot;LLM call with automatic cost tracking via Langfuse.

    CRITICAL: Always link to parent trace for cost attribution!
    &quot;&quot;&quot;

    # Link to parent analysis trace (for cost rollup)
    if analysis_id:
        langfuse_context.update_current_trace(
            name=f&quot;{agent_type}_generation&quot;,
            session_id=str(analysis_id),  # Group by analysis
            tags=[agent_type, &quot;cached&quot;],
            metadata={&quot;analysis_id&quot;: str(analysis_id)}
        )

    # Langfuse decorator automatically:
    # 1. Captures input/output tokens
    # 2. Calculates costs using model pricing
    # 3. Tags with agent_type for cost attribution
    # 4. Records cache hit/miss status

    # L1: Check exact cache
    cache_key = hash_content(prompt)
    if cache_key in lru_cache:
        # Mark as cache hit (zero cost)
        langfuse_context.update_current_observation(
            metadata={&quot;cache_layer&quot;: &quot;L1&quot;, &quot;cache_hit&quot;: True}
        )
        return lru_cache[cache_key]

    # L2: Check semantic cache
    embedding = await embed_text(prompt)
    similar = await redis_cache.find_similar(embedding, agent_type)
    if similar:
        langfuse_context.update_current_observation(
            metadata={&quot;cache_layer&quot;: &quot;L2&quot;, &quot;cache_hit&quot;: True, &quot;distance&quot;: similar.distance}
        )
        return similar.response

    # L3/L4: LLM call with prompt caching
    # Langfuse automatically tracks token usage and cost
    response = await llm.generate(
        messages=build_cached_messages(prompt),
        model=&quot;claude-3-5-sonnet-20241022&quot;
    )

    # Langfuse records:
    # - input_tokens (total)
    # - output_tokens
    # - cache_creation_input_tokens (prompt cache breakpoints)
    # - cache_read_input_tokens (cached prefix tokens)
    # - total_cost (calculated from model pricing)

    langfuse_context.update_current_observation(
        metadata={
            &quot;cache_layer&quot;: &quot;L3/L4&quot;,
            &quot;cache_hit&quot;: False,
            &quot;prompt_cache_hit&quot;: response.usage.cache_read_input_tokens &gt; 0
        }
    )

    # Store in L2 and L1 for future hits
    await redis_cache.set(embedding, response.content, agent_type)
    lru_cache[cache_key] = response.content

    return response.content

Trace Hierarchy for Cost Attribution (Production Pattern)

from langfuse import Langfuse
from langfuse.decorators import observe, langfuse_context
from uuid import uuid4, UUID

class CodeReviewWorkflow:
    &quot;&quot;&quot;Multi-agent code review with hierarchical cost tracking.&quot;&quot;&quot;

    @observe(as_type=&quot;trace&quot;)
    async def run_code_review(self, pr_id: int, diff: str, review_id: UUID) -&gt; dict:
        &quot;&quot;&quot;Parent trace - aggregates all child agent costs.

        Trace Hierarchy:
        run_code_review (trace)
        ├── security_scan_generation (generation)
        ├── performance_analysis_generation (generation)
        ├── style_check_generation (generation)
        ├── test_coverage_generation (generation)
        └── synthesis_generation (generation)

        Langfuse automatically rolls up costs to parent trace.
        &quot;&quot;&quot;

        # Set trace metadata for filtering/grouping
        langfuse_context.update_current_trace(
            name=&quot;code_review&quot;,
            session_id=str(review_id),
            user_id=f&quot;pr_{pr_id}&quot;,  # Group by PR for analysis
            tags=[&quot;multi-agent&quot;, &quot;production&quot;, &quot;code-review&quot;],
            metadata={
                &quot;review_id&quot;: str(review_id),
                &quot;pr_id&quot;: pr_id,
                &quot;agent_count&quot;: 5,
                &quot;diff_size&quot;: len(diff)
            }
        )

        # Each review agent creates a child generation
        findings = {}
        for agent in self.review_agents:
            # Child generation auto-linked to parent trace
            result = await self.run_review_agent(
                agent=agent,
                code_diff=diff,
                review_id=review_id  # Links to parent
            )
            findings[agent.name] = result

        # Synthesis also tracked as child generation
        synthesis = await self.synthesize_review(
            findings=findings,
            review_id=review_id
        )

        # Langfuse dashboard shows:
        # - Total cost for this review (sum of all child generations)
        # - Token breakdown by review agent type
        # - Cache hit rate per agent
        # - Latency per agent
        # - Quality score trends

        return {&quot;findings&quot;: findings, &quot;synthesis&quot;: synthesis, &quot;approved&quot;: synthesis.approved}

    @observe(as_type=&quot;generation&quot;)
    async def run_agent(
        self,
        agent: Agent,
        content: str,
        analysis_id: UUID
    ) -&gt; dict:
        &quot;&quot;&quot;Child generation - costs roll up to parent trace.&quot;&quot;&quot;

        langfuse_context.update_current_observation(
            name=f&quot;{agent.name}_generation&quot;,
            metadata={
                &quot;agent_type&quot;: agent.name,
                &quot;content_length&quot;: len(content)
            }
        )

        # LLM call automatically tracked
        response = await agent.analyze(content)

        return response

Cost Rollup Query Pattern

from langfuse import Langfuse
from datetime import datetime, timedelta

async def get_analysis_costs(analysis_id: UUID) -&gt; dict:
    &quot;&quot;&quot;Get total cost for an analysis (parent trace + all child generations).&quot;&quot;&quot;

    langfuse = Langfuse()

    # Fetch parent trace by session_id
    traces = langfuse.get_traces(
        session_id=str(analysis_id),
        limit=1
    )

    if not traces.data:
        return {&quot;error&quot;: &quot;Trace not found&quot;}

    trace = traces.data[0]

    # Langfuse automatically aggregates child costs
    return {
        &quot;trace_id&quot;: trace.id,
        &quot;total_cost&quot;: trace.total_cost,  # Sum of all child generations
        &quot;input_tokens&quot;: trace.usage.input_tokens,
        &quot;output_tokens&quot;: trace.usage.output_tokens,
        &quot;cache_read_tokens&quot;: trace.usage.cache_read_input_tokens,
        &quot;observations_count&quot;: trace.observation_count,  # Number of child LLM calls
        &quot;latency_ms&quot;: trace.latency,
        &quot;created_at&quot;: trace.timestamp
    }

async def get_daily_costs_by_agent() -&gt; list[dict]:
    &quot;&quot;&quot;Get cost breakdown by agent type for last 30 days.&quot;&quot;&quot;

    langfuse = Langfuse()

    # Fetch all generations from last 30 days
    from_date = datetime.now() - timedelta(days=30)
    generations = langfuse.get_generations(
        from_timestamp=from_date,
        limit=10000
    )

    # Group by agent type (from metadata)
    costs_by_agent = {}
    for gen in generations.data:
        agent_type = gen.metadata.get(&quot;agent_type&quot;, &quot;unknown&quot;)
        cost = gen.calculated_total_cost or 0.0

        if agent_type not in costs_by_agent:
            costs_by_agent[agent_type] = {
                &quot;agent_type&quot;: agent_type,
                &quot;total_cost&quot;: 0.0,
                &quot;call_count&quot;: 0,
                &quot;total_input_tokens&quot;: 0,
                &quot;total_output_tokens&quot;: 0,
                &quot;cache_hits&quot;: 0
            }

        costs_by_agent[agent_type][&quot;total_cost&quot;] += cost
        costs_by_agent[agent_type][&quot;call_count&quot;] += 1
        costs_by_agent[agent_type][&quot;total_input_tokens&quot;] += gen.usage.input or 0
        costs_by_agent[agent_type][&quot;total_output_tokens&quot;] += gen.usage.output or 0

        if gen.metadata.get(&quot;cache_hit&quot;):
            costs_by_agent[agent_type][&quot;cache_hits&quot;] += 1

    # Calculate averages
    results = []
    for stats in costs_by_agent.values():
        stats[&quot;avg_cost_per_call&quot;] = stats[&quot;total_cost&quot;] / stats[&quot;call_count&quot;]
        stats[&quot;cache_hit_rate&quot;] = stats[&quot;cache_hits&quot;] / stats[&quot;call_count&quot;]
        results.append(stats)

    # Sort by total cost descending
    results.sort(key=lambda x: x[&quot;total_cost&quot;], reverse=True)

    return results

Cost Attribution by Agent Type

# Langfuse dashboard query:
# GROUP BY metadata.agent_type
# SUM(total_cost) AS cost_per_agent
#
# Results show:
# - security_auditor: $12.45 (35% cache hit rate)
# - implementation_planner: $8.23 (42% cache hit rate)
# - tech_comparator: $5.67 (58% cache hit rate)

Cache Effectiveness Analysis

from langfuse import Langfuse

langfuse = Langfuse()

# Query all generations with cache metadata
generations = langfuse.get_generations(
    limit=1000,
    from_timestamp=datetime.now() - timedelta(days=7)
)

cache_hits = 0
cache_misses = 0
total_cost = 0.0
cost_saved = 0.0

for gen in generations:
    metadata = gen.metadata or {}
    is_cache_hit = metadata.get(&quot;cache_hit&quot;, False)

    if is_cache_hit:
        cache_hits += 1
        # Estimate saved cost (cost of equivalent full LLM call)
        cost_saved += gen.calculated_total_cost or 0  # Would be higher without cache
    else:
        cache_misses += 1
        total_cost += gen.calculated_total_cost or 0

hit_rate = cache_hits / (cache_hits + cache_misses)
print(f&quot;Cache Hit Rate: {hit_rate:.1%}&quot;)
print(f&quot;Cost Saved: ${cost_saved:.2f}&quot;)
print(f&quot;Total Cost: ${total_cost:.2f}&quot;)
print(f&quot;Savings Rate: {(cost_saved / (cost_saved + total_cost)):.1%}&quot;)

Model Pricing Registry

from dataclasses import dataclass

@dataclass
class ModelInfo:
    &quot;&quot;&quot;Model configuration with pricing.&quot;&quot;&quot;

    model_id: str
    display_name: str
    max_tokens: int
    input_cost_per_1m: float  # USD per 1M input tokens
    output_cost_per_1m: float  # USD per 1M output tokens

    def calculate_cost(self, input_tokens: int, output_tokens: int) -&gt; float:
        &quot;&quot;&quot;Calculate total cost for token usage.&quot;&quot;&quot;
        input_cost = (input_tokens / 1_000_000) * self.input_cost_per_1m
        output_cost = (output_tokens / 1_000_000) * self.output_cost_per_1m
        return input_cost + output_cost

# Claude 3.5 Sonnet (Updated March 2025)
MODEL_REGISTRY = {
    &quot;claude-3-5-sonnet-20241022&quot;: ModelInfo(
        model_id=&quot;claude-3-5-sonnet-20241022&quot;,
        display_name=&quot;Claude 3.5 Sonnet (New)&quot;,
        max_tokens=8192,
        input_cost_per_1m=3.00,  # $3 per 1M tokens
        output_cost_per_1m=15.00,  # $15 per 1M tokens
    ),
    &quot;gpt-4-turbo-2024-04-09&quot;: ModelInfo(
        model_id=&quot;gpt-4-turbo-2024-04-09&quot;,
        display_name=&quot;GPT-4 Turbo&quot;,
        max_tokens=4096,
        input_cost_per_1m=10.00,
        output_cost_per_1m=30.00,
    ),
}

Langfuse Dashboard Views

Access cost insights at http://localhost:3000:

Cost Dashboard:
- Total cost by day/week/month
- Cost breakdown by model
- Cost attribution by agent type
- Cache hit rate impact on costs
- Top 10 most expensive traces

Cache Effectiveness:
- L1/L2/L3 hit rates over time
- Cost savings from semantic cache
- Cost savings from prompt cache
- False positive rate (wrong cache hits)

Agent Performance:
- Average cost per agent invocation
- Token usage distribution
- Cache hit rate by agent type
- Quality score vs. cost correlation

RedisInsight Dashboard

Access Redis cache visualization at http://localhost:8001:

  • View cache entries
  • Monitor vector similarity distributions
  • Track hit/miss rates by agent type
  • Analyze quality score distributions
  • Identify hot keys

Local Model Considerations (Ollama)

When using local models via Ollama, the caching calculus changes:

Cost Impact:
| Provider | Caching Value | Reason |
|----------|--------------|--------|
| Cloud APIs | Critical | $3-15 per MTok |
| Ollama Local | Optional | FREE per token |

When to still cache with Ollama:
- Latency reduction: Cache provides ~1-10ms vs ~50-200ms for local inference
- Memory pressure: Avoid loading multiple models for repeated queries
- Batch CI runs: Same queries across test runs benefit from L1 cache

Simplified Cache Strategy for Local:

# With Ollama, L1 (LRU) cache is usually sufficient
# Skip L2 (Redis semantic) unless latency-critical

async def get_local_llm_response(query: str) -&gt; str:
    # L1: Exact match only (sufficient for local)
    cache_key = hash_content(query)
    if cache_key in lru_cache:
        return lru_cache[cache_key]  # ~1ms

    # Direct local inference (FREE, fast enough)
    response = await ollama_provider.ainvoke(query)  # ~50-200ms

    # Store in L1 only
    lru_cache[cache_key] = response.content
    return response.content

Best Practice: Use factory pattern to apply full caching hierarchy only for cloud APIs:

if settings.OLLAMA_ENABLED:
    # Minimal caching for local models
    return LocalCacheStrategy(l1_only=True)
else:
    # Full L1/L2/L3 caching for cloud APIs
    return CloudCacheStrategy(l1=True, l2=True, l3=True)

See ai-native-development skill section "10. Local LLM Inference with Ollama" for provider setup.


References

  • Redis Blog: Prompt Caching vs Semantic Caching (Dec 2025)
  • Redis Blog: 10 Techniques for Semantic Cache Optimization
  • RedisVL Docs: SemanticCache Guide
  • LangChain: RedisSemanticCache
  • Anthropic: Prompt Caching Guide (March 2025: cache reads free!)

Integration Examples

See:
- references/redis-setup.md - Docker Compose + RedisVL setup
- references/cache-hierarchy.md - Multi-level cache implementation
- references/cost-optimization.md - ROI calculations and benchmarks
- templates/semantic-cache-service.py - Production-ready service
- templates/prompt-cache-wrapper.py - Claude caching wrapper
- examples/project-integration.md - this project specific patterns


Skill Version: 1.3.0
Last Updated: 2025-12-28
Maintained by: this project AI Agent Hub

Changelog

v1.3.0 (2025-12-28)

  • Added "Local Model Considerations (Ollama)" section
  • Added cost comparison table for cloud vs local caching value
  • Added simplified caching strategy for local models
  • Added factory pattern example for adaptive caching
  • Cross-referenced ai-native-development skill for Ollama setup

v1.2.0 (2025-12-27)

  • Added hierarchical trace pattern for multi-agent cost rollup
  • Added session_id linking pattern for cost attribution to parent analysis
  • Added cost rollup query patterns with Langfuse API
  • Added daily cost breakdown by agent type example
  • Updated automatic cost tracking with custom trace ID support
  • Added this project-specific multi-agent workflow cost tracking pattern

v1.1.0 (2025-12-27)

  • Added comprehensive Langfuse cost tracking section
  • Added automatic cost tracking with @observe decorator
  • Added cost attribution by agent type patterns
  • Added cache effectiveness analysis with Langfuse API
  • Added model pricing registry with calculate_cost() method
  • Added Langfuse dashboard views for cost insights
  • Updated monitoring section with cost tracking best practices

v1.0.0 (2025-12-14)

  • Initial skill with double caching architecture (L1/L2/L3/L4)
  • Redis semantic cache implementation with RedisVL
  • Claude prompt caching patterns
  • Cache warming strategies
  • Similarity threshold tuning guidelines
  • Optimization techniques (reranking, metadata filtering, quality-based eviction)

常见问题

5 问
答案由本站依据条目字段整理
llm-caching-patterns怎么安装?

没有现成安装命令:按安装说明或仓库 README,把 SKILL.md 放进客户端的 skills 目录(一般一个技能一个文件夹)。

llm-caching-patterns支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

llm-caching-patterns权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

llm-caching-patterns收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

llm-caching-patterns用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。