🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Least-to-Most Prompting 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 25. · 2 Views

Least-to-Most Prompting 완벽 가이드

복잡한 문제를 작은 단위로 쪼개어 순차적으로 해결하는 Least-to-Most Prompting 전략을 배웁니다. LLM의 문제 해결 능력을 극대화하는 프롬프팅 기법을 실무 예제와 함께 살펴봅니다.


목차

  1. 문제 분해 전략
  2. 하위 문제부터 해결
  3. 점진적 복잡도 증가
  4. 컨텍스트 누적 관리
  5. 실습: 복잡한 코딩 문제 분해
  6. 실습: 수학 증명 단계별 해결

1. 문제 분해 전략

어느 날 김개발 씨는 AI 챗봇에게 복잡한 알고리즘 문제를 물어봤습니다. 하지만 답변은 중간에 꼬이고, 논리가 엉망이었습니다.

선배 박시니어 씨가 말했습니다. "한 번에 다 물어보지 말고, 작게 쪼개서 물어봐야 해요."

Least-to-Most Prompting은 복잡한 문제를 작은 하위 문제로 분해한 뒤, 가장 쉬운 것부터 순차적으로 해결하는 프롬프팅 기법입니다. 마치 큰 건물을 짓기 위해 먼저 기초 공사부터 시작하는 것과 같습니다.

이 기법을 사용하면 LLM이 복잡한 추론 과정을 단계별로 처리할 수 있습니다.

다음 코드를 살펴봅시다.

# Least-to-Most Prompting 기본 구조
def least_to_most_prompting(complex_problem):
    # 1단계: 문제 분해
    subproblems = decompose_problem(complex_problem)

    # 2단계: 하위 문제부터 순차 해결
    solutions = []
    context = ""

    for subproblem in subproblems:
        # 이전 해결책을 컨텍스트로 활용
        prompt = f"{context}\n문제: {subproblem}\n해결책:"
        solution = llm_call(prompt)
        solutions.append(solution)
        # 컨텍스트 누적
        context += f"\n{subproblem}{solution}"

    return solutions

김개발 씨는 입사 6개월 차 AI 엔지니어입니다. 오늘 팀 회의에서 중요한 과제를 받았습니다.

LLM을 활용해 복잡한 수학 문제를 자동으로 푸는 시스템을 개발하는 것이었습니다. 처음에는 자신감이 있었습니다.

"그냥 문제를 통째로 입력하면 되겠지." 하지만 결과는 참담했습니다. 간단한 문제는 잘 풀었지만, 여러 단계를 거쳐야 하는 복잡한 문제는 중간에 논리가 꼬이거나 엉뚱한 답을 내놓았습니다.

고민하던 김개발 씨에게 박시니어 씨가 조언을 건넸습니다. "김개발 씨, 사람도 어려운 문제를 풀 때 한 번에 다 풀지 않잖아요?

작게 쪼개서 하나씩 풀어나가죠. AI도 마찬가지예요." 그렇습니다.

Least-to-Most Prompting은 바로 이런 원리를 활용한 기법입니다. 쉽게 비유하자면, 큰 피자를 먹을 때를 생각해봅시다.

피자를 통째로 먹으려 하면 힘들지만, 한 조각씩 먹으면 쉽습니다. 게다가 첫 번째 조각을 먹으면서 "아, 이 피자는 좀 짜네"라는 정보를 얻고, 두 번째 조각을 먹을 때 이를 참고할 수 있습니다.

Least-to-Most Prompting도 이처럼 문제를 작게 나누고, 앞에서 얻은 정보를 뒤에서 활용합니다. 기존 프롬프팅 방식의 문제점은 무엇이었을까요?

전통적인 Zero-shot Prompting이나 Few-shot Prompting은 문제를 한 번에 해결하려고 시도합니다. 간단한 문제에는 효과적이지만, 복잡한 추론이 필요한 경우 LLM의 컨텍스트 윈도우가 꼬이거나, 중간 단계를 건너뛰는 실수를 범합니다.

더 큰 문제는 디버깅이 어렵다는 점입니다. 어느 단계에서 잘못되었는지 찾기가 매우 힘듭니다.

바로 이런 문제를 해결하기 위해 Least-to-Most Prompting이 등장했습니다. 이 기법을 사용하면 단계별 추론이 가능해집니다.

각 하위 문제의 해결책이 명확하게 드러나므로, 어디서 잘못되었는지 쉽게 파악할 수 있습니다. 또한 컨텍스트 누적을 통해 이전 단계의 정보를 다음 단계에서 활용할 수 있습니다.

무엇보다 복잡한 문제도 안정적으로 해결할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 decompose_problem 함수를 통해 복잡한 문제를 여러 개의 하위 문제로 분해합니다. 이 부분이 핵심입니다.

다음으로 반복문을 돌면서 각 하위 문제를 순차적으로 해결합니다. 중요한 점은 context 변수에 이전 해결책을 계속 누적한다는 것입니다.

마지막으로 모든 하위 문제의 해결책을 리스트로 반환합니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 법률 자문 AI 서비스를 개발한다고 가정해봅시다. "계약서 검토 후 리스크 분석"이라는 복잡한 작업을 Least-to-Most Prompting으로 분해하면 다음과 같습니다.

  1. 계약서에서 주요 조항 추출 → 2) 각 조항의 법적 의미 분석 → 3) 잠재적 리스크 식별 → 4) 개선 방안 제시. 이렇게 단계를 나누면 각 단계의 정확도가 높아지고, 전체 결과도 신뢰할 수 있습니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 문제를 너무 작게 쪼개는 것입니다.

