mcpField
P
prompt-caching

prompt-caching

github.com/sickn33开发48.7k869 次安装1 天前更新
代码仓库

关于 prompt-caching

prompt-caching 是面向 LLM 系统开发者的技能,涵盖提示词与响应缓存,以及 Cache Augmented Generation。内容包括实现模式、失效处理指导和常见缓存问题检查。

主要功能

  1. 01Anthropic 提示词缓存模式
  2. 02LLM 响应缓存
  3. 03Cache Augmented Generation
  4. 04缓存失效策略
  5. 05缓存指标与校验检查

适用场景

  1. 01复用稳定的 Claude API 提示词前缀
  2. 02缓存重复的 LLM 查询
  3. 03通过缓存提供稳定文档上下文

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill prompt-caching

安装后在 Claude Code 中输入/prompt-caching确认已加载。

详细介绍

提示词缓存

LLM 提示词缓存策略,包括 Anthropic 提示词缓存、响应缓存和 CAG(缓存增强生成)

能力

  • 提示词缓存
  • 响应缓存
  • KV 缓存
  • CAG 模式
  • 缓存失效

前置条件

  • 知识:缓存基础知识、LLM API 用法、哈希函数
  • 推荐技能:上下文窗口管理

范围

  • 不涵盖:CDN 缓存、数据库查询缓存、静态资源缓存
  • 边界:重点关注 LLM 专用缓存,涵盖提示词和响应缓存

生态系统

主要工具
  • Anthropic 提示词缓存 - Claude API 中的原生提示词缓存
  • Redis - 用于缓存响应的内存数据库(服务器上使用 ioredis;无服务器环境通过 HTTP 使用 @upstash/redis,参见 upstash-redis)
  • OpenAI 缓存 - OpenAI API 中的自动缓存

模式

Anthropic 提示词缓存

使用 Claude 的原生提示词缓存来缓存重复前缀

适用场景:使用 Claude API,且系统提示词或上下文保持稳定时

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

// 缓存提示词中稳定的部分 async function queryWithCaching(userQuery: string) { const response = await client.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, system: [ { type: "text", text: LONG_SYSTEM_PROMPT, // 详细指令 cache_control: { type: "ephemeral" } // 缓存此内容! }, { type: "text", text: KNOWLEDGE_BASE, // 大型静态上下文 cache_control: { type: "ephemeral" } } ], messages: [ { role: "user", content: userQuery } // 动态部分 ] });

// 检查缓存用量
console.log(`Cache read: ${response.usage.cache_read_input_tokens}`);
console.log(`Cache write: ${response.usage.cache_creation_input_tokens}`);

return response;

}

// 测量所选模型/提供商的实际缓存读写成本和延迟。

响应缓存

为完全相同或相似的查询缓存完整 LLM 响应

适用场景:相同查询被反复提出时

import { createHash } from 'crypto'; import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL); // 无持久连接的无服务器/边缘环境替代方案: // import { Redis } from '@upstash/redis'; const redis = Redis.fromEnv(); // 然后使用 redis.set(key, value, { ex: ttl })

class ResponseCache { private ttl = 3600; // 默认 1 小时

// 精确匹配缓存
async getCached(prompt: string): Promise<string | null> {
    const key = this.hashPrompt(prompt);
    return await redis.get(`response:${key}`);
}

async setCached(prompt: string, response: string): Promise<void> {
    const key = this.hashPrompt(prompt);
    await redis.set(`response:${key}`, response, 'EX', this.ttl);
}

private hashPrompt(prompt: string): string {
    return createHash('sha256').update(prompt).digest('hex');
}

// 语义相似度缓存
async getSemanticallySimilar(
    prompt: string,
    threshold: number = 0.95
): Promise<string | null> {
    const embedding = await embed(prompt);
    const similar = await this.vectorCache.search(embedding, 1);

    if (similar.length && similar[0].similarity > threshold) {
        return await redis.get(`response:${similar[0].id}`);
    }
    return null;
}

// 感知温度的缓存
async getCachedWithParams(
    prompt: string,
    params: { temperature: number; model: string }
): Promise<string | null> {
    // 仅缓存低温度响应
    if (params.temperature > 0.5) return null;

    const key = this.hashPrompt(
        `${prompt}|${params.model}|${params.temperature}`
    );
    return await redis.get(`response:${key}`);
}

}

缓存增强生成(CAG)

将文档预先缓存到提示词中,而不是通过 RAG 检索

适用场景:文档语料稳定,且能装入上下文

// CAG:预先计算文档上下文,并将其缓存到提示词中 // 比 RAG 更好的情况: // - 文档稳定 // - 所有内容能装入上下文窗口 // - 延迟至关重要

