목차
- 오늘 강의 개요
- 환경 설정 & 라이브러리 import
- Few-Shot 프롬프팅 - 기본 개념 복습
- FewShotPromptTemplate - LangChain 구현
- FewShotChatMessagePromptTemplate - 채팅 기반 구현
- 동적 예시 선택 (SemanticSimilarityExampleSelector)
- Few-Shot 성능 평가 (Zero-Shot vs Few-Shot)
- 평가 지표 개요 (Metrics)
- LLM의 추론 한계 - 문제 사례
- Chain-of-Thought (CoT) 프롬프팅
- Few-Shot CoT 심화 기법
- 자주 나오는 실수 / 주의사항
- [보충] 프롬프트 설계 전략 정리
1. 오늘 강의 개요
오늘 강의는 어제 배운 Few-Shot 프롬프팅을 LangChain 컴포넌트로 구현하고, 예시를 동적으로 선택하는 방법, 그리고 LLM의 추론 능력을 끌어올리는 Chain-of-Thought(CoT) 기법까지 심화 학습한다.
핵심 키워드: FewShotPromptTemplate, SemanticSimilarityExampleSelector, CoT
전체 학습 흐름
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| Few-Shot 기본 (고정 예시)
↓
FewShotPromptTemplate (LangChain 컴포넌트화)
↓
FewShotChatMessagePromptTemplate (채팅 형식)
↓
SemanticSimilarityExampleSelector (동적 예시 선택)
↓
Zero-Shot vs Few-Shot 성능 평가
↓
LLM 추론 한계 확인 (논리 퍼즐, 글자 세기)
↓
Chain-of-Thought (CoT) → 추론 능력 향상
↓
Few-Shot CoT (예시 + 단계별 사고)
|
핵심 원칙: 예시를 “잘 선택”하는 것이 Few-Shot 성능을 결정한다. 무조건 많은 예시보다, 관련성 높은 예시가 더 효과적이다.
2. 환경 설정 & 라이브러리 import
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from langchain_core.prompts import (
FewShotPromptTemplate,
PromptTemplate,
ChatPromptTemplate,
FewShotChatMessagePromptTemplate
)
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_core.vectorstores import InMemoryVectorStore
load_dotenv()
llm = ChatOpenAI(model="gpt-4o-mini")
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
|
| 컴포넌트 | 역할 |
|---|
ChatOpenAI | gpt-4o-mini LLM 인스턴스 |
OpenAIEmbeddings | 텍스트 → 벡터 변환 (예시 선택에 사용) |
FewShotPromptTemplate | 고정 예시 기반 프롬프트 생성 |
FewShotChatMessagePromptTemplate | 채팅 형식의 Few-Shot 프롬프트 |
SemanticSimilarityExampleSelector | 의미 유사도 기반 동적 예시 선택 |
InMemoryVectorStore | 예시를 메모리 내 벡터 DB에 저장 |
3. Few-Shot 프롬프팅 - 기본 개념 복습
예시(examples) 정의
1
2
3
4
5
6
| examples = [
{"input": "이 제품 정말 최고입니다!", "output": "긍정"},
{"input": "품질이 너무 안좋아요", "output": "부정"},
{"input": "보통이에요, 무난합니다.", "output": "중립"},
{"input": "디자인은 좋은데 성능이 아쉬워요", "output": "혼합"}
]
|
왜 이 구조인가? input/output 키-값 쌍으로 예시를 딕셔너리로 정의하면, LangChain의 FewShotPromptTemplate이 자동으로 포맷해준다.
예시 선택의 3원칙 (주석에서 언급됨)
- 다양성: 카테고리별로 골고루 예시 제공
- 관련성: 입력과 유사한 예시가 더 효과적
- 균형: 특정 레이블에 편향되지 않게 배분
4. FewShotPromptTemplate - LangChain 구현
4-1. 예시 포맷 템플릿 정의
1
2
3
4
| example_template = PromptTemplate(
input_variables=["input", "output"],
template="리뷰: {input}\n감정: {output}"
)
|
PromptTemplate: 예시 하나를 어떻게 텍스트로 표현할지 정의{input}, {output}: 예시 딕셔너리의 키와 매핑됨
4-2. FewShotPromptTemplate 구성
1
2
3
4
5
6
7
8
| fewshot_prompt = FewShotPromptTemplate(
examples=examples, # 예시 리스트
example_prompt=example_template, # 예시 포맷
prefix='다음 예시를 참고해서 리뷰의 감정을 분류해주세요.\n',
suffix='\n리뷰: {input}\n감정:', # 실제 입력 포맷
input_variables=['input'],
example_separator='\n\n' # 예시 사이 구분자
)
|
4-3. 프롬프트 구조 시각화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| [prefix]
다음 예시를 참고해서 리뷰의 감정을 분류해주세요.
[example 1]
리뷰: 이 제품 정말 최고입니다!
감정: 긍정
[example 2]
리뷰: 품질이 너무 안좋아요
감정: 부정
... (example_separator='\n\n' 로 구분)
[suffix]
리뷰: {실제 입력}
감정:
|
1
2
3
| # 포맷 확인
formatted = fewshot_prompt.format(input='가격은 비싸지만 품질이 뛰어나요')
print(formatted)
|
5. FewShotChatMessagePromptTemplate - 채팅 기반 구현
5-1. 채팅용 예시 템플릿
1
2
3
4
| example_prompt_chat = ChatPromptTemplate.from_messages([
('human', '리뷰 : {input}'),
('ai', '감정: {output}')
])
|
왜 채팅 형식인가? ChatGPT 계열 모델은 messages 리스트로 대화를 받는다. Human/AI 턴으로 예시를 구성하면 모델이 더 자연스럽게 패턴을 인식한다.
5-2. FewShotChatMessagePromptTemplate
1
2
3
4
| fewshot_chat_prompt = FewShotChatMessagePromptTemplate(
examples=examples,
example_prompt=example_prompt_chat
)
|
5-3. 최종 프롬프트 + 체인 구성
1
2
3
4
5
6
7
8
9
10
| final_prompt = ChatPromptTemplate.from_messages([
('system', '당신은 감정 분석 전문가입니다. 리뷰의 감정을 긍정/부정/중립/혼합 중 하나로 분류해주세요'),
fewshot_chat_prompt, # Few-Shot 예시 삽입
('human', '리뷰 : {input}')
])
chain = final_prompt | llm
result = chain.invoke({'input': '가격은 비싸지만 품질이 뛰어나요'})
print(result.content)
# 출력: 감정: 혼합
|
프롬프트 메시지 구조
1
2
3
4
5
6
7
| [system] 당신은 감정 분석 전문가입니다...
[human] 리뷰 : 이 제품 정말 최고입니다!
[ai] 감정: 긍정
[human] 리뷰 : 품질이 너무 안좋아요
[ai] 감정: 부정
...
[human] 리뷰 : {실제 입력} ← 실제 질문
|
6. 동적 예시 선택 (SemanticSimilarityExampleSelector)
6-1. 왜 동적 선택이 필요한가?
고정 예시 방식의 문제:
- 예시가 많으면 → 토큰 비용 증가, 컨텍스트 초과
- 관련 없는 예시 포함 → 오히려 성능 저하
- 새로운 도메인 입력에 대응 어려움
해결책: 입력과 의미적으로 유사한 예시만 자동 선택
6-2. 확장 예시 데이터셋
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| large_examples = [
# 긍정
{"input": "정말 만족합니다. 강력 추천!", "output": "긍정"},
{"input": "배송이 빠르고 포장이 꼼꼼해요.", "output": "긍정"},
{"input": "가격 대비 훌륭합니다.", "output": "긍정"},
# 부정
{"input": "품질이 최악입니다. 환불 요청했어요.", "output": "부정"},
{"input": "고객센터 응대가 너무 불친절합니다.", "output": "부정"},
{"input": "택배가 분실되었어요.", "output": "부정"},
# 중립
{"input": "보통이에요. 특별한 점은 없습니다.", "output": "중립"},
{"input": "사진과 동일한 제품입니다.", "output": "중립"},
# 혼합
{"input": "디자인은 예쁜데 내구성이 약해요.", "output": "혼합"},
{"input": "기능은 많은데 사용법이 복잡합니다.", "output": "혼합"},
]
|
6-3. SemanticSimilarityExampleSelector 생성
1
2
3
4
5
6
| example_selector = SemanticSimilarityExampleSelector.from_examples(
large_examples,
OpenAIEmbeddings(model="text-embedding-3-small"), # 임베딩 모델
InMemoryVectorStore, # 벡터 저장소
k=3 # 상위 k개 예시 선택
)
|
동작 원리:
1
2
3
4
5
6
7
| 입력 텍스트
↓ (임베딩)
입력 벡터
↓ (코사인 유사도 계산)
large_examples 벡터들과 비교
↓
유사도 높은 k=3개 예시 반환
|
6-4. 동적 선택 결과 확인
1
2
3
4
5
6
7
8
9
10
11
| queries = [
'배송이 너무 느려요', # → 배송 관련 부정 예시 선택
'값은 좀 나가지만 성능은 훌륭해요', # → 혼합 관련 예시 선택
'무난한 제품이에요' # → 중립 관련 예시 선택
]
for q in queries:
selected = example_selector.select_examples({'input': q})
print(f"query : {q}")
for s in selected:
print(f" selected : {s['output']}, {s['input']}")
|
쿼리에 따라 자동으로 관련성 높은 예시가 선택된다.
6-5. 동적 Few-Shot 체인 구성
1
2
3
4
5
6
7
8
9
10
11
12
| dynamic_few_shot_prompt = FewShotChatMessagePromptTemplate(
example_selector=example_selector, # 고정 examples 대신 selector 사용
example_prompt=example_prompt_chat
)
dynamic_final_prompt = ChatPromptTemplate.from_messages([
('system', '당신은 감정 분석 전문가입니다. 리뷰의 감정을 긍정/부정/중립/혼합 중 하나로 분류하세요'),
dynamic_few_shot_prompt,
('human', '리뷰 : {input}')
])
dynamic_chain = dynamic_final_prompt | llm
|
1
2
3
4
5
6
7
8
9
| # 실행 결과
test_inputs = ['택배 상자가 찌그러져왔어요', '화면은 선명한데 스피커 소리가 작아요']
for t in test_inputs:
result = dynamic_chain.invoke({'input': t})
print(f'{t} : {result.content}')
# 출력:
# 택배 상자가 찌그러져왔어요 : 감정: 부정
# 화면은 선명한데 스피커 소리가 작아요 : 감정: 혼합
|
7. Few-Shot 성능 평가 (Zero-Shot vs Few-Shot)
7-1. 평가 데이터셋 정의
1
2
3
4
5
6
7
8
9
10
| eval_dataset = [
{"text": "완벽한 제품입니다! 강력 추천합니다.", "expected": "긍정"},
{"text": "두 번 다시 구매하지 않겠습니다.", "expected": "부정"},
{"text": "평범합니다. 특별히 좋지도 나쁘지도 않아요.", "expected": "중립"},
{"text": "기능은 좋은데 AS가 아쉬워요.", "expected": "혼합"},
{"text": "가격도 착하고 품질도 좋아요.", "expected": "긍정"},
{"text": "사진과 너무 달라서 실망했습니다.", "expected": "부정"},
{"text": "그냥 쓸만 합니다.", "expected": "중립"},
{"text": "배송은 빠른데 제품이 기대 이하에요.", "expected": "혼합"},
]
|
7-2. Zero-Shot 평가 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def evaluate_zero_shot(dataset):
results = []
for item in dataset:
response = llm.invoke([
SystemMessage(content="리뷰의 감정을 분류하세요. 반드시 '긍정', '부정', '중립', '혼합' 중 하나만 출력하세요"),
HumanMessage(content=item['text'])
]).content
predicted = response.strip()
results.append({
'text': item['text'],
'expected': item['expected'],
'predicted': predicted,
'correct': predicted == item['expected'],
})
return results
|
7-3. Few-Shot 평가 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| def evaluate_few_shot(dataset, examples):
results = []
# 시스템 메시지 + 예시를 기본 메시지로 구성
base_messages = [SystemMessage(content="리뷰의 감정을 분류하세요. 반드시 '긍정', '부정', '중립', '혼합' 중 하나만 출력하세요")]
for ex in examples:
base_messages.append(HumanMessage(content=ex['input']))
base_messages.append(AIMessage(content=ex['output'])) # AI 응답으로 예시 주입
for item in dataset:
messages = base_messages + [HumanMessage(content=item['text'])]
response = llm.invoke(messages).content
predicted = response.strip()
results.append({
'text': item['text'],
'expected': item['expected'],
'predicted': predicted,
'correct': predicted == item['expected'],
})
return results
|
핵심 패턴: AIMessage를 직접 삽입해서 모델에게 “이전 대화에서 이렇게 대답했다”는 형태로 예시를 주입한다.
7-4. 평가 실행 & 결과
1
2
3
4
5
6
7
8
9
| few_shot_examples = [
{"input": "정말 만족합니다. 강력 추천!", "output": "긍정"},
{"input": "배송이 빠르고 포장이 꼼꼼해요.", "output": "긍정"},
{"input": "가격 대비 훌륭합니다.", "output": "긍정"},
{"input": "품질이 최악입니다. 환불 요청했어요.", "output": "부정"},
]
zero_results = evaluate_zero_shot(eval_dataset)
fewshot_results = evaluate_few_shot(eval_dataset, few_shot_examples)
|
| 방식 | 정확도 |
|---|
| Zero-Shot | 8/8 (100%) |
| Few-Shot | 8/8 (100%) |
이 태스크는 비교적 쉬운 감정 분류라 둘 다 100%가 나왔지만, 어려운 태스크일수록 Few-Shot의 이점이 극명하게 드러난다.
8. 평가 지표 개요 (Metrics)
LLM 출력을 평가하는 지표는 태스크 유형에 따라 달라진다.
태스크별 주요 지표
| 태스크 유형 | 지표 | 설명 |
|---|
| 분류 (classification) | Accuracy, F1, Precision, Recall | 맞은/틀린 개수 기반 |
| 회귀 (regression) | RMSE, MAE, MSE | 예측값과 실제값 차이 |
| 번역/요약/생성 | BLEU, ROUGE | 참조 텍스트와 겹치는 정도 |
RMSE 예시
1
2
3
4
5
6
7
| 실제값: 0.8, 0.9, 0.0
예측값: 0.5, 0.1, 0.2
오차: 0.3, 0.8, 0.2
제곱: 0.09, 0.64, 0.04
평균: 0.257
RMSE: √0.257 ≈ 0.507
|
BLEU 예시
1
2
3
4
| 정답: "I go to school"
예측: "I go school"
겹치는 단어: "I", "go", "school" → 3/4 = 0.75
|
BLEU는 n-gram 겹침을 측정한다. 1에 가까울수록 좋다.
9. LLM의 추론 한계 - 문제 사례
9-1. 경마 문제 (논리 퍼즐)
1
2
| 말이 25마리, 한 번에 5마리만 경주 가능
가장 빠른 3마리를 찾으려면 몇 번 경주?
|
1
| result = llm.invoke('말이 25마리 있습니다. 한 번에 5마리만 경주를 할 수 있습니다. 가장 빠른 3마리 말을 찾으려면 몇 번의 경주를 해야할까요?')
|
정답: 7번 (5그룹 예선 + 1번 결선 + 1번 최종) LLM은 대부분 정답을 맞추지만, 단순화된 변형 문제에서 틀리는 경향이 있다.
9-2. 변형 경마 문제 (6마리 → 최빠른 1마리)
1
| result = llm.invoke('말이 6마리가 있고, 가장 빠른 말을 찾고 싶습니다. 한 번에 최대 6마리가 동시에 뛸 수 있습니다. 최소 몇 번의 경주가 필요할까요?')
|
정답: 1번 (6마리를 한 번에 경주시키면 됨) LLM의 오답: 7번 → 유명 문제의 답을 그대로 적용하는 패턴 오류
9-3. 글자 세기 문제
1
2
| print(llm.invoke('LOLLAPALOOZA 라는 단어에서 L이 몇 번 나타나나요?').content)
# 오답: 3개 (실제로는 4개: LoLLapaLooza)
|
LLM이 글자를 못 세는 이유: LLM은 텍스트를 토큰 단위로 처리한다. “LOLLAPALOOZA”가 단일 토큰으로 묶일 수 있어 글자를 하나씩 세지 못한다.
9-4. 몬티 홀 문제
1
| llm.invoke('3개 문 중 하나에 황금이 있고, 나머지는 야채입니다. 당신이 1번 문을 고르고, 사회자가 2번 문으로 바꾸시겠습니까? 라고 묻습니다. 사회자는 아직 어떤 문도 열지 않았습니다. 바꾸는 것이 유리할까요?')
|
정답: 이 경우 확률이 동일하므로 유불리 없음 (사회자가 문을 열지 않음!) 그러나 LLM은 고전 몬티 홀 문제의 답(바꾸면 2/3)을 그대로 적용하는 경향이 있다. → 유명 문제 변형에 특히 취약
9-5. Sally 형제 문제
1
2
3
| Sally(여자)한테 남자 형제가 3명 있어요
각 남자형제한테는 여자 형제가 2명 있어요
Sally의 여자 형제는 몇 명?
|
정답: 1명 (Sally 자신을 포함해 각 오빠/남동생에게 여자형제가 Sally+1명 = 2명) → 조건을 꼼꼼히 읽지 않으면 오답
10. Chain-of-Thought (CoT) 프롬프팅
10-1. CoT란?
모델에게 “단계별로 생각하라”고 지시해서 추론 과정을 유도하는 기법
1
2
| 일반 프롬프트: "답이 뭔가요?" → 결과만 출력
CoT 프롬프트: "단계별로 생각해보세요" → 추론 과정 + 결과 출력
|
10-2. CoT 적용 예시 - 글자 세기
1
2
3
4
5
6
7
8
9
10
| # CoT 없이 → 오답
print(llm.invoke('LOLLAPALOOZA 라는 단어에서 L이 몇 번 나타나나요?').content)
# 출력: 3개 (틀림)
# CoT 적용 → 정답
print(llm.invoke('LOLLAPALOOZA 라는 단어에서 L이 몇 번 나타나나요? 단계별로 생각해보세요').content)
# 출력 예시:
# 1. L-O-L-L-A-P-A-L-O-O-Z-A 로 각 글자를 분리하면
# 2. L이 등장하는 위치: 1번째, 3번째, 4번째, 8번째
# 3. 총 4개입니다.
|
10-3. CoT 효과 비교
| 문제 | CoT 없이 | CoT 적용 |
|---|
| 경마 6마리 문제 | 오답 (7번) | 정답 (1번) |
| 글자 세기 (L 개수) | 오답 (3개) | 정답 (4개) |
| Sally 형제 문제 | 불안정 | 정답 |
10-4. CoT 트리거 문구 예시
1
2
3
4
5
| # 이런 문구들이 CoT를 유발한다
"단계별로 생각해보세요"
"단계적으로 추론해주세요"
"지문의 조건을 전부 나열한 후 각 조건을 검토하면서 풀어줘"
"이 문제가 기존 유명한 문제와 어떻게 다른지 분석하고 답해줘"
|
11. Few-Shot CoT 심화 기법
11-1. 시스템 프롬프트로 변형 문제 경계 설정
1
2
3
4
5
6
7
8
9
10
| system_prompt = """당신은 퍼즐 전문가입니다. 아래 문제들은 유명한 문제의 **변형**입니다.
원래 문제의 답을 그대로 적용하지 마세요. 이 문제의 조건을 정확히 읽고 답하세요.
예시:
문제: 셔츠 1장을 말리는데 4시간이 걸립니다. 셔츠 5장을 말리면 몇 시간?
[흔한 실수] 4 x 5 = 20시간
[올바른 생각] 동시에 널면 됩니다. 답: 4시간
이처럼 문제의 조건을 주의 깊게 읽고 단계별로 생각하세요
"""
|
Few-Shot CoT의 핵심: 예시에서 잘못된 사고방식(흔한 실수)와 올바른 사고방식을 함께 보여준다. 이렇게 하면 모델이 같은 패턴의 실수를 피할 수 있다.
11-2. 구조화된 단계별 프롬프트 - 평균 속도 문제
문제: 갈 때 시속 3마일, 왕복 평균 6마일을 만들려면 돌아올 때 얼마?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| prompt = """
당신은 물리 문제를 푸는 전문가입니다. 절대 직감이나 추측으로 답하지 말고, 정의에 따라 단계적으로 계산하세요.
문제:
한 사람이 친구 집까지 평균 시속 3마일로 걸어갔습니다.
왕복 전체 평균 속도를 시속 6마일로 만들려면, 돌아올 때 얼마나 빨리 달려야 할까요?
다음 순서를 반드시 따르세요:
1. 편도 거리를 D마일이라고 두세요
2. 갈 때 걸린 시간을 계산하세요
3. 왕복 전체 평균 속도가 6mph가 되기 위한 총 시간을 계산하세요
4. 이미 사용한 시간과 필요한 총 시간을 비교하세요
5. 남은 시간으로 돌아오는 것이 가능한지 판단하세요
6. 가능하다면 필요한 속도를 계산하고, 불가능하면 왜 불가능한지 논리적으로 설명하세요
"""
print(llm.invoke(prompt).content)
|
LLM의 풀이 과정:
1
2
3
4
5
6
7
| 1. 편도 거리: D마일
2. 갈 때 걸린 시간: D/3 시간
3. 왕복 총 거리: 2D마일
왕복 평균 속도 6mph → 총 시간 = 2D/6 = D/3 시간
4. 이미 갈 때 D/3 시간을 다 써버렸다
5. 남은 시간 = D/3 - D/3 = 0
6. 결론: 불가능! 돌아오는 데 0초가 필요하다 = 무한대 속도 필요
|
핵심 인사이트: 평균 속도는 총 거리 / 총 시간이다. 단순 산술 평균(3+9)/2=6이 아니다. 이처럼 단계 강제를 통해 LLM이 직관적 오류를 피하게 만든다.
11-3. Few-Shot CoT 전략 정리
1
2
3
4
5
6
7
8
9
10
11
12
13
| [전략 1] "단계별로 생각해보세요" 한 줄 추가
→ 간단하지만 효과적. 대부분의 추론 문제에 적용 가능
[전략 2] 풀이 단계를 번호로 명시 강제
→ "1. X를 구하세요, 2. Y를 구하세요..." 형식
→ 복잡한 계산/논리 문제에 효과적
[전략 3] 흔한 실수를 예시로 보여주기
→ "흔한 실수: ..., 올바른 생각: ..."
→ 모델이 같은 패턴의 함정에 빠지지 않도록 방지
[전략 4] 이 문제가 기존 유명 문제와 어떻게 다른지 분석 요구
→ 변형 문제 특화. 패턴 매칭 오류 방지
|
12. 자주 나오는 실수 / 주의사항
❌ example_selector 와 examples를 동시에 지정하면 안 된다
1
2
3
4
5
6
| # 잘못된 사용
FewShotChatMessagePromptTemplate(
examples=examples, # ❌ 둘 중 하나만!
example_selector=selector, # ❌
example_prompt=example_prompt_chat
)
|
examples와 example_selector 중 하나만 사용해야 한다.
1
2
3
4
5
| fewshot_prompt = FewShotPromptTemplate(
...
suffix='\n리뷰: {input}\n감정:',
input_variables=['input'], # suffix의 변수와 반드시 일치!
)
|
❌ CoT 없이 복잡한 추론 문제를 LLM에게 바로 던지면 안 된다
1
2
3
4
5
| # 위험한 방식
result = llm.invoke('복잡한 논리 문제...')
# 안전한 방식
result = llm.invoke('복잡한 논리 문제... 단계별로 생각해보세요')
|
❌ LLM의 글자 세기를 신뢰하면 안 된다
LLM은 토큰 단위 처리 특성상 글자 수, 문자 위치 계산을 정확히 못할 수 있다. 글자 세기가 중요한 로직은 반드시 코드(Python)로 검증하라.
❌ 평균 속도를 산술 평균으로 계산하면 안 된다
1
2
| 평균 속도 = 총 이동 거리 / 총 소요 시간 (조화 평균 개념)
≠ (속도1 + 속도2) / 2 (산술 평균 - 틀림!)
|
❌ 유명 문제의 변형을 원본과 동일하게 취급하면 안 된다
- 몬티 홀 문제: 사회자가 문을 열지 않으면 확률이 달라짐
- 경마 문제: 찾는 대상(3마리 vs 1마리)에 따라 답이 완전히 달라짐
- 이런 변형 문제에 대응하려면 CoT + 조건 분석 요구 필수
13. [보충] 프롬프트 설계 전략 정리
Few-Shot 예시 선택 기준
| 기준 | 설명 | 적용 방법 |
|---|
| 다양성 | 다양한 케이스를 커버 | 긍정/부정/중립/혼합 골고루 |
| 관련성 | 입력과 유사한 예시 | SemanticSimilarityExampleSelector |
| 균형 | 레이블 편향 방지 | 클래스별 균등 수량 |
Zero-Shot vs Few-Shot vs CoT 선택 기준
1
2
3
4
5
6
7
8
9
10
11
| 간단한 분류/생성 태스크
→ Zero-Shot 충분
패턴 학습이 필요한 태스크 (감정 분류, 문체 변환)
→ Few-Shot
추론/계산이 필요한 태스크 (수학, 논리, 퍼즐)
→ CoT
복잡한 추론 + 패턴 모두 필요
→ Few-Shot + CoT (가장 강력)
|
LangChain Few-Shot 컴포넌트 선택 기준
| 상황 | 사용할 컴포넌트 |
|---|
| 일반 텍스트 프롬프트, 예시 고정 | FewShotPromptTemplate |
| 채팅 모델, 예시 고정 | FewShotChatMessagePromptTemplate |
| 채팅 모델, 예시를 자동 선택 | FewShotChatMessagePromptTemplate + SemanticSimilarityExampleSelector |
| 예시 풀이 크고 입력 다양 | SemanticSimilarityExampleSelector (k=3~5) |
전체 오늘 학습 개념 연결도
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| Few-Shot
├── 고정 예시
│ ├── FewShotPromptTemplate (텍스트 기반)
│ └── FewShotChatMessagePromptTemplate (채팅 기반)
│
└── 동적 예시
└── SemanticSimilarityExampleSelector
├── OpenAIEmbeddings (입력 → 벡터)
└── InMemoryVectorStore (예시 벡터 저장)
CoT (Chain-of-Thought)
├── Zero-Shot CoT: "단계별로 생각해보세요"
├── 단계 강제 프롬프트: "1. X를 구하고, 2. Y를 구하고..."
└── Few-Shot CoT: 예시에서 추론 과정도 함께 제시
평가 (Evaluation)
├── 분류: Accuracy, F1
├── 회귀: RMSE, MAE
└── 생성: BLEU, ROUGE
|
끝