⏱️ 예상 읽기 시간: 25분

서론

AI 개발의 패러다임이 급속히 변화하고 있습니다. 전통적인 프롬프트 엔지니어링은 문자열 기반의 취약한 접근법으로, 복잡한 AI 시스템을 구축할 때 한계가 명확합니다.

DSPy (Declarative Self-improving Python)Stanford NLP에서 개발한 혁신적인 프레임워크로, 이러한 문제를 근본적으로 해결합니다. “Programming—not prompting—LMs”라는 철학 하에, AI 개발을 어셈블리에서 C로, 포인터 연산에서 SQL로 발전시킨 것과 같은 차원 높은 변화를 제공합니다.

DSPy가 해결하는 핵심 문제

  1. 취약한 문자열 기반 프롬프트: 하드코딩된 프롬프트는 모델이나 작업이 바뀔 때마다 수동으로 수정해야 합니다
  2. 확장성 부족: 복잡한 AI 파이프라인을 구성할 때 각 단계를 개별적으로 최적화해야 하는 번거로움
  3. 이식성 문제: 특정 모델에 최적화된 프롬프트는 다른 모델에서 성능이 떨어집니다
  4. 체계적 최적화 부재: 프롬프트 품질을 개선하는 과정이 수동적이고 비체계적입니다

DSPy의 혁신적 접근법

DSPy는 Modules, Signatures, Optimizers라는 세 가지 핵심 개념을 통해 AI 프로그래밍을 혁신합니다:

  • Modules: AI 동작을 코드로 설명하는 구성 요소
  • Signatures: 입력-출력 스키마를 선언적으로 정의
  • Optimizers: 프롬프트와 가중치를 자동으로 튜닝하는 알고리즘

이 글에서는 DSPy를 실제 macOS 환경에서 설치부터 고급 활용까지 단계별로 실습해보겠습니다.

1. DSPy 핵심 개념 이해

1.1 Modules: AI 동작을 코드로 설명하기

DSPy의 Module은 AI 동작을 선언적으로 정의하는 구성 요소입니다. 전통적인 프롬프트 엔지니어링에서 벗어나 코드로 AI 동작을 기술합니다.

# 기존 프롬프트 방식 (취약함)
prompt = "다음 질문에 답하세요: {question}"

# DSPy 방식 (구조화됨)
class QuestionAnswering(dspy.Module):
    def __init__(self):
        super().__init__()
        self.generate_answer = dspy.ChainOfThought("question -> answer")
    
    def forward(self, question):
        return self.generate_answer(question=question)

1.2 Signatures: 입출력 스키마 선언

Signature는 AI 모듈의 입력과 출력 형식을 명확하게 정의합니다. 이를 통해 타입 안전성명확한 인터페이스를 보장합니다.

# 간단한 시그니처
simple_sig = dspy.Signature("question -> answer")

# 상세한 시그니처  
class DetailedQA(dspy.Signature):
    """상세한 질문-답변 시그니처"""
    context = dspy.InputField(desc="관련 배경 정보")
    question = dspy.InputField(desc="사용자 질문")
    
    reasoning = dspy.OutputField(desc="단계별 추론 과정")
    answer = dspy.OutputField(desc="최종 답변")
    confidence = dspy.OutputField(desc="신뢰도 (1-10)")

1.3 Optimizers: 자동 프롬프트 최적화

DSPy의 가장 강력한 기능은 자동 최적화입니다. Optimizer는 여러분의 데이터와 메트릭을 기반으로 프롬프트와 모델 가중치를 자동으로 개선합니다.

# 데이터와 메트릭 정의
trainset = [dspy.Example(question="...", answer="..."), ...]
metric = lambda gold, pred, trace: gold.answer == pred.answer

# 최적화 실행
optimizer = dspy.BootstrapFewShot(metric=metric)
optimized_module = optimizer.compile(qa_module, trainset=trainset)

2. macOS 환경 설정 및 설치

2.1 개발 환경 요구사항

DSPy를 효과적으로 사용하기 위한 macOS 환경 요구사항은 다음과 같습니다:

# 시스템 요구사항 확인
python --version  # Python 3.8 이상 권장
pip --version     # 최신 pip 권장

테스트 환경:

  • OS: macOS (Apple Silicon/Intel 모두 지원)
  • Python: 3.12.8
  • DSPy: 3.0.1 (2025년 8월 기준 최신 버전)

2.2 가상환경 설정 및 DSPy 설치

안전한 개발을 위해 가상환경을 설정하고 DSPy를 설치합니다:

# 프로젝트 디렉토리 생성
mkdir -p ~/dspy-tutorial && cd ~/dspy-tutorial

# Python 가상환경 생성 및 활성화
python -m venv dspy-env
source dspy-env/bin/activate

# DSPy 최신 버전 설치
pip install -U dspy

실제 설치 결과:

Successfully installed dspy-3.0.1 openai-1.100.2 litellm-1.75.9 
pydantic-2.11.7 optuna-4.5.0 ...

DSPy는 다음과 같은 주요 의존성을 포함합니다:

  • LiteLLM: 다양한 LLM 제공업체 통합
  • OpenAI: GPT 모델 지원
  • Optuna: 하이퍼파라미터 최적화
  • Pydantic: 데이터 검증 및 직렬화

3. DSPy 기본 구조 실습

3.1 Signature 정의 실습

DSPy의 핵심인 Signature를 다양한 방법으로 정의해보겠습니다:

#!/usr/bin/env python3
import dspy

# 1. 문자열 기반 간단한 시그니처
basic_sig = dspy.Signature("question -> answer")
print(f"기본 시그니처: {basic_sig}")

# 2. 다중 필드 시그니처
multi_sig = dspy.Signature("context, question -> answer")
print(f"다중 필드 시그니처: {multi_sig}")

# 3. 클래스 기반 상세 시그니처
class AdvancedQA(dspy.Signature):
    """고급 질문-답변 시그니처"""
    context = dspy.InputField(desc="관련 컨텍스트 정보")
    question = dspy.InputField(desc="사용자 질문")
    
    reasoning = dspy.OutputField(desc="단계별 추론 과정")
    answer = dspy.OutputField(desc="최종 답변")
    confidence = dspy.OutputField(desc="답변 신뢰도 (1-10)")

print(f"고급 시그니처 입력 필드: {AdvancedQA.input_fields}")
print(f"고급 시그니처 출력 필드: {AdvancedQA.output_fields}")

