Home [AI 실전] Few-Shot Learning과 Chain of Thought (CoT) - 고급 기법
Post
Cancel

[AI 실전] Few-Shot Learning과 Chain of Thought (CoT) - 고급 기법

1. Few-Shot Prompting

개념

  • 모델에게 원하는 입력과 출력의 패턴(Pattern)을 미리 보여주는 기법
  • 비유: Unit Test Code 또는 Mock Data
    • “이 함수는 입력이 ‘A’일 때 ‘B’를 뱉어야 해”라고 assertEquals(A, B)를 보여주는 것과 동일
  • 활용: JSON 포맷팅, 말투 교정, 특정 코드 스타일 준수 시 필수

Zero-Shot vs Few-Shot 비교

비교 항목Zero-ShotFew-Shot
예제 제공없음2~5개 예제
정확도낮음 (추측 기반)높음 (패턴 학습)
토큰 사용적음많음 (예제 포함)
사용 사례간단한 질문정확한 형식 필요
개발 비유구두 요청Unit Test 제공

Zero-Shot 예시

1
2
3
4
5
6
7
8
9
10
11
12
❌ Zero-Shot Prompt:

"다음 요구사항을 SQL로 바꿔줘:
'2023년에 가입한 유저 중 주문이 3건 이상인 사람의 이메일 목록'"

AI 응답:
SELECT email 
FROM users 
WHERE signup_year = 2023 
  AND order_count >= 3;

→ 테이블 구조를 몰라서 틀린 쿼리!

Few-Shot 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
✅ Few-Shot Prompt:

"""
다음 패턴을 참고하여 자연어를 SQL로 변환해주세요:

[Example 1]
요구사항: "2022년에 가입한 유저 수"
SQL:
SELECT COUNT(*) 
FROM users 
WHERE YEAR(created_at) = 2022;

[Example 2]
요구사항: "주문 금액이 10만원 이상인 주문 목록"
SQL:
SELECT * 
FROM orders 
WHERE amount >= 100000;

[Example 3]
요구사항: "서울에 사는 유저 중 프리미엄 회원"
SQL:
SELECT * 
FROM users 
WHERE city = '서울' 
  AND membership_type = 'PREMIUM';

이제 다음 요구사항을 SQL로 변환해주세요:
"2023년에 가입한 유저 중 주문이 3건 이상인 사람의 이메일"
"""

AI 응답:
SELECT u.email
FROM users u
JOIN (
    SELECT user_id, COUNT(*) as order_count
    FROM orders
    GROUP BY user_id
    HAVING COUNT(*) >= 3
) o ON u.id = o.user_id
WHERE YEAR(u.created_at) = 2023;

→ 예제 패턴을 학습해서 정확한 쿼리 생성!

Few-Shot = Mock Data 제공

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Example 1 - Input]
User: { id: 1, email: "user1@test.com", city: "서울" }

[Example 1 - Output]
DTO: { userId: 1, email: "user1@test.com", location: "서울" }

[Example 2 - Input]
User: { id: 2, email: "user2@test.com", city: "부산" }

[Example 2 - Output]
DTO: { userId: 2, email: "user2@test.com", location: "부산" }

이제 다음 User를 DTO로 변환해줘:
User: { id: 3, email: "user3@test.com", city: "대전" }

실전 예시: 네이밍 컨벤션

1
2
3
4
5
❌ Zero-Shot:

"좋은 변수명 제안해줘"

AI: "data, info, result, value..." (너무 일반적)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
✅ Few-Shot:

"""
우리 팀의 네이밍 컨벤션:

[Example 1]
용도: 유저 목록 조회 결과
네이밍: foundUserList (동사 과거형 + 명사)

[Example 2]
용도: 주문 생성 요청 DTO
네이밍: createOrderRequest (동사원형 + 명사 + Request)

[Example 3]
용도: 상품 재고 확인 서비스
네이밍: checkProductStockService (동사원형 + 명사 + Service)

이제 다음 용도의 변수명을 제안해줘:
용도: 결제 검증 결과
"""

