🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

동적 컨텍스트 주입 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 25. · 2 Views

동적 컨텍스트 주입 완벽 가이드

LLM이 실시간으로 필요한 정보를 받아 더 정확한 답변을 생성하는 동적 컨텍스트 주입 기법을 배웁니다. RAG와의 통합, 컨텍스트 신선도 관리, 실전 구현까지 초급자도 쉽게 따라할 수 있도록 안내합니다.


목차

  1. 정적_vs_동적_컨텍스트
  2. RAG와의_통합
  3. 관련_정보_검색과_주입
  4. 컨텍스트_신선도_관리
  5. 실습_동적_예제_선택_시스템
  6. 실습_실시간_정보_주입

1. 정적 vs 동적 컨텍스트

김개발 씨는 챗봇 서비스를 개발하던 중 이상한 현상을 발견했습니다. 작년 데이터로 학습한 AI가 최신 제품 정보를 전혀 모른다는 것이었습니다.

"도대체 어떻게 해야 최신 정보를 알려줄 수 있을까요?" 선배 박시니어 씨가 웃으며 말합니다. "정적 컨텍스트만 쓰고 있네요.

동적 컨텍스트를 써봐요."

정적 컨텍스트는 미리 정해진 고정된 정보를 제공하는 방식이고, 동적 컨텍스트는 실시간으로 필요한 정보를 찾아서 주입하는 방식입니다. 마치 백과사전을 통째로 외우는 것과 필요할 때마다 검색하는 것의 차이와 같습니다.

동적 컨텍스트를 사용하면 LLM이 항상 최신 정보로 답변할 수 있습니다.

다음 코드를 살펴봅시다.

# 정적 컨텍스트: 고정된 정보
static_context = """
회사 설립일: 2023년 1월
주요 제품: A, B, C
"""

# 동적 컨텍스트: 실시간 정보 조회
def get_dynamic_context(query):
    # 데이터베이스에서 최신 정보 검색
    latest_info = database.search(query)
    return f"최신 정보 (조회일: {today}): {latest_info}"

# LLM에 주입
prompt = f"{get_dynamic_context(user_query)}\n\n질문: {user_query}"
response = llm.generate(prompt)

김개발 씨는 입사 2개월 차 AI 개발자입니다. 회사에서 고객 상담 챗봇을 만들고 있는데, 오늘 이상한 버그 리포트를 받았습니다.

"챗봇이 3개월 전에 단종된 제품을 계속 추천해요." 분명히 최신 모델을 사용하고 있는데 왜 이런 일이 생길까요? 박시니어 씨가 김개발 씨의 코드를 살펴보더니 고개를 끄덕입니다.

"아, 정적 컨텍스트만 사용하고 있네요. 이러면 모델 학습 시점의 정보만 알 수 있어요." 그렇다면 정적 컨텍스트와 동적 컨텍스트는 정확히 무엇이 다를까요?

쉽게 비유하자면, 정적 컨텍스트는 마치 종이 백과사전과 같습니다. 한번 인쇄되면 내용이 고정되어 있어서 새로운 정보가 생겨도 업데이트할 수 없습니다.

반면 동적 컨텍스트는 인터넷 검색과 같습니다. 필요할 때마다 최신 정보를 찾아서 제공할 수 있습니다.

정적 컨텍스트만 사용하던 시절에는 어땠을까요? 개발자들은 새로운 정보가 생길 때마다 모델을 다시 학습시켜야 했습니다.

시간도 오래 걸리고, 비용도 많이 들었습니다. 더 큰 문제는 학습과 학습 사이의 기간에는 AI가 구식 정보로만 답변한다는 것이었습니다.

고객들은 "이 챗봇은 최신 정보를 모르네요"라며 실망했습니다. 바로 이런 문제를 해결하기 위해 동적 컨텍스트 주입 기법이 등장했습니다.

동적 컨텍스트를 사용하면 모델 재학습 없이 최신 정보를 제공할 수 있습니다. 또한 사용자 질문에 정확히 필요한 정보만 선택적으로 가져올 수 있습니다.

무엇보다 실시간으로 변화하는 데이터에도 즉시 대응할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 정적 컨텍스트 부분을 보면 문자열로 고정된 정보가 하드코딩되어 있습니다. 이 정보는 코드를 수정하지 않는 한 절대 바뀌지 않습니다.