실행 결과:

기본 시그니처: StringSignature(question -> answer
    instructions='Given the fields `question`, produce the fields `answer`.'
    question = Field(annotation=str required=True ...)
    answer = Field(annotation=str required=True ...)
)

고급 시그니처 입력 필드: ['context', 'question']
고급 시그니처 출력 필드: ['reasoning', 'answer', 'confidence']

3.2 Module 구성 실습

다양한 유형의 DSPy Module을 구성해보겠습니다:

# 1. 기본 Predict 모듈
predictor = dspy.Predict("question -> answer")
print(f"기본 Predict 모듈: {predictor}")

# 2. Chain of Thought 모듈 
cot_module = dspy.ChainOfThought("question -> answer")
print(f"CoT 모듈: {cot_module}")

# 3. 사용자 정의 복합 모듈
class MultiStepReasoning(dspy.Module):
    def __init__(self):
        super().__init__()
        self.analyze = dspy.ChainOfThought("question -> analysis")
        self.synthesize = dspy.Predict("analysis -> conclusion")
        
    def forward(self, question):
        analysis_result = self.analyze(question=question)
        final_result = self.synthesize(analysis=analysis_result.answer)
        
        return dspy.Prediction(
            question=question,
            analysis=analysis_result.answer,
            conclusion=final_result.conclusion
        )

multi_step = MultiStepReasoning()
print(f"다단계 추론 모듈: {type(multi_step).__name__}")

3.3 데이터 구조 실습

DSPy의 데이터 구조인 Example과 Prediction을 활용해보겠습니다:

# Example 객체 생성
example = dspy.Example(
    question="파이썬에서 리스트의 특징은?",
    answer="순서가 있고 변경 가능한 데이터 컬렉션입니다.",
    context="Python 프로그래밍 언어의 기본 데이터 타입"
)

print(f"예제: {example}")
print(f"질문: {example.question}")
print(f"답변: {example.answer}")

# 입력만 있는 예제로 변환
input_only = example.with_inputs("question", "context")
print(f"입력 전용 예제: {input_only}")

# Prediction 객체 생성
prediction = dspy.Prediction(
    answer="리스트는 대괄호로 정의하며 다양한 타입을 저장할 수 있습니다.",
    confidence=9,
    reasoning="Python 문서와 실습 경험을 바탕으로 한 답변입니다."
)

print(f"예측 결과: {prediction}")
print(f"신뢰도: {prediction.confidence}")

실행 결과:

예제: Example({'question': '파이썬에서 리스트의 특징은?', 'answer': '순서가 있고 변경 가능한 데이터 컬렉션입니다.', 'context': 'Python 프로그래밍 언어의 기본 데이터 타입'}) (input_keys=None)

입력 전용 예제: Example({...}) (input_keys={'context', 'question'})

예측 결과: Prediction(
    answer='리스트는 대괄호로 정의하며 다양한 타입을 저장할 수 있습니다.',
    confidence=9,
    reasoning='Python 문서와 실습 경험을 바탕으로 한 답변입니다.'
)

4. 실제 AI 모델 연결 및 활용

4.1 언어 모델 설정

DSPy는 다양한 언어 모델 제공업체를 지원합니다. 실제 프로덕션 환경에서 사용하는 방법을 알아보겠습니다:

import dspy
import os

# 1. OpenAI GPT 모델 설정
openai_lm = dspy.LM(
    "openai/gpt-4o-mini",
    api_key=os.getenv("OPENAI_API_KEY")  # 환경변수에서 키 로드
)

# 2. Anthropic Claude 모델 설정  
claude_lm = dspy.LM(
    "anthropic/claude-3-opus-20240229",
    api_key=os.getenv("ANTHROPIC_API_KEY")
)

# 3. 로컬 모델 설정 (Ollama)
local_lm = dspy.LM(
    "ollama/llama2",
    api_base="http://localhost:11434"
)

# DSPy에 모델 설정
dspy.configure(lm=openai_lm)

4.2 실제 추론 예제

이제 실제 언어 모델을 사용한 추론을 실행해보겠습니다:

# 기본 질문-답변 모듈
qa_module = dspy.ChainOfThought("question -> answer")

# 실제 추론 실행
result = qa_module(question="DSPy가 기존 프롬프트 엔지니어링보다 좋은 점은?")

print(f"질문: {result.question}")
print(f"추론 과정: {result.reasoning}")
print(f"답변: {result.answer}")

예상 출력 (실제 LM 연결 시):

질문: DSPy가 기존 프롬프트 엔지니어링보다 좋은 점은?

추론 과정: DSPy의 장점을 체계적으로 분석해보겠습니다.
1) 구조화된 접근: 문자열 기반이 아닌 코드 기반으로 AI 동작을 정의
2) 자동 최적화: 수동 프롬프트 튜닝 대신 자동화된 최적화
3) 모듈 재사용: 컴포넌트 기반으로 복잡한 파이프라인 구성 가능
4) 이식성: 다양한 모델 간 코드 재사용 가능

답변: DSPy는 선언적 프로그래밍 패러다임을 통해 더 안정적이고 확장 가능한 AI 시스템을 구축할 수 있게 해줍니다.

4.3 복잡한 AI 파이프라인 구축

실제 애플리케이션에서 사용할 수 있는 복잡한 AI 파이프라인을 구축해보겠습니다:

class IntelligentQASystem(dspy.Module):
    """지능형 질문-답변 시스템"""
    
    def __init__(self):
        super().__init__()
        self.retrieve_context = dspy.ChainOfThought(
            "question -> relevant_topics"
        )
        self.analyze_question = dspy.ChainOfThought(
            "question, relevant_topics -> question_type, complexity"
        )
        self.generate_answer = dspy.ChainOfThought(
            "question, question_type, complexity -> reasoning, answer"
        )
        self.verify_answer = dspy.Predict(
            "question, answer, reasoning -> confidence, explanation"
        )
    
    def forward(self, question):
        # 1단계: 관련 주제 추출
        context = self.retrieve_context(question=question)
        
        # 2단계: 질문 분석
        analysis = self.analyze_question(
            question=question,
            relevant_topics=context.relevant_topics
        )
        
        # 3단계: 답변 생성
        answer = self.generate_answer(
            question=question,
            question_type=analysis.question_type,
            complexity=analysis.complexity
        )
        
        # 4단계: 답변 검증
        verification = self.verify_answer(
            question=question,
            answer=answer.answer,
            reasoning=answer.reasoning
        )
        
        return dspy.Prediction(
            question=question,
            relevant_topics=context.relevant_topics,
            question_type=analysis.question_type,
            complexity=analysis.complexity,
            reasoning=answer.reasoning,
            answer=answer.answer,
            confidence=verification.confidence,
            explanation=verification.explanation
        )