AI: validatedPaymentResult (패턴 일치!)

실전 예시: 에러 메시지 변환

1
2
3
4
5
6
❌ Zero-Shot:

"다음 에러를 사용자 친화적 메시지로 바꿔줘:
NullPointerException at line 42"

AI 응답: "오류가 발생했습니다" (너무 모호)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
✅ Few-Shot:

"""
다음 패턴으로 에러 메시지를 변환해주세요:

[Example 1]
Technical: "SQLException: Connection timeout"
User-Friendly: "데이터베이스 연결에 실패했습니다. 잠시 후 다시 시도해주세요."

[Example 2]
Technical: "IllegalArgumentException: Email format invalid"
User-Friendly: "이메일 형식이 올바르지 않습니다. 다시 확인해주세요."

[Example 3]
Technical: "AuthenticationException: Invalid credentials"
User-Friendly: "아이디 또는 비밀번호가 일치하지 않습니다."

이제 다음 에러를 변환해주세요:
Technical: "NullPointerException at UserService.getUser()"
"""

AI 응답: 
"사용자 정보를 불러오는 중 오류가 발생했습니다. 
관리자에게 문의해주세요."

2. Chain of Thought (CoT)

개념

  • 바로 답을 내지 않고, 중간 추론 과정을 단계별로 생성하게 유도하는 기법
  • 비유: Debugging 또는 Pseudo-code 작성
    • 복잡한 로직을 짤 때 바로 코딩하지 않고 주석으로 순서를 적는 행위
  • 효과: 수학 문제, 복잡한 비즈니스 로직 설계 시 정확도 대폭 상승

CoT의 매직 프롬프트

1
2
"Let's think step by step."
"단계별로 생각해봐."

Without CoT vs With CoT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
문제: "레스토랑에 48명의 손님이 있습니다. 
      각 테이블에 6명씩 앉는다면 테이블은 몇 개 필요할까요?
      하지만 12명이 나갔습니다."

❌ Without CoT:

AI: "8개의 테이블이 필요합니다."
→ 잘못된 답! (48 ÷ 6 = 8로 계산, 나간 사람 무시)

✅ With CoT:

AI:
"단계별로 생각해보겠습니다:

Step 1: 처음 손님 수 확인
- 초기 손님: 48명

Step 2: 나간 손님 계산
- 나간 손님: 12명
- 남은 손님: 48 - 12 = 36명

Step 3: 필요한 테이블 수 계산
- 테이블당 인원: 6명
- 필요 테이블: 36 ÷ 6 = 6개

따라서 6개의 테이블이 필요합니다."
→ 정확한 답!

개발자 비유: Pseudo Code

1
2
3
4
5
❌ Without CoT: 바로 코드 작성 (버그 가능성 높음)