class CAGSystem { private cachedContext: string | null = null; private lastUpdate: number = 0;

async buildCachedContext(documents: Document[]): Promise<void> {
    // 预处理并格式化文档
    const formatted = documents.map(d =>
        `## ${d.title}\n${d.content}`
    ).join('\n\n');

    // 保存并记录时间戳
    this.cachedContext = formatted;
    this.lastUpdate = Date.now();
}

async query(userQuery: string): Promise<string> {
    // 在提示词中直接使用缓存的上下文
    const response = await client.messages.create({
        model: "claude-sonnet-4-20250514",
        max_tokens: 1024,
        system: [
            {
                type: "text",
                text: "你是一位乐于助人的助手,可以访问以下文档。",
                cache_control: { type: "ephemeral" }
            },
            {
                type: "text",
                text: this.cachedContext ?? "",  // 查询前请确认上下文已初始化
                cache_control: { type: "ephemeral" }
            }
        ],
        messages: [{ role: "user", content: userQuery }]
    });

    return response.content.filter(block => block.type === "text").map(block => block.text).join("\n");
}

// 定期刷新
async refreshIfNeeded(documents: Document[]): Promise<void> {
    const stale = Date.now() - this.lastUpdate > 3600000;  // 1 小时
    if (stale) {
        await this.buildCachedContext(documents);
    }
}

}

// CAG 与 RAG 的决策矩阵: // | 因素 | CAG 更合适 | RAG 更合适 | // |------------------|------------|------------| // | 语料规模 | < 100K tokens | > 100K tokens | // | 更新频率 | 低 | 高 | // | 延迟要求 | 至关重要 | 灵活 | // | 查询具体程度 | 通用 | 具体 |

注意事项

缓存未命中导致延迟激增并带来额外开销

严重性:高

情况:缓存未命中时响应缓慢,甚至比不使用缓存更慢

症状:

  • 缓存未命中时响应缓慢
  • 缓存命中率低于 50%
  • 延迟高于未使用缓存时

原因: 检查缓存会增加延迟。 写入缓存会带来更多延迟。 未命中加上额外开销,导致耗时超过不使用缓存。

建议修复:

// 针对缓存未命中进行优化,而不只是命中

class OptimizedCache { async queryWithCache(prompt: string): Promise { const cacheKey = this.hash(prompt);

    // 非阻塞式检查缓存
    const cachedPromise = this.cache.get(cacheKey);
    // 只有在有界缓存查询未命中后,才延迟调用付费服务提供方。

    // 竞速:在 LLM 返回前优先使用缓存(如果有)
    const cached = await Promise.race([
        cachedPromise,
        sleep(50).then(() => null) // 也要根据可用性策略处理缓存错误  // 50ms 缓存超时
    ]);

    if (cached) {
        // 如果可能,取消 LLM 请求
        return cached;
    }

    // 缓存未命中:继续调用 LLM
    const response = await this.queryLLM(prompt);

    // 异步写入缓存(不要阻塞响应)
    this.cache.set(cacheKey, response).catch(console.error);

    return response;
}

}

// 替代方案:概率式缓存 // 仅缓存符合已知高频模式的查询 class SelectiveCache { private patterns: Map<string, number> = new Map();

shouldCache(prompt: string): boolean {
    const pattern = this.extractPattern(prompt);
    const frequency = this.patterns.get(pattern) || 0;

    // 仅缓存高频模式
    return frequency > 10;
}

recordQuery(prompt: string): void {
    const pattern = this.extractPattern(prompt);
    this.patterns.set(pattern, (this.patterns.get(pattern) || 0) + 1);
}

}

缓存的响应会随时间变得不准确

严重性:高

情况:用户从缓存中获取过时或错误的信息

症状:

  • 用户反馈信息有误
  • 答案与当前数据不符
  • 用户投诉响应过时

原因: 源数据已更改。 没有使缓存失效。 动态数据的 TTL 设置过长。

建议修复:

// 实现完善的缓存失效机制

class InvalidatingCache { // 基于版本的失效机制 private cacheVersion = 1;

getCacheKey(prompt: string): string {
    return `v${this.cacheVersion}:${this.hash(prompt)}`;
}

invalidateAll(): void {
    this.cacheVersion++;
    // 旧键会自动成为孤立键
}

// 基于内容哈希的失效机制
async setWithContentHash(
    key: string,
    response: string,
    sourceContent: string
): Promise<void> {
    const contentHash = this.hash(sourceContent);
    await this.cache.set(key, {
        response,
        contentHash,
        timestamp: Date.now()
    });
}

async getIfValid(
    key: string,
    currentSourceContent: string
): Promise<string | null> {
    const cached = await this.cache.get(key);
    if (!cached) return null;

    // 检查源内容是否已更改
    const currentHash = this.hash(currentSourceContent);
    if (cached.contentHash !== currentHash) {
        await this.cache.delete(key);
        return null;
    }

    return cached.response;
}

// 基于事件的失效机制
onSourceUpdate(sourceId: string): void {
    // 使所有使用此源的缓存失效
    this.invalidateByTag(`source:${sourceId}`);
}

}