# 시스템 인스턴스 생성
intelligent_qa = IntelligentQASystem()

5. DSPy Optimizer 활용하기

5.1 데이터셋 준비

최적화를 위한 훈련 데이터셋을 준비합니다:

# 훈련용 예제 데이터 생성
training_examples = [
    dspy.Example(
        question="Python에서 딕셔너리란?",
        answer="키-값 쌍으로 데이터를 저장하는 해시 테이블 기반 자료구조입니다."
    ),
    dspy.Example(
        question="머신러닝과 딥러닝의 차이점은?",
        answer="머신러닝은 알고리즘의 상위 개념이고, 딥러닝은 신경망을 사용하는 머신러닝의 하위 분야입니다."
    ),
    dspy.Example(
        question="RESTful API의 특징은?",
        answer="무상태성, 캐시 가능성, 계층화된 시스템, 인터페이스 일관성을 특징으로 하는 아키텍처 스타일입니다."
    ),
    dspy.Example(
        question="Docker와 가상머신의 차이점은?",
        answer="Docker는 OS 레벨 가상화로 더 가볍고 빠르며, 가상머신은 하드웨어 레벨 가상화로 더 강한 격리를 제공합니다."
    ),
    dspy.Example(
        question="Git의 주요 명령어들은?",
        answer="git add, git commit, git push, git pull, git branch, git merge 등이 있으며, 각각 스테이징, 커밋, 원격 저장소 동기화 등의 역할을 합니다."
    )
]

# 입력만 있는 데이터로 변환 (테스트용)
test_examples = [ex.with_inputs("question") for ex in training_examples]

print(f"훈련 예제 수: {len(training_examples)}")
print(f"테스트 예제 수: {len(test_examples)}")

5.2 평가 메트릭 정의

모델 성능을 평가할 메트릭을 정의합니다:

def accuracy_metric(gold, pred, trace=None):
    """정확도 기반 메트릭"""
    if hasattr(pred, 'answer') and hasattr(gold, 'answer'):
        return gold.answer.lower() == pred.answer.lower()
    return False

def semantic_similarity_metric(gold, pred, trace=None):
    """의미적 유사도 기반 메트릭"""
    if hasattr(pred, 'answer') and hasattr(gold, 'answer'):
        # 단어 기반 Jaccard 유사도
        gold_words = set(gold.answer.lower().split())
        pred_words = set(pred.answer.lower().split())
        
        intersection = len(gold_words & pred_words)
        union = len(gold_words | pred_words)
        
        return intersection / union if union > 0 else 0.0
    return 0.0

def comprehensive_metric(gold, pred, trace=None):
    """종합 평가 메트릭"""
    semantic_score = semantic_similarity_metric(gold, pred, trace)
    
    # 길이 점수 (너무 짧거나 긴 답변 패널티)
    if hasattr(pred, 'answer'):
        answer_length = len(pred.answer.split())
        length_score = 1.0 if 10 <= answer_length <= 100 else 0.5
    else:
        length_score = 0.0
    
    # 종합 점수 (가중 평균)
    return 0.7 * semantic_score + 0.3 * length_score

5.3 다양한 Optimizer 활용

DSPy의 강력한 optimizer들을 활용해보겠습니다:

# 1. BootstrapFewShot: 예시 기반 최적화
bootstrap_optimizer = dspy.BootstrapFewShot(
    metric=comprehensive_metric,
    max_bootstrapped_demos=4,  # 사용할 예시 수
    max_labeled_demos=2,       # 라벨된 예시 수
    num_threads=1              # 병렬 처리 스레드 수
)

# 2. MIPRO: 고급 명령어 최적화 (실제 사용 시)
mipro_optimizer = dspy.MIPROv2(
    metric=comprehensive_metric,
    prompt_model="gpt-4o-mini",  # 프롬프트 생성용 모델
    task_model="gpt-4o-mini",    # 실제 작업용 모델
    num_candidates=10            # 후보 프롬프트 수
)

# 기본 QA 모듈 정의
qa_module = dspy.ChainOfThought("question -> answer")

# 최적화 실행 (실제 API 키가 있는 경우)
try:
    optimized_qa = bootstrap_optimizer.compile(
        qa_module,
        trainset=training_examples
    )
    print("✅ 최적화 완료!")
except Exception as e:
    print(f"⚠️ 최적화 실행 중 오류 (API 키 필요): {e}")

5.4 성능 평가 및 비교

최적화 전후의 성능을 비교해보겠습니다:

def evaluate_module(module, test_data, metric_func):
    """모듈 성능 평가"""
    scores = []
    predictions = []
    
    for example in test_data:
        try:
            # 예측 실행
            pred = module(question=example.question)
            predictions.append(pred)
            
            # 메트릭 계산 (정답이 있는 경우)
            if hasattr(example, 'answer'):
                score = metric_func(example, pred)
                scores.append(score)
                
        except Exception as e:
            print(f"예측 실행 중 오류: {e}")
            scores.append(0.0)
    
    avg_score = sum(scores) / len(scores) if scores else 0.0
    
    return {
        'average_score': avg_score,
        'predictions': predictions,
        'individual_scores': scores
    }

# 성능 비교 (더미 결과)
print("📊 성능 평가 결과:")
print("원본 모듈 성능: 65.2%")
print("최적화된 모듈 성능: 87.8%")
print("성능 향상: +22.6%")

# 상세 분석
print("\n📈 세부 분석:")
print("- 의미적 유사도: 12.3% 향상") 
print("- 답변 완성도: 18.7% 향상")
print("- 일관성: 15.4% 향상")

6. 고급 활용 사례

6.1 RAG (Retrieval-Augmented Generation) 구현

DSPy를 활용한 고급 RAG 시스템을 구현해보겠습니다:

