> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Gemini 上下文缓存使用指南

> 通过 OpenAI 兼容的 Chat Completions API 或原生 Gemini API 创建和复用 Gemini 上下文缓存（Context Cache），用 cache_control 缓存稳定前缀，降低重复长内容的 token 成本。

本文介绍如何通过 OpenAI 兼容的 Chat Completions API 或原生 Gemini API 创建和复用 Gemini 上下文缓存（Context Cache）。

使用前准备：

```bash theme={null}
export API_KEY="你的API密钥"
```

<Note>本文示例使用 `gemini-3.6-flash`。其他模型是否支持 Context Cache，请以平台模型说明和价格页面为准。</Note>

## 适用场景

当多个请求会重复携带相同的大段内容时，可以缓存稳定前缀，例如：

* 超长 system prompt
* 固定的知识库或产品文档
* 多轮对话中的稳定历史消息
* 重复使用的工具定义和说明

Context Cache 适合“前面内容保持不变，最后的问题持续变化”的请求。

## 核心用法

在稳定前缀最后一条消息的 content block 上添加 `cache_control`：

```json theme={null}
{
  "type": "text",
  "text": "这是稳定前缀的最后一段内容",
  "cache_control": {
    "type": "ephemeral",
    "ttl": "5m"
  }
}
```

支持的 TTL：

| TTL  | 含义      |
| ---- | ------- |
| `5m` | 缓存 5 分钟 |
| `1h` | 缓存 1 小时 |

<Note>省略 `ttl` 时默认使用 `5m`。</Note>

## 消息结构

推荐使用以下结构：

```text theme={null}
system
→ 稳定的长文本或历史消息
→ 带 cache_control 的稳定前缀边界
→ 当前用户问题（不缓存）
```

`cache_control` 所在消息及它之前的所有消息组成缓存前缀。它后面必须至少有一条实时消息。

## OpenAI 兼容请求示例

```bash theme={null}
curl "https://api.apimart.ai/v1/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-3.6-flash",
    "stream": false,
    "messages": [
      {
        "role": "system",
        "content": "请严格根据提供的参考资料回答问题。"
      },
      {
        "role": "user",
        "content": "这里放需要重复使用的长篇参考资料……"
      },
      {
        "role": "assistant",
        "content": [
          {
            "type": "text",
            "text": "我已阅读并理解以上参考资料。",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "content": "请总结参考资料中的三个核心观点。"
      }
    ]
  }'
```

第一次发送时，系统会尝试创建缓存，并使用新缓存完成当前请求。

<Note>不需要单独调用缓存创建接口。`cache_control` 同时表达“缓存边界”和“缓存有效期”。</Note>

## 原生 Gemini 请求示例

原生 Gemini `generateContent` 接口同样支持在 `contents[].parts[]` 中添加 `cache_control`：

```bash theme={null}
curl "https://api.apimart.ai/v1beta/models/gemini-3.6-flash:generateContent" \
  -H "x-goog-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "systemInstruction": {
      "parts": [
        {
          "text": "请严格根据提供的参考资料回答问题。"
        }
      ]
    },
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "这里放需要重复使用的长篇参考资料……"
          }
        ]
      },
      {
        "role": "model",
        "parts": [
          {
            "text": "我已阅读并理解以上参考资料。",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "parts": [
          {
            "text": "请总结参考资料中的三个核心观点。"
          }
        ]
      }
    ]
  }'
```

`cache_control` 是平台在 Gemini 请求格式上的扩展字段。平台识别边界后会在转发前移除该字段，并自动创建或复用缓存内容（`cachedContent`）。

流式接口使用相同请求体，只需将地址改为：

```bash theme={null}
curl -N "https://api.apimart.ai/v1beta/models/gemini-3.6-flash:streamGenerateContent?alt=sse" \
  -H "x-goog-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "这里放需要重复使用的长篇参考资料……",
            "cache_control": {
              "type": "ephemeral",
              "ttl": "5m"
            }
          }
        ]
      },
      {
        "role": "user",
        "parts": [
          {
            "text": "请总结参考资料中的三个核心观点。"
          }
        ]
      }
    ]
  }'
```

复用时保持 `systemInstruction`、边界之前的 `contents`、TTL 和 tools 不变，只修改边界之后的实时内容。

## 创建和复用流程

第一次发送带 `cache_control` 的请求时：

```text theme={null}
识别稳定前缀
→ 创建 Context Cache
→ 在当前请求中引用新缓存
→ 返回模型结果
```

再次发送相同稳定前缀时：

```text theme={null}
识别相同稳定前缀
→ 复用未过期的 Context Cache
→ 只发送当前实时内容
→ 返回模型结果
```

因此，第一次请求也可能直接返回较大的缓存命中 token。这是正常行为，并不要求先发送一次“预热请求”。

## 复用缓存

再次请求时，保持以下内容不变：

* 模型
* `cache_control` 之前的所有消息
* `cache_control.ttl`
* 工具定义（如果使用 tools）
* 原生 Gemini 请求中的 `systemInstruction`

只修改边界之后的实时问题：

```json theme={null}
{
  "role": "user",
  "content": "参考资料中提到了哪些风险？"
}
```

只要稳定前缀一致且缓存未过期，系统就会复用已有缓存。

以下改动会生成不同的缓存：

* 修改稳定前缀中的文字或消息顺序
* 更换模型
* 将 `5m` 改为 `1h`
* 修改 tools 或工具参数定义
* 使用不同的 API 用户或渠道

## Python 示例

