LLM WikiAccess-protected knowledge portal

WIKI

DSPy 3.2: 프롬프트 문자열을 버리고 타입 선언으로 LLM을 다루는 방법

왜 지금 봐야 하나 LLM 기반 시스템의 가장 큰 운영 문제 중 하나는 프롬프트 불안정성 이다. 모델 버전이 바뀌면 정성껏 다듬은 few shot 예시가 역효과를 내고, 프로바이더를 교체하면 표현 방식을 처음부터 다시 맞춰야 하며, 파이프라인 앞단 컴포넌트가 바뀌면 뒷단 프롬프트도 연쇄적으로 깨진다. 이 문제를 "엔지니어가 더 조심하면 된다"는 방식으로 접근하는 한 규모 있는 시스템에서는 관리 비용이 선형 이상으로 늘어난다.

경로human/study/content/ai-frontier/51-dspy-3-2-programming-not-prompting-llm.md
카테고리Study
태그#ai-review #dspy #llm #not #programming #prompting #study

왜 지금 봐야 하나

LLM 기반 시스템의 가장 큰 운영 문제 중 하나는 프롬프트 불안정성이다. 모델 버전이 바뀌면 정성껏 다듬은 few-shot 예시가 역효과를 내고, 프로바이더를 교체하면 표현 방식을 처음부터 다시 맞춰야 하며, 파이프라인 앞단 컴포넌트가 바뀌면 뒷단 프롬프트도 연쇄적으로 깨진다. 이 문제를 "엔지니어가 더 조심하면 된다"는 방식으로 접근하는 한 규모 있는 시스템에서는 관리 비용이 선형 이상으로 늘어난다.

DSPy는 이 문제를 근본적으로 다른 각도로 접근한다. 프롬프트 문자열을 직접 작성하는 대신, 입출력 필드를 선언하면 옵티마이저가 자동으로 최적 프롬프트를 탐색한다. 소프트웨어 컴파일러에 비유하자면, 개발자는 고수준 인터페이스(Signature + Module)만 정의하고 컴파일러(Optimizer)가 특정 LLM에서 실제로 잘 작동하는 저수준 지시문(prompt + few-shot)을 생성한다.

DSPy 3.0이 2025년 8월에 공개되면서 프레임워크가 프로덕션 수준으로 재설계됐고, 2026년 들어 3.1(1월), 3.2(4-5월)까지 빠르게 진화했다. 3.2.1이 2026년 5월 5일에 릴리스됐고, 3.3.0b1이 5월 28일 프리릴리스 상태다. 이 글은 DSPy의 핵심 개념과 3.x 계열에서 달라진 운영 표면을 다룬다.


핵심 변화 한눈에 보기

버전릴리스핵심 변화
3.0.02025-08-12프로덕션 재설계: async, 멀티모달, GEPA·SIMBA·GRPO 옵티마이저, thread-safe 설정
3.1.02026-01-06추론 모델 네이티브 지원(o1·Claude extended thinking style), File 타입, Python 3.14 호환
3.2.02026-04-21BetterTogether 옵티마이저 체이닝, LiteLLM 의존성 분리, 입력 필드 타입 검증
3.2.12026-05-05async 스트리밍·임베딩 캐시 버그 수정, LiteLLM 상위 바운드 제거
3.3.0b12026-05-28ReActV2 모듈(네이티브 tool-calling), 타입화된 LM 경계, NumPy 선택적 의존성, BaseLM 상태 직렬화

DSPy 컴파일 파이프라인 전체 구조

