2026년 7월 21일 화요일

LLM(대규모 언어 모델) 분야에서 @ 기호는 주로 사용자 인터페이스(UI), ...



LLM(대규모 언어 모델) 분야에서 @ 기호는 주로 사용자 인터페이스(UI), 프롬프트 엔지니어링, API 환경에 따라 다른 의미로 사용됩니다.
1. 프롬프트 내 특정 컨텍스트(파일, 지식, 사용자) 지정
채팅 기반 LLM 인터페이스(예: ChatGPT, Claude 등)에서는 '@'를 사용하여 특정 파일, 템플릿, 또는 지식 베이스를 호출하거나 참조합니다.
* 의미: "이 문서(@)를 참고해서 답변해 줘"라는 의미의 참조자(Mention) 역할.
* 예시: @기획서.pdf 이 내용을 바탕으로 이메일 초안을 작성해 줘. 또는 @동료 태그를 통해 특정 작업자를 호출하는 협업 봇 환경에서 사용됩니다.
2. 파일 업로드 및 참조 매핑 (API 및 개발 환경)개발자가 LLM API를 사용하거나 복잡한 프롬프트를 작성할 때, @는 외부 파일이나 데이터를 불러오는 지시어(Operator)로 작동합니다.
* 의미: 파일의 경로(Path)나 데이터를 지시하는 기호.
* 예시: curl 명령어 등으로 LLM API에 데이터를 전송할 때 -F "file=@/path/to/document.txt" 형태로 사용됩니다.
3. 모델(Model) 또는 에이전트(Agent) 지정멀티 에이전트(Multi-agent) 시스템이나 여러 AI 모델을 사용할 수 있는 통합 플랫폼에서는 '@' 기호를 사용해 특정 역할을 하는 AI를 직접 지목합니다.
* 의미: 특정 LLM 에이전트 호출.
* 예시: @코딩봇 파이썬 코드 에러 수정해 줘, @번역봇 이 문장 영어로 번역해 줘

---

LLM UI에서 사용하는 @ 기호는 MCP(Model Context Protocol)와 기술적으로 매우 밀접하게 연결되어 작동합니다.
쉽게 설명하자면, @는 사용자가 쓰는 '입력 창구(UI)'이고, MCP는 그 뒤에서 실제로 데이터를 연결해 주는 '파이프라인(프로토콜)'입니다.
💡 @와 MCP의 핵심 관계
* @ (사용자 인터페이스, UI): 사용자가 채팅창에서 외부 데이터나 도구를 직관적으로 선택하기 위한 단축키입니다. @깃허브, @내문서처럼 입력하여 모델에게 무엇을 참고할지 지정합니다.
* MCP (모델 컨텍스트 프로토콜): @로 지정한 외부 소스(GitHub, 데이터베이스, 로컬 파일 등)에서 실제 데이터와 기능을 안전하게 가져와 LLM에게 전달하는 표준 규격입니다. Anthropic이 공개한 오픈소스 프로토콜로 유명합니다.
---
🔄 작동 방식 비교사용자가 커서(Cursor)나 윈드서프(Windsurf) 같은 AI 개발 도구에서 @를 칠 때, 백엔드에서는 MCP가 다음과 같이 움직입니다.
```
[ 사용자 ] ---> 채팅창에 "@database" 입력 (UI 역할)
                  ↓
[ MCP 클라이언트 ] -> MCP 서버에 데이터 요청 (프로토콜 역할)
                  ↓
[ MCP 서버 ] ----> 실제 데이터베이스에서 내용 조회 후 LLM에 전달
```

🧐 차이점 한눈에 보기
구분		@ 기호 (Mention)						MCP (Model Context Protocol)
정의		프롬프트 입력창에서 쓰는 호출 태그 			AI와 외부 데이터를 연결하는 표준 통신 규격
역할 	컨텍스트 지정을 시각적으로 편리하게 만듦 	다른 개발자가 만든 도구/데이터를 쉽게 연동함
예시 	@App.tsx, @Web Search 				mcp-server-postgres, mcp-server-github
결론적으로, 최근 AI 도구(Cursor, Cline 등)에서 @를 눌러 외부 정보를 가져오는 기능의 상당수가 내부적으로는 MCP 규격을 기반으로 구현되어 있습니다.

---

