[LangChain] Prompt, LLM, Parser

zzin·2025년 4월 23일

GAI

목록 보기
2/2

앞 게시글에서 LangChain의 개요에 대해 간략하게 글을 적었다.
이번 포스트에서는 LangChain 라이브러리의 여러 주요 컴포넌트 중
Prompt, LLM, Parser에 대해 작성해본다.

LangChain

LangChain(랭체인)은 대규모 언어 모델(LLM)을 활용한 애플리케이션 개발에 특화된 오픈소스 프레임워크이다. LangChain은 다양한 외부 데이터 소스와 통합하여 보다 복잡하고 유용한 애플리케이션을 만들 수 있도록 설계되었다.

LangChain은 대규모 언어 모델(LLM)을 활용하여 AI 기반 응용 프로그램을 구축할 때 유용한 다양한 컴포넌트를 제공한다.

이 중 Prompt, LLM, Parser는 핵심적인 역할을 하며, 이들의 역할과 사용 방법을 이해하면 효과적인 LangChain 체인을 구성할 수 있다.

1. Prompt

[ 개념 ]

Prompt는 LLM이 응답을 생성하는 데 필요한 입력 텍스트를 정의하는 템플릿이다.

LangChain에서는 Prompt를 보다 구조적으로 관리할 수 있도록 다양한 기능을 제공한다.

[ Prompt의 역할 ]

  • 질문을 구조화하여 일관된 입력 제공
  • LLM이 이해할 수 있는 형태로 가공하여 적절한 응답 유도
  • 템플릿화하여 다양한 입력을 자동으로 적용 가능

[ **LangChain의 Prompt 템플릿 종류 ]**

  1. PromptTemplate
    • 기본적인 프롬프트 템플릿
    • {변수} 형식을 사용하여 동적인 입력을 반영 가능
  2. ChatPromptTemplate
    • 대화형 AI 개발을 위한 프롬프트
    • OpenAI와 같은 Chat 기반 LLM과 함께 사용
  3. FewShotPromptTemplate
    • 여러 개의 예제(Few-shot learning)를 포함하는 템플릿
    • LLM이 문맥을 학습하도록 도움