class AdvancedRAGSystem(dspy.Module):
    """고급 RAG 시스템"""
    
    def __init__(self, retriever=None):
        super().__init__()
        self.retriever = retriever or self._create_dummy_retriever()
        
        # 다단계 처리 파이프라인
        self.query_reformulation = dspy.ChainOfThought(
            "original_query -> reformulated_queries"
        )
        self.relevance_scoring = dspy.Predict(
            "query, documents -> relevance_scores"
        )
        self.context_synthesis = dspy.ChainOfThought(
            "query, relevant_documents -> synthesized_context"
        )
        self.answer_generation = dspy.ChainOfThought(
            "query, context -> reasoning, answer, citations"
        )
        self.quality_check = dspy.Predict(
            "query, answer, context -> quality_score, improvement_suggestions"
        )
    
    def _create_dummy_retriever(self):
        """더미 검색기 (실제로는 벡터 DB 등 사용)"""
        knowledge_base = {
            "python": "Python은 1991년 귀도 반 로썸이 개발한 고수준 프로그래밍 언어입니다.",
            "ai": "인공지능은 기계가 인간의 지능을 모방하여 학습하고 추론하는 기술입니다.",
            "ml": "머신러닝은 데이터로부터 패턴을 학습하여 예측하는 AI의 하위 분야입니다."
        }
        
        def retrieve(query, k=3):
            # 간단한 키워드 매칭 (실제로는 임베딩 유사도 사용)
            results = []
            for key, content in knowledge_base.items():
                if key.lower() in query.lower():
                    results.append({
                        'content': content,
                        'source': f"knowledge_base:{key}",
                        'score': 0.9
                    })
            return results[:k]
        
        return retrieve
    
    def forward(self, query):
        # 1단계: 쿼리 재구성
        reformulated = self.query_reformulation(original_query=query)
        
        # 2단계: 문서 검색
        documents = self.retriever(query, k=5)
        
        # 3단계: 관련성 점수 계산
        if documents:
            relevance = self.relevance_scoring(
                query=query,
                documents=str(documents)
            )
            
            # 4단계: 컨텍스트 합성
            context = self.context_synthesis(
                query=query,
                relevant_documents=str(documents)
            )
            
            # 5단계: 답변 생성
            answer = self.answer_generation(
                query=query,
                context=context.synthesized_context
            )
            
            # 6단계: 품질 검사
            quality = self.quality_check(
                query=query,
                answer=answer.answer,
                context=context.synthesized_context
            )
            
            return dspy.Prediction(
                original_query=query,
                reformulated_queries=reformulated.reformulated_queries,
                retrieved_documents=documents,
                context=context.synthesized_context,
                reasoning=answer.reasoning,
                answer=answer.answer,
                citations=answer.citations,
                quality_score=quality.quality_score,
                suggestions=quality.improvement_suggestions
            )
        else:
            # 검색 결과가 없는 경우 기본 답변
            basic_answer = self.answer_generation(
                query=query,
                context="검색된 컨텍스트가 없습니다."
            )
            
            return dspy.Prediction(
                original_query=query,
                answer=basic_answer.answer,
                reasoning=basic_answer.reasoning,
                quality_score=0.3
            )

# RAG 시스템 인스턴스 생성
rag_system = AdvancedRAGSystem()

6.2 멀티 에이전트 시스템

DSPy로 협력하는 멀티 에이전트 시스템을 구현해보겠습니다:

class SpecialistAgent(dspy.Module):
    """전문 분야 에이전트"""
    
    def __init__(self, specialty, knowledge_domain):
        super().__init__()
        self.specialty = specialty
        self.knowledge_domain = knowledge_domain
        
        self.analyze_task = dspy.ChainOfThought(
            f"task, {specialty}_context -> task_complexity, required_expertise"
        )
        self.provide_solution = dspy.ChainOfThought(
            f"task, complexity, expertise -> {specialty}_solution, confidence"
        )
        self.collaborate = dspy.Predict(
            "task, my_solution, other_solutions -> collaboration_suggestions"
        )
    
    def forward(self, task, other_solutions=None):
        # 작업 분석
        analysis = self.analyze_task(
            task=task,
            **{f"{self.specialty}_context": self.knowledge_domain}
        )
        
        # 해결책 제시
        solution = self.provide_solution(
            task=task,
            complexity=analysis.task_complexity,
            expertise=analysis.required_expertise
        )
        
        # 다른 에이전트와 협력 (필요한 경우)
        collaboration = None
        if other_solutions:
            collaboration = self.collaborate(
                task=task,
                my_solution=solution.solution,
                other_solutions=str(other_solutions)
            )
        
        return dspy.Prediction(
            specialty=self.specialty,
            task_analysis=analysis.task_complexity,
            solution=solution.solution,
            confidence=solution.confidence,
            collaboration_suggestions=collaboration.collaboration_suggestions if collaboration else None
        )

class MultiAgentOrchestrator(dspy.Module):
    """멀티 에이전트 조율자"""
    
    def __init__(self):
        super().__init__()
        
        # 전문 에이전트들 초기화
        self.tech_agent = SpecialistAgent(
            "technology",
            "소프트웨어 개발, AI/ML, 클라우드 인프라"
        )
        self.business_agent = SpecialistAgent(
            "business",
            "전략 기획, 마케팅, 재무 분석"
        )
        self.research_agent = SpecialistAgent(
            "research",
            "데이터 분석, 학술 연구, 동향 분석"
        )
        
        # 조율 모듈들
        self.task_router = dspy.ChainOfThought(
            "task -> primary_specialist, secondary_specialists, complexity"
        )
        self.solution_synthesizer = dspy.ChainOfThought(
            "task, agent_solutions -> synthesized_solution, rationale"
        )
        self.quality_evaluator = dspy.Predict(
            "task, solution -> quality_metrics, improvement_areas"
        )
    
    def forward(self, task):
        # 1단계: 작업 라우팅
        routing = self.task_router(task=task)
        
        # 2단계: 관련 에이전트들에게 작업 분배
        agent_solutions = []
        agents = {
            'technology': self.tech_agent,
            'business': self.business_agent,
            'research': self.research_agent
        }
        
        # 주요 전문가 솔루션
        primary_agent = agents.get(routing.primary_specialist)
        if primary_agent:
            primary_solution = primary_agent(task)
            agent_solutions.append(primary_solution)
        
        # 보조 전문가 솔루션들
        if hasattr(routing, 'secondary_specialists'):
            for specialist in routing.secondary_specialists.split(','):
                specialist = specialist.strip()
                if specialist in agents:
                    secondary_solution = agents[specialist](
                        task, 
                        other_solutions=agent_solutions
                    )
                    agent_solutions.append(secondary_solution)
        
        # 3단계: 솔루션 통합
        synthesis = self.solution_synthesizer(
            task=task,
            agent_solutions=str(agent_solutions)
        )
        
        # 4단계: 품질 평가
        evaluation = self.quality_evaluator(
            task=task,
            solution=synthesis.synthesized_solution
        )
        
        return dspy.Prediction(
            task=task,
            routing_decision=routing.primary_specialist,
            agent_contributions=agent_solutions,
            synthesized_solution=synthesis.synthesized_solution,
            rationale=synthesis.rationale,
            quality_metrics=evaluation.quality_metrics,
            improvement_areas=evaluation.improvement_areas
        )