파이썬으로 직접 만든 로컬 AI(로아) 프로그램 상에서도 @ 기능을 완벽하게 구현할 수 있습니다.
기존의 개발 도구(Cursor 등)처럼 사용자가 채팅창에 @를 입력하면 특정 파일, 데이터베이스, 또는 웹 검색 결과를 가져와 LLM 프롬프트에 자동으로 합쳐주는(Context Injection) 구조를 직접 만들면 됩니다.
파이썬 환경에서 이를 구현하는 원리와 핵심 단계 3가지를 설명해 드릴게요.
---
🛠️ 파이썬으로 '@' 기능 구현하는 3단계 복제법
1단계: 정규표현식(Regex)으로 프롬프트에서 @ 명령어 파싱하기
사용자가 입력한 프롬프트 문자열에서 @ 뒤에 오는 단어나 파일명을 추출하는 정규식 필터가 필요합니다.
```python
import re

def parse_at_command(user_input):
    # 예: "이 내용 @code.py 참고해서 분석해줘" -> ['code.py'] 추출
    at_matches = re.findall(r'@(\S+)', user_input)
    
    # '@파일명' 문자열 자체는 프롬프트에서 깔끔하게 지우기
    clean_input = re.sub(r'@\S+', '', user_input).strip()
    
    return clean_input, at_matches

# 테스트
prompt = "이 파일 @secret.txt 요약해줘"
text, tags = parse_at_command(prompt)
print("순수 질문:", text) # "이 파일  요약해줘"
print("연결할 데이터 태그:", tags) # ['secret.txt']
```

2단계: 태그에 맞는 컨텍스트 데이터 로드하기 (MCP 역할 모방)@ 뒤에 붙은 키워드를 인식하여, 로컬 파일 시스템이나 데이터베이스에서 실제 텍스트 내용을 읽어옵니다.
```python
import os

def get_context_by_tags(tags):
    context_data = ""
    
    for tag in tags:
        # 1. 파일 이름일 경우 데이터 로드
        if os.path.exists(tag):
            with open(tag, 'r', encoding='utf-8') as f:
                context_data += f"\n--- 파일 [{tag}] 내용 ---\n{f.read()}\n"
        
        # 2. 특정 시스템 명령어일 경우 (예: @search)
        elif tag == "search":
            # 웹 검색 API 결과를 가져오는 함수 호출 가능
            context_data += "\n--- 웹 검색 결과 ---\n[최신 로아 패치 정보...]\n"
            
        else:
            context_data += f"\n[알림: {tag} 데이터를 찾을 수 없습니다.]\n"
            
    return context_data
```

3단계: 컨텍스트와 질문을 합쳐 LLM(API)에 전달하기파싱된 원래 질문과 @로 긁어온 데이터를 하나의 시스템 프롬프트 템플릿으로 묶어 OpenAI나 Anthropic API 혹은 로컬 LLM(Ollama 등)에 던져줍니다.
```python
def generate_llm_input(user_input):
    clean_prompt, tags = parse_at_command(user_input)
    fetched_context = get_context_by_tags(tags)
    
    # LLM이 이해할 수 있도록 프롬프트 조합 (RAG 구조)
    final_llm_prompt = f"""
[주어진 컨텍스트 정보]
{fetched_context}

[사용자 요청]
{clean_prompt}

위 제공된 컨텍스트 정보를 최우선으로 참고하여 질문에 답하세요.
"""
    return final_llm_prompt
```

---
🚀 한 걸음 더: 진짜 'MCP 표준 규격'을 붙이고 싶다면?만약 하드코딩 방식이 아니라, 외부의 다양한 MCP 서버(예: 깃허브 연동, 포스트그레 데이터베이스 연동 등)를 직접 파이썬 로아에 이식하고 싶다면, Anthropic이 제공하는 mcp 파이썬 패키지를 사용하면 됩니다.
```bash
pip install mcp
```
파이썬 소스코드 내에서 mcp.Client를 생성하여 미리 만들어진 오픈소스 MCP 서버들과 통신하도록 연동하면, 복잡한 기능들을 직접 개발하지 않고도 @github, @postgres 같은 고수준 기능을 여러분의 로아에 그대로 탑재할 수 있습니다.

---

파이썬 환경에서 실제 MCP 서버(Model Context Protocol)와 직접 통신(Client 구현)하는 핵심 코드입니다.
Anthropic의 공식 mcp 라이브러리는 비동기(asyncio) 기반으로 작동하므로 async/await 문법을 사용해야 합니다.
가장 대중적이고 쉬운 로컬 파일 시스템 MCP 서버(mcp-server-filesystem)와 통신하여 특정 디렉토리의 파일 목록을 조회하는 클라이언트 예제를 준비했습니다.1. 필수 라이브러리 설치터미널에서 MCP 파이썬 SDK를 설치합니다.
```bash
pip install mcp
```

