> ## 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>캐시 생성 API를 별도로 호출할 필요가 없습니다. `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`)를 자동으로 생성하거나 재사용합니다.

스트리밍 인터페이스는 동일한 요청 본문을 사용하며 URL만 다음과 같이 변경하면 됩니다.

```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>