# 멀티 에이전트 시스템 생성
multi_agent_system = MultiAgentOrchestrator()

7. 실제 프로젝트 적용 사례

7.1 기술 문서 자동 생성 시스템

실제 프로덕션에서 사용할 수 있는 기술 문서 생성 시스템을 구현해보겠습니다:

class TechnicalDocumentationGenerator(dspy.Module):
    """기술 문서 자동 생성 시스템"""
    
    def __init__(self):
        super().__init__()
        
        # 문서 구조 분석
        self.analyze_codebase = dspy.ChainOfThought(
            "code_structure, file_contents -> architecture_overview, key_components"
        )
        
        # 섹션별 생성기들
        self.generate_overview = dspy.ChainOfThought(
            "architecture_overview -> project_summary, key_features"
        )
        self.generate_api_docs = dspy.ChainOfThought(
            "code_functions, components -> api_documentation, usage_examples"
        )
        self.generate_setup_guide = dspy.ChainOfThought(
            "dependencies, configuration -> installation_steps, configuration_guide"
        )
        self.generate_troubleshooting = dspy.Predict(
            "common_errors, solutions -> troubleshooting_guide"
        )
        
        # 문서 통합 및 포맷팅
        self.format_documentation = dspy.ChainOfThought(
            "sections, target_audience -> formatted_document, navigation_structure"
        )
    
    def forward(self, codebase_info):
        # 1단계: 코드베이스 분석
        analysis = self.analyze_codebase(
            code_structure=codebase_info.get('structure', ''),
            file_contents=codebase_info.get('contents', '')
        )
        
        # 2단계: 각 섹션 생성
        overview = self.generate_overview(
            architecture_overview=analysis.architecture_overview
        )
        
        api_docs = self.generate_api_docs(
            code_functions=codebase_info.get('functions', ''),
            components=analysis.key_components
        )
        
        setup_guide = self.generate_setup_guide(
            dependencies=codebase_info.get('dependencies', ''),
            configuration=codebase_info.get('config', '')
        )
        
        troubleshooting = self.generate_troubleshooting(
            common_errors=codebase_info.get('errors', ''),
            solutions=codebase_info.get('solutions', '')
        )
        
        # 3단계: 문서 통합
        final_doc = self.format_documentation(
            sections={
                'overview': overview.project_summary,
                'features': overview.key_features,
                'api': api_docs.api_documentation,
                'setup': setup_guide.installation_steps,
                'troubleshooting': troubleshooting.troubleshooting_guide
            },
            target_audience="developers"
        )
        
        return dspy.Prediction(
            project_overview=overview.project_summary,
            key_features=overview.key_features,
            api_documentation=api_docs.api_documentation,
            setup_instructions=setup_guide.installation_steps,
            troubleshooting_guide=troubleshooting.troubleshooting_guide,
            formatted_document=final_doc.formatted_document,
            navigation_structure=final_doc.navigation_structure
        )

# 사용 예제
doc_generator = TechnicalDocumentationGenerator()

# 샘플 코드베이스 정보
sample_codebase = {
    'structure': 'Flask 웹 애플리케이션, MVC 패턴, PostgreSQL DB',
    'contents': 'REST API 엔드포인트, 사용자 인증, 데이터 처리 모듈',
    'functions': 'create_user(), authenticate(), process_data()',
    'dependencies': 'Flask, SQLAlchemy, JWT, pytest',
    'config': 'database.py, config.py, requirements.txt',
    'errors': 'Database connection errors, Authentication failures',
    'solutions': 'Connection pooling, Token refresh strategies'
}

7.2 코드 리뷰 자동화 시스템

AI를 활용한 코드 리뷰 자동화 시스템을 구현해보겠습니다:

class AutomatedCodeReviewer(dspy.Module):
    """자동화된 코드 리뷰 시스템"""
    
    def __init__(self):
        super().__init__()
        
        # 코드 분석 모듈들
        self.analyze_code_quality = dspy.ChainOfThought(
            "code_diff, programming_language -> quality_issues, best_practice_violations"
        )
        self.check_security = dspy.ChainOfThought(
            "code_changes -> security_vulnerabilities, risk_assessment"
        )
        self.evaluate_performance = dspy.Predict(
            "code_logic, algorithms -> performance_concerns, optimization_suggestions"
        )
        self.assess_maintainability = dspy.ChainOfThought(
            "code_structure, complexity -> maintainability_score, refactoring_suggestions"
        )
        
        # 리뷰 생성
        self.generate_feedback = dspy.ChainOfThought(
            "analysis_results -> constructive_feedback, priority_levels"
        )
        self.suggest_improvements = dspy.Predict(
            "issues, code_context -> specific_improvements, code_examples"
        )
    
    def forward(self, code_review_request):
        code_diff = code_review_request.get('diff', '')
        language = code_review_request.get('language', 'python')
        
        # 1단계: 다각도 분석
        quality_analysis = self.analyze_code_quality(
            code_diff=code_diff,
            programming_language=language
        )
        
        security_analysis = self.check_security(
            code_changes=code_diff
        )
        
        performance_analysis = self.evaluate_performance(
            code_logic=code_diff,
            algorithms=code_review_request.get('algorithms', '')
        )
        
        maintainability_analysis = self.assess_maintainability(
            code_structure=code_diff,
            complexity=code_review_request.get('complexity', 'medium')
        )
        
        # 2단계: 종합 피드백 생성
        feedback = self.generate_feedback(
            analysis_results={
                'quality': quality_analysis,
                'security': security_analysis,
                'performance': performance_analysis,
                'maintainability': maintainability_analysis
            }
        )
        
        # 3단계: 구체적 개선 제안
        improvements = self.suggest_improvements(
            issues=feedback.constructive_feedback,
            code_context=code_diff
        )
        
        return dspy.Prediction(
            overall_assessment=feedback.constructive_feedback,
            priority_issues=feedback.priority_levels,
            quality_score=quality_analysis.quality_issues,
            security_risks=security_analysis.security_vulnerabilities,
            performance_notes=performance_analysis.performance_concerns,
            maintainability_score=maintainability_analysis.maintainability_score,
            improvement_suggestions=improvements.specific_improvements,
            code_examples=improvements.code_examples
        )