2. 파이썬 MCP 클라이언트 전체 소스 코드
```python
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def run_mcp_client():
    # 1. 연결할 MCP 서버 설정 (여기서는 Node.js 기반 파일시스템 MCP 서버 사용)
    # npx mcp-server-filesystem <허용할_디렉토리_경로>
    server_params = StdioServerParameters(
        command="npx",
        args=[
            "-y", 
            "@modelcontextprotocol/server-filesystem", 
            "./"  # 현재 디렉토리 접근 허용
        ],
        env=None
    )

    print("🔌 MCP 서버에 연결 시도 중...")
    
    # 2. 입출력(Stdio) 채널을 통해 서버와 연결을 수립합니다.
    async with stdio_client(server_params) as (read_stream, write_stream):
        # 3. 세션을 생성하고 초기화(Initialize) 단계를 거칩니다.
        async with ClientSession(read_stream, write_stream) as session:
            await session.initialize()
            print("✅ MCP 서버 연결 및 초기화 완료!")

            # 4. 서버가 제공하는 사용 가능한 도구(Tools) 목록을 조회합니다.
            tools_response = await session.list_tools()
            print("\n🛠️ 사용 가능한 MCP 도구 목록:")
            for tool in tools_response.tools:
                print(f"- {tool.name}: {tool.description}")

            # 5. 특정 도구를 직접 호출(통신)해 봅니다.
            # 여기서는 현재 디렉토리 파일 목록을 보는 'list_directory' 도구를 호출합니다.
            print("\n🔄 'list_directory' 도구 호출 중...")
            try:
                result = await session.call_tool(
                    name="list_directory",
                    arguments={"path": "."}  # 현재 디렉토리 경로 전달
                )
                
                print("\n📄 [MCP 서버 응답 결과]:")
                print(result.content[0].text)
                
            except Exception as e:
                print(f"❌ 도구 호출 실패: {e}")

# 비동기 함수 실행
if __name__ == "__main__":
    asyncio.run(run_mcp_client())
```
---
💡 코드 핵심 짚어보기
1. StdioServerParameters: 파이썬 프로그램이 MCP 서버 프로세스(Node.js나 다른 Python 스크립트)를 자식 프로세스로 직접 실행하고 통신하기 위한 설정입니다.
2. stdio_client: 표준 입출력(Standard I/O) 스트림을 열어 서버와 JSON-RPC 메시지를 주고받을 수 있는 통로를 만듭니다.
3. session.initialize(): 클라이언트와 서버가 서로 어떤 기능을 지원하는지 프로토콜 규격에 맞춰 버전을 맞추는 필수 악수(Handshake) 과정입니다.
4. session.call_tool(): 이게 핵심입니다. LLM이 프롬프트를 분석한 뒤 @filesystem 같은 도구가 필요하다고 판단하면, 파이썬 코드가 이 함수를 통해 외부 데이터를 긁어와 LLM 프롬프트에 동적으로 꽂아 주게 됩니다.

---

앞서 만든 MCP 클라이언트 코드와 로컬 LLM(Ollama 활용 예시)을 연동한 전체 파이썬 소스코드입니다.
사용자가 대화창에 @를 입력하면, 파이썬이 이를 감지하여 MCP 서버에서 로컬 데이터를 가져온 뒤, 로컬 LLM에게 컨텍스트와 함께 질문을 던지는 구조(RAG 시스템)입니다.
0. 사전 준비 (필수 라이브러리 및 로컬 LLM)코드를 실행하기 전에 터미널에 아래 라이브러리들을 설치하고, 로컬 LLM(Ollama)이 실행 중인지 확인하세요.
```bash
pip install mcp openai
```

