한국어
Week 2: 추론
주말 프로젝트

주말 프로젝트: 자가 수정 코더

노트북: week2_reasoning/project_self_correcting_coder.ipynb

핵심 질문

더 작고 저렴한 모델이 "더 많이 생각함"으로써 거대한 모델을 이길 수 있을까?

이 프로젝트에서는 코드를 한 번만 작성하는 것이 아니라 실행하고, 테스트하고, 반복적으로 수정하는 자가 수정 코딩 에이전트를 구축하여 이 가설을 검증합니다.

프로젝트 개요

세 가지 접근법을 비교합니다:

접근법모델전략
Naive GPT-4ogpt-4o일회성: "이 코드를 작성해"
Agentic GPT-4o-minigpt-4o-mini리플렉션: "작성, 테스트, 수정, 반복"
Agentic Llama-3llama-3-8b오픈 모델로 동일한 패턴

아키텍처: 코드용 리플렉션 패턴

자가 수정 코더는 코드 생성에 리플렉션 패턴을 적용합니다:

도전 과제 문제

차이를 보려면 다음과 같은 문제가 필요합니다:

  1. 첫 시도에서 틀리기 쉬움
  2. 검증이 쉬움 (명확한 통과/실패 테스트)

문제: Basic Calculator II "3+2*2" 같은 간단한 수식을 평가하는 계산기를 구현하세요.

제약 조건:

  • 음이 아닌 정수, +, -, *, / 및 공백만 포함
  • 정수 나눗셈은 0 방향으로 버림
  • eval() 사용 금지 - 파싱 로직을 직접 구현
  • 연산자 우선순위 처리 (*/+-보다 먼저)

왜 어려운가?: Naive 구현은 종종:

  • 왼쪽에서 오른쪽으로 실행 (우선순위 무시)
  • 여러 자릿수 숫자 파싱 오류
  • 엣지 케이스 잘못 처리

테스트 케이스

TEST_CASES = [
    ("3+2*2", 7),       # 10이 아님! 우선순위가 중요
    (" 3/2 ", 1),       # 0 방향으로 버림
    (" 3+5 / 2 ", 5),   # 공백과 혼합 연산자
    ("42", 42),         # 단일 숫자
    ("100*2+12", 212),  # 여러 자릿수 숫자
    ("1-1+1", 1),       # 같은 우선순위는 왼쪽에서 오른쪽
    ("2*3*4", 24),      # 연쇄 연산자
]

구현

Step 1: Naive 접근법

가장 강력한 모델로 일회성 생성:

def naive_solve(model_name, problem):
    response = client.chat.completions.create(
        model=model_name,
        messages=[
            {"role": "system", "content": "당신은 Python 전문가입니다. 코드만 출력하세요."},
            {"role": "user", "content": problem}
        ]
    )
    return response.choices[0].message.content
 
# 실행
code_gpt4o = naive_solve("gpt-4o", PROBLEM_DESCRIPTION)

Step 2: 테스트 인프라

코드를 실행하고 테스트 케이스로 검증:

def run_and_test(code_str, test_cases):
    try:
        # 격리된 네임스페이스에서 실행
        local_env = {}
        exec(code_str, {}, local_env)
 
        solve_func = local_env.get('calculate')
        if not solve_func:
            return False, "'calculate' 함수를 찾을 수 없습니다."
 
        # 테스트 케이스 실행
        for inp, expected in test_cases:
            result = solve_func(inp)
            if result != expected:
                return False, f"'{inp}'에서 실패. 예상: {expected}, 결과: {result}"
 
        return True, "모든 테스트 통과!"
 
    except Exception as e:
        return False, f"에러: {e}"
⚠️

보안 주의: exec()는 샌드박스 환경에서 자신의 에이전트가 생성한 코드에만 사용하세요. 신뢰할 수 없는 코드는 절대 실행하지 마세요.

Step 3: Agentic 접근법

더 작은 모델로 자가 수정 루프:

def agentic_solve(model_name, problem, max_retries=3):
    history = [
        {"role": "system", "content": "당신은 Python 전문가입니다. 유효한 Python 코드만 출력하세요. eval() 사용 금지."},
        {"role": "user", "content": problem}
    ]
 
    for i in range(max_retries):
        print(f"🔄 시도 {i+1} ({model_name})...")
 
        # 1. 생성
        response = client.chat.completions.create(
            model=model_name,
            messages=history
        )
        code = clean_code(response.choices[0].message.content)
 
        # 2. 테스트
        success, feedback = run_and_test(code, TEST_CASES)
        print(f"   결과: {feedback}")
 
        if success:
            return code, i+1  # 코드와 시도 횟수 반환
 
        # 3. 리플렉션 - 에러를 히스토리에 추가
        history.append({"role": "assistant", "content": code})
        history.append({
            "role": "user",
            "content": f"코드가 다음 에러로 실패했습니다: {feedback}\n\n수정해주세요."
        })
 
    return None, max_retries

대결: 결과

Naive GPT-4o (일회성)

--- Naive GPT-4o ---
통과? True (모든 테스트 통과!)

강력한 모델은 한 번에 맞추는 경우가 많지만... 항상은 아닙니다.

Agentic GPT-4o-mini (자가 수정)

--- Agentic GPT-4o-mini ---
🔄 시도 1 (gpt-4o-mini)...
   결과: '3+2*2'에서 실패. 예상: 7, 결과: 10
🔄 시도 2 (gpt-4o-mini)...
   결과: 모든 테스트 통과!
✅ 2번 시도로 해결!

작은 모델은 처음에 틀리지만 (우선순위 무시), 에러 피드백을 받으면 스스로 수정합니다.

비용 분석

접근법모델호출 수쿼리당 예상 비용
NaiveGPT-4o1~$0.015
AgenticGPT-4o-mini × 3≤3~$0.0015
절감~10배 저렴

핵심 인사이트: 3번의 재시도를 해도 GPT-4o-mini를 사용한 agentic 접근법은 단일 GPT-4o 호출보다 약 10배 저렴하며, 에러를 잡아서 수정할 수 있어 종종 더 신뢰할 수 있습니다.

자가 수정이 효과적인 이유

  1. 에러 신호: 실행 에러가 구체적이고 실행 가능한 피드백 제공
  2. 집중된 컨텍스트: 모델이 정확히 무엇이 잘못됐는지 알 수 있음
  3. 반복적 개선: 각 시도가 이전 학습을 기반으로 구축
  4. 안전망: 강력한 모델도 하는 사소한 실수를 잡아냄

프로젝트 확장하기

다음 도전 과제를 시도해보세요:

더 많은 테스트 케이스 추가

Naive 구현을 깨뜨리는 엣지 케이스 생성

타임아웃 구현

무한 루프를 잡기 위한 실행 타임아웃 추가

다국어 지원

JavaScript, Rust 또는 다른 언어로 확장

로컬 모델 사용

Ollama + Llama-3로 완전 로컬 실행 시도

디버깅 설명 추가

에이전트가 무엇을 왜 변경했는지 설명하게 하기

고급: 시도의 트리

선형 재시도 대신 병렬 탐색 시도:

def parallel_solve(problem, num_candidates=3):
    # 여러 솔루션을 병렬로 생성
    candidates = [generate_code(problem) for _ in range(num_candidates)]
 
    # 모든 후보 테스트
    for code in candidates:
        success, _ = run_and_test(code, TEST_CASES)
        if success:
            return code
 
    # 모두 실패하면 최선의 실패를 선택하고 반복
    return iterative_fix(best_candidate)

핵심 요점

  1. 에이전시가 순수 성능을 이긴다 - 자가 수정 루프가 일회성 생성보다 종종 더 나음
  2. 검증이 핵심 - 명확한 통과/실패 테스트가 리플렉션 루프를 가능하게 함
  3. 비용 효율성 - 작은 모델 + 반복이 더 저렴하고 더 신뢰할 수 있음
  4. 에러 컨텍스트가 중요 - 구체적인 에러 메시지가 더 나은 수정을 유도

참고 자료 & 추가 학습

학술 논문

다음 단계

Week 2를 완료한 것을 축하합니다! 배운 내용:

  • Chain of Thought 프롬프팅
  • Plan-and-Execute 에이전트
  • 리플렉션 및 자가 수정 패턴
  • 구조화된 추론 기법

Week 3에서는 멀티 에이전트 시스템을 탐구합니다—여러 전문화된 에이전트가 협력하여 복잡한 문제를 함께 해결합니다.

노트북 실행

jupyter notebook week2_reasoning/project_self_correcting_coder.ipynb