LLM 응답속도 개선
기존 META LLM MSP의 채팅 기능은 가벼운 질문에 대한 답변에도,
필요 이상으로 많은 시간이 소요되는 문제가 있었습니다.
이러한 원인을 분석하고 최대한 속도를 개선하는 중입니다.
임베딩 모델
API를 통해 답변을 받아올 경우 LLM 자체의 응답속도를 빠르게 할 수는 없지만,
중간 과정에서 임베딩을 통해 소요되는 시간을 단축할 수는 있습니다.
CORE
embedding = "OpenAI API KEY"
임베딩 모델의 경우 어차피 사용자의 API 키가 아닌,
공통적으로 제공되는 API 키를 사용하고 있습니다.
그렇기 때문에 임베딩 모델의 경우에는 굳이 API 키를 찾기 위해 DB에 접속할 필요가 없습니다.
CORE의 Config에 직접 OpenAI API 키를 하나 입력합니다.
임베딩 모델 호출
from langchain_openai import OpenAIEmbeddings
import core.config as config
def get_embeddings():
return OpenAIEmbeddings(
api_key = config.EMBEDDING_API,
model=config.EMBEDDING_MODEL
)
임베딩 모델의 API키를 매번 DB에서 받아오는 불필요한 과정이 사라져서,
SELECT 쿼리문이 돌아가는 시간을 단축할 수 있게 되었습니다.
답변 생성 후 저장
기존 코드 문제점
vector2 = text_to_vector(response_text)
add_message(db=db, session_id=session_id, project_id=project_id, user_email=user_email,
message_role='user', conversation=conversation, vector_memory=vector)
add_message(db=db, session_id=session_id, project_id=project_id, user_email=user_email,
message_role='assistant', conversation=response_text, vector_memory=vector2)
return response_text
실질적으로 엔드포인트에서 넘겨줘야 하는 값은 Response Text 값 뿐입니다.
이후 답변 내용을 임베딩하여 DB에 저장하고 Cost를 계산하는 과정도 물론 중요한 기능이지만,
굳이 이러한 과정을 전부 마친 후에 답변을 반환하는 것은 사용자가 답변을 받기까지의 시간을 지연시킵니다.
QA CHAIN 코드
def qa_chain(db: Session, session_id, project_id, user_email, conversation, provider="openai", model=None, api_key : str = None):
llm = get_llm(provider, model, api_key = api_key)
vector = text_to_vector(conversation)
relevant_messages = get_relevant_messages(db, session_id, vector, top_n=5)
formatted_history = "\n".join(
[f"{msg['message_role'].capitalize()}: {msg['conversation']}" for msg in relevant_messages]
)
prompt = PromptTemplate(
input_variables=["history", "input"],
template="{history}\nHuman: {input}\nAI:"
)
chain = prompt | llm | StrOutputParser()
response_text = chain.invoke({"history": formatted_history, "input": conversation})
return response_text
기존 Q&A Chain 코드는 질문이 입력되고, 답변이 생성되며 DB에 기록이 저장되기까지의 모든 과정을 전부 처리하는 함수였습니다.
그러나 현재 수정된 Chain 코드는 후처리 과정을 전부 생략하며 LangChain으로 빠르게 답변만 생성한 뒤 반환합니다.
후처리 과정
def process_usage_in_background(db, user_email, provider, model, formatted_history, response_text, session_id, project_id, conversation, vector):
if provider == "openai":
with get_openai_callback() as cb:
update_usage(db = db, user_email = user_email, provider = provider, usage = cb.total_tokens)
print(f"[LLM 사용량 - OpenAI] prompt: {cb.prompt_tokens} / completion: {cb.completion_tokens} / total: {cb.total_tokens} / cost: ${cb.total_cost:.6f}")
elif provider == "anthropic":
prompt_tokens = count_tokens(formatted_history)
completion_tokens = count_tokens(response_text)
cost_data = estimate_claude_cost(model, prompt_tokens, completion_tokens)
update_usage(db=db, user_email=user_email, provider=provider, usage=cost_data['total'])
print(f"[LLM 사용량 - Claude] prompt: {cost_data['prompt']} / completion: {cost_data['completion']} / total: {cost_data['total']} / cost: ${cost_data['cost']:.6f}")
else:
print("[LLM 사용량] 추적 불가: 미지원 provider")
vector2 = text_to_vector(response_text)
add_message(db=db, session_id=session_id, project_id=project_id, user_email=user_email,
message_role='user', conversation=conversation, vector_memory=vector)
add_message(db=db, session_id=session_id, project_id=project_id, user_email=user_email,
message_role='assistant', conversation=response_text, vector_memory=vector2)
Q&A Chain 코드에서 후처리 과정을 전부 생략했으나,
후처리 과정 또한 전부 META LLM MSP의 정상적인 작동을 위해서는 반드시 필요한 코드입니다.
그렇기에 QA Chain 코드에서 처리하진 않지만, 백그라운드에서 따로 실행이 되도록 설정했습니다.
엔드포인트
@langchain_router.post('/RequestMessage')
async def request_message(request: RequestMessageRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db)):
email = request.user_email
project_id = request.project_id
message = request.messageInput
session = request.session
model = request.selected_model
if model in config.OPENAI_MODELS:
provider = "openai"
elif model in config.ANTHROPIC_MODELS:
provider = "anthropic"
else:
return "해당 모델은 아직 지원되지 않는 모델입니다.\n다른 모델을 선택해주세요."
api_key = get_api_key(db=db, user_email=email, provider=provider)
if not api_key:
return "보유 중인 API키가 없습니다.\n우선 API키를 등록해주세요."
first = is_this_first(db=db, session_id=session)
if first:
agent_executor = get_session_agent(session)
response = agent_executor(message)
change_session_title(db=db, session_id=session, content=response.content)
try :
response_text, vector, formatted_history = qa_chain(
db = db, session_id=session, project_id=project_id, user_email=email, conversation=message, provider=provider, model=model, api_key=api_key
)
background_tasks.add_task(
process_usage_in_background,
db, session, project_id, email, provider, model,
message, response_text, formatted_history, vector
)
return response_text
except Exception as e:
print(f"Error Occured f{e}")
return "현재 등록하신 API 키는 유효하지 않습니다.\n유효하는 API키를 등록해주세요."
엔드포인트에서도 답변만 바로 return한 다음,
이후의 임베딩 및 DB 관리 기능은 전부 백그라운드로 넘깁니다.
응답 속도 비교
기존 코드 ( 로컬 )

‘FastAPI에 대해 소개해주세요’ 라는 질문에 대하여,
응답이 출력되기까지 약 16초에 시간이 소요됩니다.
기존 코드 ( AWS )

기존 코드의 로직을 사용함에도 불구하고 AWS에서 돌리는 것이 로컬 신규 코드보다 느립니다.
신규 코드 ( 로컬 )

장문의 답변을 완전히 출력하는데 10초가 소요됩니다.
똑같이 로컬에서 작동했을 때에는 압도적으로 빠르며,
AWS 기반 서버보다도 약간 더 빠릅니다.
속도 차이 요약