# 코드 리뷰 시스템 인스턴스
code_reviewer = AutomatedCodeReviewer()

8. 성능 최적화 및 베스트 프랙티스

8.1 메모리 및 성능 최적화

DSPy 애플리케이션의 성능을 최적화하는 방법을 알아보겠습니다:

import dspy
from functools import lru_cache
import asyncio

# 1. 모듈 캐싱을 통한 성능 개선
class OptimizedQAModule(dspy.Module):
    def __init__(self):
        super().__init__()
        self.qa_predictor = dspy.ChainOfThought("question -> answer")
        
        # 결과 캐싱을 위한 LRU 캐시
        self._cached_predict = lru_cache(maxsize=1000)(self._predict)
    
    def _predict(self, question_hash, question):
        """내부 예측 함수 (캐시됨)"""
        return self.qa_predictor(question=question)
    
    def forward(self, question):
        # 질문의 해시값을 캐시 키로 사용
        question_hash = hash(question)
        return self._cached_predict(question_hash, question)

# 2. 비동기 처리를 통한 병렬화
class AsyncBatchProcessor(dspy.Module):
    def __init__(self, base_module):
        super().__init__()
        self.base_module = base_module
    
    async def process_batch_async(self, questions, batch_size=5):
        """비동기 배치 처리"""
        results = []
        
        for i in range(0, len(questions), batch_size):
            batch = questions[i:i + batch_size]
            
            # 배치 내 병렬 처리
            tasks = [
                asyncio.create_task(self._async_predict(q))
                for q in batch
            ]
            
            batch_results = await asyncio.gather(*tasks, return_exceptions=True)
            results.extend(batch_results)
        
        return results
    
    async def _async_predict(self, question):
        """비동기 예측"""
        # 실제로는 aiohttp 등을 통한 비동기 API 호출
        return self.base_module(question=question)

# 3. 모델 라우팅을 통한 효율성 개선
class SmartModelRouter(dspy.Module):
    def __init__(self):
        super().__init__()
        
        # 다양한 복잡도의 모델들
        self.simple_model = dspy.Predict("question -> answer")
        self.complex_model = dspy.ChainOfThought("question -> answer")
        
        # 복잡도 분류기
        self.complexity_classifier = dspy.Predict(
            "question -> complexity_level, reasoning"
        )
    
    def forward(self, question):
        # 질문 복잡도 판단
        complexity = self.complexity_classifier(question=question)
        
        # 복잡도에 따른 모델 선택
        if complexity.complexity_level == "simple":
            result = self.simple_model(question=question)
            return dspy.Prediction(
                answer=result.answer,
                model_used="simple",
                complexity=complexity.complexity_level
            )
        else:
            result = self.complex_model(question=question)
            return dspy.Prediction(
                answer=result.answer,
                reasoning=result.reasoning,
                model_used="complex",
                complexity=complexity.complexity_level
            )

# 성능 최적화된 시스템
optimized_qa = OptimizedQAModule()
async_processor = AsyncBatchProcessor(optimized_qa)
smart_router = SmartModelRouter()

8.2 에러 처리 및 견고성

프로덕션 환경에서 중요한 에러 처리 패턴을 구현해보겠습니다:

import logging
from typing import Optional, Dict, Any
import time

class RobustDSPyModule(dspy.Module):
    """견고한 DSPy 모듈 (에러 처리 포함)"""
    
    def __init__(self, max_retries=3, timeout=30):
        super().__init__()
        self.max_retries = max_retries
        self.timeout = timeout
        
        self.predictor = dspy.ChainOfThought("question -> answer")
        
        # 로깅 설정
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
    
    def forward_with_retry(self, question: str) -> Optional[dspy.Prediction]:
        """재시도 로직이 포함된 실행"""
        for attempt in range(self.max_retries):
            try:
                start_time = time.time()
                
                # 타임아웃 설정으로 예측 실행
                result = self._execute_with_timeout(question)
                
                execution_time = time.time() - start_time
                self.logger.info(f"예측 성공 (시도: {attempt + 1}, 시간: {execution_time:.2f}s)")
                
                return result
                
            except TimeoutError:
                self.logger.warning(f"타임아웃 발생 (시도: {attempt + 1})")
                if attempt == self.max_retries - 1:
                    return self._create_fallback_response(question, "timeout")
                    
            except Exception as e:
                self.logger.error(f"예측 실행 중 오류 (시도: {attempt + 1}): {e}")
                if attempt == self.max_retries - 1:
                    return self._create_fallback_response(question, "error")
                
                # 지수적 백오프
                time.sleep(2 ** attempt)
        
        return None
    
    def _execute_with_timeout(self, question: str) -> dspy.Prediction:
        """타임아웃이 적용된 실행"""
        # 실제로는 concurrent.futures.ThreadPoolExecutor 등 사용
        return self.predictor(question=question)
    
    def _create_fallback_response(self, question: str, error_type: str) -> dspy.Prediction:
        """폴백 응답 생성"""
        fallback_answers = {
            "timeout": "죄송합니다. 처리 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.",
            "error": "죄송합니다. 일시적인 오류가 발생했습니다. 잠시 후 다시 시도해주세요."
        }
        
        return dspy.Prediction(
            question=question,
            answer=fallback_answers.get(error_type, "오류가 발생했습니다."),
            error_type=error_type,
            is_fallback=True
        )

# 사용 예제
robust_module = RobustDSPyModule(max_retries=3, timeout=30)

8.3 모니터링 및 로깅

DSPy 애플리케이션의 모니터링 시스템을 구현해보겠습니다:

import json
import time
from datetime import datetime
from typing import Dict, List
from dataclasses import dataclass, asdict

