1. 코드 표시용 표준 HTML 소스
Markdown Backup Source
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" 같은 옵션을 추가할 수 있습니다.
---