커스텀 DSPy 모듈로 AI 애플리케이션 만들기

커스텀 DSPy 모듈로 AI 애플리케이션 만들기 (Building AI Applications by Customizing DSPy Modules)

이 가이드에서는 dspy.Module 을 커스터마이징해 GenAI 애플리케이션을 만드는 방법을 차근차근 알아봅니다.

출처: 문서

본문

DSPy 모듈은 DSPy 프로그램의 빌딩 블록입니다.

  • 각 내장 모듈은 프롬프트 기법(chain-of-thought이나 ReAct 같은)을 추상화합니다. 중요한 것은 어떤 시그니처든 처리하도록 일반화되어 있다는 점입니다.
  • DSPy 모듈은 학습 가능한 파라미터(즉 프롬프트와 LM 가중치를 이루는 작은 조각들)를 지니며, 입력을 처리하고 출력을 반환하도록 호출(invoke) 될 수 있습니다.
  • 여러 모듈은 더 큰 모듈(프로그램)로 조합될 수 있습니다. DSPy 모듈은 PyTorch의 NN 모듈에서 직접 영감을 받았지만, LM 프로그램에 적용된 것입니다.

커스텀 모듈을 구현하지 않고도 DSPy 프로그램을 만들 수 있지만, 조사 결과 커스텀 모듈로 로직을 넣는 것을 강력히 권장합니다. 그래야 DSPy 최적화기나 MLflow DSPy 트레이싱 같은 다른 DSPy 기능을 쓸 수 있으니까요.

시작하기 전에 DSPy가 설치되어 있는지 확인하세요:

!pip install dspy

권장: 내부에서 무슨 일이 일어나는지 이해하도록 MLflow Tracing을 설정하세요.

MLflow DSPy 통합

MLflow는 DSPy와 네이티브로 통합되는 LLMOps 도구로, 설명 가능성과 실험 추적을 제공합니다. 이 튜토리얼에서는 MLflow로 프롬프트와 최적화 진행을 트레이스로 시각화해 DSPy 동작을 더 잘 이해할 수 있습니다. 네 단계로 쉽게 설정할 수 있습니다:

  1. MLflow 설치 — %pip install mlflow>=3.0.0
  2. 별도 터미널에서 MLflow UI 시작 — mlflow ui --port 5000 --backend-store-uri sqlite:///mlruns.db
  3. 노트북을 MLflow에 연결 — import mlflow / mlflow.set_tracking_uri("http://localhost:5000") / mlflow.set_experiment("DSPy")
  4. 트레이싱 활성화 — mlflow.dspy.autolog()

통합에 대해 더 알고 싶다면 MLflow DSPy 문서를 방문하세요.

DSPy 모듈 커스터마이징

커스텀 프롬프트 로직을 구현하고 외부 도구·서비스를 통합하려면 dspy.Module 을 서브클래싱하고 다음 두 메서드를 구현하세요:

  • __init__: 생성자입니다. 프로그램의 속성과 하위 모듈을 정의합니다.
  • forward: DSPy 프로그램의 핵심 로직이 담긴 메서드입니다.

forward() 메서드 안에서는 다른 DSPy 모듈을 호출하는 것에 제한되지 않습니다. Langchain/Agno 에이전트, MCP 도구, 데이터베이스 핸들러와 상호작용하는 표준 Python 함수도 통합할 수 있습니다.

커스텀 DSPy 모듈의 기본 구조는 이렇습니다:

class MyProgram(dspy.Module):

    def __init__(self, ...):
        # Define attributes and sub-modules here
        {constructor_code}

    def forward(self, input_name1, input_name2, ...):
        # Implement your program's logic here
        {custom_logic_code}

