앞 게시글에서 LangChain의 개요에 대해 간략하게 글을 적었다.
이번 포스트에서는 LangChain 라이브러리의 여러 주요 컴포넌트 중
Prompt, LLM, Parser에 대해 작성해본다.
LangChain(랭체인)은 대규모 언어 모델(LLM)을 활용한 애플리케이션 개발에 특화된 오픈소스 프레임워크이다. LangChain은 다양한 외부 데이터 소스와 통합하여 보다 복잡하고 유용한 애플리케이션을 만들 수 있도록 설계되었다.
LangChain은 대규모 언어 모델(LLM)을 활용하여 AI 기반 응용 프로그램을 구축할 때 유용한 다양한 컴포넌트를 제공한다.
이 중 Prompt, LLM, Parser는 핵심적인 역할을 하며, 이들의 역할과 사용 방법을 이해하면 효과적인 LangChain 체인을 구성할 수 있다.
[ 개념 ]
Prompt는 LLM이 응답을 생성하는 데 필요한 입력 텍스트를 정의하는 템플릿이다.
LangChain에서는 Prompt를 보다 구조적으로 관리할 수 있도록 다양한 기능을 제공한다.
[ Prompt의 역할 ]
[ **LangChain의 Prompt 템플릿 종류 ]**
PromptTemplate{변수} 형식을 사용하여 동적인 입력을 반영 가능ChatPromptTemplateFewShotPromptTemplate[ Prompt 코드 예시 ]
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_variables와 template을 명시적으로 지정하여 객체를 생성하고, 형식 검사를 강화할 수 있다.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="바나나"))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: chat과 messages 구조를 사용한다.# 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="오늘 날씨 어때?")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
)[ 개념 ]
LLM은 LangChain의 핵심 AI 엔진으로, Prompt를 입력받아 응답을 생성하는 역할을 한다.
LangChain은 다양한 LLM을 쉽게 연결할 수 있도록 추상화된 인터페이스를 제공한다.
[ LLM의 역할 ]
[ **LangChain에서 지원하는 주요 LLM ]**
OpenAI GPT
gpt-4o, gpt-4-turbo, gpt-3.5-turbotemperature, max_tokens 등을 통해 응답 품질 제어| 모델명 | 특징 | Context 길이 | Max Token |
|---|---|---|---|
| gpt-4o | 다중모드 지원, 빠름, 저렴 | 128K | 4,096 |
| gpt-4-turbo | 고성능 GPT-4 변형 | 128K | 4,096 |
| gpt-4o-mini | 빠르고 가벼운 GPT-4 | 128K | 16,384 |
| o1-preview / o1-mini | 추론, 수학, 코딩 특화 | 128K | 32K~65K |
LangChain 연동 모듈: langchain_openai.ChatOpenAI
Anthropic (Claude 시리즈)
Claude 3 Opus, Claude 3.5 Sonnet, Claude 3 Haiku| 모델명 | 특징 | 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
Cohere
Command R+: 128K 토큰, 검색강화, 다국어Aya: 101개 언어 지원 오픈소스 모델LangChain 연동 모듈: langchain_cohere.ChatCohere
Upstage
Solar, Solar-ProLangChain 연동 모듈: langchain_upstage.ChatUpstage
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(): LLM 응답을 토큰 단위로 실시간 출력하는 메서드
streaming=True: LLM 모델이 스트리밍 응답을 지원하도록 설정하는 옵션
즉, stream()은 실행 방식, streaming은 모델 설정입니다.
❓ Q. streaming=True만 쓰면 스트리밍이 되나요?
⛔ 아니요. 반드시
stream()메서드를 함께 써야 실제로 토큰 단위로 응답을 받을 수 있습니다.
❓ Q. invoke()에서 streaming=True를 쓰면 무슨 효과가 있나요?
✅ OpenAI 백엔드에서 스트리밍으로 응답을 받아 LangChain 내부에서
invoke()결과로 조립해줄 수는 있지만, 사용자 입장에선 한 번에 받는 것이라 의미 없습니다.
[Cache 설정]
LLM의 캐시는 동일한 입력에 대해 매번 모델을 호출하지 않고, 이전에 생성된 응답을 저장해 두었다가 즉시 재사용하는 기능이다.
같은 입력이 들어올 때마다 결과가 달라지는 것을 방지하여 응답 일관성을 유지하여, 속도 향상 및 중복된 요청을 줄여 API호출 최적화로 비용을 절감할 수 있다. 채팅봇, 검색 서비스 같이 실시간성 보장이 필요한 곳에 빠른 응답 시간을 보장할 수 있다.
⇒ 캐시는 성능 최적화, 비용 절감, 일관성 있는 응답을 위해 필수적인 기능이다.
LangChain은 아래와 같은 캐시 구현 방식을 통해 캐시 백엔드를 기본 제공하며, set_llm_cache()함수로 설정할 수 있다.
InMemoryCache
from langchain.globals import set_llm_cache
from langchain.cache import InMemoryCache
set_llm_cache(InMemoryCache())
SQLiteCache
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"))
캐시 성능 비교 예시
| 입력 | 실행 시간 | 설명 |
|---|---|---|
"튀르키에" (최초 요청) | 2.51초 | LLM 모델 호출 및 응답 생성 |
"튀르키에" (캐시 적용) | 1.8ms | InMemoryCache에서 즉시 반환 |
"이집트" (SQLite 적용) | 3ms | SQLite DB에서 응답 조회 |
캐시가 없다면 매번 수초의 시간이 소요되지만, 캐시 덕분에 수 ms 내 응답 가능해집니다.
[ 모델 직렬화(Serialization) ]
[ 토큰 사용량 ]
[ 개념 ]
Parser는 LLM의 출력을 가공하여 원하는 형태로 변환하는 기능을 수행한다.
LangChain에서 생성된 응답을 구조화된 데이터(텍스트, JSON, 리스트, 숫자 등)로 변환하는 데 사용된다.
[ Parser의 역할 ]
[ LangChain에서 제공하는 주요 Parser ]
StrOutputParser : 기본적으로 LLM의 출력을 문자열 그대로 반환JsonOutputParser : JSON 형태의 출력을 생성하고 파싱RegexParser : 정규 표현식을 활용해 특정 패턴을 추출CommaSeparatedListOutputParser : 출력 값을 쉼표로 구분된 리스트로 변환본 게시글은 https://wikidocs.net/book/14314 을 인용하였습니다.