※ 로컬 LLM은 OpenAI 호환 API를 지원하는 Ollama(ollama run llama3 등)가 실행 중이라고 가정합니다.
---
1. MCP + 로컬 LLM 연동 파이썬 전체 코드
```python
import asyncio
import re
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from openai import OpenAI

# 1. 로컬 LLM(Ollama) 클라이언트 설정
# Ollama는 기본적으로 11434 포트에서 OpenAI 규격 API를 지원합니다.
llm_client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"  # 로컬이므로 무작위 문자열 입력 가능
)

# [기능 1] 사용자의 질문에서 @명령어 파싱하는 함수
def parse_at_command(user_input):
    # 예: "@dir 현재 폴더에 뭐가 있어?" -> ['dir'] 추출
    at_matches = re.findall(r'@(\S+)', user_input)
    # '@명령어' 텍스트는 프롬프트에서 깨끗하게 제거
    clean_input = re.sub(r'@\S+', '', user_input).strip()
    return clean_input, at_matches

# [기능 2] 핵심 MCP 및 LLM 프로세스 구동 함수
async def run_local_ai_with_mcp(user_prompt):
    # 프롬프트 분석 및 @태그 분리
    clean_prompt, tags = parse_at_command(user_prompt)
    
    # 2. 파일시스템 MCP 서버 설정 (예시로 현재 디렉토리 폴더 연결)
    server_params = StdioServerParameters(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-filesystem", "./"],
        env=None
    )

    fetched_context = ""

    # 사용자가 @ 기호(예: @dir)를 썼을 때만 MCP 작동
    if tags:
        print(f"🔍 감지된 @ 명령어: {tags}")
        print("🔌 MCP 서버에 연결하여 데이터를 가져오는 중...")
        
        async with stdio_client(server_params) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()

                # @dir 태그가 입력되면 특정 MCP 도구(list_directory) 호출
                if "dir" in tags:
                    try:
                        mcp_result = await session.call_tool(
                            name="list_directory",
                            arguments={"path": "."}
                        )
                        # MCP 서버로부터 받은 결과 저장
                        fetched_context = mcp_result.content[0].text
                        print("✅ MCP 외부 데이터 수집 완료!")
                    except Exception as e:
                        print(f"❌ MCP 도구 호출 실패: {e}")
    else:
        print("ℹ️ @ 명령어가 없습니다. 로컬 LLM 일반 모드로 답변합니다.")

    # 3. 로컬 LLM용 파이널 프롬프트 조립 (Context Injection)
    system_message = "당신은 사용자를 돕는 로컬 AI 비서입니다."
    if fetched_context:
        system_message += f"\n\n[참고할 MCP 외부 데이터]\n{fetched_context}\n\n위 데이터를 반드시 참고하여 사용자의 질문에 정확히 답변하세요."

    print("\n🤖 로컬 LLM이 답변 생성 중...")
    
    # 4. 로컬 LLM(Ollama 등)에 요청 보내기
    response = llm_client.chat.completions.create(
        model="llama3",  # PC에 다운로드 받아둔 Ollama 모델명 입력
        messages=[
            {"role": "system", "content": system_message},
            {"role": "user", "content": clean_prompt}
        ],
        temperature=0.3
    )

    # 5. 최종 답변 출력
    print("\n================ [AI 답변] ================")
    print(response.choices[0].message.content)
    print("===========================================")

# 🚀 실행 테스트용 메인 루프
async def main():
    print("🤖 로컬 AI 시스템이 준비되었습니다.")
    
    # 예시 1: MCP를 사용하는 질문 (@dir 입력)
    test_prompt_1 = "@dir 현재 폴더 안에 있는 파일들의 이름을 나열하고 요약해줘."
    print(f"\n[유저 질문]: {test_prompt_1}")
    await run_local_ai_with_mcp(test_prompt_1)
    
if __name__ == "__main__":
    asyncio.run(main())
```
💡 작동 프로세스 구조
1. parse_at_command: 사용자가 @dir 현재 폴더에 뭐가 있어?라고 치면 @dir를 지우고 현재 폴더에 뭐가 있어?라는 본문과 ['dir']이라는 태그 배열을 추출합니다.
2. stdio_client 연동: 태그 배열에 dir이 포함되어 있다면 MCP 파일시스템 서버를 켜서 내 컴퓨터의 실제 디렉토리 구조 텍스트를 긁어옵니다.
3. Prompt Injection (RAG): 긁어온 텍스트(예: main.py, README.md...)를 로컬 LLM의 System Message 영역에 몰래 집어넣습니다.
4. 로컬 LLM 답변: LLM은 내 컴퓨터 파일 주소를 직접 읽지 못하지만, 파이썬(MCP)이 주입해 준 데이터를 기반으로 마치 실시간 파일 탐색을 한 것처럼 똑똑하게 답변합니다.

---

오픈AI 라이브러리(SDK) 대신, Hugging Face의 transformers 라이브러리와 토크나이저의 apply_chat_template()을 활용하여 로컬 LLM을 직접 구동하는 코드입니다.
이 방식은 Llama 3, Mistral, Qwen 등 Hugging Face 포맷 모델을 내 컴퓨터 메모리(GPU/CPU)에 직접 올려서 구동할 때 표준으로 사용하는 방식입니다.
1. 필수 라이브러리 설치터미널에서 허깅페이스 트랜스포머와 가속화 라이브러리를 설치합니다.
```bash
pip install mcp transformers torch accelerate
```