이렇게 하면 오히려 전체 맥락이 흐려지고, LLM 호출 횟수가 지나치게 많아져 비용이 증가합니다. 따라서 적절한 크기의 하위 문제로 분해하는 것이 중요합니다.

일반적으로 3-7개 정도의 단계가 적절합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 조언을 듣고 김개발 씨는 문제를 단계별로 분해했습니다. "아, 이렇게 하니까 훨씬 결과가 좋네요!" 문제 분해 전략을 제대로 이해하면 LLM의 추론 능력을 극대화할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 문제를 3-7개 정도의 적절한 크기로 분해하세요

  • 각 하위 문제는 독립적으로도 의미가 있어야 합니다
  • 첫 번째 단계는 가장 쉽고 명확한 것으로 시작하세요

2. 하위 문제부터 해결

김개발 씨가 문제를 여러 개로 쪼개는 데는 성공했습니다. 하지만 어떤 순서로 풀어야 할지 막막했습니다.

박시니어 씨가 웃으며 말했습니다. "제일 쉬운 것부터 시작하세요.

기초가 탄탄해야 다음 단계도 제대로 풀립니다."

하위 문제부터 해결하는 것은 Least-to-Most Prompting의 핵심 원칙입니다. 가장 기본적이고 단순한 문제를 먼저 해결한 뒤, 그 결과를 바탕으로 점차 복잡한 문제로 나아갑니다.

마치 수학에서 기본 공식을 먼저 배운 뒤 응용 문제를 푸는 것과 같습니다.

다음 코드를 살펴봅시다.

# 하위 문제 순서 결정 예제
def solve_from_easiest(problem_statement):
    # 문제 분해 단계
    decomposition_prompt = f"""
다음 문제를 하위 문제로 분해하세요.
가장 쉬운 것부터 어려운 순서로 나열하세요.

문제: {problem_statement}

하위 문제 목록:
"""
    subproblems = llm_call(decomposition_prompt)

    # 가장 쉬운 문제부터 순차 해결
    accumulated_knowledge = ""

    for i, subproblem in enumerate(subproblems):
        solving_prompt = f"""
이전 단계 결과:
{accumulated_knowledge}

현재 문제: {subproblem}
해결 방법을 설명하세요.
"""
        solution = llm_call(solving_prompt)
        accumulated_knowledge += f"\n단계 {i+1}: {solution}"

    return accumulated_knowledge

김개발 씨는 이제 문제를 작게 쪼개는 방법을 알았습니다. 하지만 새로운 고민이 생겼습니다.

"어떤 순서로 풀어야 하지?" 예를 들어 "웹 애플리케이션 성능 최적화"라는 문제를 세 개의 하위 문제로 나눴다고 가정해봅시다. A) 데이터베이스 쿼리 최적화, B) 프론트엔드 렌더링 개선, C) 캐싱 전략 수립.

어디서부터 시작해야 할까요? 박시니어 씨가 힌트를 주었습니다.

"김개발 씨, 집을 지을 때 지붕부터 만들지 않잖아요? 기초 공사부터 시작하죠.

문제도 마찬가지예요." 이것이 바로 하위 문제부터 해결하는 원칙입니다. 쉽게 비유하자면, 요리를 할 때를 생각해봅시다.

복잡한 코스 요리를 만든다고 할 때, 먼저 기본 육수를 만들고, 그 다음 전처리를 하고, 마지막으로 조리를 합니다. 만약 순서를 바꿔서 조리부터 시작하면 제대로 된 요리가 나올 수 없습니다.

Least-to-Most Prompting도 이처럼 기초적인 정보부터 차근차근 쌓아 올립니다. 왜 순서가 중요할까요?

LLM은 컨텍스트에 크게 의존합니다. 만약 복잡한 문제를 먼저 던지면, LLM은 필요한 기초 정보가 없는 상태에서 추론을 시작합니다.

이는 마치 구구단을 모르는 학생에게 미적분을 가르치는 것과 같습니다. 결과적으로 추론 오류가 발생하기 쉽고, 논리적 비약이 생깁니다.

반대로 가장 쉬운 문제부터 시작하면 어떻게 될까요? 첫 번째 단계에서 기초적인 사실과 개념을 확립합니다.

두 번째 단계에서는 첫 번째 결과를 기반으로 조금 더 복잡한 추론을 합니다. 세 번째 단계에서는 앞의 두 단계에서 얻은 정보를 모두 활용해 최종 답을 도출합니다.

이렇게 하면 추론의 일관성이 유지되고, 오류 발생률이 크게 감소합니다. 위의 코드를 자세히 살펴보겠습니다.

먼저 decomposition_prompt에서 LLM에게 문제를 분해하되, "가장 쉬운 것부터 어려운 순서로"라는 명확한 지시를 줍니다. 이것이 핵심입니다.

LLM은 이 지시에 따라 자연스럽게 난이도 순으로 하위 문제를 정렬합니다. 그 다음 반복문에서 각 하위 문제를 순차적으로 해결하면서, accumulated_knowledge 변수에 이전 단계의 결과를 계속 추가합니다.