@dataclass
class PredictionMetrics:
    """예측 메트릭 데이터 클래스"""
    timestamp: str
    question_length: int
    answer_length: int
    execution_time: float
    model_used: str
    confidence_score: Optional[float] = None
    error_occurred: bool = False
    error_type: Optional[str] = None

class MonitoredDSPyModule(dspy.Module):
    """모니터링이 포함된 DSPy 모듈"""
    
    def __init__(self, metrics_file="dspy_metrics.jsonl"):
        super().__init__()
        self.predictor = dspy.ChainOfThought("question -> answer")
        self.metrics_file = metrics_file
        self.metrics_buffer: List[PredictionMetrics] = []
        
        # 메트릭 수집용
        self.total_predictions = 0
        self.successful_predictions = 0
        self.total_execution_time = 0.0
    
    def forward(self, question: str) -> dspy.Prediction:
        start_time = time.time()
        timestamp = datetime.now().isoformat()
        
        try:
            # 예측 실행
            result = self.predictor(question=question)
            execution_time = time.time() - start_time
            
            # 메트릭 수집
            metrics = PredictionMetrics(
                timestamp=timestamp,
                question_length=len(question),
                answer_length=len(result.answer) if hasattr(result, 'answer') else 0,
                execution_time=execution_time,
                model_used="chain_of_thought",
                confidence_score=self._calculate_confidence(result),
                error_occurred=False
            )
            
            self._record_metrics(metrics)
            self.successful_predictions += 1
            
            return result
            
        except Exception as e:
            execution_time = time.time() - start_time
            
            # 에러 메트릭 수집
            error_metrics = PredictionMetrics(
                timestamp=timestamp,
                question_length=len(question),
                answer_length=0,
                execution_time=execution_time,
                model_used="chain_of_thought",
                error_occurred=True,
                error_type=type(e).__name__
            )
            
            self._record_metrics(error_metrics)
            raise
        
        finally:
            self.total_predictions += 1
            self.total_execution_time += execution_time
    
    def _calculate_confidence(self, result) -> Optional[float]:
        """신뢰도 점수 계산 (예시)"""
        if hasattr(result, 'answer'):
            # 답변 길이, 키워드 밀도 등을 바탕으로 신뢰도 계산
            answer_length = len(result.answer.split())
            if 5 <= answer_length <= 100:
                return min(0.9, 0.5 + (answer_length / 200))
            else:
                return 0.3
        return None
    
    def _record_metrics(self, metrics: PredictionMetrics):
        """메트릭 기록"""
        self.metrics_buffer.append(metrics)
        
        # 버퍼가 가득 차면 파일에 기록
        if len(self.metrics_buffer) >= 100:
            self._flush_metrics()
    
    def _flush_metrics(self):
        """메트릭을 파일에 기록"""
        with open(self.metrics_file, 'a', encoding='utf-8') as f:
            for metric in self.metrics_buffer:
                f.write(json.dumps(asdict(metric), ensure_ascii=False) + '\n')
        
        self.metrics_buffer.clear()
    
    def get_performance_summary(self) -> Dict[str, Any]:
        """성능 요약 반환"""
        if self.total_predictions == 0:
            return {"message": "아직 예측이 실행되지 않았습니다."}
        
        success_rate = self.successful_predictions / self.total_predictions
        avg_execution_time = self.total_execution_time / self.total_predictions
        
        return {
            "total_predictions": self.total_predictions,
            "successful_predictions": self.successful_predictions,
            "success_rate": f"{success_rate:.2%}",
            "average_execution_time": f"{avg_execution_time:.3f}s",
            "total_execution_time": f"{self.total_execution_time:.3f}s"
        }

# 모니터링이 포함된 모듈 사용
monitored_module = MonitoredDSPyModule()

# 성능 요약 출력
print(monitored_module.get_performance_summary())

9. 실제 macOS 환경에서의 설정 및 실행

9.1 zshrc Aliases 설정

macOS 개발 환경에서 DSPy를 효율적으로 사용하기 위한 alias들을 설정합니다:

# ~/.zshrc에 추가할 DSPy 관련 aliases

# === DSPy 개발 환경 Aliases ===
export DSPY_PROJECT_DIR="$HOME/dspy-projects"

# 디렉토리 이동
alias dspy-dir="cd $DSPY_PROJECT_DIR"
alias dspy-tutorial="cd $DSPY_PROJECT_DIR/tutorial"

# 가상환경 관리
alias dspy-env="source $DSPY_PROJECT_DIR/dspy-env/bin/activate"
alias dspy-deactivate="deactivate"

# DSPy 개발 도우미
alias dspy-install="pip install -U dspy"
alias dspy-version="python -c 'import dspy; print(f\"DSPy {dspy.__version__}\")'"
alias dspy-test="python -m pytest tests/ -v"

# 자주 사용하는 명령어들
alias dspy-jupyter="jupyter notebook --ip=0.0.0.0 --port=8888"
alias dspy-docs="open https://dspy.ai/docs"
alias dspy-github="open https://github.com/stanfordnlp/dspy"

# 개발 유틸리티
alias dspy-lint="flake8 . --exclude=venv"
alias dspy-format="black . && isort ."
alias dspy-clean="find . -name '*.pyc' -delete && find . -name '__pycache__' -delete"

# 로그 확인
alias dspy-logs="tail -f dspy_metrics.jsonl | jq ."
alias dspy-errors="grep -i error dspy_metrics.jsonl | jq ."

9.2 개발환경 검증 스크립트

설치가 제대로 되었는지 확인하는 검증 스크립트입니다:

#!/bin/bash
# 파일명: verify_dspy_setup.sh

echo "🔍 DSPy 개발환경 검증 시작..."

# Python 버전 확인
echo "📋 Python 버전:"
python --version

# DSPy 설치 확인
echo "📋 DSPy 설치 상태:"
if python -c "import dspy; print(f'DSPy {dspy.__version__} 설치됨')" 2>/dev/null; then
    echo "✅ DSPy 정상 설치됨"
else
    echo "❌ DSPy 설치되지 않음. 'pip install dspy' 실행 필요"
    exit 1
fi

# 주요 의존성 확인
echo "📋 주요 의존성 확인:"
dependencies=("openai" "litellm" "pydantic" "optuna")

for dep in "${dependencies[@]}"; do
    if python -c "import $dep" 2>/dev/null; then
        echo "✅ $dep 설치됨"
    else
        echo "⚠️ $dep 설치 확인 필요"
    fi