public List<User> getActiveUsers() {
    return userRepository.findAll(); // 잘못된 로직!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
✅ With CoT: 의사 코드로 먼저 설계

/*
 * Pseudo Code (사고 과정):
 * 1. 모든 유저 조회
 * 2. 활성 상태인 유저만 필터링
 * 3. 마지막 로그인 날짜 순으로 정렬
 * 4. 결과 반환
 */

// 실제 구현
return userRepository.findAll().stream()
    .filter(user -> user.isActive())              // Step 2
    .sorted(Comparator.comparing(User::getLastLogin).reversed()) // Step 3
    .collect(Collectors.toList());                // Step 4

실전 예시: 복잡한 비즈니스 로직

1
2
3
4
5
6
7
8
9
10
❌ Without CoT:

"주문 취소 로직을 구현해줘"

AI:
@Service
public void cancelOrder(Long orderId) {
    orderRepository.deleteById(orderId);
}
→ 너무 단순! 결제 취소, 재고 복구 등 누락
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
✅ With CoT:

"""
주문 취소 로직을 구현해줘.

단계별로 생각해서 필요한 모든 처리를 포함해줘:
1. 무엇을 먼저 확인해야 할까?
2. 어떤 순서로 처리해야 할까?
3. 예외 상황은 무엇이 있을까?
"""

AI:
"""
단계별로 분석하겠습니다:

[Step 1: 주문 상태 확인]
- 주문이 취소 가능한 상태인가?
- 이미 배송 시작되었나?
- 결제 완료 상태인가?

[Step 2: 결제 취소]
- 결제 게이트웨이 API 호출
- 환불 처리

[Step 3: 재고 복구]
- 주문했던 상품들의 재고 증가
- 재고 히스토리 기록

[Step 4: 주문 상태 변경]
- 주문 상태를 '취소'로 변경
- 취소 사유 및 시간 기록

[Step 5: 알림 발송]
- 사용자에게 취소 알림
- 관리자에게 로그 전송

[구현 코드]
@Service
@Transactional
@RequiredArgsConstructor
public class OrderCancellationService {
    
    public void cancelOrder(Long orderId, String reason) {
        // Step 1: 주문 조회 및 검증
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
        
        if (!order.isCancellable()) {
            throw new OrderNotCancellableException();
        }
        
        // Step 2: 결제 취소
        if (order.isPaid()) {
            paymentService.refund(order.getPaymentId());
        }
        
        // Step 3: 재고 복구
        order.getOrderItems().forEach(item -> 
            inventoryService.increaseStock(
                item.getProductId(), 
                item.getQuantity()
            )
        );
        
        // Step 4: 주문 상태 변경
        order.cancel(reason);
        orderRepository.save(order);
        
        // Step 5: 알림 발송
        notificationService.notifyOrderCancelled(order);
    }
}
"""
→ 모든 엣지 케이스를 고려한 완벽한 구현!

실전 예시: SQL 쿼리 최적화

1
2
3
4
5
6
7
8
9
❌ Without CoT:

"이 쿼리를 최적화해줘:
SELECT * FROM orders WHERE user_id = 1;"

AI:
"인덱스를 추가하세요:
CREATE INDEX idx_user_id ON orders(user_id);"
→ 피상적인 조언
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
✅ With CoT:

"""
다음 쿼리를 최적화해줘. 단계별로 분석해서 설명해줘:

[현재 쿼리]
SELECT * FROM orders WHERE user_id = 1;

[테이블 정보]
- orders 테이블: 100만 건
- user_id별 평균 주문 수: 50건
"""

AI:
"""
단계별로 최적화를 분석하겠습니다:

[Step 1: 문제점 파악]
1. SELECT * 사용 → 불필요한 컬럼까지 조회
2. 인덱스 없음 → Full Table Scan 발생
3. 필요한 컬럼이 명시되지 않음

[Step 2: 실행 계획 예상]
- 현재: Full Table Scan (100만 건 스캔)
- 예상 시간: 2-3초
- I/O 비용: 매우 높음

[Step 3: 최적화 전략]
1. 필요한 컬럼만 SELECT
2. user_id에 인덱스 추가
3. 자주 조회되는 컬럼은 커버링 인덱스 고려

[Step 4: 최적화된 쿼리]
-- 인덱스 생성
CREATE INDEX idx_user_id ON orders(user_id);

-- 최적화된 쿼리
SELECT id, created_at, amount, status
FROM orders 
WHERE user_id = 1
ORDER BY created_at DESC;

[Step 5: 성능 개선 예상]
- Before: Full Scan (100만 건)
- After: Index Scan (50건만)
- 개선율: 약 20,000배
"""
→ 깊이 있는 분석과 구체적인 해결책!

3. Few-Shot + CoT 콤보

개념

  • Few-Shot과 CoT를 함께 사용하는 조합
  • 예시 제공(Few-Shot) + 단계별 사고(CoT)로 최고의 결과 도출

실전 예시: 레거시 코드 리팩토링

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
✅ Few-Shot + CoT 콤보:

"""
[Few-Shot Examples]
다음은 좋은 리팩토링 예시입니다:

[Example 1]
Before:
public void process(User u) {
    if(u!=null&&u.getAge()>=18){
        // ...
    }
}

After:
public void processAdultUser(User user) {
    if (isValidAdultUser(user)) {
        // ...
    }
}

Reasoning:
1. 네이밍 개선 (u → user)
2. 매직 넘버 제거 (18 → ADULT_AGE)
3. 조건 로직 메서드 추출

[Example 2]
Before:
public List<Order> getOrders() {
    List<Order> list = new ArrayList<>();
    for(Order o : orderRepository.findAll()) {
        if(o.getStatus().equals("PAID")) {
            list.add(o);
        }
    }
    return list;
}

After:
public List<Order> getPaidOrders() {
    return orderRepository.findAll().stream()
        .filter(Order::isPaid)
        .collect(Collectors.toList());
}

Reasoning:
1. 메서드명에 의도 명시
2. Stream API로 간결화
3. 문자열 비교 대신 메서드 사용

[Task]
이제 다음 레거시 코드를 리팩토링해주세요.
단계별로 생각하면서 진행해주세요:

[Legacy Code]
@Service
public class UserService {
    @Autowired
    UserRepository repo;
    
    public User get(Long id) {
        User u = repo.findById(id).get();
        if(u.getStatus() == 1) {
            return u;
        }
        return null;
    }
}

[Instructions]
1. 문제점을 단계별로 분석
2. 각 문제의 해결 방법 설명
3. 리팩토링된 코드 제시
4. 개선 사항 요약
"""

AI 응답:
"""
[Step 1: 문제점 분석]
1. NoSuchElementException 위험 (findById().get())
2. 매직 넘버 사용 (status == 1)
3. Null 반환 (NPE 위험)
4. 필드 주입 사용 (테스트 어려움)
5. 모호한 메서드명 (get())
6. 트랜잭션 미적용

[Step 2: 해결 전략]
1. Optional 처리 개선 → orElseThrow()
2. Enum으로 상태 관리
3. Optional 반환
4. 생성자 주입
5. 메서드명 개선
6. 트랜잭션 추가

[Step 3: 리팩토링된 코드]
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class UserService {
    
    private final UserRepository userRepository;
    
    public Optional<User> findActiveUser(Long userId) {
        return userRepository.findById(userId)
            .filter(User::isActive);
    }
    
    public User getActiveUser(Long userId) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException(userId));
        
        if (!user.isActive()) {
            throw new UserNotActiveException(userId);
        }
        
        return user;
    }
}

[Step 4: 개선 사항 요약]
- 안정성 향상: 예외 처리 명확화
- 가독성 향상: 매직 넘버 → Enum
- 유지보수성 향상: 생성자 주입, 불변성 보장
"""

4. 활용 가이드

상황별 추천 기법

상황추천 기법이유
정확한 형식 필요Few-Shot패턴 학습으로 일관성 확보
복잡한 논리CoT단계별 검증으로 오류 방지
레거시 리팩토링Few-Shot + CoT예시 + 논리적 분석
코드 생성Few-Shot + 낮은 Temperature일관성 + 정확성
버그 분석CoT논리적 디버깅 과정

참고 자료

  • Few-Shot Learning: https://arxiv.org/abs/2005.14165
  • Chain of Thought Prompting: https://arxiv.org/abs/2201.11903
  • OpenAI Best Practices: https://platform.openai.com/docs/guides/prompt-engineering
Contents

[AI 실전] 프롬프트 설계 - Temperature, Persona, 함수 호출 관점

[AI 실전] Zero-shot, Few-shot, CoT 비교 및 선택 가이드