안녕하세요, 저는 5년간 AI 시스템 구축을 해온 개발자입니다. 오늘은 MCP(Model Context Protocol)를 활용해서 RAG(검색 증강 생성) 시스템을 구현하는 방법을 초보자도 이해할 수 있도록 단계별로 알려드리겠습니다. 이 튜토리얼을 마치면, 문서 검색과 AI 생성 결과를 자연스럽게 연결하는 시스템을 만들 수 있게 됩니다.

MCP와 RAG이란 무엇인가요?

MCP(Model Context Protocol)란?

MCP는 AI 모델이 외부 도구나 데이터 소스와 안전하게 통신할 수 있게 해주는 프로토콜입니다. 마치 번역가가 두 나라 사람 사이에서 대화하게 해주는 통역사처럼, MCP는 AI가 데이터베이스, 검색 엔진, 파일 시스템 등 다양한 리소스에 접근할 수 있게 합니다.

RAG(검색 증강 생성)란?

RAG는 AI가 먼저 관련 문서를 검색한 뒤, 그 내용을 기반으로 답변을 생성하는 기법입니다. 예를 들어, 여러 개의 계약서 문서가 있을 때 "특정 조항이 포함된 계약서는?"이라고 물으면, AI가 먼저 관련 문서를 찾고 그 내용을 바탕으로 정확한 답변을 제공합니다.

왜 이 둘을 함께 사용하나요?

저는 실제로 수천 개의 기술 문서를 관리하는 시스템을 구축할 때 이 조합을 사용했습니다. 각 문서가 자주 업데이트되고, 사용자가 자연어로 검색해야 하는 상황이었습니다. MCP를 사용하면 검색 도구를 표준화된 방식으로 연결할 수 있어서, 나중에 검색 엔진을 바꾸거나 추가하더라도 코드를 크게 수정할 필요가 없었습니다.

기본 환경 설정하기

시작하기 전에 필요한 도구들을 설치하겠습니다. 이 튜토리얼에서는 Python 3.9 이상이 필요합니다.

# 필요한 패키지 설치
pip install mcp holysheep-sdk python-dotenv langchain langchain-community

프로젝트 폴더 생성 및 이동

mkdir rag-mcp-tutorial cd rag-mcp-tutorial

환경 변수 파일 생성

touch .env

HolySheep AI API 키 설정

지금 가입해서 HolySheep AI에서 무료 크레딧을 받으세요. HolySheep AI는海外 신용카드 없이 로컬 결제를 지원하며, 단일 API 키로 GPT-4.1, Claude, Gemini 등 모든 주요 모델을 통합할 수 있습니다. 가입 후 받은 API 키를 .env 파일에 저장합니다.

# .env 파일에 다음 내용 작성
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
BASE_URL=https://api.holysheep.ai/v1

MCP 서버 구성하기

MCP 서버는 검색 도구와 AI 모델 사이에서 중개자 역할을 합니다. 이번 섹션에서는 파일 기반 검색 서버와 데이터베이스 검색 서버 두 가지를 만들어보겠습니다.

# mcp_server.py
import json
import os
from pathlib import Path
from typing import List, Dict, Any, Optional