[ Prompt 코드 예시 ]

  1. PromptTemplate

    • from_template() 메서드 사용
      템플릿 문자열에서 변수명을 중괄호 {}로 감싸 정의하고, from_template() 메서드를 사용하여 PromptTemplate 객체를 생성한다.
      ```python
      from langchain_core.prompts import PromptTemplate
      
      template = "{country}의 수도는 어디인가요?"
      prompt = PromptTemplate.from_template(template)
      
      print(prompt.format(country="대한민국"))
      # 대한민국의 수도는 어디인가요?
      ```
    • PromptTemplate 객체 직접 생성 input_variablestemplate을 명시적으로 지정하여 객체를 생성하고, 형식 검사를 강화할 수 있다.
      prompt = PromptTemplate(
          input_variables=["fruit"],
          template="{fruit}의 색깔은 무엇인가요?"
      )
      
      print(prompt.format(fruit="사과"))
    • partial_variables 부분 변수 채우기 일부 변수를 고정시켜 재사용 가능한 프롬프트를 만들 수 있다. 주로 날짜, 사용자명 등 고정값에 활용된다.
      from datetime import datetime
      
      def get_today():
          return datetime.now().strftime("%Y-%m-%d")
      
      prompt = PromptTemplate(
          input_variables=["task"],
          partial_variables={"today": get_today},
          template="오늘은 {today}입니다. 해야 할 일은 {task}입니다."
      )
      
      print(prompt.format(task="보고서 작성"))
    • load_prompt() 템플릿 불러오기 프롬프트 구성을 YAML 파일에 저장하고 load_prompt() 를 통해 불러올 수 있다. 코드와 텍스트를 분리해 관리할 수 있다.
      # prompts/fruit_color.yaml
      _type: prompt
      input_variables:
        - fruit
      template: "{fruit}의 색깔은 무엇인가요?"
      from langchain_core.prompts import load_prompt
      
      prompt = load_prompt("prompts/fruit_color.yaml")
      print(prompt.format(fruit="바나나"))
  2. ChatPromptTemplate

    • from_messages()로 대화형 프롬프트 구성 대화의 각 메시지를 역할별로 정의하고 순차적으로 배열하여 구성할 수 있다. 시스템, 사용자, AI 역할을 구분한다.
      from langchain_core.prompts import ChatPromptTemplate
      
      chat_prompt = ChatPromptTemplate.from_messages([
          ("system", "당신은 친절한 AI 어시스턴트입니다."),
          ("human", "당신의 이름은 무엇인가요?")
      ])
      
      messages = chat_prompt.format_messages()
      print(messages)
    • 다양한 역할 메시지 추가 (system, human, ai) 시스템/사용자/AI 메시지를 구분하여 시나리오 기반의 대화 흐름을 구성할 수 있다.
      chat_prompt = ChatPromptTemplate.from_messages([
          ("system", "당신은 요리 전문가입니다."),
          ("human", "김치찌개를 만들려면 어떻게 해야 하나요?"),
          ("ai", "먼저 돼지고기를 볶고..."),
          ("human", "된장은 꼭 넣어야 하나요?")
      ])
    • partial()로 부분 변수 고정 PromptTemplate 과 마찬가지로 partial() 을 통해 일부 변수 값을 고정할 수 있다.
      chat_prompt = ChatPromptTemplate.from_messages([
          ("system", "당신은 {role}입니다."),
          ("human", "{question}")
      ])
      
      partial_prompt = chat_prompt.partial(role="AI 요약 어시스턴트")
      messages = partial_prompt.format_messages(question="요약해 주세요.")
    • MessagesPlaceholder로 이전 대화 삽입 MessagesPlaceholder는 LangChain의 ChatPromptTemplate에서 이전 대화 내용을 삽입할 자리를 만들어주는 컴포넌트이다. 이걸 사용하면 지금까지 사용자와 AI가 주고받은 대화 기록을 그대로 프롬프트에 포함시킬 수 있다. 하지만 여기서 짚고 넘어가야할 건 아래와 같다.

      ❗️MessagesPlaceholder는 실제로 대화 내용을 기억하지 않는다.

      컴포넌트 이름처럼 단순히 "이 위치에 과거 대화를 넣어줘"라는 구조만 만들어줄 뿐이며, 실제 대화 내용은 사용자가 직접 conversation=[...]로 넘기거나, Memory를 사용해서 자동으로 관리해야 한다.

      따라서 MessagesPlaceholder를 사용할 경우, 다음 중 하나는 반드시 수행해야 합니다.
      • 직접 conversation 리스트를 관리하고 넘겨주기 (수동 방식)

      • LangChain의 ConversationBufferMemory 같은 메모리 컴포넌트를 이용해 대화 상태 자동 관리하기 (자동 방식) → 이건 Memory에서 좀 더 자세히 다뤄보겠다

        from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
        
        # 1. 대화 요약 프롬프트 정의
        chat_prompt = ChatPromptTemplate.from_messages([
            ("system", "당신은 대화 요약 전문가입니다."),
            MessagesPlaceholder(variable_name="conversation"),  # 대화 내용이 삽입될 자리
            ("human", "지금까지 대화를 한 문장으로 요약해주세요.")
        ])
        
        # 2. 이전 대화 내용 수동 정의
        conversation = [
            ("human", "안녕하세요, 오늘 일정 알려주세요."),
            ("ai", "회의는 오후 2시입니다."),
            ("human", "회의 장소는 어디인가요?"),
            ("ai", "회의실 A에서 진행됩니다.")
        ]
        
        # 3. 전체 메시지 구성
        formatted_messages = chat_prompt.format_messages(
            conversation=conversation  # 직접 넘겨야 합니다!
        )
        
        # 4. 출력 확인
        for msg in formatted_messages:
            print(f"{msg.type.upper()} >> {msg.content}")

        출력 예시

        css
        복사편집
        SYSTEM >> 당신은 대화 요약 전문가입니다.
        HUMAN >> 안녕하세요, 오늘 일정 알려주세요.
        AI >> 회의는 오후 2시입니다.
        HUMAN >> 회의 장소는 어디인가요?
        AI >> 회의실 A에서 진행됩니다.
        HUMAN >> 지금까지 대화를 한 문장으로 요약해주세요.
    • load_prompt()로 YAML 템플릿 불러오기 대화형 프롬프트도 YAML 파일로 저장하고 불러올 수 있다. _type: chatmessages 구조를 사용한다.
      # prompts/chat_prompt.yaml
      _type: chat
      input_variables:
        - name
        - user_input
      messages:
        - role: system
          content: "당신은 AI 비서이며 이름은 {name}입니다."
        - role: human
          content: "{user_input}"
      from langchain_core.prompts import load_prompt
      
      chat_prompt = load_prompt("prompts/chat_prompt.yaml")
      messages = chat_prompt.format_messages(name="테디", user_input="오늘 날씨 어때?")
  3. FewShotPromptTemplate

    • examples를 직접 코드에 정의하여 사용 예시 데이터를 코드 내에 직접 정의하고, 이를 본문에 삽입하는 방식이다. 소규모 예제에 적합하다.
    • ExamplePrompt로 예시 포맷 정의 예시 데이터를 어떻게 포맷할지를 정의하는 PromptTemplate 객체를 별도로 설정한다.
    • prefix, suffix 구성으로 본문 완성
    from langchain.prompts import FewShotPromptTemplate, PromptTemplate
    
    examples = [
        {"fruit": "사과", "color": "빨강"},
        {"fruit": "바나나", "color": "노랑"},
    ]
    
    example_prompt = PromptTemplate(
        input_variables=["fruit", "color"],
        template="{fruit}의 색깔은 {color}입니다."
    )
    
    prompt = FewShotPromptTemplate(
        examples=examples,
        example_prompt=example_prompt,
        prefix="아래는 과일과 그 색깔 예시입니다:\n",
        suffix="{fruit}의 색깔은?",
        input_variables=["fruit"]
    )
    
    print(prompt.format(fruit="포도"))
    • example_selector로 동적 샷 선택 입력과 유사한 예제를 자동으로 선택하여 few-shot 구성에 활용할 수 있다. 예제 선택 로직을 포함한다.
      from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
      from langchain.vectorstores import FAISS
      from langchain.embeddings import OpenAIEmbeddings
      
      example_selector = SemanticSimilarityExampleSelector.from_examples(
          examples,
          OpenAIEmbeddings(),
          FAISS,
          k=1
      )