DSPy 컴파일 파이프라인: 선언 → 탐색 → 배포 개발자 작성 영역 Signature (입출력 선언) question: str → answer: str context: list[str], query: str → summary: str 프롬프트 문자열 없음. 필드 이름과 타입만 선언. Module (로직 구성) Predict ChainOfThought ReAct / ReActV2 ProgramOfThought 모듈을 조합해 파이프라인 구성 트레이닝 입력 Training set (입출력 예시 20개) Metric 정확도·F1·LLM judge Optimizer (Teleprompter) 후보 지시문과 few-shot 예시 조합을 탐색하고 metric으로 평가 BootstrapFewShot 성공 예시 bootstrapping few-shot 자동 선택 가장 빠른 시작점 MIPROv2 베이지안 탐색 지시문+예시 동시 최적화 효과적이나 비용 높음 GEPA 유전자·파레토 탐색 MIPROv2 대비 1/35 비용 성능 20% 향상(3.0) BetterTogether "p→w→p" 전략 체이닝 프롬프트 탐색→파인튜닝 →프롬프트 재탐색 (3.2) 평가 루프: 후보 실행 → metric 측정 → 상위 k개 유지 → 다음 후보 생성 컴파일된 프로그램 최적 지시문 + 선별된 few-shot 예시 최적화된 Signature "당신은 전문 질의응답 시스템입니다." "아래 예시를 참고하세요: ..." 3~5개의 선별된 few-shot 예시 save() / load() JSON 직렬화로 재사용 및 배포 LLM Provider GPT-5, Claude, Gemini, Ollama ... 교체 가능 핵심 철학 프롬프트 엔지니어링은 "지시문을 설계"하는 것. DSPy는 "시스템 구조를 설계"하고 지시문 탐색은 옵티마이저에게 위임한다.
DSPy는 선언적 프로그램을 LLM-특화 프롬프트로 컴파일한다

DSPy가 푸는 문제: 프롬프팅은 왜 취약한가

LLM 파이프라인을 수동으로 프롬프팅하면 세 가지 구조적 문제가 생긴다.

1. 모델 결합(Model coupling). "You are a helpful assistant. Think step by step. Format your output as JSON with fields..."처럼 작성한 프롬프트는 GPT-4o에 최적화된 것이다. Gemini나 Claude로 옮기면 다시 조정해야 한다. 프롬프트가 프로바이더-특정 아티팩트가 되는 순간 시스템은 교체 비용이 높아진다.

2. 조합 취약성(Composition fragility). RAG → 요약 → 분류 파이프라인에서 RAG 모듈이 반환 형식을 바꾸면 뒷단 프롬프트도 연쇄적으로 깨진다. 각 컴포넌트가 암묵적인 포맷 계약으로 연결되어 있기 때문이다.

3. 최적화 불가능성. 수동 프롬프트는 사람이 직관으로 작성한 것이다. 실제 최적 프롬프트를 찾으려면 수백 개 변형을 체계적으로 탐색해야 하는데, 수작업으로는 이 공간을 커버하기 어렵다.

DSPy는 이 세 가지를 모두 구조적으로 해결하려는 시도다.


세 가지 추상화: Signature, Module, Optimizer

Signature: 프롬프트 대신 타입 선언

Signature는 LLM 호출의 입출력 명세다. 프롬프트 문자열이 아니라, Python 클래스 또는 인라인 표현으로 정의한다.

# 인라인 선언 (짧은 작업)
predict = dspy.Predict("question -> answer")

# 클래스 선언 (복잡한 작업, 설명 포함)
class SummarizeDocument(dspy.Signature):
    """긴 문서를 핵심 포인트 중심으로 한국어로 요약한다."""
    document: str = dspy.InputField()
    key_points: list[str] = dspy.OutputField(desc="가장 중요한 3~5개 포인트")
    summary: str = dspy.OutputField(desc="200자 이내 요약문")

Signature의 핵심 특징은 세 가지다.

Module: 로직 구성 단위

Module은 하나 이상의 Signature를 조합해 복잡한 LLM 로직을 표현한다. 내장 모듈들은 검증된 프롬프팅 기법을 캡슐화한다.

모듈역할특징
Predict기본 Signature 실행단순 입출력, 가장 빠름
ChainOfThought내부 reasoning 필드 추가rationale → answer 패턴
ProgramOfThoughtPython 코드를 생성·실행코드 인터프리터 활용
ReActReasoning + Acting 반복도구 호출과 관찰 루프
ReActV2 (3.3b1)네이티브 tool-callingsubmit 도구 포함, 예외 처리 강화
MultiChainComparison여러 체인 결과 비교앙상블 추론
class RAGPipeline(dspy.Module):
    def __init__(self, num_passages=3):
        self.retrieve = dspy.Retrieve(k=num_passages)
        self.generate = dspy.ChainOfThought("context, question -> answer")

    def forward(self, question):
        ctx = self.retrieve(question).passages
        return self.generate(context=ctx, question=question)

Optimizer: 자동 프롬프트 탐색

Optimizer(구 Teleprompter)는 DSPy 프로그램을 받아서 최적 프롬프트를 탐색한다. 입력은 프로그램 + 트레이닝 예시 20~100개 + metric 함수다.

# 1. 탐색할 프로그램 정의
pipeline = RAGPipeline()