class SimpleSearchServer:
    """간단한 문서 검색 MCP 서버"""
    
    def __init__(self, documents_path: str):
        self.documents_path = Path(documents_path)
        self.document_index: Dict[str, Any] = {}
        
    def initialize(self):
        """문서 인덱스 초기화"""
        if not self.documents_path.exists():
            self.documents_path.mkdir(parents=True, exist_ok=True)
            self._create_sample_documents()
        
        self._build_index()
        print(f"인덱스 초기화 완료: {len(self.document_index)}개 문서 로드됨")
    
    def _create_sample_documents(self):
        """샘플 문서 생성"""
        documents = {
            "product_guide_ko.txt": """HolySheep AI 제품 가이드
                - HolySheep AI는 글로벌 AI API 게이트웨이입니다
                - GPT-4.1 모델 제공 (비용: 미션당 8달러)
                - Claude Sonnet 4.5 모델 제공 (비용: 미션당 15달러)
                - Gemini 2.5 Flash 모델 제공 (비용: 미션당 2.5달러)
                - DeepSeek V3.2 모델 제공 (비용: 미션당 0.42달러)
                - 로컬 결제 지원으로海外 신용카드 불필요
                """,
            "api_reference.txt": """HolySheep AI API 레퍼런스
                - 기본 URL: https://api.holysheep.ai/v1
                - 채팅 완성 API: POST /chat/completions
                - 임베딩 API: POST /embeddings
                - 사용량 조회: GET /usage
                - API 키는 HolySheep 대시보드에서 생성 가능
                """,
            "pricing_info.txt": """요금제 정보
                - 월간 구독 또는 종량제 선택 가능
                - 무료 크레딧: 신규 가입 시 제공
                - 사용량 초과 시 자동 결제
                - 한국 원화로 결제 가능
                """
        }
        
        for filename, content in documents.items():
            filepath = self.documents_path / filename
            filepath.write_text(content, encoding='utf-8')
    
    def _build_index(self):
        """문서 인덱스 구축"""
        for filepath in self.documents_path.glob("*.txt"):
            self.document_index[filepath.name] = filepath.read_text(encoding='utf-8')
    
    def search(self, query: str, max_results: int = 3) -> List[Dict[str, Any]]:
        """문서 검색 수행"""
        results = []
        query_lower = query.lower()
        
        # 단순 키워드 매칭 기반 검색
        for filename, content in self.document_index.items():
            content_lower = content.lower()
            if any(keyword in content_lower for keyword in query_lower.split()):
                # 관련성 점수 계산
                score = sum(1 for keyword in query_lower.split() if keyword in content_lower)
                results.append({
                    "filename": filename,
                    "score": score,
                    "content": content.strip(),
                    "preview": content[:200].replace("\n", " ") + "..."
                })
        
        # 점수순 정렬
        results.sort(key=lambda x: x["score"], reverse=True)
        return results[:max_results]
    
    def get_document(self, filename: str) -> Optional[str]:
        """특정 문서 내용 반환"""
        return self.document_index.get(filename)
    
    def list_documents(self) -> List[str]:
        """모든 문서 목록 반환"""
        return list(self.document_index.keys())


MCP 도구 정의

TOOLS = [ { "name": "search_documents", "description": "사용자의 질문과 관련된 문서를 검색합니다. 질문어 포함된 모든 키워드를 검색합니다.", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "검색할 질문"}, "max_results": {"type": "integer", "description": "반환할 최대 결과 수", "default": 3} }, "required": ["query"] } }, { "name": "get_document_content", "description": "특정 문서의 전체 내용을 가져옵니다.", "input_schema": { "type": "object", "properties": { "filename": {"type": "string", "description": "문서 파일명"} }, "required": ["filename"] } }, { "name": "list_all_documents", "description": "사용 가능한 모든 문서의 목록을 반환합니다.", "input_schema": {"type": "object", "properties": {}} } ] if __name__ == "__main__": # 테스트 실행 server = SimpleSearchServer("./documents") server.initialize() # 도구 목록 출력 print("\n=== 사용 가능한 MCP 도구 ===") for tool in TOOLS: print(f"- {tool['name']}: {tool['description']}")

RAG 시스템 구현하기

이제 HolySheep AI API와 MCP 검색 서버를 연결해서 RAG 파이프라인을 구축하겠습니다.

# rag_system.py
import os
import json
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from dotenv import load_dotenv

환경 변수 로드