由于前缀发生变化,提示词缓存无法正常工作

严重性:中

情况:提示词相似,但缓存未命中

症状:

  • 缓存命中率低于预期
  • 缓存创建令牌数较高,读取令牌数较低
  • 相似的提示词未命中缓存

原因: Anthropic 缓存要求前缀完全匹配。 前缀中包含时间戳或动态内容。 消息顺序不同。

建议的修复方法:

// 为优化缓存而组织提示词

class CacheOptimizedPrompts { // 错误:缓存的前缀中包含动态内容 buildPromptBad(query: string): SystemMessage[] { return [ { type: "text", text: You are helpful. Current time: ${new Date()}, // BREAKS CACHE! cache_control: { type: "ephemeral" } } ]; }

// 正确:前缀保持静态,将动态内容放在末尾
buildPromptGood(query: string): SystemMessage[] {
    return [
        {
            type: "text",
            text: STATIC_SYSTEM_PROMPT,  // 永不更改
            cache_control: { type: "ephemeral" }
        },
        {
            type: "text",
            text: STATIC_KNOWLEDGE_BASE,  // 极少更改
            cache_control: { type: "ephemeral" }
        }
        // 将动态内容放在消息中,而不是系统提示词中
    ];
}

// 前缀顺序很重要
buildWithConsistentOrder(components: string[]): SystemMessage[] {
    // 对组件排序,以保持顺序一致
    const sorted = [...components]; // 保留语义顺序;确保构造过程具有确定性
    return sorted.map((c, i) => ({
        type: "text",
        text: c,
        cache_control: i === sorted.length - 1
            ? { type: "ephemeral" }
            : undefined  // 仅缓存完整前缀
    }));
}

}

验证检查

缓存高温度响应

严重性:警告

消息:使用高温度进行缓存。响应具有非确定性。

修复操作:定义针对具体任务的新鲜度/复用策略;温度不能保证确定性或安全性

缓存未设置 TTL

严重性:警告

消息:缓存未设置 TTL。可能会无限期提供过时数据。

修复操作:根据数据新鲜度要求设置适当的 TTL

缓存前缀中的动态内容

严重性:警告

消息:缓存前缀中包含动态内容。这会导致缓存未命中。

修复操作:将动态内容移出 cache_control 块

没有缓存指标

严重性:信息

消息:缓存未跟踪命中/未命中情况。无法衡量效果。

修复操作:添加缓存命中/未命中指标和日志记录

协作

委派触发条件
  • context window|token -> context-window-management(需要优化上下文)
  • rag|retrieval -> rag-implementation(需要检索系统)
  • memory -> conversation-memory(需要记忆持久化)
高性能大模型系统

技能:prompt-caching、context-window-management、rag-implementation

工作流:

1. Analyze query patterns
2. Implement prompt caching for stable prefixes
3. Add response caching for frequent queries
4. Consider CAG for stable document sets
5. Monitor and optimize hit rates

相关技能

适合搭配使用:context-window-management、rag-implementation、conversation-memory

适用场景

  • 用户提及或暗示:提示词缓存
  • 用户提及或暗示:缓存提示词
  • 用户提及或暗示:响应缓存
  • 用户提及或暗示:cag
  • 用户提及或暗示:增强缓存

限制

  • 仅当任务明确符合上述范围时,才使用此技能。
  • 不要将输出视为环境专属验证、测试或专家审核的替代品。
  • 如果缺少必要输入、权限、安全边界或成功标准,请暂停并请求澄清。

所有缓存类都只是示意,其中省略了适配器、辅助工具和类型。缓存键必须包含租户/用户授权范围、模型/版本、完整指令、工具/架构版本、生成参数和来源版本;对提示词进行哈希并不能让缓存的敏感内容匿名化。应将精确响应缓存与提供商的前缀/KV 缓存分开;后者仍会生成新答案,并且有提供商特定的最低令牌数、TTL 和定价。语义相似并不等于答案等价;未经验证,不要在不同权限或安全敏感情境之间重复使用缓存。将缓存文档视为不可信的源数据,而非权威系统指令。在工作进程/重启之间持久化版本/失效状态,显式序列化 Redis 对象,并应用保留/删除策略。100K 令牌和 0.5 温度阈值仅为示例,并非通用标准。

评论

支持 Markdown