실무에서 이 원칙은 어떻게 활용될까요? 고객 지원 챗봇을 개발하는 경우를 생각해봅시다.

고객이 "환불이 안 되는데 왜 그런가요?"라고 물었을 때, 바로 환불 거부 이유를 설명하기보다는 다음 순서로 접근합니다. 1) 주문 정보 확인 (가장 쉬운 단계) → 2) 환불 정책 조회 → 3) 고객 케이스와 정책 비교 → 4) 거부 사유 설명 및 대안 제시.

이렇게 단계를 밟으면 고객에게 훨씬 설득력 있는 답변을 제공할 수 있습니다. 하지만 주의할 점이 있습니다.

"가장 쉬운 것"의 기준이 애매할 수 있습니다. 일반적으로는 사실 확인 단계가 가장 쉽고, 추론이나 판단이 필요한 단계가 어렵습니다.

예를 들어 "데이터 조회"는 쉽지만, "데이터를 바탕으로 미래 예측"은 어렵습니다. 이런 기준으로 난이도를 구분하면 됩니다.

김개발 씨는 이제 확신을 가지고 코드를 작성했습니다. 가장 쉬운 하위 문제부터 순서대로 풀어나가니, LLM의 답변 품질이 눈에 띄게 개선되었습니다.

하위 문제부터 해결하는 전략을 마스터하면, 복잡한 AI 추론 시스템을 안정적으로 구축할 수 있습니다. 여러분의 프로젝트에도 이 원칙을 적용해 보세요.

실전 팁

💡 - 사실 확인 단계를 가장 먼저 배치하세요

  • 추론이나 판단은 나중 단계로 미루세요
  • 각 단계의 출력을 명확하게 구조화하면 다음 단계에서 활용하기 쉽습니다

3. 점진적 복잡도 증가

김개발 씨의 시스템이 점점 안정화되었습니다. 하지만 여전히 중간 단계에서 가끔 헷갈리는 답변이 나왔습니다.

박시니어 씨가 코드를 보더니 말했습니다. "각 단계의 난이도 차이가 너무 커요.

계단을 오르듯이 조금씩 어려워져야 합니다."

점진적 복잡도 증가는 각 하위 문제의 난이도를 조금씩 높여가는 전략입니다. 1단계와 2단계 사이의 간극이 너무 크면 LLM이 논리적 비약을 일으킬 수 있습니다.

마치 계단을 오를 때 한 계단씩 올라가야 하는 것처럼, 추론도 부드럽게 이어져야 합니다.

다음 코드를 살펴봅시다.

# 점진적 복잡도 증가 예제
def gradual_complexity_increase(base_problem):
    # 난이도별 하위 문제 설계
    stages = [
        {
            "level": "기초",
            "task": "문제에서 주어진 조건과 변수를 나열하세요",
            "complexity": 1
        },
        {
            "level": "분석",
            "task": "각 조건이 어떻게 연관되는지 관계를 파악하세요",
            "complexity": 3
        },
        {
            "level": "추론",
            "task": "관계를 바탕으로 중간 결과를 도출하세요",
            "complexity": 5
        },
        {
            "level": "종합",
            "task": "모든 중간 결과를 통합해 최종 답을 구하세요",
            "complexity": 7
        }
    ]

    context = f"원본 문제: {base_problem}\n\n"

    for stage in stages:
        prompt = f"{context}[난이도 {stage['complexity']}/10]\n{stage['task']}"
        result = llm_call(prompt)
        context += f"{stage['level']} 단계 결과: {result}\n\n"

    return context

김개발 씨의 시스템은 대부분 잘 작동했지만, 가끔 이상한 결과가 나왔습니다. 특히 2단계에서 3단계로 넘어갈 때 LLM이 갑자기 엉뚱한 추론을 하는 경우가 있었습니다.

박시니어 씨가 로그를 살펴보더니 원인을 찾았습니다. "여기 봐요.

2단계는 너무 쉽고, 3단계는 갑자기 너무 어려워요. 중간 다리가 없어서 LLM이 비약을 하는 거예요." 이것이 바로 점진적 복잡도 증가가 필요한 이유입니다.

쉽게 비유하자면, 헬스장에서 운동할 때를 생각해봅시다. 처음에는 5kg 아령으로 시작합니다.

그 다음 7kg, 10kg, 15kg 순으로 무게를 올립니다. 만약 5kg에서 바로 50kg으로 뛰어넘으면 부상을 입기 쉽습니다.

LLM의 추론도 마찬가지입니다. 난이도가 급격히 증가하면 논리적 오류가 발생합니다.

기존 방식의 문제점은 무엇이었을까요? 많은 개발자들이 하위 문제를 나눌 때 단계 수만 고려합니다.

하지만 각 단계 사이의 난이도 차이는 신경 쓰지 않습니다. 예를 들어 3개의 단계로 나눴는데, 1단계는 난이도 1, 2단계는 난이도 2, 3단계는 난이도 9라면 어떻게 될까요?

2단계에서 3단계로 넘어갈 때 LLM은 필요한 중간 추론 과정을 건너뛰게 됩니다. 점진적 복잡도 증가 전략을 사용하면 어떻게 개선될까요?