done

# 기본 기능 테스트
echo "📋 기본 기능 테스트:"
python -c "
import dspy
try:
    # 시그니처 생성 테스트
    sig = dspy.Signature('question -> answer')
    print('✅ Signature 생성 성공')
    
    # 모듈 생성 테스트
    module = dspy.ChainOfThought(sig)
    print('✅ Module 생성 성공')
    
    # Example 생성 테스트
    example = dspy.Example(question='test', answer='test')
    print('✅ Example 생성 성공')
    
    print('🎉 모든 기본 기능 테스트 통과!')
    
except Exception as e:
    print(f'❌ 기본 기능 테스트 실패: {e}')
    exit(1)
"

echo "✅ DSPy 개발환경 검증 완료!"
echo ""
echo "🚀 다음 단계:"
echo "1. API 키 설정 (OPENAI_API_KEY 등)"
echo "2. 첫 번째 DSPy 프로그램 작성"
echo "3. DSPy 문서 참조: https://dspy.ai/"

9.3 실제 테스트 실행 결과

제가 macOS 환경에서 실제로 실행한 테스트 결과들입니다:

기본 테스트 결과:

$ python test_dspy_basic.py
🚀 DSPy 기본 기능 테스트 시작

🧪 DSPy 가져오기 테스트
DSPy 버전: 3.0.1
✅ DSPy 가져오기 성공

🧪 DSPy Signature 기본 테스트
기본 시그니처: StringSignature(question -> answer ...)
✅ Signature 테스트 성공

🎉 모든 기본 테스트 완료!

고급 테스트 결과:

$ python test_dspy_advanced.py
🚀 DSPy 고급 기능 테스트 시작

🧪 데이터셋 생성 및 관리 테스트
생성된 예제 수: 3
예제 1: 파이썬에서 리스트란?...
✅ 데이터셋 생성 테스트 성공

🧪 평가 메트릭 테스트
예측 결과 수: 3
정답 예제 수: 3
정확도: 66.67%
✅ 평가 메트릭 테스트 성공

🎉 모든 고급 테스트 완료!

10. 트러블슈팅 및 FAQ

10.1 자주 발생하는 문제들

Q: ImportError: No module named 'dspy' 오류가 발생합니다.

A: 가상환경이 활성화되었는지 확인하고 DSPy가 설치되었는지 확인하세요:

# 가상환경 활성화 확인
source dspy-env/bin/activate

# DSPy 설치 확인
pip list | grep dspy

# 없다면 설치
pip install -U dspy

Q: API 키 설정 없이 테스트할 수 있나요?

A: 네, 가능합니다. 더미 LM을 사용하거나 모듈 구조만 테스트할 수 있습니다:

# 더미 LM으로 구조 테스트
class DummyLM(dspy.LM):
    def __init__(self):
        super().__init__("dummy")
    
    def __call__(self, prompt, **kwargs):
        return ["더미 응답입니다."]

dspy.configure(lm=DummyLM())

Q: litellm 관련 오류가 발생합니다.

A: LiteLLM 의존성 문제일 수 있습니다. 다음과 같이 해결하세요:

# LiteLLM 재설치
pip uninstall litellm
pip install litellm

# 또는 특정 버전 설치
pip install "litellm>=1.64.0"

10.2 성능 최적화 팁

1. 캐싱 활용:

from functools import lru_cache

@lru_cache(maxsize=1000)
def cached_prediction(question_hash):
    return module(question=question)

2. 배치 처리:

# 여러 질문을 한 번에 처리
questions = ["질문1", "질문2", "질문3"]
results = [module(question=q) for q in questions]

3. 모델 선택:

# 간단한 작업에는 가벼운 모델 사용
simple_tasks = dspy.Predict("question -> answer")
complex_tasks = dspy.ChainOfThought("question -> answer")

10.3 프로덕션 배포 고려사항

1. 환경 변수 관리:

# .env 파일 사용
OPENAI_API_KEY=your_api_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
DSPY_CACHE_DIR=/tmp/dspy_cache

2. 로깅 설정:

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('dspy_app.log'),
        logging.StreamHandler()
    ]
)

3. 에러 처리:

try:
    result = module(question=question)
except Exception as e:
    logger.error(f"예측 실행 중 오류: {e}")
    # 폴백 로직 실행
    result = fallback_response(question)

결론

DSPy는 AI 개발의 패러다임을 근본적으로 바꾸는 혁신적인 프레임워크입니다. 기존의 취약한 문자열 기반 프롬프트 엔지니어링에서 벗어나 구조화된 코드 기반 AI 프로그래밍을 가능하게 합니다.

핵심 장점 요약

  1. 구조화된 접근: Signature와 Module을 통한 명확한 인터페이스 정의
  2. 자동 최적화: Optimizer를 통한 프롬프트와 가중치 자동 튜닝
  3. 모듈형 설계: 재사용 가능한 컴포넌트로 복잡한 AI 시스템 구축
  4. 이식성: 다양한 모델과 제공업체 간 코드 재사용
  5. 확장성: 복잡한 멀티 에이전트 시스템까지 확장 가능

실습을 통해 학습한 내용

  • 기본 구조: Signature, Module, Prediction의 핵심 개념
  • 고급 활용: RAG, 멀티 에이전트, 자동화 시스템 구현
  • 성능 최적화: 캐싱, 비동기 처리, 모니터링
  • 프로덕션 배포: 에러 처리, 로깅, 견고성 확보

다음 단계 추천

  1. 실제 프로젝트 적용: 현재 진행 중인 AI 프로젝트에 DSPy 도입
  2. 고급 Optimizer 활용: MIPRO, GEPA 등 최신 최적화 기법 적용
  3. 커스텀 모듈 개발: 특정 도메인에 특화된 DSPy 모듈 개발
  4. 커뮤니티 참여: DSPy GitHubDiscord 참여

DSPy는 단순한 도구가 아닙니다. AI 개발의 미래를 제시하는 새로운 패러다임입니다. 이 글의 실습 예제들을 바탕으로 여러분만의 혁신적인 AI 시스템을 구축해보시기 바랍니다.


관련 자료:

테스트 환경:

  • macOS (Apple Silicon/Intel)
  • Python 3.12.8
  • DSPy 3.0.1
  • 2025년 8월 기준