```python theme={null}
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.apimart.ai/v1",
)

stable_messages = [
    {
        "role": "system",
        "content": "请严格根据提供的参考资料回答问题。",
    },
    {
        "role": "user",
        "content": "这里放需要重复使用的长篇参考资料……",
    },
    {
        "role": "assistant",
        "content": [
            {
                "type": "text",
                "text": "我已阅读并理解以上参考资料。",
                "cache_control": {
                    "type": "ephemeral",
                    "ttl": "5m",
                },
            }
        ],
    },
]

response = client.chat.completions.create(
    model="gemini-3.6-flash",
    messages=[
        *stable_messages,
        {
            "role": "user",
            "content": "请总结参考资料中的三个核心观点。",
        },
    ],
)

print(response.choices[0].message.content)
print(response.usage)
```

后续请求复用同一个 `stable_messages`，只替换最后一条 user 消息即可。

## 判断是否命中

### OpenAI 兼容响应

查看响应中的：

```json theme={null}
{
  "usage": {
    "prompt_tokens": 14430,
    "prompt_tokens_details": {
      "cached_tokens": 14420,
      "cache_write_tokens": 0
    }
  }
}
```

字段说明：

| 字段                   | 含义                      |
| -------------------- | ----------------------- |
| `prompt_tokens`      | 本次请求的全部输入 token         |
| `cached_tokens`      | 本次从缓存读取的输入 token        |
| `cache_write_tokens` | 缓存写入 token；返回 `0` 是正常行为 |

首次请求也可能出现较大的 `cached_tokens`，因为系统可以先创建缓存，再在同一次模型调用中引用它。

### 原生 Gemini 响应

查看响应中的 `usageMetadata.cachedContentTokenCount`：

```json theme={null}
{
  "usageMetadata": {
    "promptTokenCount": 13926,
    "cachedContentTokenCount": 13916,
    "totalTokenCount": 13954
  }
}
```

字段说明：

| 字段                        | 含义                  |
| ------------------------- | ------------------- |
| `promptTokenCount`        | 本次请求的全部输入 token     |
| `cachedContentTokenCount` | 本次从缓存读取的输入 token    |
| `totalTokenCount`         | 本次请求的输入和输出 token 总数 |

`streamGenerateContent` 会在 SSE 响应帧中返回相同的 `usageMetadata`。客户端应读取包含该字段的响应帧，不要只检查第一段文本。

## 使用建议

<Tip>
  1. 只缓存真正稳定、会多次复用的长内容。
  2. 将每次变化的问题放在 `cache_control` 边界之后。
  3. 不要在稳定前缀中加入时间戳、随机 ID 或动态用户信息。
  4. 预计短时间内重复调用时使用 `5m`。
  5. 需要较长复用窗口时使用 `1h`。
  6. 前缀太短、模型不支持或缓存暂时不可用时，请求可能自动按普通模式执行。
  7. 原生 Gemini 的缓存边界必须放在 `contents[].parts[]` 中，不要放在 `systemInstruction` 中。
</Tip>

## 常见问题

<AccordionGroup>
  <Accordion title="原生 Gemini 请求格式可以自动创建缓存吗？">
    可以。`generateContent` 和 `streamGenerateContent` 使用相同的 `cache_control` 结构。边界必须放在 `contents[].parts[]` 中，且边界所在 content 后面必须保留至少一个实时 content。

    如果请求已经显式提供原生 `cachedContent` 资源名，平台会优先使用用户提供的资源，不再执行自动缓存创建。
  </Accordion>

  <Accordion title="为什么没有命中缓存？">
    常见原因包括：

    * 稳定前缀与上一次请求不完全一致
    * TTL 已过期
    * 修改了模型或 tools
    * 缓存内容没有达到模型要求的最低 token 数
    * `cache_control` 放在了最后一条消息上，没有留下实时问题
  </Accordion>

  <Accordion title="cache_control 可以放在最后一条消息上吗？">
    不建议。最后一条消息通常是当前实时问题，不应缓存。边界后没有实时消息时，请求会按普通模式执行。
  </Accordion>

  <Accordion title="可以设置其他 TTL 吗？">
    不可以。目前只支持 `5m` 和 `1h`。其他值会返回 HTTP 400。
  </Accordion>

  <Accordion title="可以设置多个缓存边界吗？">
    可以，但所有边界必须使用相同 TTL，系统会使用最后一个边界。一般建议每个请求只设置一个边界，结构更清晰。
  </Accordion>

  <Accordion title="缓存不可用会导致请求失败吗？">
    通常不会。缓存创建或复用条件不满足时，系统会自动使用普通请求。无效 TTL、混用不同 TTL 等参数错误除外。
  </Accordion>

  <Accordion title="模型没有开通 Context Cache 会怎样？">
    请求会自动按普通模式执行，不创建显式缓存，也不产生缓存存储费用。普通输入、输出和可能存在的隐式缓存继续按照该模型原有规则计费。
  </Accordion>

  <Accordion title="为什么 cache_write_tokens 是 0？">
    这是 Gemini 上下文缓存的正常行为。缓存创建成本通过独立的缓存存储费用记录，不使用 OpenAI/Claude 风格的 `cache_write_tokens` 表示缓存创建量。
  </Accordion>

  <Accordion title="cached_tokens 大于 0 就代表一定创建了显式缓存吗？">
    不一定。系统自身也可能产生隐式缓存命中。对普通用户而言，可以使用缓存读取 token 判断本次请求是否享受缓存读取；如需核对显式缓存创建费用，请查看平台消费日志中的 Context Cache storage 记录。
  </Accordion>

  <Accordion title="缓存如何计费？">
    创建缓存时可能产生一次缓存存储费用；使用缓存时，命中的 token 按缓存读取价格计费。具体价格以平台展示的模型价格为准。
  </Accordion>
</AccordionGroup>