다음으로 get_dynamic_context 함수에서는 데이터베이스를 실시간으로 검색합니다. 마지막으로 프롬프트를 구성할 때 동적으로 조회한 최신 정보를 포함시켜서 LLM에 전달합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 전자상거래 쇼핑몰의 상담 챗봇을 개발한다고 가정해봅시다.

재고 상황은 실시간으로 변합니다. 동적 컨텍스트를 활용하면 "이 제품 재고 있나요?"라는 질문에 현재 시점의 정확한 재고 정보로 답변할 수 있습니다.

네이버, 쿠팡 같은 대형 플랫폼에서 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 모든 정보를 동적으로 조회하려는 것입니다. 이렇게 하면 불필요한 데이터베이스 호출이 많아져서 응답 속도가 느려집니다.

따라서 자주 바뀌지 않는 정보는 정적으로, 실시간성이 중요한 정보만 동적으로 처리해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 단종 제품을 추천했던 거군요!" 동적 컨텍스트를 제대로 이해하면 항상 최신 정보를 제공하는 AI 시스템을 만들 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 정적 컨텍스트는 회사 소개, 정책 등 불변 정보에 사용하세요

  • 동적 컨텍스트는 재고, 가격, 뉴스 등 변동 정보에 활용하세요
  • 두 방식을 조합하면 효율성과 정확성을 동시에 얻을 수 있습니다

2. RAG와의 통합

동적 컨텍스트 개념을 이해한 김개발 씨는 궁금해졌습니다. "그런데 필요한 정보를 어떻게 찾아오죠?

데이터가 수백만 건인데요." 박시니어 씨가 화이트보드에 그림을 그리며 설명합니다. "바로 RAG를 사용하는 거예요.

Retrieval-Augmented Generation, 검색 증강 생성이라고 합니다."

RAG는 벡터 데이터베이스에서 질문과 관련된 정보를 검색한 뒤 LLM에 주입하는 기법입니다. 마치 도서관 사서가 질문을 듣고 관련 책을 찾아주는 것과 같습니다.

RAG와 동적 컨텍스트를 결합하면 방대한 데이터 속에서 정확히 필요한 정보만 실시간으로 가져올 수 있습니다.

다음 코드를 살펴봅시다.

from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

# 벡터 데이터베이스 초기화
vectorstore = Chroma(embedding_function=OpenAIEmbeddings())

# 문서들을 임베딩하여 저장
documents = ["제품A 가격: 50000원", "제품B 재고: 100개", ...]
vectorstore.add_texts(documents)

# 사용자 질문과 유사한 문서 검색
def retrieve_context(query):
    # 상위 3개 관련 문서 검색
    results = vectorstore.similarity_search(query, k=3)
    context = "\n".join([doc.page_content for doc in results])
    return context

# LLM에 주입
user_query = "제품A 가격이 얼마예요?"
context = retrieve_context(user_query)
prompt = f"컨텍스트: {context}\n\n질문: {user_query}"

김개발 씨는 동적 컨텍스트의 개념은 이해했지만 실제 구현이 막막했습니다. 회사 데이터베이스에는 제품 정보만 100만 건이 넘습니다.

사용자가 "겨울 코트 추천해줘"라고 물으면 100만 건을 어떻게 검색해야 할까요? 일일이 키워드 매칭을 할 수도 없고, SQL 쿼리로는 한계가 있습니다.

박시니어 씨가 노트북을 열어 코드를 보여줍니다. "RAG 파이프라인을 구축하면 돼요.

벡터 임베딩으로 의미 기반 검색을 하는 거죠." 그렇다면 RAG는 정확히 어떻게 작동할까요? 쉽게 비유하자면, RAG는 마치 도서관의 스마트 사서와 같습니다.

사용자가 "겨울에 입을 따뜻한 옷 찾아줘"라고 하면, 사서는 단순히 "겨울"이라는 단어만 찾지 않습니다. "따뜻한", "방한", "코트", "패딩" 같은 관련 개념까지 이해해서 최적의 책을 골라줍니다.

RAG도 마찬가지로 질문의 의미를 이해해서 관련 정보를 찾아냅니다. RAG가 없던 시절에는 어땠을까요?

개발자들은 키워드 기반 검색에 의존했습니다. "겨울 코트"라고 검색하면 정확히 그 단어가 들어간 문서만 찾을 수 있었습니다.

"방한 패딩"처럼 다른 표현으로 작성된 문서는 놓쳤습니다. 더 큰 문제는 동의어나 유사 개념을 처리하기 위해 수많은 규칙을 하드코딩해야 했다는 것입니다.