load_dotenv() @dataclass class SearchResult: """검색 결과 데이터 클래스""" filename: str content: str preview: str score: float @dataclass class RAGResponse: """RAG 응답 데이터 클래스""" answer: str sources: List[SearchResult] model_used: str tokens_used: int latency_ms: float class HolySheepAIClient: """HolySheep AI API 클라이언트""" def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"): self.api_key = api_key self.base_url = base_url self.model = "gpt-4.1" # 기본 모델: GPT-4.1 def chat_completion( self, messages: List[Dict[str, str]], temperature: float = 0.7, max_tokens: int = 1000 ) -> Dict[str, Any]: """채팅 완성 API 호출""" import requests url = f"{self.base_url}/chat/completions" headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" } payload = { "model": self.model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens } response = requests.post(url, headers=headers, json=payload) response.raise_for_status() return response.json() class RAGPipeline: """RAG 파이프라인: 검색 + 생성 통합 시스템""" def __init__(self, search_server, ai_client: HolySheepAIClient): self.search_server = search_server self.ai_client = ai_client def _format_context(self, results: List[SearchResult]) -> str: """검색 결과를 컨텍스트 형식으로 변환""" context_parts = [] for i, result in enumerate(results, 1): context_parts.append( f"[문서 {i}: {result.filename}]\n{result.content}\n" ) return "\n".join(context_parts) def _build_prompt(self, query: str, context: str) -> List[Dict[str, str]]: """RAG 프롬프트 구성""" system_prompt = """당신은 문서 기반 질문 답변 어시스턴트입니다. 주어진 문서를 참조해서 정확하고 상세한 답변을 제공하세요. 답변을 만들 때 반드시 참조한 문서의 출처를 명시하세요. 문서에서 답변에 대한 정보를 찾을 수 없으면, "제공된 문서에서 해당 정보를 찾을 수 없습니다"라고 답변하세요.""" user_prompt = f"""=== 참조 문서 === {context} === 질문 === {query} === 답변 작성 규칙 === 1. 참조 문서 내용을 기반으로 답변하세요 2. 답변에 사용된 출처(문서명)를 명시하세요 3. 문서에 없는 내용은 추측하지 마세요""" return [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ] def query(self, question: str, max_search_results: int = 3) -> RAGResponse: """질문 처리 파이프라인 실행""" import time start_time = time.time() # 1단계: 관련 문서 검색 search_results_raw = self.search_server.search( query=question, max_results=max_search_results ) search_results = [ SearchResult( filename=r["filename"], content=r["content"], preview=r["preview"], score=r["score"] ) for r in search_results_raw ] # 2단계: 컨텍스트 구성 context = self._format_context(search_results) # 3단계: HolySheep AI API로 답변 생성 messages = self._build_prompt(question, context) response = self.ai_client.chat_completion(messages) # 4단계: 결과 정리 latency_ms = (time.time() - start_time) * 1000 return RAGResponse( answer=response["choices"][0]["message"]["content"], sources=search_results, model_used=response.get("model", "gpt-4.1"), tokens_used=response.get("usage", {}).get("total_tokens", 0), latency_ms=latency_ms ) def interactive_mode(self): """대화형 질문 모드""" print("\n=== RAG 검색 시스템에 오신 것을 환영합니다 ===") print("질문을 입력하세요. '종료'를 입력하면 프로그램이 끝납니다.\n") while True: question = input("질문: ").strip() if question.lower() in ["종료", "exit", "quit"]: print("감사합니다. 프로그램을 종료합니다.") break if not question: print("질문을 입력해주세요.\n") continue try: result = self.query(question) print(f"\n📄 답변:\n{result.answer}") print(f"\n📚 참조 문서 ({len(result.sources)}개):") for i, source in enumerate(result.sources, 1): print(f" [{i}] {source.filename} (관련성: {source.score})") print(f"\n⏱️ 처리 시간: {result.latency_ms:.2f}ms") print(f"🤖 사용 모델: {result.model_used}\n") except Exception as e: print(f"오류 발생: {str(e)}\n") if __name__ == "__main__": from mcp_server import SimpleSearchServer # HolySheep AI 클라이언트 초기화 api_key = os.getenv("HOLYSHEEP_API_KEY") if not api_key: print("오류: HOLYSHEEP_API_KEY 환경 변수가 설정되지 않았습니다.") print(".env 파일을 확인해주세요.") exit(1) ai_client = HolySheepAIClient(api_key=api_key) # 검색 서버 초기화 search_server = SimpleSearchServer("./documents") search_server.initialize() # RAG 파이프라인 생성 rag_pipeline = RAGPipeline(search_server, ai_client) # 대화형 모드 시작 rag_pipeline.interactive_mode()

검색 도구와 생성 모델의 자동 협력 구현

더 발전된 형태로, AI가 질문 내용을 분석해서 자동으로 적절한 검색 도구를 선택하고, 필요하면 추가 검색도 수행하는 시스템을 만들어보겠습니다.

# mcp_rag_orchestrator.py
import os
import json
import time
from typing import List, Dict, Any, Optional, Union
from dataclasses import dataclass, field
from enum import Enum
from dotenv import load_dotenv

load_dotenv()

class ToolType(Enum):
    """사용 가능한 도구 유형"""
    DOCUMENT_SEARCH = "document_search"
    WEB_SEARCH = "web_search"
    DATABASE_QUERY = "database_query"
    CALCULATOR = "calculator"

@dataclass
class ToolCall:
    """도구 호출 정보"""
    tool_name: str
    tool_type: ToolType
    parameters: Dict[str, Any]
    reasoning: str

@dataclass
class Message:
    """대화 메시지"""
    role: str  # "user", "assistant", "system"
    content: str
    tool_calls: List[ToolCall] = field(default_factory=list)

class MCPToolRegistry:
    """MCP 도구 레지스트리: 사용 가능한 도구 관리"""
    
    def __init__(self):
        self.tools: Dict[str, Dict[str, Any]] = {}
        self._register_default_tools()
    
    def _register_default_tools(self):
        """기본 도구 등록"""
        self.register_tool(
            name="document_search",
            description="문서库에서 관련 정보를 검색합니다. 사실 확인, 제품 정보, 가이드 등 문서 기반 질문에 사용됩니다.",
            tool_type=ToolType.DOCUMENT_SEARCH,
            parameters={
                "query": {"type": "string", "required": True},
                "max_results": {"type": "integer", "default": 5}
            }
        )
        
        self.register_tool(
            name="web_search",
            description="웹에서 실시간 정보를 검색합니다. 뉴스, 최신 동향, 날씨 등 현재 정보가 필요한 질문에 사용됩니다.",
            tool_type=ToolType.WEB_SEARCH,
            parameters={
                "query": {"type": "string", "required": True},
                "num_results": {"type": "integer", "default": 5}
            }
        )
        
        self.register_tool(
            name="calculate",
            description="수학적 계산이나 데이터 분석을 수행합니다.",
            tool_type=ToolType.CALCULATOR,
            parameters={
                "expression": {"type": "string", "required": True}
            }
        )
    
    def register_tool(
        self, 
        name: str, 
        description: str, 
        tool_type: ToolType,
        parameters: Dict[str, Any]
    ):
        """새 도구 등록"""
        self.tools[name] = {
            "name": name,
            "description": description,
            "type": tool_type,
            "parameters": parameters
        }
    
    def get_tools_for_prompt(self) -> str:
        """프롬프트에 포함할 도구 설명 생성"""
        tool_descriptions = []
        for name, tool in self.tools.items():
            param_info = ", ".join(
                f"{p}: {info.get('type', 'any')}" 
                for p, info in tool["parameters"].items()
            )
            tool_descriptions.append(
                f"- {name}({param_info}): {tool['description']}"
            )
        return "\n".join(tool_descriptions)
    
    def execute_tool(self, name: str, parameters: Dict[str, Any]) -> Any:
        """도구 실행"""
        if name not in self.tools:
            raise ValueError(f"도구를 찾을 수 없습니다: {name}")
        
        tool = self.tools[name]
        
        # 도구 유형에 따른 실제 실행 로직
        if tool["type"] == ToolType.DOCUMENT_SEARCH:
            return self._execute_document_search(**parameters)
        elif tool["type"] == ToolType.WEB_SEARCH:
            return self._execute_web_search(**parameters)
        elif tool["type"] == ToolType.CALCULATOR:
            return self._execute_calculate(**parameters)
        else:
            return {"error": "지원되지 않는 도구 유형"}
    
    def _execute_document_search(self, query: str, max_results: int = 5) -> Dict[str, Any]:
        """문서 검색 실행"""
        # 실제 구현에서는 여기서 벡터 데이터베이스나 파일 시스템을 查询
        return {
            "results": [
                {"title": "문서 1", "snippet": f"'{query}' 관련 내용입니다", "score": 0.95},
                {"title": "문서 2", "snippet": f"'{query}'에 대한 추가 정보", "score": 0.87}
            ],
            "total": 2
        }
    
    def _execute_web_search(self, query: str, num_results: int = 5) -> Dict[str, Any]:
        """웹 검색 실행"""
        return {
            "results": [
                {"title": f"'{query}' 관련 웹 결과", "url": "https://example.com"}
            ]
        }
    
    def _execute_calculate(self, expression: str) -> Dict[str, Any]:
        """계산 실행"""
        try:
            result = eval(expression)  # 실제 환경에서는 안전한 eval 사용 필요
            return {"expression": expression, "result": result}
        except Exception as e:
            return {"error": str(e)}

class IntelligentRAGOrchestrator:
    """지능형 RAG 오케스트레이터: 도구 선택 + 검색 + 생성 자동화"""
    
    def __init__(self, api_key: str, tool_registry: MCPToolRegistry):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        self.tool_registry = tool_registry
        self.conversation_history: List[Message] = []
        self.max_iterations = 3  # 최대 도구 호출 반복 횟수
    
    def _call_api(self, messages: List[Dict[str, Any]]) -> Dict[str, Any]:
        """HolySheep AI API 호출"""
        import requests
        
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        payload = {
            "model": "g