2. MCP + Hugging Face apply_chat_template 연동 전체 코드
```python
import asyncio
import re
import torch
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from transformers import AutoModelForCausalLM, AutoTokenizer

# 1. 로컬 LLM 모델 및 토크나이저 로드 (예: Qwen2.5 또는 Llama-3-8B 등 사용 가능)
MODEL_ID = "Qwen/Qwen2.5-7B-Instruct"  # 내 PC 환경에 맞는 모델 ID 지정

print("🤖 로컬 LLM 및 토크나이저 로드 중... (시간이 걸릴 수 있습니다)")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(
    MODEL_ID,
    torch_dtype=torch.bfloat16,  # GPU 맞춤 데이터 타입
    device_map="auto"            # 자동으로 사용 가능한 GPU/CPU 할당
)

# [기능 1] 사용자의 질문에서 @명령어 파싱하는 함수
def parse_at_command(user_input):
    at_matches = re.findall(r'@(\S+)', user_input)
    clean_input = re.sub(r'@\S+', '', user_input).strip()
    return clean_input, at_matches

# [기능 2] MCP 호출 및 토크나이저 챗 템플릿 연동 함수
async def run_local_hf_with_mcp(user_prompt):
    clean_prompt, tags = parse_at_command(user_prompt)
    
    # MCP 파일시스템 서버 설정
    server_params = StdioServerParameters(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-filesystem", "./"],
        env=None
    )

    fetched_context = ""

    # @dir 태그 감지 시 MCP 작동
    if tags and "dir" in tags:
        print(f"🔍 감지된 @ 명령어: {tags}")
        print("🔌 MCP 서버에 연결하여 데이터를 가져오는 중...")
        
        async with stdio_client(server_params) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                try:
                    mcp_result = await session.call_tool(
                        name="list_directory",
                        arguments={"path": "."}
                    )
                    fetched_context = mcp_result.content.text
                    print("✅ MCP 외부 데이터 수집 완료!")
                except Exception as e:
                    print(f"❌ MCP 도구 호출 실패: {e}")
    else:
        print("ℹ️ @ 명령어가 없거나 일치하지 않아 일반 모드로 진행합니다.")

    # 3. Chat Template을 위한 메시지 리스트 구조 정의
    system_content = "당신은 유용한 로컬 AI 비서입니다."
    if fetched_context:
        system_content += f"\n\n[참고할 MCP 외부 데이터]\n{fetched_context}\n\n위 데이터를 반드시 참고하여 답변하세요."

    messages = [
        {"role": "system", "content": system_content},
        {"role": "user", "content": clean_prompt}
    ]

    # 4. 🔥 핵심: tokenizer.apply_chat_template 활용
    # 모델에 맞는 특수 토크(<|im_start|>, <|eot_id|> 등)를 자동으로 결합하여 프롬프트 문자열로 변환합니다.
    prompt_text = tokenizer.apply_chat_template(
        messages, 
        tokenize=False, 
        add_generation_prompt=True
    )

    # 5. 모델 입력을 위한 토큰화 및 텐서 변환
    inputs = tokenizer([prompt_text], return_tensors="pt").to(model.device)

    print("\n🤖 로컬 LLM이 추론(Generation) 중...")
    
    # 6. 답변 생성 실행
    with torch.no_grad():
        generated_ids = model.generate(
            **inputs,
            max_new_tokens=512,
            temperature=0.3,
            do_sample=True,
            pad_token_id=tokenizer.eos_token_id
        )
    
    # 입력된 프롬프트 부분을 제외하고 모델이 새로 생성한 답변 토큰만 추출
    generated_ids = [
        output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, generated_ids)
    ]

    # 7. 디코딩하여 사람이 읽을 수 있는 텍스트로 복원
    response_text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]

    print("\n================ [HF 로컬 LLM 답변] ================")
    print(response_text.strip())
    print("====================================================")

# 🚀 실행 테스트용 메인 함수
async def main():
    test_prompt = "@dir 현재 폴더 안에 어떤 파일들이 있는지 목록을 정리해줘."
    print(f"\n[유저 질문]: {test_prompt}")
    await run_local_hf_with_mcp(test_prompt)
    
if __name__ == "__main__":
    asyncio.run(main())
```
---
💡 달라진 핵심 포인트 코드 분석
* tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True):
	* messages 리스트 구조를 받아서, 모델 고유의 대화 포맷(ChatML, Llama-3-Instruct 포맷 등)을 가진 하나의 긴 텍스트 문장으로 완벽하게 바꿔줍니다.
	* add_generation_prompt=True 옵션은 끝에 AI가 답변을 시작하라는 유도 토큰(예: <|im_start|>assistant\n)을 붙여주는 역할을 합니다.