코드는 점점 복잡해지고 유지보수가 악몽이었습니다. 바로 이런 문제를 해결하기 위해 RAG 기법이 등장했습니다.

RAG를 사용하면 의미 기반 검색이 가능해집니다. "겨울 코트"와 "방한 패딩"이 비슷한 의미라는 것을 자동으로 이해합니다.

또한 관련도 순으로 정렬되어 가장 적합한 정보를 먼저 가져올 수 있습니다. 무엇보다 규칙 작성 없이 자연어 질문만으로 정확한 검색이 가능하다는 큰 이점이 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 Chroma 벡터 데이터베이스를 초기화합니다.

OpenAI의 임베딩 모델을 사용해서 텍스트를 벡터로 변환합니다. 다음으로 add_texts로 문서들을 벡터화하여 저장합니다.

이 과정에서 각 문서는 수백 차원의 벡터로 표현됩니다. similarity_search 함수는 질문을 벡터로 변환한 뒤, 저장된 문서 벡터들과 코사인 유사도를 계산합니다.

마지막으로 가장 유사한 상위 k개 문서를 반환하고, 이를 컨텍스트로 LLM에 전달합니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 법률 자문 AI 서비스를 개발한다고 가정해봅시다. 수천 페이지의 법률 문서 중에서 사용자 질문과 관련된 조항만 찾아야 합니다.

RAG를 활용하면 "회사 퇴사할 때 퇴직금은 언제 받나요?"라는 자연어 질문으로 근로기준법의 해당 조항을 정확히 검색해서 AI에게 제공할 수 있습니다. 로톡, 헬프미 같은 리걸테크 스타트업에서 이런 기술을 적극 활용하고 있습니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 너무 많은 문서를 검색하는 것입니다.

k값을 50, 100으로 설정하면 관련 없는 정보까지 섞여서 LLM이 혼란스러워합니다. 따라서 3~5개 정도의 핵심 문서만 검색하는 것이 효과적입니다.

또한 임베딩 모델의 선택도 중요합니다. 한국어 데이터라면 다국어 모델이나 한국어 특화 모델을 사용해야 정확도가 높아집니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 코드를 본 김개발 씨는 눈이 반짝였습니다.

"와, 이렇게 간단하게 의미 검색이 되는군요!" RAG와 동적 컨텍스트를 결합하면 실시간으로 정확한 정보를 찾아 AI에게 제공할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 벡터DB는 Chroma, Pinecone, Weaviate 등 다양한 선택지가 있습니다

  • k값은 3~5로 시작하고, 실험을 통해 최적값을 찾으세요
  • 한국어 데이터는 multilingual 임베딩 모델을 사용하세요

3. 관련 정보 검색과 주입

RAG의 원리를 이해한 김개발 씨는 실제 구현을 시작했습니다. 그런데 검색 결과가 이상했습니다.

"청바지 추천해줘"라고 물으면 운동화 정보가 나오기도 했습니다. 박시니어 씨가 코드를 보더니 말합니다.

"검색 전략이 부족해요. 단순 유사도만으로는 안 되고, 필터링과 재순위화가 필요합니다."

관련 정보 검색은 단순 벡터 유사도를 넘어 메타데이터 필터링, 하이브리드 검색, 재순위화 등의 기법을 활용합니다. 마치 검색 엔진이 단순 키워드 매칭을 넘어 페이지 랭크, 신선도 등을 고려하는 것과 같습니다.

정교한 검색 전략으로 진짜 필요한 정보만 주입할 수 있습니다.

다음 코드를 살펴봅시다.

# 메타데이터 기반 필터링
def retrieve_with_filter(query, category=None, date_from=None):
    # 1단계: 벡터 유사도 검색
    results = vectorstore.similarity_search(query, k=10)

    # 2단계: 메타데이터 필터링
    filtered = []
    for doc in results:
        if category and doc.metadata.get('category') != category:
            continue  # 카테고리 불일치 제외
        if date_from and doc.metadata.get('date') < date_from:
            continue  # 오래된 문서 제외
        filtered.append(doc)

    # 3단계: 재순위화 (신선도 가중치)
    filtered.sort(key=lambda x: x.metadata.get('date'), reverse=True)
    return filtered[:3]  # 상위 3개만 반환

김개발 씨는 RAG를 구현하고 나름 만족했습니다. 하지만 QA 팀에서 버그 리포트가 쏟아졌습니다.