각 단계의 난이도 간격을 균등하게 조정합니다. 예를 들어 1 → 3 → 5 → 7처럼 일정한 간격으로 난이도를 높입니다.

이렇게 하면 LLM이 각 단계에서 필요한 추론을 충분히 수행할 수 있고, 논리적 연결성이 유지됩니다. 또한 디버깅할 때도 어느 단계에서 문제가 생겼는지 쉽게 파악할 수 있습니다.

위의 코드를 분석해보겠습니다. stages 배열에서 각 단계마다 complexity 값을 명시적으로 지정합니다.

1, 3, 5, 7로 균등하게 증가하는 것을 볼 수 있습니다. 이것이 핵심입니다.

각 단계의 task 설명도 점점 복잡해집니다. "나열" → "관계 파악" → "중간 결과 도출" → "최종 통합" 순으로 자연스럽게 이어집니다.

context 변수에 각 단계의 결과를 누적하면서, LLM은 이전 정보를 바탕으로 다음 단계를 수행합니다. 실무에서는 어떻게 활용할까요?

금융 투자 자문 AI를 개발한다고 가정해봅시다. "포트폴리오 최적화 추천"이라는 복잡한 작업을 다음과 같이 단계별로 나눕니다.

  1. 고객의 현재 자산 현황 정리 (난이도 1) → 2) 위험 성향 분석 (난이도 3) → 3) 시장 상황과 고객 성향 매칭 (난이도 5) → 4) 구체적 투자 비율 계산 (난이도 7) → 5) 리스크 시나리오 분석 (난이도 9). 이렇게 점진적으로 난이도를 높이면 LLM이 안정적으로 복잡한 금융 추론을 수행할 수 있습니다.

주의할 점도 있습니다. 난이도를 너무 세밀하게 나누면 단계가 지나치게 많아집니다.

예를 들어 1, 2, 3, 4, 5, 6, 7, 8, 9, 10처럼 10단계로 나누면 오히려 비효율적입니다. LLM 호출 비용도 증가하고, 전체 처리 시간도 길어집니다.

적절한 균형이 중요합니다. 보통 4-6단계 정도가 효율적입니다.

또 다른 실수는 난이도를 주관적으로 판단하는 것입니다. "이 정도면 쉽겠지"라고 생각했는데 막상 LLM은 어려워할 수 있습니다.

따라서 실제 테스트를 통해 각 단계의 성공률을 측정하고, 난이도가 급격히 떨어지는 구간이 있다면 중간 단계를 추가해야 합니다. 김개발 씨는 난이도 간격을 조정한 뒤 다시 테스트했습니다.

"와, 정확도가 15% 이상 올랐어요!" 박시니어 씨가 미소 지었습니다. "이제 제대로 된 Least-to-Most Prompting이네요." 점진적 복잡도 증가 전략을 마스터하면, LLM이 복잡한 추론도 안정적으로 수행할 수 있습니다.

여러분의 AI 시스템에도 이 원칙을 적용해 보세요.

실전 팁

💡 - 각 단계 사이의 난이도 간격을 균등하게 유지하세요

  • 실제 테스트를 통해 난이도를 검증하세요
  • 보통 4-6단계 정도가 효율과 정확도의 균형점입니다

4. 컨텍스트 누적 관리

김개발 씨의 시스템이 거의 완성 단계에 이르렀습니다. 하지만 5단계쯤 가면 컨텍스트가 너무 길어져서 LLM이 초반 정보를 잊어버리는 문제가 생겼습니다.

박시니어 씨가 말했습니다. "무조건 다 쌓는 게 능사가 아니에요.

중요한 정보만 정리해서 넘겨야 해요."

컨텍스트 누적 관리는 각 단계의 결과를 다음 단계로 전달할 때, 불필요한 정보는 제거하고 핵심만 요약해서 넘기는 기법입니다. 컨텍스트가 무한정 길어지면 LLM의 성능이 저하되고 비용도 증가합니다.

마치 여행 가방을 쌀 때 꼭 필요한 것만 챙기는 것처럼, 컨텍스트도 선별적으로 관리해야 합니다.

다음 코드를 살펴봅시다.

# 컨텍스트 누적 관리 예제
def managed_context_accumulation(problem, max_context_length=2000):
    subproblems = decompose_problem(problem)

    # 핵심 정보만 저장하는 요약 컨텍스트
    summarized_context = {
        "facts": [],          # 확정된 사실
        "intermediate": [],   # 중간 결과
        "assumptions": []     # 가정
    }

    full_history = ""  # 전체 이력 (참고용)

    for i, subproblem in enumerate(subproblems):
        # 현재 단계에 필요한 정보만 선택
        relevant_facts = "\n".join(summarized_context["facts"][-3:])
        relevant_intermediate = "\n".join(summarized_context["intermediate"][-2:])

        prompt = f"""
이전 확정 사실:
{relevant_facts}

이전 중간 결과:
{relevant_intermediate}

현재 문제: {subproblem}
"""
        solution = llm_call(prompt)

        # 새로운 정보 분류 및 저장
        new_info = extract_key_info(solution)
        if new_info["type"] == "fact":
            summarized_context["facts"].append(new_info["content"])
        elif new_info["type"] == "result":
            summarized_context["intermediate"].append(new_info["content"])

        full_history += f"단계 {i+1}: {solution}\n"

    return summarized_context, full_history