* len(input_ids): 슬라이싱:
	* Hugging Face의 model.generate()는 질문과 답변이 이어져서 출력됩니다. 따라서 내가 처음에 집어넣은 질문 토큰의 길이만큼 잘라내야 순수한 AI의 답변 내용만 화면에 표시할 수 있습니다.

---

llama.cpp 파이썬 바인딩 라이브러리(llama-cpp-python)와 GGUF 포맷 모델을 활용하여 MCP와 연동하는 코드입니다.
VRAM(그래픽 메모리)이 부족한 환경이거나 맥북(Apple Silicon), CPU 환경에서 로컬 LLM을 매우 가볍고 빠르게 돌릴 때 가장 추천하는 방식입니다.
llama-cpp-python 역시 내장된 metadata나 별도 처리를 통해 Chat Template을 지원하므로, Jinja 템플릿 표준 방식으로 apply_chat_template 역할을 직접 구현하거나 내장 챗 핸들러를 사용해 연동할 수 있습니다.
1. 필수 라이브러리 설치터미널에서 llama-cpp-python과 mcp 라이브러리를 설치합니다.
```bash
# 기본 설치 (CPU 환경)
pip install mcp llama-cpp-python

# 만약 NVIDIA GPU(CUDA) 환경이라면 아래 명령어로 가속 버전을 설치하세요
# CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python --force-reinstall
```

※ 로컬에 Llama-3-8B-Instruct.Q4_K_M.gguf 같은 GGUF 파일이 미리 다운로드되어 있어야 합니다.
2. MCP + llama.cpp GGUF 연동 전체 코드
```python
import asyncio
import re
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from llama_cpp import Llama

# 1. 로컬 GGUF 모델 로드 (내 PC에 받아둔 .gguf 파일 경로 지정)
MODEL_PATH = "./models/Meta-Llama-3-8B-Instruct.Q4_K_M.gguf"

print("🤖 llama.cpp GGUF 모델 로드 중...")
llm = Llama(
    model_path=MODEL_PATH,
    n_ctx=4096,         # 컨텍스트 창 크기 설정 (MCP 데이터를 받기 위해 넉넉하게)
    n_gpu_layers=-1,    # GPU 가속 레이어 수 (-1은 가능한 한 전부 GPU에 할당)
    verbose=False       # 지저분한 내부 로그 숨기기
)
print("✅ 모델 로드 완료!")

# [기능 1] 사용자의 질문에서 @명령어 파싱하는 함수
def parse_at_command(user_input):
    at_matches = re.findall(r'@(\S+)', user_input)
    clean_input = re.sub(r'@\S+', '', user_input).strip()
    return clean_input, at_matches

# [기능 2] MCP 호출 및 llama.cpp 내장 Chat Completion 연동 함수
async def run_llama_cpp_with_mcp(user_prompt):
    clean_prompt, tags = parse_at_command(user_prompt)
    
    # MCP 파일시스템 서버 설정
    server_params = StdioServerParameters(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-filesystem", "./"],
        env=None
    )

    fetched_context = ""

    # @dir 태그 감지 시 MCP 작동
    if tags and "dir" in tags:
        print(f"🔍 감지된 @ 명령어: {tags}")
        print("🔌 MCP 서버에 연결하여 데이터를 가져오는 중...")
        
        async with stdio_client(server_params) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                try:
                    mcp_result = await session.call_tool(
                        name="list_directory",
                        arguments={"path": "."}
                    )
                    fetched_context = mcp_result.content.text
                    print("✅ MCP 외부 데이터 수집 완료!")
                except Exception as e:
                    print(f"❌ MCP 도구 호출 실패: {e}")
    else:
        print("ℹ️ @ 명령어가 없거나 일치하지 않아 일반 모드로 진행합니다.")

    # 3. Chat 구조 정의
    system_content = "당신은 유용한 로컬 AI 비서입니다."
    if fetched_context:
        system_content += f"\n\n[참고할 MCP 외부 데이터]\n{fetched_context}\n\n위 데이터를 반드시 참고하여 사용자의 질문에 정확히 답변하세요."

    messages = [
        {"role": "system", "content": system_content},
        {"role": "user", "content": clean_prompt}
    ]

    print("\n🤖 llama.cpp 로컬 LLM이 추론(Generation) 중...")
    
    # 4. 🔥 핵심: `create_chat_completion` 메소드 사용
    # llama-cpp-python은 모델 내부에 포함된 chat_template 스크립트를 자동으로 읽어와서 
    # HuggingFace의 apply_chat_template과 똑같이 특수 토큰 처리를 자동으로 수행합니다.
    response = llm.create_chat_completion(
        messages=messages,
        max_tokens=512,
        temperature=0.3,
        stream=False
    )

    # 5. 결과 파싱 및 출력
    response_text = response["choices"][0]["message"]["content"]

    print("\n================ [GGUF 로컬 LLM 답변] ================")
    print(response_text.strip())
    print("======================================================")

# 🚀 실행 테스트용 메인 함수
async def main():
    test_prompt = "@dir 현재 내 작업 폴더에 어떤 파일들이 존재하는지 보고해줘."
    print(f"\n[유저 질문]: {test_prompt}")
    await run_llama_cpp_with_mcp(test_prompt)
    
if __name__ == "__main__":
    asyncio.run(main())
```
💡 GGUF 연동 코드의 핵심 포인트
1. llm.create_chat_completion():
	* Hugging Face의 apply_chat_template과 토큰화 과정을 한 번에 묶어놓은 고수준 API입니다.
	* GGUF 파일 헤더 정보에 들어있는 내장 템플릿(예: Llama-3의 <|begin_of_text|>)을 알아서 파싱해서 문자열을 조립해 주기 때문에 복잡한 토크나이저 연동 처리가 필요 없습니다.