"겨울 코트를 물어봤는데 여름 티셔츠를 추천해요", "작년 단종 제품이 계속 나와요." 분명히 벡터 검색은 작동하는데 왜 엉뚱한 결과가 나올까요? 박시니어 씨가 검색 결과를 함께 분석했습니다.

"아, 벡터 유사도만 보면 '코트'와 '티셔츠'가 둘 다 '의류'라서 유사하다고 판단할 수 있어요. 추가 필터가 필요합니다." 그렇다면 정교한 검색 전략이란 정확히 무엇일까요?

쉽게 비유하자면, 단순 벡터 검색은 마치 색깔만 보고 과일을 고르는 것과 같습니다. 빨간색이라고 해서 사과만 있는 게 아니라 딸기, 토마토도 있습니다.

정교한 검색은 색깔, 크기, 모양, 신선도를 모두 고려해서 진짜 원하는 과일을 찾아냅니다. 검색 시스템도 마찬가지로 벡터 유사도뿐만 아니라 메타데이터를 활용해야 합니다.

단순 검색만 사용하던 시절에는 어땠을까요? 개발자들은 검색 결과를 받은 뒤 애플리케이션 레이어에서 수동으로 필터링했습니다.

코드가 복잡해지고, 성능도 떨어졌습니다. 더 큰 문제는 벡터 검색으로 이미 100개를 가져온 뒤 필터링하면, 정작 필요한 문서가 101번째에 있을 수도 있다는 것입니다.

진짜 원하는 정보를 놓치는 경우가 많았습니다. 바로 이런 문제를 해결하기 위해 하이브리드 검색재순위화 기법이 등장했습니다.

하이브리드 검색을 사용하면 벡터 검색과 메타데이터 필터를 동시에 적용할 수 있습니다. 또한 날짜, 카테고리, 가격대 같은 구조화된 정보로 결과를 정제할 수 있습니다.

무엇보다 재순위화를 통해 단순 유사도가 아닌 비즈니스 로직을 반영할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 similarity_search로 k=10개의 후보 문서를 가져옵니다. 이것은 1차 스크리닝입니다.

다음으로 메타데이터 필터링 단계에서 카테고리가 맞지 않거나 날짜가 오래된 문서를 제외합니다. 이 과정에서 10개가 3개로 줄어들 수 있습니다.

마지막으로 남은 문서들을 날짜 기준으로 정렬해서 최신 정보 우선으로 재배치합니다. 최종적으로 상위 3개만 반환하여 LLM에 주입합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 부동산 매물 검색 서비스를 개발한다고 가정해봅시다.

사용자가 "강남 신축 아파트"라고 검색하면 벡터 검색만으로는 강남의 구축 아파트나 오피스텔까지 섞여 나올 수 있습니다. 메타데이터 필터로 "지역=강남구", "매물유형=아파트", "준공년도>=2020"을 적용하면 정확히 원하는 매물만 추출됩니다.

직방, 다방 같은 프롭테크 서비스가 이런 방식을 사용합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 필터를 너무 많이 추가하는 것입니다. 이렇게 하면 검색 결과가 아예 0건이 될 수 있습니다.

따라서 필수 필터와 선택 필터를 구분해야 합니다. 필수 필터는 반드시 만족해야 하고, 선택 필터는 결과가 충분하면 적용하는 식으로 단계적으로 접근하세요.

다시 김개발 씨의 이야기로 돌아가 봅시다. 메타데이터 필터를 추가한 김개발 씨의 챗봇은 훨씬 정확한 답변을 하기 시작했습니다.

"이제야 제대로 작동하네요!" 정교한 검색 전략을 사용하면 진짜 필요한 정보만 정확히 찾아서 AI에게 제공할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 메타데이터는 문서 저장 시점에 함께 인덱싱하세요

  • 필터는 너무 많으면 결과가 없을 수 있으니 필수/선택을 구분하세요
  • 재순위화는 날짜뿐만 아니라 클릭률, 평점 등 다양한 지표를 활용할 수 있습니다

4. 컨텍스트 신선도 관리

검색 정확도를 높인 김개발 씨는 새로운 문제에 직면했습니다. 어제 가격이 바뀐 제품인데, 챗봇은 여전히 옛날 가격을 말하고 있었습니다.

벡터 데이터베이스를 확인해보니 3일 전 데이터가 그대로 남아있었습니다. 박시니어 씨가 고개를 끄덕입니다.

"컨텍스트 신선도 관리가 필요한 시점이네요. TTL과 증분 업데이트를 설정해야 합니다."