김개발 씨의 시스템이 드디어 완성 단계에 이르렀습니다. 대부분의 문제를 잘 해결했지만, 하나의 문제가 남아 있었습니다.

단계가 많은 복잡한 문제를 풀 때, 후반부로 갈수록 정확도가 떨어지는 현상이었습니다. 로그를 확인해보니 원인을 알 수 있었습니다.

컨텍스트가 너무 길어져서 LLM의 컨텍스트 윈도우 한계에 도달했고, 결과적으로 초반의 중요한 정보를 놓치고 있었습니다. 박시니어 씨가 설명했습니다.

"LLM의 컨텍스트 윈도우는 무한하지 않아요. 게다가 너무 길면 중간 부분의 정보는 잘 활용하지 못하는 'Lost in the Middle' 현상이 생기죠." 이것이 바로 컨텍스트 누적 관리가 필요한 이유입니다.

쉽게 비유하자면, 회의록을 작성할 때를 생각해봅시다. 5시간짜리 회의의 모든 대화를 다 기록하면 나중에 읽기도 힘들고, 핵심을 파악하기 어렵습니다.

대신 각 안건별로 결론액션 아이템만 정리하면 훨씬 효율적입니다. 컨텍스트 관리도 이와 같습니다.

무분별한 컨텍스트 누적의 문제점은 무엇일까요? 첫째, 토큰 비용이 기하급수적으로 증가합니다.

각 단계마다 이전 모든 내용을 포함하면, 10단계쯤 가면 컨텍스트 길이가 수만 토큰에 달할 수 있습니다. 둘째, 처리 시간이 느려집니다.

LLM은 긴 컨텍스트를 처리하는 데 더 많은 시간이 걸립니다. 셋째, 정확도가 저하됩니다.

너무 많은 정보 속에서 정말 중요한 것을 놓칠 수 있습니다. 컨텍스트 누적 관리를 적용하면 어떻게 개선될까요?

각 단계에서 핵심 정보만 추출해서 다음 단계로 넘깁니다. 위의 코드에서 summarized_context 객체를 보세요.

정보를 "확정된 사실", "중간 결과", "가정" 세 가지 카테고리로 분류합니다. 그리고 각 단계에서는 최근 2-3개의 항목만 선택해서 사용합니다.

이렇게 하면 컨텍스트 길이를 일정하게 유지하면서도 필요한 정보는 모두 보존할 수 있습니다. 코드를 더 자세히 살펴보겠습니다.

relevant_facts = "\n".join(summarized_context["facts"][-3:])이 부분이 핵심입니다. 파이썬의 슬라이싱을 사용해 최근 3개의 사실만 선택합니다.

오래된 정보는 필요할 때만 full_history에서 찾을 수 있습니다. extract_key_info 함수는 LLM의 출력에서 새로운 사실인지, 중간 결과인지를 분류합니다.

이렇게 정보를 구조화하면 나중에 필요한 정보를 빠르게 찾을 수 있습니다. 실무에서는 어떻게 활용할까요?

의료 진단 지원 시스템을 개발한다고 가정해봅시다. 환자의 증상, 검사 결과, 과거 병력 등을 단계별로 분석할 때, 모든 정보를 계속 전달하면 컨텍스트가 폭발합니다.

대신 다음과 같이 관리합니다. "확정 진단" 카테고리에는 최종 확정된 소견만 저장, "의심 질환" 카테고리에는 현재 의심되는 질환 상위 3개만 유지, "제외된 질환" 카테고리는 별도 기록.

이렇게 하면 최종 단계에서도 명확하게 추론할 수 있습니다. 주의할 점이 있습니다.

너무 공격적으로 요약하면 중요한 정보를 잃을 수 있습니다. 예를 들어 "환자가 페니실린 알레르기가 있다"는 사실은 절대 잊으면 안 됩니다.

따라서 카테고리별로 다른 보존 전략을 사용해야 합니다. "절대 필수 정보"는 모든 단계에 포함하고, "참고 정보"는 최근 몇 개만 유지하는 식입니다.

또 다른 팁은 압축 기법을 활용하는 것입니다. 여러 개의 유사한 사실을 하나로 통합할 수 있습니다.

예를 들어 "사용자는 빨간색을 좋아한다", "사용자는 파란색을 싫어한다", "사용자는 노란색을 좋아한다"를 "사용자 선호 색상: 빨강, 노랑 (파랑 제외)"로 압축할 수 있습니다. 김개발 씨가 컨텍스트 관리 로직을 추가한 뒤 다시 테스트했습니다.

"이제 10단계가 넘는 복잡한 문제도 정확하게 풀어요!" 박시니어 씨가 칭찬했습니다. "완벽한 Least-to-Most Prompting 시스템이네요." 컨텍스트 누적 관리를 마스터하면, 아무리 복잡한 다단계 추론도 안정적으로 처리할 수 있습니다.

여러분의 AI 시스템에도 이 기법을 적용해 보세요.

실전 팁

💡 - 정보를 카테고리별로 분류해서 관리하세요

  • 절대 필수 정보와 참고 정보를 구분하세요
  • 최근 2-3개 항목만 유지하면 대부분의 경우 충분합니다

5. 실습: 복잡한 코딩 문제 분해

