Skip to main content

Chain-of-Thought 프롬프팅

Chain-of-Thought(CoT) 프롬프팅은 LLM에게 답변 전에 추론 과정을 단계별로 서술하도록 유도하는 기법입니다. “Let’s think step by step”이라는 간단한 문구 하나로 수학, 논리, 상식 추론 태스크에서 성능이 극적으로 향상됩니다.

학습 목표

이 문서를 완료하면 다음을 수행할 수 있습니다.
  • CoT 프롬프팅이 효과적인 이유와 작동 원리를 설명할 수 있습니다
  • Zero-shot CoT와 Few-shot CoT를 구현하고 비교할 수 있습니다
  • Self-Consistency 기법으로 추론 일관성을 높일 수 있습니다
  • CoT가 효과적인 태스크와 그렇지 않은 태스크를 구분할 수 있습니다

왜 중요한가

표준 프롬프팅에서 LLM은 질문에 대해 바로 답변을 생성합니다. 복잡한 추론이 필요한 문제에서는 이 “즉답” 방식이 오류를 만듭니다.
# 표준 프롬프팅 (즉답)
Q: 카페에 손님이 23명 있었습니다. 점심에 12명이 들어오고 7명이 나갔습니다.
   그 후 5명이 더 들어왔습니다. 지금 몇 명이 있나요?
A: 33명  ← 오답

# CoT 프롬프팅 (단계적 추론)
Q: (같은 질문) 단계별로 생각해봅시다.
A: 처음 손님: 23명
   점심에 12명이 들어옴: 23 + 12 = 35명
   7명이 나감: 35 - 7 = 28명
   5명이 더 들어옴: 28 + 5 = 33명
   정답: 33명  ← 정답
핵심 인사이트: CoT는 모델에게 “생각할 공간”을 제공합니다. 중간 추론 단계를 명시적으로 생성하면서 모델이 스스로 오류를 감지하고 수정할 기회를 얻습니다. 이는 사람이 복잡한 문제를 풀 때 메모지에 중간 과정을 적는 것과 유사합니다.

핵심 개념

Zero-shot CoT

“Let’s think step by step” (단계별로 생각해봅시다) 문구만 추가합니다. 예시가 필요 없어 가장 간편합니다.
from openai import OpenAI
client = OpenAI()

def zero_shot_cot(question):
    """Zero-shot CoT: 마법의 한 문장 추가"""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "user", "content": f"{question}\n\n단계별로 생각해봅시다."},
        ],
        temperature=0,
    )
    return response.choices[0].message.content

# 수학 추론
question = """한 서점에서 소설책을 개당 12,000원에 판매합니다.
3권을 사면 10% 할인, 5권 이상이면 20% 할인입니다.
철수가 7권을 산다면 얼마를 내야 하나요?"""

print(zero_shot_cot(question))
# 1단계: 원가 계산 → 7 × 12,000 = 84,000원
# 2단계: 할인율 확인 → 5권 이상이므로 20% 할인
# 3단계: 할인 금액 → 84,000 × 0.2 = 16,800원
# 4단계: 최종 금액 → 84,000 - 16,800 = 67,200원
# 정답: 67,200원
효과적인 Zero-shot CoT 변형:
프롬프트효과적합한 상황
”단계별로 생각해봅시다”범용 추론 유도일반적인 추론 문제
”먼저 문제를 분석한 후 풀어봅시다”문제 구조 파악 유도복잡한 구조의 문제
”각 단계의 근거를 설명하며 풀어봅시다”설명 가능성 강화근거 제시가 중요한 경우
”가정을 명확히 하고 순서대로 추론합시다”가정 명시 유도조건이 복잡한 문제

Few-shot CoT