컨텍스트 신선도는 저장된 정보가 얼마나 최신 상태를 유지하는지를 관리하는 것입니다. 마치 편의점에서 유통기한을 체크하고 상한 음식을 버리는 것과 같습니다.

TTL 설정, 증분 업데이트, 버전 관리 등을 통해 AI가 항상 최신 정보로 답변하도록 보장할 수 있습니다.

다음 코드를 살펴봅시다.

from datetime import datetime, timedelta

# TTL 기반 신선도 체크
def check_freshness(doc, ttl_hours=24):
    """문서가 신선한지 확인 (TTL: Time To Live)"""
    created_at = doc.metadata.get('created_at')
    now = datetime.now()
    age = now - created_at
    return age < timedelta(hours=ttl_hours)

# 증분 업데이트
def incremental_update(new_docs):
    """변경된 문서만 업데이트"""
    for doc in new_docs:
        doc_id = doc.metadata['id']
        # 기존 문서 삭제
        vectorstore.delete(filter={'id': doc_id})
        # 새 버전 추가
        vectorstore.add_texts([doc.content], metadatas=[{
            'id': doc_id,
            'created_at': datetime.now(),
            'version': doc.metadata.get('version', 0) + 1
        }])

김개발 씨는 완벽한 RAG 시스템을 구축했다고 생각했습니다. 하지만 운영 2주 차에 심각한 문제가 발생했습니다.

고객이 "이 제품 가격이 뭐예요?"라고 물으면 AI는 지난주 가격을 답했습니다. 실제로는 특가 행사로 30% 할인 중인데 말이죠.

고객 불만이 쌓여갔습니다. 박시니어 씨가 데이터베이스를 점검하더니 한숨을 쉽니다.

"벡터 DB에 오래된 데이터가 쌓여있네요. 신선도 관리를 안 하면 이렇게 돼요." 그렇다면 컨텍스트 신선도 관리란 정확히 무엇일까요?

쉽게 비유하자면, 컨텍스트 신선도는 마치 냉장고 관리와 같습니다. 냉장고에 음식을 넣어두면 영원히 신선한 게 아닙니다.

유통기한을 확인하고, 상한 음식은 버리고, 새 음식으로 교체해야 합니다. 벡터 데이터베이스도 마찬가지입니다.

오래된 문서는 **TTL(Time To Live)**로 관리하고, 변경된 정보는 증분 업데이트로 갱신해야 합니다. 신선도 관리를 하지 않던 시절에는 어땠을까요?

개발자들은 벡터 DB를 한번 구축하면 그대로 방치했습니다. 데이터가 바뀌어도 수동으로 전체 재구축을 해야 했습니다.

시간도 오래 걸리고, 그 사이에 또 데이터가 바뀌면 영원히 따라잡을 수 없었습니다. 더 큰 문제는 어떤 문서가 오래됐는지 알 수 없어서 전체를 다시 임베딩해야 했다는 것입니다.

비용도 많이 들고 비효율적이었습니다. 바로 이런 문제를 해결하기 위해 신선도 관리 시스템이 등장했습니다.

신선도 관리를 사용하면 자동으로 오래된 문서를 감지할 수 있습니다. 또한 변경된 문서만 선택적으로 업데이트해서 효율성을 높일 수 있습니다.

무엇보다 버전 관리를 통해 같은 문서의 새 버전과 옛날 버전을 구분할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 check_freshness 함수는 문서의 생성 시각을 확인합니다. 현재 시각과 비교해서 TTL 시간을 초과했는지 판단합니다.

예를 들어 TTL을 24시간으로 설정하면 하루가 지난 문서는 False를 반환합니다. 다음으로 incremental_update 함수는 문서 ID로 기존 문서를 삭제하고 새 버전을 추가합니다.

메타데이터에 버전 번호를 증가시켜서 같은 문서의 여러 버전을 추적할 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 뉴스 요약 AI 서비스를 개발한다고 가정해봅시다. 속보성 뉴스는 1시간 단위로 업데이트되고, 일반 기사는 24시간 단위로 충분합니다.

카테고리별로 다른 TTL을 설정하면 효율적입니다. 실제로 네이버 뉴스봇, 구글 뉴스 같은 서비스는 시간대별 차등 TTL을 적용해서 속보는 빠르게, 일반 기사는 느리게 갱신합니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 TTL을 너무 짧게 설정하는 것입니다.