이론은 충분히 배웠습니다. 이제 실전입니다.

김개발 씨는 LeetCode Hard 난이도 문제를 LLM에게 풀게 하는 실습을 시작했습니다. 박시니어 씨가 문제를 하나 던졌습니다.

"이 동적 계획법 문제를 Least-to-Most Prompting으로 풀어보세요."

복잡한 코딩 문제를 Least-to-Most Prompting으로 해결하는 실전 예제입니다. 동적 계획법 문제는 여러 하위 문제를 해결해야 하므로, 이 기법을 적용하기에 완벽한 사례입니다.

실제 코드와 함께 단계별 분해 과정을 살펴봅니다.

다음 코드를 살펴봅시다.

# 복잡한 코딩 문제 분해 예제: 최장 공통 부분 수열(LCS)
def solve_lcs_with_least_to_most(str1, str2):
    # 1단계: 문제 이해 및 조건 정리
    stage1_prompt = f"""
두 문자열의 최장 공통 부분 수열 문제입니다.
입력: str1="{str1}", str2="{str2}"

다음을 답하세요:
1) 부분 수열이란 무엇인가?
2) 이 문제의 입력과 출력은?
"""
    understanding = llm_call(stage1_prompt)

    # 2단계: 기본 케이스 분석
    stage2_prompt = f"""
{understanding}

가장 단순한 경우를 분석하세요:
1) 빈 문자열이면?
2) 한 글자만 같으면?
"""
    base_cases = llm_call(stage2_prompt)

    # 3단계: 재귀 관계식 도출
    stage3_prompt = f"""
{base_cases}

재귀 관계식을 만드세요:
LCS(i, j)는 str1[:i]와 str2[:j]의 LCS 길이일 때,
- str1[i] == str2[j]이면?
- str1[i] != str2[j]이면?
"""
    recurrence = llm_call(stage3_prompt)

    # 4단계: 동적 계획법 테이블 설계
    stage4_prompt = f"""
{recurrence}

DP 테이블을 설계하세요:
1) 테이블 크기는?
2) 초기값은?
3) 채우는 순서는?
"""
    dp_design = llm_call(stage4_prompt)

    # 5단계: 최종 코드 생성
    stage5_prompt = f"""
{dp_design}

Python 코드로 구현하세요.
"""
    final_code = llm_call(stage5_prompt)

    return final_code

김개발 씨 앞에 어려운 코딩 문제가 놓였습니다. "두 문자열의 최장 공통 부분 수열을 구하시오." 동적 계획법을 사용해야 하는 전형적인 Hard 난이도 문제였습니다.

처음에는 문제를 그대로 LLM에게 던졌습니다. 하지만 LLM이 생성한 코드는 논리 오류가 있었고, 엣지 케이스를 처리하지 못했습니다.

"역시 한 번에는 안 되는구나." 박시니어 씨가 조언했습니다. "Least-to-Most Prompting을 사용해보세요.

문제를 작은 단계로 쪼개는 거예요." 복잡한 코딩 문제를 Least-to-Most 방식으로 접근하면 어떻게 될까요? 먼저 문제를 5단계로 분해합니다.

  1. 문제 이해 및 조건 정리 → 2) 기본 케이스 분석 → 3) 재귀 관계식 도출 → 4) DP 테이블 설계 → 5) 최종 코드 생성. 각 단계는 이전 단계의 결과를 바탕으로 진행됩니다.

이렇게 접근하면 무엇이 좋을까요? 첫째, LLM이 문제를 제대로 이해합니다.

1단계에서 "부분 수열"의 정의부터 명확히 하므로, 나중에 잘못된 가정을 하지 않습니다. 둘째, 기본 케이스를 놓치지 않습니다.

2단계에서 빈 문자열, 한 글자 같은 엣지 케이스를 먼저 생각하므로, 최종 코드에서도 이를 반영합니다. 셋째, 논리적 일관성이 유지됩니다.

3단계에서 재귀 관계식을 정확히 도출한 뒤, 4단계에서 이를 DP로 변환하므로 논리 오류가 없습니다. 위의 코드를 단계별로 살펴보겠습니다.

stage1_prompt에서는 문제의 정의부터 확인합니다. "부분 수열이란 무엇인가?"라는 질문으로 시작합니다.

이것이 중요합니다. LLM이 문제를 잘못 이해하면 모든 다음 단계가 틀어집니다.

stage2_prompt에서는 가장 쉬운 케이스부터 분석합니다. 동적 계획법의 핵심인 기본 케이스를 먼저 확립하는 것입니다.

stage3_prompt가 가장 중요한 단계입니다. 재귀 관계식을 도출하는 것은 동적 계획법 문제의 핵심입니다.

이전 단계에서 기본 케이스를 이미 정리했으므로, LLM은 이를 바탕으로 재귀 관계식을 자연스럽게 유도할 수 있습니다. stage4_prompt에서는 재귀를 DP 테이블로 변환하는 방법을 묻습니다.

마지막 stage5_prompt에서 비로소 코드 생성을 요청합니다. 실제로 이 방식을 사용한 결과는 어땠을까요?

김개발 씨가 테스트해본 결과, 한 번에 문제를 던졌을 때는 정확도가 60% 정도였지만, Least-to-Most Prompting을 사용하니 95% 이상으로 올라갔습니다. 특히 엣지 케이스 처리와 논리적 일관성이 크게 개선되었습니다.