실제 코드 예시로 알아봅시다. 여러 단계로 된 간단한 RAG(검색 증강 생성) 애플리케이션을 만들어 볼게요:

  1. 쿼리 생성(Query Generation): 사용자 질문에 기반해 관련 컨텍스트를 검색할 적합한 쿼리를 생성.
  2. 컨텍스트 검색(Context Retrieval): 생성된 쿼리로 컨텍스트를 가져옴.
  3. 답변 생성(Answer Generation): 검색된 컨텍스트와 원래 질문에 기반해 최종 답변 생성.

이 다단계 프로그램의 코드 구현은 아래와 같습니다:

import dspy

class QueryGenerator(dspy.Signature):
    """Generate a query based on question to fetch relevant context"""
    question: str = dspy.InputField()
    query: str = dspy.OutputField()

def search_wikipedia(query: str) -> list[str]:
    """Query ColBERT endpoint, which is a knowledge source based on wikipedia data"""
    results = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')(query, k=1)
    return [x["text"] for x in results]

class RAG(dspy.Module):
    def __init__(self):
        self.query_generator = dspy.Predict(QueryGenerator)
        self.answer_generator = dspy.ChainOfThought("question,context->answer")

    def forward(self, question, **kwargs):
        query = self.query_generator(question=question).query
        context = search_wikipedia(query)[0]
        return self.answer_generator(question=question, context=context).answer

forward 메서드를 살펴볼게요. 먼저 self.query_generator(a dspy.Predict)에 질문을 보내 컨텍스트 검색용 쿼리를 얻습니다. 그다음 그 쿼리로 ColBERT를 호출해 검색된 첫 컨텍스트를 유지합니다. 마지막으로 질문과 컨텍스트를 self.answer_generator(a dspy.ChainOfThought)에 보내 최종 답변을 생성합니다.

다음으로 RAG 모듈 인스턴스를 만들어 프로그램을 실행합니다.

중요: 커스텀 DSPy 모듈을 호출할 때는 forward() 메서드를 직접 호출하지 말고 모듈 인스턴스를 직접 사용해야 합니다(__call__ 메서드를 내부적으로 호출). __call__ 은 forward 로직을 실행하기 전에 필요한 내부 처리를 처리합니다.

import os

os.environ["OPENAI_API_KEY"] = "{your_openai_api_key}"

dspy.configure(lm=dspy.LM("openai/gpt-4o-mini"))
rag = RAG()
print(rag(question="Is Lebron James the basketball GOAT?"))

결과:

The question of whether LeBron James is the basketball GOAT is subjective and depends on personal opinions. Many consider him one of the greatest due to his achievements and impact on the game, but others may argue for different players like Michael Jordan.

그게 전부입니다! 요약하면, GenAI 애플리케이션을 만들려면 커스텀 로직을 forward() 메서드에 넣고, 모듈 인스턴스를 만든 뒤 그 인스턴스 자신을 호출하면 됩니다.

왜 모듈을 커스터마이징하는가

DSPy는 가벼운 작성·최적화 프레임워크로, 프롬프트 엔지니어링의 난잡함을 풀려고 합니다. LLM을 프롬프트(문자열 in, 문자열 out)하는 것에서 프로그래밍(구조화 입력 in, 구조화 출력 out) 하는 것으로 바꿔 견고한 AI 시스템을 만드는 것이죠.

추론용 dspy.ChainOfThought, 도구 호출 에이전트용 dspy.ReAct 같은 커스텀 프롬프트 로직을 가진 사전 구축 모듈을 제공하지만, 에이전트를 구축하는 방식을 표준화하려는 의도는 아닙니다.

DSPy에서 애플리케이션 로직은 단순히 커스텀 Module의 forward 메서드에 들어갑니다. Python 코드를 쓰는 한 어떤 제약도 없어요. 이 배치 덕분에 DSPy는 다른 프레임워크나 바닐라 SDK 사용에서의 마이그레이션이 쉽고, 기본적으로 그냥 Python 코드이므로 밖으로 나가기도 쉽습니다.

더 알아보기 (Learn more)