이렇게 하면 문서가 자주 사라져서 검색 결과가 부족해질 수 있습니다. 따라서 데이터 변경 주기를 분석한 뒤 적절한 TTL을 설정해야 합니다.

또한 증분 업데이트 시 문서 ID 관리가 중요합니다. ID가 중복되거나 누락되면 같은 문서가 여러 번 저장될 수 있습니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. TTL과 증분 업데이트를 적용한 김개발 씨의 시스템은 이제 항상 최신 가격을 제공했습니다.

"드디어 실시간 정보를 제공할 수 있게 됐어요!" 컨텍스트 신선도를 관리하면 AI가 항상 최신 정보로 정확한 답변을 할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - TTL은 데이터 특성에 맞게 카테고리별로 다르게 설정하세요

  • 증분 업데이트는 CDC(Change Data Capture) 패턴과 결합하면 효과적입니다
  • 버전 관리로 문서 변경 이력을 추적하면 디버깅이 쉬워집니다

5. 실습 동적 예제 선택 시스템

이론은 충분히 배운 김개발 씨는 이제 실전 프로젝트를 시작하려 합니다. 박시니어 씨가 첫 번째 과제를 줍니다.

"코딩 교육 플랫폼을 만들어 보세요. 학생이 질문하면 관련된 예제 코드를 동적으로 찾아서 보여주는 거예요." 김개발 씨는 지금까지 배운 내용을 총동원해서 시스템을 설계하기 시작했습니다.

동적 예제 선택 시스템은 사용자 질문에 맞는 코드 예제를 실시간으로 검색해서 제공하는 실전 RAG 애플리케이션입니다. 마치 선생님이 학생 수준에 맞춰 적절한 예시를 골라주는 것과 같습니다.

벡터 검색, 메타데이터 필터, 난이도 분석을 결합하여 최적의 학습 경험을 제공할 수 있습니다.

다음 코드를 살펴봅시다.

# 코드 예제 DB 구축
code_examples = [
    {"code": "def add(a, b): return a + b", "topic": "함수", "level": "초급"},
    {"code": "class Person:\n    def __init__(self, name):\n        self.name = name",
     "topic": "클래스", "level": "중급"},
]

# 벡터 DB에 저장
for ex in code_examples:
    vectorstore.add_texts(
        [ex['code']],
        metadatas=[{'topic': ex['topic'], 'level': ex['level']}]
    )

# 동적 예제 선택
def select_examples(user_query, user_level="초급"):
    # 1. 벡터 검색으로 관련 예제 찾기
    results = vectorstore.similarity_search(user_query, k=10)
    # 2. 사용자 레벨에 맞게 필터링
    filtered = [r for r in results if r.metadata['level'] == user_level]
    # 3. 가장 관련성 높은 상위 2개 반환
    return filtered[:2]

김개발 씨는 과제를 받고 흥분했습니다. 드디어 실전 프로젝트입니다.

요구사항은 이렇습니다. 학생이 "파이썬에서 리스트를 정렬하는 방법"을 물으면, AI가 단순히 설명만 하는 게 아니라 관련 코드 예제를 함께 보여줘야 합니다.

그것도 학생의 실력 수준에 맞춰서 말이죠. 박시니어 씨가 추가 요구사항을 설명합니다.

"초급 학생에게는 간단한 예제를, 고급 학생에게는 심화 예제를 보여줘야 해요. 그리고 예제는 항상 최신 버전의 코드여야 합니다." 그렇다면 동적 예제 선택 시스템은 어떻게 설계해야 할까요?

쉽게 비유하자면, 이 시스템은 마치 개인 맞춤형 과외 선생님과 같습니다. 학생이 "삼각형 넓이 구하기"를 어려워하면 쉬운 예제를 보여주고, "삼각함수 활용"을 물으면 심화 예제를 제시합니다.

핵심은 학생의 질문 의도실력 수준을 동시에 고려하는 것입니다. 정적 예제만 사용하던 시절에는 어떨까요?

전통적인 교육 플랫폼은 모든 학생에게 동일한 예제를 보여줬습니다. 초급 학생은 어려운 예제에 좌절하고, 고급 학생은 쉬운 예제에 지루함을 느꼈습니다.

더 큰 문제는 새로운 예제를 추가할 때마다 커리큘럼을 수동으로 재배치해야 했다는 것입니다. 확장성이 전혀 없었습니다.

바로 이런 문제를 해결하기 위해 동적 예제 선택 시스템이 필요합니다. 동적 시스템을 사용하면 질문 의도를 자동으로 파악해서 관련 예제를 찾습니다.