다른 종류의 코딩 문제에도 적용할 수 있을까요? 물론입니다.

그래프 알고리즘 문제라면 1) 그래프 표현 방법 결정 → 2) 탐색 전략 선택 → 3) 방문 처리 로직 설계 → 4) 종료 조건 정의 → 5) 최종 구현 순으로 나눌 수 있습니다. 백트래킹 문제라면 1) 상태 공간 정의 → 2) 유효성 검사 조건 → 3) 가지치기 전략 → 4) 종료 조건 → 5) 구현 순입니다.

주의할 점이 있습니다. 모든 문제를 5단계로 나눌 필요는 없습니다.

문제의 복잡도에 따라 유연하게 조정하세요. 간단한 문제는 3단계로도 충분하고, 매우 복잡한 문제는 7-8단계로 나눌 수도 있습니다.

중요한 것은 각 단계가 명확한 목적을 가지고, 이전 단계의 결과를 활용한다는 점입니다. 김개발 씨는 이제 자신감이 생겼습니다.

"이 방법으로 어떤 Hard 문제도 풀 수 있을 것 같아요!" 박시니어 씨가 웃으며 말했습니다. "맞아요.

문제를 제대로 분해하면 LLM은 훌륭한 코딩 파트너가 됩니다." 복잡한 코딩 문제를 Least-to-Most Prompting으로 분해하는 기술을 마스터하면, LLM을 활용한 알고리즘 학습과 개발이 훨씬 효과적이 됩니다. 여러분도 다음 코딩 테스트에서 이 기법을 활용해 보세요.

실전 팁

💡 - 문제 이해 단계를 절대 건너뛰지 마세요

  • 기본 케이스부터 시작하면 엣지 케이스를 놓치지 않습니다
  • 재귀 관계식을 먼저 도출한 뒤 코드로 변환하세요

6. 실습: 수학 증명 단계별 해결

코딩 문제는 성공했습니다. 이제 더 어려운 도전입니다.

박시니어 씨가 수학 증명 문제를 내밀었습니다. "이번에는 귀납법 증명을 LLM에게 시켜볼까요?" 김개발 씨는 긴장했지만, Least-to-Most Prompting이면 가능할 것 같았습니다.

수학 증명은 LLM에게 가장 어려운 작업 중 하나입니다. 논리적 비약이 있으면 안 되고, 각 단계가 엄밀하게 이어져야 합니다.

Least-to-Most Prompting을 활용하면 복잡한 수학 증명도 단계별로 구성할 수 있습니다. 귀납법 증명을 예로 실습합니다.

다음 코드를 살펴봅시다.

# 수학 증명 단계별 해결: 수학적 귀납법
def prove_by_induction_least_to_most(statement, variable="n"):
    # 1단계: 명제 이해 및 구조 파악
    stage1_prompt = f"""
다음 명제를 증명하려 합니다:
"{statement}"

다음을 답하세요:
1) 이 명제는 무엇을 주장하는가?
2) 변수 {variable}의 범위는?
3) 귀납법을 사용할 수 있는가?
"""
    understanding = llm_call(stage1_prompt)

    # 2단계: 기저 사례 증명
    stage2_prompt = f"""
{understanding}

기저 사례를 증명하세요:
{variable}=1일 때 명제가 성립함을 보이세요.
각 계산 단계를 명시하세요.
"""
    base_case = llm_call(stage2_prompt)

    # 3단계: 귀납 가정 설정
    stage3_prompt = f"""
{base_case}

귀납 가정을 설정하세요:
{variable}=k일 때 명제가 성립한다고 가정합니다.
이를 수식으로 표현하세요.
"""
    induction_hypothesis = llm_call(stage3_prompt)

    # 4단계: 귀납 단계 증명 (분해)
    stage4_prompt = f"""
{induction_hypothesis}

{variable}=k+1일 때를 증명하기 위해 다음을 수행하세요:
1) {variable}=k+1을 명제에 대입
2) 귀납 가정을 어디에 사용할지 파악
3) 대수적 변형 필요 여부 확인
"""
    induction_step_plan = llm_call(stage4_prompt)

    # 5단계: 귀납 단계 상세 계산
    stage5_prompt = f"""
{induction_step_plan}

각 변형 단계를 상세히 계산하세요:
- 귀납 가정 적용
- 대수적 변형
- 최종 결과 도출
"""
    induction_step_detail = llm_call(stage5_prompt)

    # 6단계: 증명 종합
    stage6_prompt = f"""
{induction_step_detail}

증명을 정리하세요:
1) 기저 사례
2) 귀납 가정
3) 귀납 단계
4) 결론
"""
    final_proof = llm_call(stage6_prompt)

    return final_proof

김개발 씨 앞에 수학 문제가 놓였습니다. "1 + 2 + 3 + ...

  • n = n(n+1)/2 를 수학적 귀납법으로 증명하시오." 고등학교 때 배운 내용이지만, LLM에게 정확하게 증명하도록 만드는 것은 다른 문제였습니다. 처음 시도에서 LLM은 중간 단계를 건너뛰고 결론으로 점프했습니다.

"귀납 가정을 사용하면 자명하다"는 식의 엉성한 증명이었습니다. 이런 증명은 수학적으로 인정받을 수 없습니다.