# 2. Metric 정의 (0~1 범위)
def answer_metric(gold, pred, trace=None):
    return gold.answer.lower() in pred.answer.lower()

# 3. Optimizer 실행
optimizer = dspy.MIPROv2(metric=answer_metric, auto="medium")
compiled = optimizer.compile(pipeline, trainset=train_examples)

# 4. 저장 및 재사용
compiled.save("rag_compiled.json")
loaded = RAGPipeline().load("rag_compiled.json")

컴파일 흐름의 내부 동작

컴파일은 크게 세 단계로 작동한다.

1단계: 부트스트래핑(Bootstrapping). 트레이닝 예시로 파이프라인을 실행하고, metric을 통과한 예시의 중간 reasoning trace를 수집한다. 이 trace가 few-shot 후보 풀이 된다.

2단계: 지시문 탐색(Instruction search). MIPROv2나 GEPA 같은 옵티마이저가 다양한 지시문 변형을 생성하고, 각 변형을 dev set에서 평가한다. 베이지안 서로게이트 모델(MIPROv2) 또는 진화 알고리즘(GEPA)으로 탐색 공간을 효율적으로 커버한다.

3단계: 검증 및 선택(Validation + selection). 지시문·few-shot 조합 중 metric 최고점을 받은 것을 최종 컴파일 결과로 확정한다. 이 결과물이 JSON으로 직렬화되어 프로덕션에 배포된다.

GEPA의 비용 효율: 3.0에서 추가된 GEPA는 유전자 알고리즘과 파레토 프론트 탐색을 결합해 MIPROv2 대비 1/35 비용으로 20% 더 높은 성능을 보인다. 탐색 예산이 제한적인 팀에 실용적 선택지다.


DSPy 3.x 계열의 변화 궤적

3.0.0 (2025-08-12): 프로덕션 재설계

2.x 대비 가장 큰 구조 변화는 다음 세 가지다.

3.1.0 (2026-01-06): 추론 모델 지원

o1, o3, Claude extended thinking 스타일 모델에서 reasoning 토큰이 별도로 생성된다는 점을 DSPy가 명시적으로 다루기 시작했다. File 타입이 Signature에 추가되어 문서 기반 작업에서 직접 파일 참조가 가능해졌다.

3.2.0 (2026-04-21): BetterTogether와 LiteLLM 분리

BetterTogether는 프롬프트 탐색과 파인튜닝을 전략 문자열로 체이닝한다.

optimizer = dspy.BetterTogether(
    metric=answer_metric,
    p=dspy.GEPA(metric=answer_metric),          # 프롬프트 탐색기
    w=dspy.BootstrapFinetune(metric=answer_metric)  # 파인튜닝기
)
compiled = optimizer.compile(
    pipeline,
    trainset=train_examples,
    strategy="p -> w -> p"  # 프롬프트 → 파인튜닝 → 재프롬프트
)

strategy="p -> w -> p" 패턴의 의미: 먼저 프롬프트를 최적화하고, 그 프로그램으로 학습 데이터를 생성해 모델을 파인튜닝하고, 파인튜닝된 모델에서 다시 프롬프트를 최적화한다. 각 단계는 dev set에서 독립적으로 평가되고 최고 결과가 다음 단계로 전달된다.

LiteLLM 의존성 분리: 이전까지 LiteLLM이 직접 의존성이었지만 3.2.0부터 선택적으로 바뀌었다. Optuna도 마찬가지다. pip install dspy는 이제 핵심 기능만 설치하고, 고급 옵티마이저가 필요한 경우 별도 설치한다.


3.3.0b1: ReActV2와 타입화된 LM 경계

3.3.0b1(2026-05-28 프리릴리스)에서 가장 중요한 변화는 두 가지다.

ReActV2: 도구 호출 신뢰성 강화

ReAct 모듈이 ReActV2로 대체됐다. 주요 개선점은 다음과 같다.

타입화된 LM 경계

BaseLM이 이제 타입화된 런타임 상태를 소유한다. dump_state() / load_state()로 LM 설정을 직렬화·복원할 때 API 키는 제외되고, 이전 버전으로 저장된 상태와 하위 호환성이 유지된다. NumPy가 선택적 의존성이 되면서 기본 설치 용량이 줄었다.


운영 관점의 판단 기준

어떤 팀에 적합한가

DSPy는 다음 조건이 갖춰진 팀에서 투자 대비 효과가 높다.

한계와 주의점


운영 체크리스트

References