또한 난이도 메타데이터로 학생 수준에 맞는 예제만 필터링할 수 있습니다. 무엇보다 새 예제를 추가하면 즉시 검색 가능하다는 큰 이점이 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 코드 예제 데이터를 준비합니다.

각 예제는 코드 본문과 함께 주제, 난이도 메타데이터를 포함합니다. 다음으로 이 예제들을 벡터 DB에 저장합니다.

코드 자체가 임베딩되어 의미 검색이 가능해집니다. select_examples 함수는 사용자 질문으로 유사한 예제 10개를 먼저 찾습니다.

그 다음 사용자의 현재 레벨과 일치하는 예제만 필터링합니다. 마지막으로 관련성이 가장 높은 상위 2개를 반환해서 AI 답변에 포함시킵니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 프로그래머스, 백준 같은 코딩 테스트 플랫폼을 개발한다고 가정해봅시다.

사용자가 "문자열 처리"라고 검색하면 그의 과거 문제 풀이 이력을 분석해서 적절한 난이도의 예제를 추천합니다. 또한 사용자가 막힌 문제와 유사한 해설 예제를 자동으로 찾아서 힌트로 제공할 수 있습니다.

실제로 리트코드는 이런 방식으로 개인화된 학습 경험을 제공합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 너무 정확히 일치하는 예제만 찾으려는 것입니다. 이렇게 하면 검색 결과가 없을 수 있습니다.

따라서 유사한 주제의 예제도 포함하도록 검색 범위를 적절히 넓혀야 합니다. 또한 난이도 판정 기준을 명확히 해야 합니다.

코드 길이, 사용된 개념 수, 중첩 깊이 등을 조합해서 자동으로 난이도를 산정하는 것도 좋은 방법입니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

첫 번째 실습 과제를 완성한 김개발 씨는 뿌듯했습니다. "학생들이 자기 수준에 맞는 예제를 바로 볼 수 있겠어요!" 동적 예제 선택 시스템을 구축하면 개인화된 학습 경험을 자동으로 제공할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 예제 코드는 반드시 실행 가능한 완전한 코드로 저장하세요

  • 난이도는 초급/중급/고급 외에도 숫자(1~10)로 세밀하게 관리할 수 있습니다
  • 사용자 피드백(좋아요/싫어요)을 수집해서 예제 품질을 지속적으로 개선하세요

6. 실습 실시간 정보 주입

첫 번째 과제를 성공적으로 마친 김개발 씨에게 박시니어 씨가 두 번째 과제를 줍니다. "이번엔 주식 투자 상담 챗봇이에요.

실시간 주가, 뉴스, 재무제표를 동적으로 주입해서 답변해야 합니다." 김개발 씨는 긴장했습니다. 이번엔 외부 API와의 연동까지 필요한 고난도 과제입니다.

실시간 정보 주입은 외부 API에서 최신 데이터를 가져와서 즉시 LLM에 제공하는 기법입니다. 마치 기자가 뉴스 속보를 받아서 즉시 보도하는 것과 같습니다.

API 호출, 데이터 파싱, 캐싱 전략을 결합하여 지연 없이 신선한 정보를 주입할 수 있습니다.

다음 코드를 살펴봅시다.

import requests
from functools import lru_cache
from datetime import datetime

# 실시간 주가 조회 (API 호출)
@lru_cache(maxsize=100)  # 5분간 캐싱
def get_stock_price(ticker, cache_key=None):
    """주식 시세 실시간 조회 (5분 캐싱)"""
    url = f"https://api.stock.com/price/{ticker}"
    response = requests.get(url)
    data = response.json()
    return {
        'price': data['current_price'],
        'change': data['change_percent'],
        'timestamp': datetime.now()
    }

# 동적 컨텍스트 주입
def answer_stock_query(user_query):
    # 1. 질문에서 종목 코드 추출
    ticker = extract_ticker(user_query)  # 예: "삼성전자" -> "005930"
    # 2. 실시간 주가 조회 (5분마다 캐시 무효화)
    cache_key = datetime.now().strftime("%Y%m%d%H%M")[:11]  # 5분 단위
    stock_info = get_stock_price(ticker, cache_key)
    # 3. 컨텍스트 구성
    context = f"{ticker} 현재가: {stock_info['price']}원 ({stock_info['change']}%)"
    # 4. LLM에 주입
    prompt = f"컨텍스트: {context}\n\n질문: {user_query}"
    return llm.generate(prompt)

