프롬프트 캐싱이란 무엇인가? 최선의 관행 설명

Young-jae

Young-jae

17 April 2025

프롬프트 캐싱이란 무엇인가? 최선의 관행 설명

대규모 언어 모델(LLM)은 우리가 AI와 상호작용하는 방식을 혁신적으로 변화시켰으며, 텍스트 생성, 번역, 질문 응답 등과 같은 복잡한 작업을 가능하게 하고 있습니다. 그러나 이러한 강력한 모델과 상호작용하는 것은, 특히 정교한 프롬프트를 사용할 때, 상당한 계산 비용과 지연을 초래할 수 있습니다. 많은 애플리케이션은 유사하거나 부분적으로 동일한 프롬프트를 반복적으로 전송하는 것을 포함합니다. 고정된 시스템 프롬프트를 가진 챗봇, 동일한 지침으로 청크를 처리하는 문서 분석 도구 또는 일관된 도구 정의를 사용하는 에이전트를 상상해 보십시오. 이러한 시나리오에서 LLM은 동일한 초기 정보(프롬프트 접두사)를 반복적으로 처리하여 계산 자원을 낭비하고 응답 시간을 증가시킵니다.

프롬프트 캐싱은 이러한 비효율성을 해결하기 위한 강력한 최적화 기술로 떠오릅니다. 이는 LLM 제공자가 프롬프트의 초기 정적 부분(접두사)과 관련된 중간 계산 상태를 저장할 수 있게 합니다. 후속 요청에서 동일한 접두사를 사용할 경우, 모델은 이 캐시된 상태를 재사용하여 중복된 계산을 건너뛰고 프롬프트의 새로운 동적 부분(접미사)만 처리합니다. 이로 인해 지연 시간과 비용이 모두 크게 개선되어 LLM 애플리케이션이 더 빠르고 경제적으로 됩니다.

이 가이드는 프롬프트 캐싱에 대한 포괄적인 개요, 작동 방식, 이점, 구현 세부정보(Anthropic의 API에 중점을 두며, AWS Bedrock의 Claude 모델과도 관련됨), 가격 고려사항, 한계 및 모범 사례를 제공합니다.

💡
아름다운 API 문서를 생성하는 훌륭한 API 테스트 도구를 원하십니까?

개발 팀이 최대 생산성으로 함께 작업할 수 있는 통합된 올인원 플랫폼이 필요하신가요?

Apidog은 모든 요구를 충족시키며, Postman을 훨씬 더 저렴한 가격에 대체합니다!
button

프롬프트 캐싱의 작동 방식: 메커니즘

프롬프트 캐싱의 핵심은 많은 LLM 상호작용의 반복적인 특성을 활용하는 것입니다. 프롬프트를 LLM에 전송하면 모델은 입력 토큰을 순차적으로 처리하여 내부 표현 또는 상태를 생성합니다. 프롬프트 캐싱은 이 프로세스를 가로챕니다.

  1. 캐시 미스(첫 요청/변경된 접두사): 최근에 보지 못했거나 기존 캐시 항목과 일치하지 않는 프롬프트 접두사로 요청이 들어올 경우, LLM은 전체 프롬프트를 일반적으로 처리합니다. 그러나 특정 접두사 경계에 대해 캐싱이 활성화된 경우, 시스템은 이를 처리한 후 해당 접두사와 관련된 내부 모델 상태를 저장합니다. 이는 종종 캐시 쓰기라고 불립니다. 이 저장된 상태는 접두사 내용(시스템 프롬프트, 도구 정의 및 캐시 지점까지의 메시지 포함)의 암호 해시와 같은 고유 식별자와 연관되어 캐시 키 역할을 합니다.
  2. 캐시 히트(후속 요청): 후속 요청이 캐시의 TTL(수명) 내에 도착하고 그 접두사가 저장된 캐시 키와 정확히 일치할 경우, 시스템은 저장된 내부 상태를 검색합니다. LLM은 재처리 없이 접두사의 끝으로 즉시 진행할 수 있으며, 그 다음에는 프롬프트의 새로운 부분(접미사)만 처리하면 됩니다. 이는 캐시 읽기 또는 캐시 히트라고 알려져 있습니다.

캐시된 접두사:

"접두사"를 구성하는 정확한 요소는 API와 요청 구조에 따라 달라집니다. 일반적으로 재사용하려는 프롬프트의 정적 부분을 포함합니다. 예를 들어, Anthropic의 API 구조를 참조하면:

순서가 중요합니다(예: Anthropic은 tools를 먼저 처리한 다음 system, messages를 처리합니다). 캐시 가능한 접두사가 어디에서 끝나는지 특정 API 매개변수를 사용하여 표시합니다.

캐시 특성:

프롬프트 캐싱을 사용하는 이유는 무엇인가요?

프롬프트 캐싱을 구현하면 성능 및 비용 효율성에 중점을 두고 중요한 이점을 제공합니다.

  1. 지연 시간 감소: 이는 가장 즉각적인 이점입니다. 수천 개의 접두사 토큰을 재처리하는 과정을 건너뛰면 LLM은 관련된 새로운 정보(프롬프트 접미사)를 훨씬 더 빨리 처리할 수 있습니다. 이는 최종 사용자에게 더 빠른 응답 시간으로 직접 연결됩니다. 챗봇이나 코드 보조자와 같은 실시간 상호작용을 요구하는 애플리케이션의 경우 이 속도 향상은 중요합니다. AWS Bedrock은 지원되는 모델에 대해 최대 85%의 잠재적 지연 시간 감소를 보고하고 있습니다.
  2. 비용 절감: LLM API는 일반적으로 처리된 입력 및 출력 토큰 수에 따라 요금을 부과합니다. 캐시 히트가 발생하면 캐시에서 읽은 토큰에 대해 표준 입력 토큰 요금에 비해 상당히 낮은 요금을 부과받는 경우가 많습니다. 접미사에 있는 새로운 토큰에 대해서만 표준 입력 요금을 지불하며(최초 캐시 쓰기에 대해 조금 더 높은 요금을 부과받을 수 있습니다), 이렇게 많은 호출이 있는 경우에는 상당한 비용 절감으로 이어질 수 있습니다. AWS Bedrock은 최대 90%의 비용 절감이 가능하다고 제안합니다.
  3. 일반적인 사용 사례에 대한 최적화된 성능: 캐싱은 본질적으로 프롬프트 반복이 포함된 애플리케이션에 특히 영향력 있습니다:
  1. 원활한 통합: 프롬프트 캐싱은 다른 LLM 기능과 함께 작동하도록 설계되었습니다. 예를 들어, AWS Bedrock의 에이전트 및 가드레일과 통합되어 반복된 구성 요소에 대한 전체 지연 비용을 발생시키지 않고 복잡한 설정을 활용할 수 있습니다.

프롬프트 캐싱을 활성화하는 방법

프롬프트 캐싱을 활성화하는 방법은 LLM 제공업체와 API에 따라 약간 다릅니다. 여기서는 Anthropic의 메시지 API를 사용한 구현에 중점을 두며, 이는 Anthropic 또는 AWS Bedrock과 같은 플랫폼을 통해 Claude 모델에 직접 적용됩니다.

일반 원칙: 프롬프트 구조화

핵심은 API 호출을 구조화하여 정적 재사용 가능한 콘텐츠가 먼저 나타나고, 동적 콘텐츠가 후속되는 것입니다.

+-------------------------+--------------------------+
|      정적 접두사       |     동적 접미사         |
| (시스템 프롬프트, 도구, | (새 사용자 쿼리, 최신  |
|  몇 가지 예시,        |  대화 턴 등)            |
|  초기 맥락)           |                          |
+-------------------------+--------------------------+
          ^
          |
   캐시 중단점 여기

Anthropic API 구현 (cache_control)

Anthropic은 메시지 API 요청 본문 내에서 cache_control 매개변수를 사용하여 캐싱을 활성화하고 관리합니다.

예제 (Anthropic Python SDK): 시스템 프롬프트 캐싱

import anthropic

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

# 첫 번째 요청 (캐시 쓰기)
response1 = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            # 캐싱하려는 시스템 프롬프트
            "text": "당신은 천체물리학을 전문으로 하는 유용한 보조자입니다. 당신의 지식 기반에는 별의 진화, 우주론 및 행성 과학에 대한 방대한 세부 정보가 포함되어 있습니다. 정확하고 간결하게 응답하세요.",
            "cache_control": {"type": "ephemeral"} # 캐싱 표시
        }
    ],
    messages=[
        {
            "role": "user",
            # 프롬프트에서 캐시되지 않는 동적 부분
            "content": "찬드라세카르 한계란 무엇인가요?"
        }
    ]
)
print("첫 번째 응답:", response1.content)
print("사용량 (쓰기):", response1.usage)
# 예제 사용 출력은 다음과 같을 수 있습니다:
# Usage(쓰기): Usage(input_tokens=60, output_tokens=50, cache_creation_input_tokens=60, cache_read_input_tokens=0)