2. LLM

[ 개념 ]

LLM은 LangChain의 핵심 AI 엔진으로, Prompt를 입력받아 응답을 생성하는 역할을 한다.

LangChain은 다양한 LLM을 쉽게 연결할 수 있도록 추상화된 인터페이스를 제공한다.

[ LLM의 역할 ]

  • 프롬프트를 기반으로 자연어 처리(NLP) 작업 수행
  • 검색된 문서 또는 데이터베이스 정보를 활용하여 RAG 기반 응답 생성
  • 체인의 다른 요소(예: retriever, parser)와 결합하여 복합적인 AI 기능 구현

[ **LangChain에서 지원하는 주요 LLM ]**

  1. OpenAI GPT

    • 대표 모델: gpt-4o, gpt-4-turbo, gpt-3.5-turbo
    • 특징:
      • 128K context 지원
      • 다양한 모드 지원: JSON 모드, 함수 호출, 비전 입력 등
      • temperature, max_tokens 등을 통해 응답 품질 제어
    모델명특징Context 길이Max Token
    gpt-4o다중모드 지원, 빠름, 저렴128K4,096
    gpt-4-turbo고성능 GPT-4 변형128K4,096
    gpt-4o-mini빠르고 가벼운 GPT-4128K16,384
    o1-preview / o1-mini추론, 수학, 코딩 특화128K32K~65K

    LangChain 연동 모듈: langchain_openai.ChatOpenAI

  2. Anthropic (Claude 시리즈)

    • 대표 모델: Claude 3 Opus, Claude 3.5 Sonnet, Claude 3 Haiku
    • 특징:
      • 200K 토큰의 긴 문맥 처리
      • 자연어 추론, 코딩, 다국어 지원에 특화
      • 빠른 속도 및 유연한 비용 옵션 제공
    모델명특징Context 길이입력/출력 비용
    Claude 3 Opus최고 성능, 복잡한 작업 최적화200K$15 / $75
    Claude 3.5 Sonnet빠르고 효율적인 성능200K$3 / $15
    Claude 3 Haiku초고속 반응, 실시간 앱에 적합200K$0.25 / $1.25

    LangChain 연동 모듈: langchain_anthropic.ChatAnthropic

  3. Cohere

    • 대표 모델:
      • Command R+: 128K 토큰, 검색강화, 다국어
      • Aya: 101개 언어 지원 오픈소스 모델
    • 특징:
      • 기업용 AI 모델 제공
      • 멀티랭귀지, 오픈 데이터셋 기반

    LangChain 연동 모듈: langchain_cohere.ChatCohere

  4. Upstage

    • 대표 모델: Solar, Solar-Pro
    • 특징:
      • 한국 스타트업의 자체 LLM
      • 고속, 비용 효율적, 실시간 챗봇 AskUp 서비스 제공

    LangChain 연동 모듈: langchain_upstage.ChatUpstage

  5. Hugging Face Transformers (BERT, BLOOM, T5 등)

[ LLM 설정 주요 파라미터 ]