2. n_gpu_layers=-1:
	* 시스템의 VRAM 용량에 맞춰 LLM의 신경망 레이어를 GPU로 넘기는 옵션입니다. -1로 설정하면 그래픽카드가 감당할 수 있는 최대 레이어를 알아서 연동하므로 연산 속도를 대폭 끌어올릴 수 있습니다.
3. n_ctx=4096:
	* 기본 컨텍스트 길이가 짧게 잡혀있으면 MCP 서버에서 큰 파일 내용이나 대용량 DB 데이터를 가져와 주입할 때 문장이 잘릴 수 있으므로 최소 4096 이상으로 넉넉하게 설정하는 것이 좋습니다.

---

파이썬 스크립트 내부에서 vllm 라이브러리를 인라인 모드(LLM Engine 부팅)로 직접 실행하여 MCP 서버와 연동하는 소스코드입니다.
vllm은 Hugging Face 토크나이저의 apply_chat_template 구조를 내장하여 완벽히 지원하므로, 오프라인 추론 방식으로 매우 강력하고 빠르게 로컬 AI(로아)를 구성할 수 있습니다.
1. 필수 라이브러리 설치vllm은 성능 최적화를 위해 특정 Linux 환경이나 적절한 CUDA 환경이 필요합니다. 환경에 맞춰 설치해 주세요.
```bash
pip install mcp vllm torch
```
2. MCP + vLLM 내부 연동 전체 파이썬 코드
```python
import asyncio
import re
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from vllm import LLM, SamplingParams

# 1. vLLM 인라인 엔진 및 토크나이저 초기화 (파이썬 내부에서 독립 구동)
MODEL_ID = "Qwen/Qwen2.5-7B-Instruct"  # 내 PC 환경에 맞는 모델 ID 지정

print("🤖 vLLM 내부 엔진 초기화 중... (VRAM 상태를 체크하세요)")
# 외부 서버(vllm serve)를 띄우지 않고, 파이썬 프로세스 내에서 직접 모델을 메모리에 올립니다.
llm_engine = LLM(
    model=MODEL_ID,
    trust_remote_code=True,
    max_model_len=4096  # MCP 컨텍스트 삽입을 고려하여 컨텍스트 길이 설정
)
# vLLM 엔진 내부에 탑재된 토크나이저 가져오기
tokenizer = llm_engine.get_tokenizer()
print("✅ vLLM 엔진 로드 완료!")

# [기능 1] 사용자의 질문에서 @명령어 파싱하는 함수
def parse_at_command(user_input):
    at_matches = re.findall(r'@(\S+)', user_input)
    clean_input = re.sub(r'@\S+', '', user_input).strip()
    return clean_input, at_matches

# [기능 2] MCP 호출 및 vLLM 추론 연동 함수
async def run_vllm_with_mcp(user_prompt):
    clean_prompt, tags = parse_at_command(user_prompt)
    
    # MCP 파일시스템 서버 설정
    server_params = StdioServerParameters(
        command="npx",
        args=["-y", "@modelcontextprotocol/server-filesystem", "./"],
        env=None
    )

    fetched_context = ""

    # @dir 태그 감지 시 MCP 작동
    if tags and "dir" in tags:
        print(f"🔍 감지된 @ 명령어: {tags}")
        print("🔌 MCP 서버에 연결하여 데이터를 가져오는 중...")
        
        async with stdio_client(server_params) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                try:
                    mcp_result = await session.call_tool(
                        name="list_directory",
                        arguments={"path": "."}
                    )
                    fetched_context = mcp_result.content.text
                    print("✅ MCP 외부 데이터 수집 완료!")
                except Exception as e:
                    print(f"❌ MCP 도구 호출 실패: {e}")
    else:
        print("ℹ️ @ 명령어가 없거나 일치하지 않아 일반 모드로 진행합니다.")

    # 3. Chat 구조 정의 (System + User Prompt)
    system_content = "당신은 유용한 로컬 AI 비서입니다."
    if fetched_context:
        system_content += f"\n\n[참고할 MCP 외부 데이터]\n{fetched_context}\n\n위 데이터를 반드시 참고하여 질문에 답하세요."

    messages = [
        {"role": "system", "content": system_content},
        {"role": "user", "content": clean_prompt}
    ]

    # 4. 🔥 핵심: 토크나이저를 활용한 Chat Template 프롬프트 변환
    # vLLM 엔진에서 가져온 토크나이저로 모델 전용 특수 토큰 포맷 텍스트를 만듭니다.
    prompt_text = tokenizer.apply_chat_template(
        messages, 
        tokenize=False, 
        add_generation_prompt=True
    )

    # 5. vLLM 전용 생성 파라미터(SamplingParams) 설정
    sampling_params = SamplingParams(
        temperature=0.3,
        max_tokens=512,
        stop_token_ids=[tokenizer.eos_token_id]  # 모델이 답변을 끝마치도록 유도
    )

    print("\n🤖 vLLM 엔진이 고속 추론 중...")
    
    # 6. vLLM 인라인 엔진으로 답변 생성 처리
    # vLLM은 기본적으로 배치(List) 입력을 받아 병렬 처리하므로 대괄호 [] 로 감싸 전달합니다.
    outputs = llm_engine.generate([prompt_text], sampling_params)
    
    # 7. 결과 추출
    response_text = outputs[0].outputs[0].text

    print("\n================ [vLLM 로컬 AI 답변] ================")
    print(response_text.strip())
    print("======================================================")

# 🚀 실행 테스트용 메인 함수
async def main():
    test_prompt = "@dir 현재 디렉토리에 있는 폴더 구조를 읽고 정리해줘."
    print(f"\n[유저 질문]: {test_prompt}")
    await run_vllm_with_mcp(test_prompt)
    
if __name__ == "__main__":
    asyncio.run(main())
```
---
💡 vLLM 내부 연동 코드의 핵심 포인트
1. LLM(model=...) 생성:
	* 터미널에서 vllm serve ... 명령어로 FastAPI 외부 서버를 생성하는 방식이 아닙니다. 파이썬 코드 내에서 LLM 클래스 객체를 직접 인스턴스화하여 스크립트 귀속형 인라인 엔진으로 구동합니다.
2. llm_engine.get_tokenizer():
	* 모델 내부에 내장된 tokenizer_config.json 파일을 분석하여 자동으로 대화형 템플릿(apply_chat_template) 규칙을 가져옵니다.
3. llm_engine.generate([prompt_text], ...):
	* 내부 메모리에 상주한 KV 캐시와 PagedAttention 기술을 사용하여 다른 라이브러리들보다 압도적으로 빠른 텍스트 생성 속도를 보여줍니다.
vLLM은 뛰어난 속도를 내는 대신 기본적으로 그래픽카드 VRAM을 90% 이상 선점하려는 성질이 있습니다. 혹시 VRAM 부족 에러(OOM)가 나거나 사양이 제한적이시라면 LLM() 매개변수에 gpu_memory_utilization=0.7 또는 양자화를 위한 quantization="awq" 같은 옵션을 추가할 수 있습니다.

---


Cargo.toml

# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # rustup update # cargo update [pack...