# 후속 요청 (캐시 히트 - TTL 내, 예: 5분 이하)
response2 = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            # 이전과 EXACTLY 동일한 시스템 프롬프트
            "text": "당신은 천체물리학을 전문으로 하는 유용한 보조자입니다. 당신의 지식 기반에는 별의 진화, 우주론 및 행성 과학에 대한 방대한 세부 정보가 포함되어 있습니다. 정확하고 간결하게 응답하세요.",
            "cache_control": {"type": "ephemeral"} # 다시 캐싱 표시
        }
    ],
    messages=[
        {
            "role": "user",
            # 새로운 동적 쿼리
            "content": "어두운 에너지의 개념을 설명해 주세요."
        }
    ]
)
print("\\\\n두 번째 응답:", response2.content)
print("사용량 (히트):", response2.usage)
# 예제 사용 출력은 다음과 같을 수 있습니다:
# Usage(히트): Usage(input_tokens=8, output_tokens=75, cache_creation_input_tokens=0, cache_read_input_tokens=60)

이 예제에서:

  1. 첫 번째 호출에서는 시스템 프롬프트의 60 토큰과 사용자 메시지의 8 토큰을 처리합니다. 시스템 프롬프트를 캐싱합니다 (cache_creation_input_tokens: 60).
  2. 두 번째 호출은 동일한 시스템 프롬프트에 대한 캐시 히트를 찾습니다 (cache_read_input_tokens: 60). 새로운 사용자 메시지의 8 토큰만 표준 입력으로 처리하면 됩니다 (input_tokens: 8).

예제 (대화에서의 점진적 캐싱):

대화 이력을 캐싱하려면 캐시할 다음 턴에 포함할 마지막 메시지에 cache_control를 배치할 수 있습니다.

# 턴 1 (사용자가 질문하고, 보조자가 응답 - 시스템 캐시 + 턴 1)
response_turn1 = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=500,
    system=[{"type": "text", "text": "친근한 성격을 유지하세요."}], # 이것도 캐시
    messages=[
        {"role": "user", "content": "안녕 Claude!"}, 
        {"role": "assistant", "content": "안녕하세요! 오늘 무엇을 도와드릴까요?", "cache_control": {"type": "ephemeral"}} # 여기까지 캐시
    ]
)
# 사용자가 턴 2를 위해 또 다른 메시지를 추가하는 것으로 가정
# 턴 2 (시스템 + 턴 1에 대해 캐시 히트, 턴 2에 대해 캐시 쓰기)
response_turn2 = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=500,
    system=[{"type": "text", "text": "친근한 성격을 유지하세요."}], # 동일한 시스템 프롬프트
    messages=[
        {"role": "user", "content": "안녕 Claude!"}, # 이제 캐시된 접두사의 일부
        {"role": "assistant", "content": "안녕하세요! 오늘 무엇을 도와드릴까요?"}, # 이제 캐시된 접두사의 일부
        {"role": "user", "content": "재미있는 사실을 알려주세요."}, # 새로운 동적 콘텐츠
        {"role": "assistant", "content": "꿀은 절대 상하지 않는다는 것을 알고 계셨나요?", "cache_control": {"type": "ephemeral"}} # 턴 2 끝까지 캐시
    ]
)
# 턴 2 사용량에서는 시스템+턴 1에 대한 캐시 읽기 입력 토큰과 턴 2의 새로운 사용자/보조자 메시지에 대한 캐시 생성 입력 토큰이 표시됩니다.

캐시 성능 추적:

API 응답에는 캐시 사용 여부를 나타내는 사용량 메트릭이 포함됩니다:

이 필드를 모니터링하는 것은 캐싱 전략이 효과적인지 이해하는 데 중요합니다. 시간에 따른 cache_read_input_tokenscache_creation_input_tokens에 비해 높으면 성공적인 캐싱을 나타냅니다.

AWS Bedrock:

AWS Bedrock을 통해 접근하는 Anthropic Claude와 같은 모델의 경우, 캐싱 메커니즘은 일반적으로 모델 호출 요청 본문(application/json 형식)에 동일한 cache_control 매개변수를 사용하여 활성화됩니다. JSON 본문을 특정 모델의 요구 사항(예: Anthropic의 메시지 API 형식)에 맞게 구조화하고, 위와 같이 cache_control 필드를 포함해야 합니다. 사용하는 모델 공급자에 대한 특정 Bedrock 문서를 확인하십시오.

프롬프트 캐싱의 가격은 얼마인가요?

프롬프트 캐싱은 표준 토큰 비용에 비해 더 세분화된 가격 구조를 도입합니다. 전반적인 이점이 있지만, 관련된 다양한 토큰 유형을 이해하는 것이 중요합니다:

  1. 기본 입력 토큰: 이는 캐시된 접두사의 일부가 아닌 표준 입력 토큰입니다(즉, 캐시 히트 시 동적 접미사, 또는 캐싱이 사용되지 않거나 미스되는 경우 전체 프롬프트). 이들은 모델의 표준 입력 토큰 요금으로 부과됩니다.
  2. 캐시 쓰기 토큰 (cache_creation_input_tokens): 접두사가 처음으로 처리되거나(캐시 미스 후) 캐시에 기록될 때, 해당 접두사와 관련된 토큰은 종종 프리미엄 요금이 부과됩니다. 예를 들어, Anthropic은 캐시 쓰기에 대해 기본 입력 토큰 요금보다 25% 더 많은 가격을 부과합니다. 이는 캐시 항목을 처리하고 저장하는 비용을 반영합니다.
  3. 캐시 읽기 토큰/캐시 히트 토큰 (cache_read_input_tokens): 캐시 히트가 발생할 경우, 캐시에서 불러온 접두서와 관련된 토큰은 상당히 할인된 요금이 부과됩니다. 예를 들어, Anthropic은 캐시 읽기에 대해 기본 입력 토큰 요금의 10% (90% 할인)를 부과합니다. 이는 계산 비용 절감을 반영합니다.
  4. 출력 토큰: LLM이 응답으로 생성한 토큰은 입력에 대해 캐싱이 사용되었는지와 관계없이 모델의 표준 출력 토큰 요금이 부과됩니다.

예시 가격표 (Anthropic Claude 모델 - 예시 요율):

모델 기본 입력 (/MTok) 캐시 쓰기 (/MTok) (+25%) 캐시 읽기 (/MTok) (-90%) 출력 (/MTok)
Claude 3.5 Sonnet $3.00 $3.75 $0.30 $15.00
Claude 3 Haiku $0.25 $0.30 $0.03 $1.25
Claude 3 Opus $15.00 $18.75 $1.50 $75.00

(참고: 항상 Anthropic 및 AWS Bedrock의 공식 가격 페이지를 참조하여 가장 최신 요금을 확인하십시오.)

비용 효율성은 특정 접두사에 대해 캐시 히트를 얼마나 자주 얻는지에 달려 있습니다. 대규모 접두사가 여러 번 재사용되는 경우, 캐시 쓰기의 초기 높은 비용은 이후 캐시 읽기에서 상당한 절감으로 빠르게 상쇄됩니다.

프롬프트 캐싱의 한계

강력하지만, 프롬프트 캐싱에는 한계와 고려해야 할 요소가 있습니다:

최소 캐시 가능 길이: 모델에는 접두사가 캐싱에 적합하도록 하기 위한 최소 토큰 요구 사항이 종종 있습니다. 이 제한보다 짧은 프롬프트는 cache_control로 표시되더라도 캐시될 수 없습니다.

캐시 무효화(캐시 버스팅): 캐시는 접두사의 변경에 매우 민감합니다. 어떤 수정 사항이라도 캐시를 깨뜨리고 새 쓰기를 강요합니다:

캐시 수명(TTL): 캐시는 일시적입니다(예: Anthropic의 경우 최소 5분 TTL, 사용 시 갱신됨). TTL 내에 재사용되지 않는 접두사는 만료됩니다. 현재 캐시를 수동으로 지우거나 자동 동작을 초과해 연장할 방법은 없습니다.