박시니어 씨가 조언했습니다. "수학 증명은 특히 엄밀함이 중요해요.

각 단계를 명확하게 나눠야 합니다." 수학 증명에 Least-to-Most Prompting을 적용하면 어떻게 달라질까요? 수학적 귀납법은 본질적으로 단계적 구조를 가지고 있습니다.

기저 사례 → 귀납 가정 → 귀납 단계 → 결론. 하지만 각 부분을 더 세분화할 수 있습니다.

특히 귀납 단계는 계획과 실행으로 나누면 훨씬 명확해집니다. 이렇게 접근하면 무엇이 개선될까요?

첫째, 논리적 비약이 사라집니다. 각 단계마다 명확한 질문을 던지므로, LLM이 중간 과정을 건너뛸 수 없습니다.

둘째, 계산 실수가 줄어듭니다. 5단계에서 "각 변형 단계를 상세히"라고 명시하므로, LLM이 대수적 변형을 하나씩 수행합니다.

셋째, 증명의 구조가 명확해집니다. 6단계에서 전체를 정리하므로, 완결된 형태의 증명이 나옵니다.

위의 코드를 단계별로 분석해보겠습니다. stage1_prompt는 명제를 이해하는 단계입니다.

"귀납법을 사용할 수 있는가?"라는 질문이 중요합니다. 모든 명제가 귀납법으로 증명되는 것은 아니므로, 먼저 적용 가능성을 확인합니다.

stage2_prompt에서 기저 사례를 증명할 때 "각 계산 단계를 명시"하라고 강조합니다. 이것이 핵심입니다.

stage3_prompt는 귀납 가정을 명확히 수식으로 표현하도록 요구합니다. "n=k일 때 성립한다"는 말이 아니라, 실제 수식으로 적어야 다음 단계에서 활용하기 쉽습니다.

stage4_prompt가 특히 중요합니다. 귀납 단계를 바로 계산하는 것이 아니라, 계획을 먼저 세우도록 합니다.

"귀납 가정을 어디에 사용할지"를 먼저 파악하면, 계산할 때 방향성이 명확해집니다. stage5_prompt에서 비로소 상세한 계산을 수행합니다.

이전 단계에서 계획을 세웠으므로, 이제는 각 단계를 기계적으로 실행하면 됩니다. stage6_prompt에서 전체를 정리하면서, 표준적인 증명 형식으로 만듭니다.

실제로 이 방법을 사용한 결과는 어땠을까요? 김개발 씨가 테스트한 결과, 한 번에 증명을 요청했을 때는 10번 중 3번 정도만 완벽한 증명이 나왔습니다.

나머지는 중간 단계가 불명확하거나 논리적 비약이 있었습니다. 하지만 Least-to-Most Prompting을 사용하니 10번 중 9번은 완벽한 증명을 생성했습니다.

다른 종류의 수학 증명에도 적용할 수 있을까요? 물론입니다.

모순을 이용한 증명이라면 1) 가정 설정 → 2) 논리적 추론 → 3) 모순 도출 → 4) 결론 순으로 나눌 수 있습니다. 직접 증명이라면 1) 전제 확인 → 2) 정의 및 정리 적용 → 3) 논리적 추론 → 4) 결론 순입니다.

핵심은 각 증명 방법의 구조를 파악하고, 그에 맞게 단계를 설계하는 것입니다. 주의할 점이 있습니다.

수학 증명은 엄밀성이 생명입니다. "자명하다", "명백하다" 같은 표현으로 중간 과정을 생략하면 안 됩니다.

따라서 각 프롬프트에서 "상세히", "각 단계를", "명시적으로" 같은 표현을 사용해 LLM이 세부 사항을 생략하지 않도록 해야 합니다. 또 다른 팁은 기호와 수식을 명확히 하는 것입니다.

자연어로만 설명하면 애매할 수 있으므로, 각 단계에서 수식을 명시적으로 적도록 요구하세요. 예를 들어 "귀납 가정: 1 + 2 + ...

  • k = k(k+1)/2"처럼 수식으로 적으면 다음 단계에서 정확히 활용할 수 있습니다. 김개발 씨는 감탄했습니다.

"수학 증명까지 LLM이 할 수 있다니!" 박시니어 씨가 덧붙였습니다. "물론 LLM이 만든 증명도 사람이 검토해야 해요.

하지만 Least-to-Most Prompting을 사용하면 검토하기 훨씬 쉬운 구조화된 증명이 나옵니다." 수학 증명을 단계별로 해결하는 기술을 마스터하면, LLM을 수학 학습과 연구의 강력한 도구로 활용할 수 있습니다. 특히 복잡한 증명의 초안을 작성하거나, 증명 아이디어를 검증할 때 유용합니다.

여러분도 다음 수학 문제에서 이 기법을 시도해 보세요.

실전 팁

💡 - 각 단계에서 "상세히", "명시적으로" 같은 표현을 사용해 생략을 방지하세요

  • 귀납 단계는 계획과 실행으로 나누면 논리가 명확해집니다
  • 최종 단계에서 전체를 정리하면 표준적인 증명 형식이 완성됩니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#LLM#Prompting#ProblemSolving#ChainOfThought#ContextManagement#LLM,문제분해,프롬프트

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.