추론 과정이 포함된 예시를 제공합니다. Zero-shot보다 더 정교한 추론 패턴을 유도할 수 있습니다.
def few_shot_cot(question):
    """Few-shot CoT: 추론 과정이 포함된 예시 제공"""
    system_prompt = """당신은 논리적 추론 전문가입니다.
문제를 풀 때 반드시 아래 형식을 따르세요:

[분석] 주어진 조건 정리
[풀이] 단계별 추론
[검증] 답변 검증
[정답] 최종 답변"""

    examples = """
## 예시 1
Q: 버스에 15명이 타고 있었습니다. 첫 번째 정류장에서 4명이 내리고 8명이 탔습니다.
   두 번째 정류장에서 6명이 내리고 3명이 탔습니다. 지금 몇 명이 타고 있나요?

[분석] 초기: 15명 / 정류장 2곳에서 승하차 발생
[풀이]
- 첫 번째 정류장: 15 - 4 + 8 = 19명
- 두 번째 정류장: 19 - 6 + 3 = 16명
[검증] 15 - 4 + 8 - 6 + 3 = 15 + (8+3) - (4+6) = 15 + 11 - 10 = 16 ✓
[정답] 16명

## 예시 2
Q: 도서관에 과학 책이 수학 책의 3배만큼 있습니다. 수학 책이 45권이라면,
   과학 책과 수학 책을 합쳐 총 몇 권인가요?

[분석] 과학 = 수학 × 3 / 수학 = 45권 / 구하기: 과학 + 수학
[풀이]
- 과학 책: 45 × 3 = 135권
- 전체: 45 + 135 = 180권
[검증] 135 ÷ 45 = 3 (3배 조건 충족) ✓ / 45 + 135 = 180 ✓
[정답] 180권
"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": f"{examples}\n\n## 문제\nQ: {question}"},
        ],
        temperature=0,
    )
    return response.choices[0].message.content

# 실행
print(few_shot_cot("사과 한 상자에 24개가 들어 있습니다. "
                    "3상자를 사서 친구 5명에게 균등하게 나누어주면 "
                    "1명당 몇 개를 받나요? 남는 사과는 몇 개인가요?"))

Self-Consistency

핵심 아이디어: 동일한 질문에 대해 여러 번 추론하고, 가장 많이 나온 답변(다수결)을 선택합니다. 하나의 추론 경로에서 발생한 오류를 다른 경로가 보정합니다.
import json
from collections import Counter

def self_consistency(question, n_paths=5, temperature=0.7):
    """Self-Consistency: 다수결로 최적 답변 선택"""
    answers = []

    for i in range(n_paths):
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "user",
                 "content": f"{question}\n\n단계별로 생각한 후, "
                            f"마지막 줄에 '정답: [답]' 형식으로 답하세요."},
            ],
            temperature=temperature,  # 다양한 추론 경로 유도
        )

        result = response.choices[0].message.content
        # 마지막 줄에서 정답 추출
        lines = result.strip().split("\n")
        for line in reversed(lines):
            if "정답:" in line:
                answer = line.split("정답:")[-1].strip()
                answers.append(answer)
                break

    # 다수결 투표
    vote_result = Counter(answers)
    best_answer = vote_result.most_common(1)[0]

    print(f"투표 결과: {dict(vote_result)}")
    print(f"최종 답: {best_answer[0]} ({best_answer[1]}/{n_paths} 표)")
    return best_answer[0]

# 실행
self_consistency("한 반에 학생이 30명 있습니다. 남학생이 여학생보다 4명 많습니다. "
                 "여학생은 몇 명인가요?")
# 투표 결과: {'13명': 4, '13': 1}
# 최종 답: 13명 (4/5 표)

CoT가 효과적인 경우 vs 그렇지 않은 경우

효과적인 경우효과 낮은 경우
수학/산술 추론단순 사실 질문 (“프랑스의 수도는?”)
논리 추론감성 분류 (“이 리뷰의 감성은?”)
다단계 문제 해결창의적 생성 (시, 소설 작성)
코드 디버깅번역
인과 관계 분석정보 추출 (NER)
상식 추론스타일 변환
CoT의 비용: 추론 과정을 생성하므로 출력 토큰이 크게 증가합니다. 간단한 태스크에 CoT를 적용하면 비용만 증가하고 성능 향상은 미미할 수 있습니다. Self-Consistency는 N번 호출하므로 비용이 N배가 됩니다. 코스트와 정확도의 트레이드오프를 고려하세요.

AI/ML 활용: 코드 디버깅에 CoT 적용

debug_prompt = """다음 Python 코드에 버그가 있습니다.
단계별로 분석하여 버그를 찾고 수정하세요.

[분석] 코드의 의도 파악
[추적] 변수 값 추적 (Line by Line)
[버그] 발견된 문제
[수정] 수정된 코드

```python
def find_average(numbers):
    total = 0
    for i in range(len(numbers)):
        total += numbers[i]
    return total / len(numbers) - 1
```"""

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": debug_prompt}],
    temperature=0,
)
print(response.choices[0].message.content)
# [분석] numbers 리스트의 평균을 구하려는 함수
# [추적] total = sum(numbers) → 올바름
# [버그] return total / len(numbers) - 1
#        → 연산자 우선순위 문제: (total/len) - 1 로 계산됨
#        → 의도: total / (len - 1) 이거나 단순 평균
# [수정] return total / len(numbers)
상황에 따라 다릅니다. Zero-shot CoT (“단계별로 생각하세요”)만으로 충분한 경우가 많습니다. 특히 GPT-4 수준의 모델에서는 Zero-shot CoT의 성능이 매우 높습니다. Few-shot CoT는 특정 추론 형식을 강제하고 싶을 때 유용합니다. 비용 대비 효과를 고려하여 선택하세요.
연구에 따르면 5~10개 경로가 일반적으로 효과적입니다. 경로 수를 늘릴수록 정확도가 향상되지만, 5개 이후로는 향상 폭이 줄어듭니다. 비용(N배 API 호출)과 정확도의 트레이드오프를 고려하세요. 중요한 결정에는 10개, 일반적인 경우에는 5개가 적절합니다.
CoT는 추론 과정을 투명하게 만들어 오류 발견을 용이하게 합니다. 잘못된 추론이 반복되면: 1) Few-shot 예시에 올바른 추론 패턴을 추가하세요. 2) “각 단계를 검증하세요” 지시를 추가하세요. 3) Self-Consistency로 다수결 검증을 적용하세요. 4) 더 강력한 모델(GPT-4o)을 사용하세요.

체크리스트

학습을 마치기 전에 아래 항목을 확인하세요.
  • CoT가 표준 프롬프팅보다 효과적인 이유를 설명할 수 있는가?
  • Zero-shot CoT와 Few-shot CoT의 차이를 이해하는가?
  • Self-Consistency의 다수결 메커니즘을 구현할 수 있는가?
  • CoT가 효과적인 태스크와 그렇지 않은 태스크를 구분할 수 있는가?
  • 비용과 정확도의 트레이드오프를 고려하여 적절한 기법을 선택할 수 있는가?

다음 문서