동시성: 동일한 요청을 캐시 진입점에 대해 먼저 제출한 후 요청이 완료되고 캐시 항목이 쓰여지기 전에 여러 요청을 동시에 보내는 경우, 후속 요청은 캐시 미스를 초래할 수 있습니다. 보장된 캐시 히트를 위해서는 다른 요청을 보내기 전에 첫 번째 응답을 기다려야 할 수도 있습니다.

지원되는 모델: 프롬프트 캐싱은 모든 모델이나 제공업체에서 보편적으로 제공되지 않습니다. 사용하려는 특정 모델의 문서를 항상 확인하세요. (현재 다양한 Claude 3 및 3.5 모델에 대해 확인되었습니다.)

디버깅: 캐시 미스를 유도하는 미세한 변화를 식별하는 것은 때때로 까다로울 수 있습니다. 호출 간의 정확한 접두사 콘텐츠를 신중하게 비교하는 것이 필요합니다.

효과적인 캐싱을 위한 모범 사례

프롬프트 캐싱의 이점을 극대화하려면:

  1. 프롬프트를 스마트하게 구조화하세요: 가장 안정적이고 재사용 가능한 정보를(시스템 프롬프트, 지침, 도구 정의, 맥락 문서, 몇 가지 예시) 프롬프트 시퀀스의 시작 부분에 배치합니다. 동적이고 자주 바뀌는 정보(사용자 쿼리, 최신 대화 턴)는 캐시 중단점 이후에 배치합니다.
  2. 최적의 중단점 식별: cache_control를 의도적으로 사용합니다. 실제 정적 콘텐츠의 가장 큰 블록의 끝을 표시합니다. 다수의 중단점을 사용할 경우(Anthropic이 허용하는 경우), 프롬프트 구조의 다양한 안정성 수준을 고려하세요.
  3. 캐시 사용량 모니터링: API 응답에서 cache_creation_input_tokenscache_read_input_tokens를 정기적으로 확인하세요. 시간이 지남에 따라 높은 읽기:쓰기 비율을 목표로 합니다. 쓰기만 주로 보이는 경우, 접두사가 너무 자주 변경되거나 캐시 가능 길이보다 작을 수 있습니다.
  4. 불필요한 변경 피하기: 접두사 콘텐츠에 대한 사소한 변경(공간 추가 또는 구두점 변경 등)도 캐시를 깨뜨립니다. 접두사를 생성하는 방법의 일관성을 유지하세요.
  5. 비용 거래 고려: 캐싱은 길고 자주 재사용되는 접두사에 가장 효과적입니다. 매우 짧은 접두사는 캐시하는 것이 최소한의 절약으로 복잡성만 더할 수 있으므로, 피하는 것이 좋습니다. 변화가 심한 사용자 입력은 캐싱하지 않는 것이 좋습니다.
  6. 테스트 및 반복: 특정 애플리케이션의 작업량 및 사용 패턴에 최적의 전략을 찾기 위해 다양한 프롬프트 구조 및 캐시 중단점을 실험하세요.

결론

프롬프트 캐싱은 대규모 언어 모델 위에 애플리케이션을 구축하는 모든 사람에게 중요한 최적화 기술입니다. 정적 프롬프트 접두사의 계산 상태를 지능적으로 저장하고 재사용함으로써 긴 또는 반복적인 프롬프트와 관련된 지연 및 비용 문제를 직접 해결합니다. 캐싱이 작동하는 방식, 선택한 LLM 제공업체의 구체적인 구현 세부정보(예: Anthropic의 cache_control), 관련된 가격 세부 사항 및 제한 사항을 이해하면 더 효율적이고 반응이 빠르며 경제적인 AI 애플리케이션을 설계할 수 있습니다. LLM 사용 사례가 복잡성과 규모에서 증가함에 따라, 프롬프트 캐싱과 같은 기능을 활용하는 것이 성능이 뛰어나고 지속 가능한 솔루션을 구축하는 데 더욱 중요해질 것입니다.

💡
아름다운 API 문서를 생성하는 훌륭한 API 테스트 도구를 원하십니까?

개발 팀이 최대 생산성으로 함께 작업할 수 있는 통합된 올인원 플랫폼이 필요하신가요?

Apidog은 모든 요구를 충족시키며, Postman을 훨씬 더 저렴한 가격에 대체합니다!
button

Apidog에서 API 설계-첫 번째 연습

API를 더 쉽게 구축하고 사용하는 방법을 발견하세요