김개발 씨는 두 번째 과제 앞에서 막막했습니다. 주식 정보는 초 단위로 변합니다.

벡터 DB에 저장했다가는 이미 옛날 정보가 됩니다. 그렇다고 매 질문마다 API를 호출하면 비용이 폭발하고 응답 속도도 느려집니다.

어떻게 해야 할까요? 박시니어 씨가 힌트를 줍니다.

"API 호출과 캐싱의 균형을 찾아야 해요. 5분 정도는 같은 데이터를 재사용해도 괜찮지 않을까요?" 그렇다면 실시간 정보 주입 시스템은 어떻게 설계해야 할까요?

쉽게 비유하자면, 실시간 정보 주입은 마치 날씨 앱과 같습니다. 사용자가 앱을 열 때마다 기상청 서버에 요청하면 서버가 과부하됩니다.

대신 5~10분마다 한 번씩 데이터를 가져와서 잠시 저장해뒀다가 재사용합니다. 날씨는 5분 안에 크게 안 바뀌니까요.

주식 정보도 마찬가지로 적절한 캐싱 전략이 핵심입니다. 실시간 주입 없이 정적 데이터만 사용하던 시절에는 어떨까요?

투자 상담 서비스는 하루 전 종가 데이터로 답변했습니다. 아침에 주가가 급등했는데도 AI는 어제 가격을 기준으로 조언했습니다.

투자자들은 "이 봇은 쓸모없네"라며 떠났습니다. 더 큰 문제는 속보성 뉴스를 전혀 반영하지 못했다는 것입니다.

긴급 공시가 나와도 AI는 알 수 없었습니다. 바로 이런 문제를 해결하기 위해 실시간 API 연동 기법이 필요합니다.

실시간 연동을 사용하면 최신 시장 데이터를 즉시 반영할 수 있습니다. 또한 캐싱 레이어로 불필요한 API 호출을 줄여서 비용을 절감할 수 있습니다.

무엇보다 TTL 기반 캐시 무효화로 신선도와 효율성의 균형을 맞출 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 lru_cache 데코레이터로 함수 결과를 메모리에 캐싱합니다. 같은 인자로 호출하면 API를 다시 호출하지 않고 캐시된 값을 반환합니다.

다음으로 cache_key를 5분 단위 타임스탬프로 생성합니다. 5분이 지나면 키가 바뀌어서 자동으로 새로 호출됩니다.

get_stock_price 함수는 외부 API에서 현재가와 등락률을 가져옵니다. 마지막으로 이 실시간 정보를 컨텍스트 문자열로 만들어서 프롬프트에 포함시킵니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 배달앱 챗봇을 개발한다고 가정해봅시다.

사용자가 "치킨 주문하고 싶어"라고 하면 현재 위치 기준 실시간 배달 가능한 매장을 API로 조회합니다. 재고, 영업 시간, 배달 소요 시간까지 실시간으로 가져와서 "A치킨은 30분 배달 가능, B치킨은 품절"처럼 정확한 정보를 제공합니다.

배달의민족, 쿠팡이츠가 이런 방식으로 작동합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 캐시 없이 매번 API 호출하는 것입니다. 이렇게 하면 외부 API의 rate limit에 걸려서 차단당할 수 있습니다.

따라서 캐싱을 반드시 적용하되, 데이터 특성에 맞게 TTL을 설정하세요. 주가는 5분, 날씨는 30분, 환율은 1시간처럼 데이터마다 적절한 간격이 다릅니다.

또한 API 호출 실패 시 폴백 전략도 필요합니다. 캐시된 옛날 데이터라도 보여주거나, "현재 정보를 가져올 수 없습니다"라고 명확히 안내해야 합니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 실시간 정보 주입 시스템을 완성한 김개발 씨는 테스트를 돌려봤습니다.

"삼성전자 주가 알려줘"라고 물으니 정확히 현재 시세가 나왔습니다. "성공했어요!" 실시간 정보 주입 시스템을 구축하면 AI가 항상 최신 데이터로 정확한 답변을 할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 캐싱 TTL은 데이터 변경 주기보다 약간 짧게 설정하세요

  • API rate limit을 고려해서 캐시를 적극 활용하세요
  • 외부 API 장애에 대비한 폴백 전략을 반드시 준비하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#LLM#DynamicContext#RAG#ContextInjection#VectorDB#LLM,동적컨텍스트,RAG

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.