개념
제로샷 vs 퓨샷: 예시가 지시보다 나을 때
제로샷 프롬프팅(Zero-Shot Prompting)은 모델에 작업만 주고 그 외에는 아무것도 주지 않습니다. 퓨샷 프롬프팅(Few-Shot Prompting)은 먼저 예시를 줍니다.
Wei et al. (2022)은 8개 벤치마크에서 이를 측정했습니다. 감성 분류처럼 단순한 작업에서는 제로샷과 퓨샷의 성능 차이가 2% 이내였습니다. 하지만 다단계 산술이나 기호 추론(Symbolic Reasoning)처럼 복잡한 작업에서는 퓨샷이 정확도를 10-25% 개선했습니다.
직관은 이렇습니다. 예시는 압축된 지시입니다. 출력 형식을 설명하는 대신 보여줍니다. 추론 과정을 설명하는 대신 시연합니다. 모델은 추상적 지시를 해석하는 것보다 예시의 패턴을 맞추는 일을 더 안정적으로 수행합니다.
graph TD
subgraph Comparison["Zero-Shot vs Few-Shot"]
direction LR
Z["Zero-Shot\n'이 리뷰를 분류하세요'\n모델이 형식을 추측\nGSM8K 78%"]
F["Few-Shot\n'예시 3개가 있습니다...\n이제 이 리뷰를 분류하세요'\n모델이 패턴을 맞춤\nGSM8K 85%"]
end
Z ~~~ F
style Z fill:#1a1a2e,stroke:#e94560,color:#fff
style F fill:#1a1a2e,stroke:#51cf66,color:#fff
퓨샷이 이기는 경우: 형식에 민감한 작업, 분류, 구조화 추출, 도메인 특화 전문 용어, 모델이 특정 패턴을 맞춰야 하는 모든 작업.
제로샷이 이기는 경우: 단순 사실 질문, 예시가 창의성을 제한하는 창의적 작업, 좋은 예시를 찾는 일이 좋은 지시를 쓰는 것보다 어려운 작업.
예시 선택: 무작위보다 유사성이 낫다
모든 예시가 동등하지 않습니다. 대상 입력과 유사한 예시를 고르면 분류 작업에서 무작위 선택보다 5-15% 더 좋은 성능을 냅니다(Liu et al., 2022). 세 가지 원칙이 있습니다.
- 의미적 유사성(Semantic Similarity): 임베딩 공간(Embedding Space)에서 입력과 가장 가까운 예시를 고릅니다.
- 라벨 다양성(Label Diversity): 출력 범주를 모두 덮도록 예시를 구성합니다.
- 난이도 매칭(Difficulty Matching): 대상 문제와 비슷한 복잡도 수준의 예시를 고릅니다.
대부분의 작업에서 최적 예시 수는 3-5개입니다. 3개보다 적으면 모델이 패턴을 추출하기에 신호가 부족합니다. 5개를 넘으면 수익 체감이 시작되고 컨텍스트 창 토큰을 낭비합니다. 라벨이 많은 분류 작업에서는 라벨당 하나의 예시를 사용합니다.
사고 연쇄: 모델에게 연습장을 주기
사고 연쇄(Chain-of-Thought, CoT) 프롬프팅은 Google Brain의 Wei et al. (2022)이 소개했습니다. 아이디어는 단순합니다. 모델에 답만 요청하는 대신, 먼저 추론 단계를 보여달라고 요청합니다.
graph LR
subgraph Standard["표준 프롬프팅"]
Q1["Q: Roger는 공 5개가 있습니다.\n공 3개짜리 캔 2개를 삽니다.\n공은 몇 개입니까?"] --> A1["A: 11"]
end
subgraph CoT["사고 연쇄 프롬프팅"]
Q2["Q: Roger는 공 5개가 있습니다.\n공 3개짜리 캔 2개를 삽니다.\n공은 몇 개입니까?"] --> R2["Roger는 5개로 시작합니다.\n3개짜리 캔 2개 = 6개.\n5 + 6 = 11."] --> A2["A: 11"]
end
style Q1 fill:#1a1a2e,stroke:#e94560,color:#fff
style A1 fill:#1a1a2e,stroke:#e94560,color:#fff
style Q2 fill:#1a1a2e,stroke:#51cf66,color:#fff
style R2 fill:#1a1a2e,stroke:#ffa500,color:#fff
style A2 fill:#1a1a2e,stroke:#51cf66,color:#fff
이것은 기계적으로 왜 작동할까요? 트랜스포머가 생성한 각 토큰은 다음 토큰의 컨텍스트가 됩니다. CoT가 없으면 모델은 모든 추론을 단일 순전파(Forward Pass)의 은닉 상태에 압축해야 합니다. CoT가 있으면 모델은 중간 계산을 토큰으로 외부화합니다. 각 추론 토큰은 유효 계산 깊이를 늘립니다.
GSM8K 벤치마크(초등 수학, 8.5K 문제):
| 모델 | Zero-Shot | Zero-Shot CoT | Few-Shot CoT |
|---|
| GPT-4o | 78% | 91% | 95% |
| GPT-5 | 94% | 97% | 98% |
| o4-mini (reasoning) | 97% | — | — |
| Claude Opus 4.7 | 93% | 97% | 98% |
| Gemini 3 Pro | 92% | 96% | 98% |
| Llama 4 70B | 80% | 89% | 94% |
| DeepSeek-V3.1 | 89% | 94% | 96% |
추론 모델(Reasoning Models)에 대한 참고. OpenAI o-series(o3, o4-mini)와 DeepSeek-R1 같은 모델은 답을 내기 전에 내부적으로 사고 연쇄를 실행합니다. 이런 추론 모델에 "Let's think step by step"을 추가하는 것은 중복이며, 때로는 역효과가 날 수 있습니다. 이미 내부에서 수행했기 때문입니다.
CoT에는 두 가지 변형이 있습니다.
제로샷 CoT(Zero-Shot CoT): 프롬프트 뒤에 "Let's think step by step"을 붙입니다. 예시는 필요 없습니다. Kojima et al. (2022)은 이 한 문장이 산술, 상식, 기호 추론 작업 전반에서 정확도를 개선한다는 것을 보였습니다.
퓨샷 CoT(Few-Shot CoT): 추론 단계가 포함된 예시를 제공합니다. 모델이 기대하는 정확한 추론 형식을 볼 수 있으므로 제로샷 CoT보다 더 효과적입니다.
CoT가 해로운 경우: 단순 사실 회상("프랑스의 수도는?"), 단일 단계 분류, 정확도보다 속도가 더 중요한 작업입니다. CoT는 질의(query)당 50-200 토큰의 추론 오버헤드를 추가합니다. 처리량이 높고 복잡도가 낮은 작업에서는 비용 낭비입니다.
자기 일관성(Self-Consistency): 여러 번 샘플링하고 한 번 투표하기
Wang et al. (2023)은 자기 일관성(Self-Consistency)을 소개했습니다. 핵심 통찰은 이렇습니다. 하나의 CoT 경로에는 추론 오류가 들어갈 수 있습니다. 하지만 N개의 독립적인 추론 경로를 0보다 큰 온도(temperature > 0)로 샘플링하고 최종 답에 대해 다수결을 취하면 오류가 상쇄됩니다.
graph TD
P["문제: '상점에 사과 48개가 있습니다.\n월요일에 1/3을 팔고\n화요일에 남은 것의 1/4을 팝니다.\n몇 개가 남나요?'"]
P --> Path1["경로 1: 48 - 16 = 32\n32 - 8 = 24\n답: 24"]
P --> Path2["경로 2: 48의 1/3 = 16\n남은 것: 32\n32의 1/4 = 8\n32 - 8 = 24\n답: 24"]
P --> Path3["경로 3: 48/3 = 16 판매\n48 - 16 = 32\n32/4 = 8 판매\n32 - 8 = 24\n답: 24"]
P --> Path4["경로 4: 1/3 판매: 48 - 12 = 36\n1/4 판매: 36 - 9 = 27\n답: 27"]
P --> Path5["경로 5: 월요일: 48 * 2/3 = 32\n화요일: 32 * 3/4 = 24\n답: 24"]
Path1 --> V["다수결\n24: 4표\n27: 1표\n최종: 24"]
Path2 --> V
Path3 --> V
Path4 --> V
Path5 --> V
style P fill:#1a1a2e,stroke:#ffa500,color:#fff
style Path1 fill:#1a1a2e,stroke:#51cf66,color:#fff
style Path2 fill:#1a1a2e,stroke:#51cf66,color:#fff
style Path3 fill:#1a1a2e,stroke:#51cf66,color:#fff
style Path4 fill:#1a1a2e,stroke:#e94560,color:#fff
style Path5 fill:#1a1a2e,stroke:#51cf66,color:#fff
style V fill:#1a1a2e,stroke:#51cf66,color:#fff
원래 PaLM 540B 실험에서 자기 일관성은 GSM8K 정확도를 단일 CoT 56.5%에서 N=40 기준 74.4%로 개선했습니다. GPT-5에서는 기본 정확도가 이미 포화 상태라 개선 폭이 작습니다(97%에서 98%). 이 기법은 기본 CoT 정확도가 60-85%인 모델에서 가장 빛납니다. 단일 경로 오류가 자주 나오지만 체계적으로 틀리지는 않는 구간입니다. 추론 모델(o-series, R1)에서는 자기 일관성이 내장된 내부 샘플링에 흡수됩니다.
트레이드오프(trade-off)는 비용과 지연시간입니다. N개의 샘플은 API 비용과 지연시간이 N배라는 뜻입니다. 실무에서는 N=5가 대부분의 이점을 잡아냅니다. 의미 있는 투표를 위한 최소값은 N=3입니다. 대부분의 작업에서 N > 10은 수익 체감이 큽니다.
사고 트리: 가지를 뻗는 탐색
Yao et al. (2023)은 사고 트리(Tree-of-Thought, ToT)를 소개했습니다. CoT가 하나의 선형 추론 경로를 따르는 반면, ToT는 여러 가지를 탐색하고 어떤 가지가 더 유망한지 평가한 뒤 계속 진행합니다.
graph TD
Root["문제"] --> B1["생각 1a"]
Root --> B2["생각 1b"]
Root --> B3["생각 1c"]
B1 --> E1["평가: 0.8"]
B2 --> E2["평가: 0.3"]
B3 --> E3["평가: 0.9"]
E1 -->|계속| B1a["생각 2a"]
E1 -->|계속| B1b["생각 2b"]
E3 -->|계속| B3a["생각 2a"]
E3 -->|계속| B3b["생각 2b"]
E2 -->|가지치기| X["X"]
B1a --> E4["평가: 0.7"]
B3a --> E5["평가: 0.95"]
E5 -->|최선 경로| Final["해결책"]
style Root fill:#1a1a2e,stroke:#ffa500,color:#fff
style E2 fill:#1a1a2e,stroke:#e94560,color:#fff
style X fill:#1a1a2e,stroke:#e94560,color:#fff
style E5 fill:#1a1a2e,stroke:#51cf66,color:#fff
style Final fill:#1a1a2e,stroke:#51cf66,color:#fff
style B1 fill:#1a1a2e,stroke:#808080,color:#fff
style B2 fill:#1a1a2e,stroke:#808080,color:#fff
style B3 fill:#1a1a2e,stroke:#808080,color:#fff
style B1a fill:#1a1a2e,stroke:#808080,color:#fff
style B1b fill:#1a1a2e,stroke:#808080,color:#fff
style B3a fill:#1a1a2e,stroke:#808080,color:#fff
style B3b fill:#1a1a2e,stroke:#808080,color:#fff
style E1 fill:#1a1a2e,stroke:#808080,color:#fff
style E3 fill:#1a1a2e,stroke:#808080,color:#fff
style E4 fill:#1a1a2e,stroke:#808080,color:#fff
ToT에는 세 가지 구성 요소가 있습니다.
- 생각 생성(Thought Generation): 여러 후보 다음 단계를 생성합니다.
- 상태 평가(State Evaluation): 각 후보에 점수를 매깁니다. LLM 자체를 평가자로 사용할 수 있습니다.
- 탐색 알고리즘(Search Algorithm): 트리에 대해 너비 우선 탐색(BFS) 또는 깊이 우선 탐색(DFS)을 수행하고 낮은 점수의 가지를 잘라냅니다.
24 게임(Game of 24) 작업에서는 네 숫자를 사칙연산으로 조합해 24를 만들어야 합니다. GPT-4는 표준 프롬프팅으로 7.3%를 해결합니다. CoT는 4.0%입니다. 여기서는 탐색 공간이 넓기 때문에 CoT가 오히려 해롭습니다. ToT를 쓰면 74%까지 올라갑니다.
ToT는 비쌉니다. 트리의 각 노드마다 LLM 호출이 필요합니다. 분기 계수 3, 깊이 3인 트리는 최대 39번의 LLM 호출을 요구합니다. 따라서 탐색 공간이 크지만 평가 가능한 문제에만 사용합니다. 예: 계획, 퍼즐 풀이, 제약이 있는 창의적 문제 해결.
ReAct: 생각하기 + 행동하기
Yao et al. (2022)은 추론 흔적(Reasoning Trace)과 행동(Action)을 결합했습니다. 모델은 생각하기, 즉 추론 생성과 행동하기, 즉 도구 호출, 검색, 계산을 번갈아 수행합니다.
graph LR
Q["질문:\n에펠탑이 있는\n나라의 인구는\n얼마인가?"]
T1["Thought: 먼저\n에펠탑이 어느 나라에\n있는지 찾아야 함"]
A1["Action: search\n'Eiffel Tower location'"]
O1["Observation:\nParis, France"]
T2["Thought: 이제\n프랑스 인구가 필요함"]
A2["Action: search\n'France population 2024'"]
O2["Observation:\n68.4 million"]
T3["Thought: 답을 얻었음"]
F["Answer:\n68.4 million"]
Q --> T1 --> A1 --> O1 --> T2 --> A2 --> O2 --> T3 --> F
style Q fill:#1a1a2e,stroke:#ffa500,color:#fff
style T1 fill:#1a1a2e,stroke:#51cf66,color:#fff
style A1 fill:#1a1a2e,stroke:#e94560,color:#fff
style O1 fill:#1a1a2e,stroke:#808080,color:#fff
style T2 fill:#1a1a2e,stroke:#51cf66,color:#fff
style A2 fill:#1a1a2e,stroke:#e94560,color:#fff
style O2 fill:#1a1a2e,stroke:#808080,color:#fff
style T3 fill:#1a1a2e,stroke:#51cf66,color:#fff
style F fill:#1a1a2e,stroke:#51cf66,color:#fff
ReAct는 지식 집약적 작업에서 순수 CoT보다 성능이 좋습니다. 외부 데이터에 추론을 접지(Grounding)할 수 있기 때문입니다. 다중 홉 질문 답변 벤치마크인 HotpotQA에서 GPT-4 기반 ReAct는 완전 일치(exact match) 35.1%를 달성했고, CoT만 사용한 경우는 29.4%였습니다. 진짜 힘은 관찰(Observation)이 추론 오류를 수정한다는 데 있습니다. 모델은 실행 중간에 계획을 업데이트할 수 있습니다.
ReAct는 현대 AI 에이전트의 기반입니다. 모든 에이전트 프레임워크(LangChain, CrewAI, AutoGen)는 Thought-Action-Observation 루프의 변형을 구현합니다. Phase 14에서 전체 에이전트를 만들 것입니다. 이 lesson은 그 프롬프팅 패턴을 다룹니다.
구조화 프롬프팅: XML 태그, 구분자, 헤더
프롬프트가 복잡해질수록 구조는 모델이 섹션을 혼동하지 않도록 막아줍니다. 세 가지 접근이 있습니다.
XML 태그(XML Tags): Claude에서 가장 잘 작동하고, 다른 모델에서도 탄탄합니다.
<context>
당신은 pull request를 리뷰하고 있습니다.
이 코드베이스는 TypeScript와 React를 사용합니다.
</context>
<task>
다음 diff에서 버그, 보안 이슈, 스타일 위반을 리뷰하세요.
</task>
<diff>
{diff_content}
</diff>
<output_format>
각 이슈를 file, line, severity(critical/warning/info), description으로 나열하세요.
</output_format>
Markdown 헤더(Markdown Headers): 범용적으로 작동합니다.
## 역할
핀테크 회사의 시니어 보안 엔지니어.
## 작업
이 API endpoint의 취약점을 분석하세요.
## 입력
{api_code}
## 규칙
- OWASP Top 10에 집중하세요.
- 각 발견사항의 등급을 critical, high, medium, low로 매기세요.
- 수정 단계를 포함하세요.
구분자(Delimiters): 작지만 효과적입니다.
---INPUT---
{user_text}
---END INPUT---
---INSTRUCTIONS---
위 내용을 3개의 글머리표로 요약하세요.
---END INSTRUCTIONS---
프롬프트 체이닝(Prompt Chaining): 순차적 분해
일부 작업은 하나의 프롬프트로 처리하기에 너무 복잡합니다. 프롬프트 체이닝은 작업을 단계로 나누고, 한 프롬프트의 출력을 다음 프롬프트의 입력으로 사용합니다.
graph LR
I["원시 입력"] --> P1["프롬프트 1:\n핵심 사실\n추출"]
P1 --> O1["사실"]
O1 --> P2["프롬프트 2:\n사실\n분석"]
P2 --> O2["분석"]
O2 --> P3["프롬프트 3:\n추천\n생성"]
P3 --> F["최종 출력"]
style I fill:#1a1a2e,stroke:#808080,color:#fff
style P1 fill:#1a1a2e,stroke:#e94560,color:#fff
style O1 fill:#1a1a2e,stroke:#ffa500,color:#fff
style P2 fill:#1a1a2e,stroke:#e94560,color:#fff
style O2 fill:#1a1a2e,stroke:#ffa500,color:#fff
style P3 fill:#1a1a2e,stroke:#e94560,color:#fff
style F fill:#1a1a2e,stroke:#51cf66,color:#fff
체이닝이 단일 프롬프트보다 나은 이유는 세 가지입니다.
- 각 단계가 더 단순합니다: 모델이 모든 것을 동시에 처리하는 대신 하나의 집중된 작업을 처리합니다.
- 중간 출력이 검사 가능합니다: 단계 사이에서 검증하고 수정할 수 있습니다.
- 단계마다 다른 모델을 사용할 수 있습니다: 추출에는 저렴한 모델을, 추론에는 비싼 모델을 사용할 수 있습니다.
성능 비교
| 기법 | 가장 적합한 경우 | GSM8K 정확도(GPT-5) | API 호출 | 토큰 오버헤드 | 복잡도 |
|---|
| Zero-Shot | 단순 작업 | 94% | 1 | 없음 | 매우 낮음 |
| Few-Shot | 형식 맞추기 | 96% | 1 | 200-500 토큰 | 낮음 |
| Zero-Shot CoT | 빠른 추론 개선 | 97% | 1 | 50-200 토큰 | 매우 낮음 |
| Few-Shot CoT | 단일 호출 최대 정확도 | 98% | 1 | 300-600 토큰 | 낮음 |
| Self-Consistency (N=5) | 중요한 추론 | 98.5% | 5 | 토큰 비용 5배 | 중간 |
| Reasoning model (o4-mini) | CoT 대체 | 97% | 1 | 숨겨짐(내부 2-10배) | 매우 낮음 |
| Tree-of-Thought | 탐색/계획 문제 | N/A (Game of 24에서 74%) | 10-40+ | 토큰 비용 10-40배 | 높음 |
| ReAct | 지식 기반 추론 | N/A (HotpotQA에서 35.1%) | 3-10+ | 가변 | 높음 |
| Prompt Chaining | 복잡한 다단계 작업 | 96% (파이프라인) | 2-5 | 토큰 비용 2-5배 | 중간 |
올바른 기법은 세 가지 요소에 따라 달라집니다. 정확도 요구사항, 지연시간 예산, 비용 허용 범위입니다. 대부분의 프로덕션(production) 시스템에서는 3샘플 자기 일관성 폴백(fallback)을 가진 퓨샷 CoT가 사용 사례의 90%를 덮습니다.
직접 만들기
퓨샷 프롬프팅, 사고 연쇄 추론, 자기 일관성 투표를 하나의 파이프라인으로 결합한 수학 문제 풀이기를 만듭니다. 그다음 어려운 문제를 위해 사고 트리를 추가합니다.
전체 구현은 code/advanced_prompting.py에 있습니다. 핵심 구성 요소는 다음과 같습니다.
Step 1: 퓨샷 예시 저장소
첫 번째 구성 요소는 퓨샷 예시를 관리하고, 주어진 문제에 가장 관련 있는 예시를 선택합니다.
GSM8K_EXAMPLES = [
{
"question": "Janet의 오리는 하루에 알 16개를 낳습니다. Janet은 매일 아침 3개를 먹고 친구들을 위해 머핀을 굽는 데 4개를 씁니다. 남은 알을 farmers' market에서 개당 $2에 팝니다. Janet은 매일 farmers' market에서 얼마를 버나요?",
"reasoning": "Janet의 오리는 하루에 알 16개를 낳습니다. 3개를 먹고 4개로 굽기 때문에 3 + 4 = 7개를 사용합니다. 따라서 16 - 7 = 9개가 남습니다. 각 알을 $2에 팔므로 하루에 9 * 2 = $18를 법니다.",
"answer": "18"
},
...
]
각 예시는 세 부분을 가집니다. 질문, 추론 체인, 최종 답입니다. 추론 체인이 일반 퓨샷 예시를 CoT 퓨샷 예시로 바꿉니다.
Step 2: 사고 연쇄 프롬프트 빌더
프롬프트 빌더는 시스템 메시지, 추론 체인이 있는 퓨샷 예시, 대상 질문을 하나의 프롬프트로 조립합니다.
def build_cot_prompt(question, examples, num_examples=3):
system = (
"당신은 정밀한 수학 문제 풀이기입니다. "
"각 문제마다 단계별 추론을 명확히 보여준 뒤, "
"마지막 줄에 최종 숫자 답을 정확히 다음 형식으로 쓰세요. "
"'The answer is [number]'."
)
example_text = ""
for ex in examples[:num_examples]:
example_text += f"Q: {ex['question']}\n"
example_text += f"A: {ex['reasoning']} The answer is {ex['answer']}.\n\n"
user = f"{example_text}Q: {question}\nA:"
return system, user
형식 제약("The answer is [number]")은 매우 중요합니다. 이 제약이 없으면 자기 일관성이 여러 샘플의 답을 추출하고 비교할 수 없습니다.
Step 3: 자기 일관성 투표
N개의 추론 경로를 샘플링하고 다수결 답을 선택합니다.
def self_consistency_solve(question, examples, client, model, n_samples=5):
system, user = build_cot_prompt(question, examples)
answers = []
reasonings = []
for _ in range(n_samples):
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user}
],
temperature=0.7
)
text = response.choices[0].message.content
reasonings.append(text)
answer = extract_answer(text)
if answer is not None:
answers.append(answer)
vote_counts = Counter(answers)
best_answer = vote_counts.most_common(1)[0][0] if vote_counts else None
confidence = vote_counts[best_answer] / len(answers) if best_answer else 0
return best_answer, confidence, reasonings, vote_counts
온도(temperature) 0.7이 중요합니다. 온도 0.0이면 N개의 샘플이 모두 같아져 목적이 사라집니다. 다양한 추론 경로를 만들 만큼의 무작위성은 필요하지만, 모델이 의미 없는 출력을 만들 정도로 높으면 안 됩니다.
Step 4: 사고 트리 풀이기
선형 추론이 실패하는 문제에서는 ToT가 여러 접근을 탐색하고 어떤 방향이 가장 유망한지 평가합니다.
def tree_of_thought_solve(question, client, model, breadth=3, depth=3):
thoughts = generate_initial_thoughts(question, client, model, breadth)
scored = [(t, evaluate_thought(t, question, client, model)) for t in thoughts]
scored.sort(key=lambda x: x[1], reverse=True)
for current_depth in range(1, depth):
next_thoughts = []
for thought, score in scored[:2]:
extensions = extend_thought(thought, question, client, model, breadth)
for ext in extensions:
ext_score = evaluate_thought(ext, question, client, model)
next_thoughts.append((ext, ext_score))
scored = sorted(next_thoughts, key=lambda x: x[1], reverse=True)
best_thought = scored[0][0] if scored else ""
return extract_answer(best_thought), best_thought
평가자는 그 자체로 LLM 호출입니다. 모델에게 "이 추론 경로가 문제 해결에 얼마나 유망한지 0.0에서 1.0 사이 점수로 평가하세요"라고 묻습니다. 이것이 ToT의 핵심 통찰입니다. 모델이 자신의 부분 해결책을 평가합니다.
Step 5: 전체 파이프라인
파이프라인은 모든 기법을 단계적 확대 전략(Escalation Strategy)과 결합합니다.
def solve_with_escalation(question, examples, client, model):
system, user = build_cot_prompt(question, examples)
single_response = call_llm(client, model, system, user, temperature=0.0)
single_answer = extract_answer(single_response)
sc_answer, confidence, _, _ = self_consistency_solve(
question, examples, client, model, n_samples=5
)
if confidence >= 0.8:
return sc_answer, "self_consistency", confidence
tot_answer, _ = tree_of_thought_solve(question, client, model)
return tot_answer, "tree_of_thought", None
단계적 확대 로직은 이렇습니다. 먼저 저렴한 단일 CoT를 시도합니다. 자기 일관성 신뢰도가 0.8보다 낮으면, 즉 5개 샘플 중 4개 미만이 동의하면 ToT로 확대합니다. 이렇게 하면 비용과 정확도의 균형을 잡을 수 있습니다. 대부분의 문제는 저렴하게 풀고, 어려운 문제에는 더 많은 계산을 씁니다.
사용해보기
LangChain과 함께 사용하기
LangChain은 퓨샷과 CoT 패턴을 단순화하는 프롬프트 템플릿과 출력 파싱 기능을 기본 제공합니다.
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import ChatOpenAI
example_prompt = PromptTemplate(
input_variables=["question", "reasoning", "answer"],
template="Q: {question}\nA: {reasoning} The answer is {answer}."
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="Q: {input}\nA: Let's think step by step.",
input_variables=["input"]
)
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
chain = few_shot_prompt | llm
result = chain.invoke({"input": "If a train travels 120 km in 2 hours..."})
LangChain에는 의미적 유사성 선택을 위한 ExampleSelector 클래스도 있습니다.
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_openai import OpenAIEmbeddings
selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
k=3
)
DSPy와 함께 사용하기
DSPy는 프롬프팅 전략을 최적화 가능한 모듈로 취급합니다. CoT 프롬프트를 손으로 만드는 대신, 시그니처(Signature)를 정의하고 DSPy가 프롬프트를 최적화하게 합니다.
import dspy
dspy.configure(lm=dspy.LM("openai/gpt-4o", temperature=0.7))
class MathSolver(dspy.Module):
def __init__(self):
self.solve = dspy.ChainOfThought("question -> answer")
def forward(self, question):
return self.solve(question=question)
solver = MathSolver()
result = solver(question="Janet's ducks lay 16 eggs per day...")
DSPy의 ChainOfThought는 추론 흔적을 자동으로 추가합니다. dspy.majority는 자기 일관성을 구현합니다.
result = dspy.majority(
[solver(question=q) for _ in range(5)],
field="answer"
)
비교: 직접 구현 vs 프레임워크
| 기능 | 직접 구현(이 lesson) | LangChain | DSPy |
|---|
| 프롬프트 형식 제어 | 전체 제어 | 템플릿 기반 | 자동 |
| 자기 일관성 | 수동 투표 | 수동 | 내장(dspy.majority) |
| 예시 선택 | 커스텀 로직 | ExampleSelector | dspy.BootstrapFewShot |
| 사고 트리 | 커스텀 트리 탐색 | 커뮤니티 체인 | 내장 아님 |
| 프롬프트 최적화 | 수동 반복 | 수동 | 자동 컴파일 |
| 가장 적합한 경우 | 학습, 커스텀 파이프라인 | 표준 워크플로 | 연구, 최적화 |
산출물 만들기
이 lesson은 두 가지 산출물을 만듭니다.
1. 추론 체인 프롬프트 (outputs/prompt-reasoning-chain.md): 자기 일관성을 지원하는 프로덕션(production) 준비 퓨샷 CoT 프롬프트 템플릿입니다. 예시와 문제 도메인을 끼워 넣어 사용합니다.
2. CoT 패턴 선택 스킬(Skill) (outputs/skill-cot-patterns.md): 작업 유형, 정확도 요구사항, 비용 제약에 따라 올바른 추론 기법을 고르는 의사결정 프레임워크(framework)입니다.
연습문제
-
차이 측정하기: GSM8K 문제 10개를 가져옵니다. 각 문제를 제로샷, 퓨샷, 제로샷 CoT, 퓨샷 CoT로 풉니다. 각 정확도를 기록하세요. 어떤 기법이 사용하는 모델에서 가장 큰 개선을 주나요?
-
예시 선택 실험: 같은 10개 문제에서 무작위 예시 선택과 손으로 고른 유사 예시를 비교하세요. 정확도 차이를 측정합니다. 어느 지점에서 예시 품질이 예시 수보다 더 중요해지나요?
-
자기 일관성 비용 곡선: GSM8K 문제 20개에 대해 N=1, 3, 5, 7, 10으로 자기 일관성을 실행하세요. 정확도 대비 비용(총 토큰)을 그래프로 그립니다. 사용하는 모델에서 곡선의 무릎 지점은 어디인가요?
-
ReAct 루프 만들기: 파이프라인에 계산기 도구를 추가하세요. 모델이 수학식을 생성하면 Python의 eval()을 샌드박스(sandbox)에서 실행하고 결과를 다시 넣습니다. 도구로 접지된 추론이 순수 CoT보다 나은지 측정하세요.
-
창의적 작업을 위한 ToT: 사고 트리 풀이기를 창의적 글쓰기 작업에 맞게 바꾸세요. 예: "웃기면서도 슬픈 6단어 이야기를 쓰세요." LLM을 평가자로 사용합니다. 가지 탐색이 단일 생성보다 더 나은 창의적 출력을 만드나요?
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 퓨샷 프롬프팅(Few-Shot Prompting) | "예시 몇 개 주기" | 모델의 출력 형식과 행동을 고정하기 위해 프롬프트 안에 입력-출력 데모를 포함하는 방식 |
| 사고 연쇄(Chain-of-Thought) | "단계별로 생각하게 하기" | 최종 답을 내기 전에 중간 추론 토큰을 유도해 모델의 유효 계산을 확장하는 방식 |
| 자기 일관성(Self-Consistency) | "여러 번 실행하기" | 0보다 큰 온도(temperature > 0)에서 N개의 다양한 추론 경로를 샘플링하고 가장 흔한 최종 답을 다수결로 선택하는 방식 |
| 사고 트리(Tree-of-Thought) | "선택지를 탐색하게 하기" | 각 부분 해결책을 평가하고 유망한 경로만 확장하는 구조화된 추론 가지 탐색 |
| ReAct | "생각 + 도구 사용" | Thought-Action-Observation 루프에서 추론 흔적과 외부 행동(검색, 계산, API 호출)을 번갈아 수행하는 방식 |
| 프롬프트 체이닝(Prompt Chaining) | "단계로 나누기" | 복잡한 작업을 순차 프롬프트로 분해하고 각 출력이 다음 입력으로 들어가게 하는 방식 |
| 제로샷 CoT(Zero-Shot CoT) | "'think step by step'만 붙이기" | 예시 없이 추론 유도 문구를 붙여 모델의 잠재 추론 능력에 기대는 방식 |
더 읽을거리