파라미터명간략 설명
temperature응답의 창의성 조절. 높을수록 다양하고 창의적인 결과 생성
max_tokens한 번에 생성할 응답의 최대 길이 (토큰 기준) 설정
top_p확률 누적 상위 p% 후보 중 무작위로 선택하는 샘플링 기법
frequency_penalty같은 단어나 문장의 반복 생성 빈도를 낮춤
presence_penalty새로운 주제나 단어가 등장하도록 유도
model_name사용할 LLM 모델 이름 지정 (예: gpt-4o, claude-3.5)
streaming응답을 실시간으로 스트리밍 출력할지 여부 설정
tools계산기, 검색기 등 외부 툴을 연결하여 사용할 수 있도록 설정
seed결과 재현성을 위한 난수 시드 설정 (일관된 출력용)
  • stream() vs streaming
    • stream(): LLM 응답을 토큰 단위로 실시간 출력하는 메서드

    • streaming=True: LLM 모델이 스트리밍 응답을 지원하도록 설정하는 옵션

      즉, stream()은 실행 방식, streaming은 모델 설정입니다.

      ❓ Q. streaming=True만 쓰면 스트리밍이 되나요?

      아니요. 반드시 stream() 메서드를 함께 써야 실제로 토큰 단위로 응답을 받을 수 있습니다.

      ❓ Q. invoke()에서 streaming=True를 쓰면 무슨 효과가 있나요?

      ✅ OpenAI 백엔드에서 스트리밍으로 응답을 받아 LangChain 내부에서 invoke() 결과로 조립해줄 수는 있지만, 사용자 입장에선 한 번에 받는 것이라 의미 없습니다.

[Cache 설정]

LLM의 캐시는 동일한 입력에 대해 매번 모델을 호출하지 않고, 이전에 생성된 응답을 저장해 두었다가 즉시 재사용하는 기능이다.

같은 입력이 들어올 때마다 결과가 달라지는 것을 방지하여 응답 일관성을 유지하여, 속도 향상 및 중복된 요청을 줄여 API호출 최적화로 비용을 절감할 수 있다. 채팅봇, 검색 서비스 같이 실시간성 보장이 필요한 곳에 빠른 응답 시간을 보장할 수 있다.
⇒ 캐시는 성능 최적화, 비용 절감, 일관성 있는 응답을 위해 필수적인 기능이다.

LangChain은 아래와 같은 캐시 구현 방식을 통해 캐시 백엔드를 기본 제공하며, set_llm_cache()함수로 설정할 수 있다.

  1. InMemoryCache

    • 설명: 가장 단순한 메모리 기반 캐시
    • 특징:
      • 실행 중인 파이썬 프로세스 내에서 메모리에 저장
      • 휘발성: 세션 종료 시 캐시 삭제
      • 빠른 응답: 디스크 접근이 없어 응답 속도가 매우 빠름
    • 활용 예시:
      • 실험/테스트 환경
      • 단기 세션 기반 앱
    from langchain.globals import set_llm_cache
    from langchain.cache import InMemoryCache
    
    set_llm_cache(InMemoryCache())
  2. SQLiteCache

    • 설명: SQLite 기반의 영구 저장형 캐시
    • 특징:
      • 파일 기반으로 캐시를 저장하여 프로그램 종료 후에도 데이터 유지
      • 소규모 서비스에서 간단한 DB 형태로 캐시 관리 가능
    • 활용 예시:
      • 장기 운영되는 서비스
      • LLM 호출 횟수 절감이 중요한 시스템
    from langchain_community.cache import SQLiteCache
    import os
    
    if not os.path.exists("cache"):
        os.makedirs("cache")
    
    set_llm_cache(SQLiteCache(database_path="cache/llm_cache.db"))
  1. 캐시 성능 비교 예시

    입력실행 시간설명
    "튀르키에" (최초 요청)2.51초LLM 모델 호출 및 응답 생성
    "튀르키에" (캐시 적용)1.8msInMemoryCache에서 즉시 반환
    "이집트" (SQLite 적용)3msSQLite DB에서 응답 조회

    캐시가 없다면 매번 수초의 시간이 소요되지만, 캐시 덕분에 수 ms 내 응답 가능해집니다.

[ 모델 직렬화(Serialization) ]

[ 토큰 사용량 ]

3. Parser

[ 개념 ]

Parser는 LLM의 출력을 가공하여 원하는 형태로 변환하는 기능을 수행한다.

LangChain에서 생성된 응답을 구조화된 데이터(텍스트, JSON, 리스트, 숫자 등)로 변환하는 데 사용된다.

[ Parser의 역할 ]

  • LLM이 생성한 자연어 응답을 정제하여 가독성을 높임
  • JSON, 리스트 등 특정 포맷으로 변환하여 후처리 용이
  • 체인의 다른 컴포넌트와 연계하여 응답을 최적화

[ LangChain에서 제공하는 주요 Parser ]

  1. StrOutputParser : 기본적으로 LLM의 출력을 문자열 그대로 반환
  2. JsonOutputParser : JSON 형태의 출력을 생성하고 파싱
  3. RegexParser : 정규 표현식을 활용해 특정 패턴을 추출
  4. CommaSeparatedListOutputParser : 출력 값을 쉼표로 구분된 리스트로 변환
📌

본 게시글은 https://wikidocs.net/book/14314 을 인용하였습니다.

0개의 댓글