OpenAI
Langchain - 캐싱
프갭
2024. 5. 10. 08:59
LLM 애플리케이션을 개발 및 운영 하다보면 동일한 프롬프트로 반복해서 호출해야 하는 경우가 생긴다.
이럴 경우 캐시를(적절하게) 사용 하여 비용적인 부분 및 퍼포먼스를 향상 시킬수 있다
메모리 캐싱
로컬 시스템의 메모리를 사용
from dotenv import load_dotenv
import os
# 환경 변수 로드
load_dotenv()
# 환경 변수 사용
azure_openai_api_key = os.getenv('azure_openai_api_key')
azure_openai_endpoint = os.getenv('azure_openai_endpoint')
azure_open_api_version = os.getenv('azure_open_api_version')
azure_deployment_gpt4test = os.getenv('azure_deployment_gpt4test')
azure_model_version_gpt4test = os.getenv('azure_model_version_gpt4test')
from typing import List
from langchain_openai import AzureChatOpenAI
from langchain.callbacks import get_openai_callback
from langchain.globals import set_llm_cache
from langchain.cache import InMemoryCache
#캐시 설정
set_llm_cache(InMemoryCache())
llm = AzureChatOpenAI(
api_version=azure_open_api_version,
azure_deployment=azure_deployment_gpt4test,
azure_endpoint=azure_openai_endpoint,
api_key=azure_openai_api_key,
)
prompt = "What is famous street foods in Seoul Korea in 200 characters"
with get_openai_callback() as callback:
response = llm.invoke(prompt)
print("First Total Tokens:", callback.total_tokens)
with get_openai_callback() as callback:
response = llm.invoke(prompt)
print("Second Total Tokens:", callback.total_tokens)
First Total Tokens: 84
Second Total Tokens: 0
호출 결과를 보면 첫번째 호출은 API를 호출하였기 때문에, 토큰이 84개의 토큰을 사용한것을 확인할 수 있다.
그러나 두번째 호출은 캐싱이 되었기 때문에, 결과가 첫번째 호출과 정확하게 같고, 사용된 토큰의 수가 0개 인것을 확인할 수 있다.
llm2 = AzureChatOpenAI(
api_version=azure_open_api_version,
azure_deployment=azure_deployment_gpt35turbo,
azure_endpoint=azure_openai_endpoint,
api_key=azure_openai_api_key,
cache=False, <-----
)
만약 특정 LLM에서는 캐시를 사용하고 싶지 않을경우 “cache=False” 를 지정하여 명시적으로 캐시를 사용하지 않도록
설정 할수 있다.
Redis 캐싱
Langchain은 외부의 데이터베이스를 캐시 저장소로 사용할 수 있다.
로컬에서 작동하는 SqlLite에서 부터 Redis와 같은 메모리 스토어, Cassandra와 같은 NoSQL 데이터 베이스를 지원한다.
지원되는 데이터 베이스와 개발 가이드는 Langchain 문서 https://python.langchain.com/docs/integrations/llms/llm_caching 를 참고하기 바란다.
여기서는 캐시로 가장 널리 사용되는 Redis 를 캐시로 사용하는 방법을 살펴본다.
테스트를 위한 redis 인스턴스는 https://redis.com/ 에서 제공하는 무료 redis instance를 사용하였다. (30M 인스턴스까지는 무료로 사용이 가능하다.)
from dotenv import load_dotenv
import os
# 환경 변수 로드
load_dotenv()
# 환경 변수 사용
azure_openai_api_key = os.getenv('azure_openai_api_key')
azure_openai_endpoint = os.getenv('azure_openai_endpoint')
azure_open_api_version = os.getenv('azure_open_api_version')
azure_deployment_gpt4test = os.getenv('azure_deployment_gpt4test')
azure_model_version_gpt4test = os.getenv('azure_model_version_gpt4test')
redis_host = os.getenv('redis_host')
redis_port = os.getenv('redis_port')
redis_password = os.getenv('redis_password')
# Redis cache example
from langchain_openai import AzureChatOpenAI
from langchain.callbacks import get_openai_callback
from langchain.globals import set_llm_cache
from langchain.cache import RedisCache
from redis import Redis
r = Redis(
host=redis_host,
port=redis_port,
password=redis_password)
rcache = RedisCache(r)
rcache.clear()
set_llm_cache(
rcache
)
llm = AzureChatOpenAI(
api_version=azure_open_api_version,
azure_deployment=azure_deployment_gpt4test,
azure_endpoint=azure_openai_endpoint,
api_key=azure_openai_api_key,
)
prompt = "What is famous street foods in Seoul Korea in 200 characters"
with get_openai_callback() as callback:
response = llm.invoke(prompt)
print("First Total Tokens:", callback.total_tokens)
with get_openai_callback() as callback:
response = llm.invoke(prompt)
print("Second Total Tokens:", callback.total_tokens)
First Total Tokens: 82
Second Total Tokens: 82
결과를 보면 아래와 같이 첫번째는 토큰 카운트가 82으로 실제로 opneai api를 호출하였고, 두번째는 0으로 캐싱된 내용을 활용한것을 확인할 수 있다.
Semantic 캐싱
LLM에 대한 프롬프트 캐싱을 하는데 생각해봐야 하는 문제는 우리가 사용하는 프롬프트는 자연어 라는 사실이다.
즉 같은 의미를 갖는 질문이라도, 문자열 관점에서 봤을때는 다른 프롬프트로 인식될 수 있다.
예를 들어 “서울에서 유명한 음식 5가지?”와 “서울에서 맛볼 수 있는 유명한 음식 5가지?”는 문맥상으로는 같은 의미지만,
문자열이 다르기 때문에 캐시가 히트가 되지 않는다.
유사도 검사를 위해 RedisSemanticChache를 사용 하면된다.
from dotenv import load_dotenv
import os
# 환경 변수 로드
load_dotenv()
# 환경 변수 사용
azure_openai_api_key = os.getenv('azure_openai_api_key')
azure_openai_endpoint = os.getenv('azure_openai_endpoint')
azure_open_api_version = os.getenv('azure_open_api_version')
azure_deployment_gpt4test = os.getenv('azure_deployment_gpt4test')
azure_model_version_gpt4test = os.getenv('azure_model_version_gpt4test')
azure_deployment_embed_ada002 = os.getenv('azure_deployment_embed_ada002')
redis_host = os.getenv('redis_host')
redis_port = os.getenv('redis_port')
redis_password = os.getenv('redis_password')
redis_api_url = os.getenv('redis_api_url')
# Redis Semantic cache example
from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings
from langchain.callbacks import get_openai_callback
from langchain.globals import set_llm_cache
from redis import Redis
from langchain.cache import RedisSemanticCache
from langchain.cache import RedisCache
# from langchain.embeddings import OpenAIEmbeddings, AzureOpenAIEmbeddings
import os
os.environ["AZURE_OPENAI_API_KEY"] = azure_openai_api_key
os.environ["AZURE_OPENAI_ENDPOINT"] = azure_openai_endpoint
llm = AzureChatOpenAI(
api_version=azure_open_api_version,
azure_deployment=azure_deployment_gpt4test,
azure_endpoint=azure_openai_endpoint,
api_key=azure_openai_api_key,
)
embedd=AzureOpenAIEmbeddings(
azure_deployment=azure_deployment_embed_ada002,
openai_api_version=azure_open_api_version,
)
r = Redis(
host=redis_host,
port=redis_port,
password=redis_password)
# redis cache data 초기화
rcache = RedisCache(r)
rcache.clear()
# with Semantic Cache
set_llm_cache(
RedisSemanticCache(
redis_url=redis_api_url,
embedding=embedd
)
)
prompt1 = "What is top 10 famous street foods in Seoul Korea in 200 characters"
prompt2 = "What is top 5 famous street foods in Seoul Korea in 200 characters"
with get_openai_callback() as callback:
response = llm.invoke(prompt1)
print(response)
print("First Total Tokens:", callback.total_tokens)
with get_openai_callback() as callback:
# llm.cache = False
response = llm.invoke(prompt2)
print(response)
print("Second Total Tokens:", callback.total_tokens)
1. Tteokbokki: Spicy rice cakes
2. Gimbap: Seaweed rice rolls
3. Pajeon: Green onion pancake
4. Sundae: Korean sausage
5. Mandu: Dumplings
6. Bungeoppang: Fish-shaped pastry
7. Hotteok: Sweet pancakes
8. Odeng: Fish cake \n9. Dakkochi: Chicken skewers
10. Twigim: Korean-style tempura.
First Total Tokens: 124
1. Tteokbokki: Spicy rice cakes
2. Gimbap: Seaweed rice rolls
3. Pajeon: Green onion pancake
4. Sundae: Korean sausage
5. Mandu: Dumplings
6. Bungeoppang: Fish-shaped pastry
7. Hotteok: Sweet pancakes
8. Odeng: Fish cake \n9. Dakkochi: Chicken skewers
10. Twigim: Korean-style tempura.
Second Total Tokens: 0
RedisSemanticChache 를 사용 하여 캐시에서 정보를 가지고는 왔지만 내가 원하는 5개의 리스트가 아닌 10개의 리스트를 가지고 오는거를 확인할수 있다.
자연어의 유사도로 내용을 가지고 올수는 있지만 그 의미는 캐치 하지 못하기 때문에 이런부분을 유의하여 개발을 진행 하면 좋을듯 하다.
반응형