반응형

 

요즘 AI 가 발달하면서 생겨난 일명 "바이브 코딩" 이라는 기술로, 아이디어만 있으면, 누구나 개발이 가능한 환경이 갖춰졌다.

 

나 역시도 마찬가지로 바이브 코딩으로 하고 싶었던 주제로 서비스를 만들어보기도 하고, 툴을 만들면서 내 업무의 생산성을 높이고 있다.

 

오늘 다룰 Github Action 이라는 주제와 맞지 않는 서문일 수 있지만, 우리가 만든 개발물은 결국 Git 을 통해 형상관리를 해줘야 손이 덜 가게 된다는 것을 느낄 것이다.

 

Github 는 Git 을 온라인 호스팅으로 서비스하고 있는 솔루션으로 보면 되고, 마이크소프트 산하의 제품이다.

 

Github 든 GitLab 이던 Repository 를 만들어 코드를 관리하고 있다면, 매번 새로운 Commit, Merge 가 일어날 때마다 서버에 진입하며 코드를 최신화 ($ git pull) 하고 서비스를 재시작 해야 할 것이다.

 

보통은 이 과정들을 Script 로 만들고, Jenkins , GoCD 와 같은 CI/CD 서비스 를 이용하여 Git 에 커밋이 발생할 때 자동으로 배포하게 만든다.

 

Github Action 도 비슷한 기능이 있어, 아래 소개한다.

  • 준비
    • 서비스 자동화 스크립트
    • 서비스 자동화 스크립트가 배포된 EC2 서버
  • 시나리오
    • 서비스 서버에 배포용 SSH 키페어를 생성한다.
    • 생성된 공개키를 서버의 authorized_keys 에 추가한다.
    • Github Secrets 설정에서 원격 서버의 정보를 저장한다.
    • workflow yaml 파일을 작성한다.
    • Git Action 에서 워크플로우를 실행한다.

전체 과정

1. SSH 키 생성 및 설정

 

 

먼저 로컬에서 SSH 키 페어를 생성

ssh-keygen -t ed25519 -C "github-actions"

 

생성된 공개키(id_ed25519.pub)를 서버의 ~/.ssh/authorized_keys에 추가, 개인키(id_ed25519)는 일단 복사한다.

2. GitHub Secrets 설정

Repository → Settings → Secrets and variables → Actions에서 다음 정보들을 등록

  • SSH_PRIVATE_KEY: SSH 개인키 내용 (1번 과정에서 복사한 개인키를 복사해서 붙여넣는다.)
  • SERVER_HOST: 서버 IP 또는 도메인
  • SERVER_USER: SSH 접속 유저명
  • SERVER_PORT: SSH 포트 (기본값 22)

3. GitHub Actions Workflow 파일 생성

.github/workflows/deploy.yml 파일을 생성합니다:## 더 간단한 방법: marketplace action 사용

SSH 액션을 직접 작성하는 대신, 검증된 marketplace action을 사용할 수도 있습니다:

  1. 코드 푸시: 워크플로우 파일을 커밋하고 main 브랜치에 푸시
  2. Actions 탭 확인: GitHub Repository의 Actions 탭에서 실행 상태 확인
  3. 로그 확인: 각 step의 실행 로그를 확인하여 성공/실패 여부 파악
    name: Test Workflow (여기 이름이 워크플로우 목록에 뜸)
    
    on:
      # 수동 실행 가능하도록 설정 (테스트용)
      workflow_dispatch:
      
      # push할 때도 실행 (원하면 주석 해제)
      # push:
      #   branches:
      #     - main
    
    jobs:  #워크플로우에서 실행할 작업(job)들을 정의합
      test-workflow: # 작업(job)의 고유한 이름입니다. 여기서는 '연결 테스트' 역할
        runs-on: ubuntu-latest
        # 이 작업이 실행될 환경을 지정. 최신 버전의 Ubuntu 가상 머신에서 실행
        steps:
        - name: Checkout code 
          uses: actions/checkout@v4 # GitHub 저장소의 코드를 가상 환경으로 가져오는(체크아웃하는) 공식 액션(v4 버전)을 사용
      
        - name: SSH Connection Test 
          uses: appleboy/ssh-action@v1.0.0 # SSH를 통해 원격 서버에 명령을 실행하는 서드파티 액션(v1.0.0 버전)을 사용
          with:
            host: ${{ secrets.SERVER_HOST }}
            username: ${{ secrets.SERVER_USER }}
            key: ${{ secrets.SSH_PRIVATE_KEY }}
            port: 22
            script: |
              echo "SSH Connection Test Success"
              [여기에 자동화 스크립트 실행 명령어를 넣으면 된다.]

 

결과 

이 방법으로 코드가 푸시될 때마다 자동으로 서버에 배포하거나 특정 작업을 실행할 수 있다.

 


결론

  • 배포 자동화는 사실 구성하기 귀찮다. 손도 많이가고, 시간도 많이 소요된다.
  • 그런데 한 번 구성해놓으면, 그 후는 정말 편하다.
  • 서비스 배포를 한동안 안하다가 오랜만에 하면 까먹고, 다시 기억해내려면 시간이 또 걸리거든.

 

반응형
반응형

잡담.

MCP 서버를 내가 외부에 제공한다고 했을 때, 여러 방법이 있을 것 같다.

가장 먼저 생각나는 건 서버 코드를 공유하고 stdio 실행 방식으로 제공하는 방식, 그리고 또 다른 방법은 웹서버를 띄워 StreamableHTTP 방식으로 제공하는 것이다.

MCP 서버를 직접 운영하면서 오픈한다는 의미는 인증과 인가를 붙이겠다는 의미일 것 같다.

AWS Lambda 환경에서 MCP 서버를 배포할 경우 아래의 장점이 있다.
- 인프라 관리가 불필요하여, 서버 운영 부담이 없다.
- 사용한 만큼 비용이 지불되기 때문에 비용 효율성이 높다.
- 자동으로 스케일링이 되기 때문에 트래픽에 따라 h/w 자원을 동적으로 변경할 수 있다.
- 보안 및 권한 관리가 쉽고, 도메인 주소관리, SSL 관리 등의 네트워크 리소스 운영 작업이 편하다.

단점도 있다.
- 최대 실행시간과 메모리 제한이 있고, 로깅과 제어가 제한적이다.

이 포스팅의 목적은 비교적 최근에 나온 MCP 서버를 AWS Lambda 환경에서 MCP 배포가 가능할지 확인해보기 위함이다.


그럼 시작해보자.

 

 


AWS Lambda 에서 MCP 서버 구축 해보기

A) 사전 준비

  • fastMCP 로 구현한 mcp server 코드
  • 컨테이너 이미지 로 생성할 Dockerfile 코드
  • requirements.txt 패키지 설치 파일

B) 시나리오 

1. teams webhook 을 이용해 message 를 발송하는 MCP 서버 구축
2. mcp 서버를 컨테이너 이미지로 생성하고, ECR 레포지토리에 업로드
3. aws lambda 를 생성하고 ecr 에 업로드 된 이미지를 이용하여 배포 
4. 아래 두 가지 방식으로 MCP Server 테스트를 한다.

  a. Lambda 의 FunctionURL 을 이용해 MCP Client 로 연결하여 Teams 채널로 메세지를 전송해본다.
(LLM 모델은 로컬에서 테스트를 위해 Ollama 모델을 활용)
  b. cursor 의 MCP tool 로 등록해서 Teams 채널로 메세지를 전송해본다.

 

C) Step by Step

MCP 서버 코드 

from fastmcp import FastMCP
import os
import requests
import json
from typing import Optional, Dict, Any
WEBHOOK_URL = "웹훅 주소"
mcp = FastMCP("MCP-TEST-SERVER")

@mcp.tool()
async def get_my_info() -> str:
    """ 안녕하세요. 저는 러닝을 좋아하는 jssvs 입니다."""
    return f""

@mcp.tool()
async def get_weather() -> str:
    """현재 날씨를 조회합니다"""
    return f"오늘 날씨는 맑습니다."


@mcp.tool()
def send_teams_message(
    message: str,
    title: str = None,
) -> bool:
    """
    Microsoft Teams 채널에 webhook을 통해 메시지를 전송합니다.
    
    Args:
        message (str): 전송할 메시지 내용
        title (str, optional): 메시지 제목
    Returns:
        bool: 전송 성공 여부
    Example:
        send_teams_message(message="안녕하세요!", title="알림")
    """
    color = "00FF00"
    # MessageCard 형식으로 페이로드 구성
    payload = {
        "@type": "MessageCard",
        "@context": "https://schema.org/extensions",
        "summary": title or "알림",
        "sections": [{
            "activityTitle": title,
            "text": message
        }]
    }
    
    # 색상이 지정된 경우 추가
    if color:
        payload["themeColor"] = color
    
    # 제목이 없는 경우 섹션에서 제거
    if not title:
        del payload["sections"][0]["activityTitle"]
    
    try:
        response = requests.post(
            WEBHOOK_URL,
            headers={"Content-Type": "application/json"},
            data=json.dumps(payload)
        )
        
        # 성공 여부 확인
        if response.status_code == 200:
            print(f"✓ 메시지 전송 성공")
            return True
        else:
            print(f"✗ 메시지 전송 실패: {response.status_code}")
            print(f"응답: {response.text}")
            return False
            
    except Exception as e:
        print(f"✗ 오류 발생: {str(e)}")
        return False

def main():
    host = os.environ.get("HOST","0.0.0.0")
    port = int(os.environ.get("PORT",8080))
    print(f"Starting FastMCP HTTP Server on {host}:{port}")
    print("Available tools:")
    print("- get_bdp_team_info: Returns BDP team information")
    print("- get_weather: Returns weather information")
    print(f"Server accessible at: http://{host}:{port}")
    mcp.run(transport="http",host=host, port=port, stateless_http=True)

if __name__ == "__main__":
    main()

requirements.txt

fastmcp>=0.1.0
requests

 

Dockerfile

FROM python:3.12-slim
WORKDIR /app

# ### Environment Variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1


# Install the specified packages
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# MCP 서버 코드 복사
COPY mcp_server.py .

# 환경 변수 설정
ENV PORT=8080
ENV AWS_LWA_INVOKE_MODE=response_stream
ENV AWS_LWA_READINESS_CHECK_PORT=8080
ENV AWS_LWA_READINESS_CHECK_PATH=/health

# === Lambda Web Adapter ===
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.0 /lambda-adapter /opt/extensions/lambda-adapter

# 서버 시작 명령어
CMD ["python", "mcp_server.py"]

 

D) 참고해야 할 부분 !

1) Lambda 설정 시 참고 
-> 환경변수에 2개의 값 추가 (Dockerfile 에 ENV 에 추가해줬다면 생략해도 된다.)

2) 기본 TImeout 값으로는 MCP tool 호출이 되기 전에 호출이 종료되기 때문에 값을 조정한다.

 

 

 

E) 테스트 해보기

 

 

A) Strands Agent 코드를 작성하여 테스트 발송

from strands import Agent
from strands.models.ollama import OllamaModel
from strands.tools.mcp.mcp_client import MCPClient
from mcp.client.streamable_http import streamablehttp_client


client = MCPClient(
    lambda: streamablehttp_client(
        # url = "http://127.0.0.1:8080/mcp"
        url = "https://LambdaFunctinURL 주소.lambda-url.ap-northeast-2.on.aws/mcp"
    )
)
with client:
    tools = client.list_tools_sync()
    
    base_model = OllamaModel(
        host = "http://localhost:11434",
        model_id = "llama3.1:8b",
        temperature = 0.1
        )
    agent = Agent(tools = tools, model = base_model)
    # print(agent.tool.get_weather())
    result = agent("teams 채널에 아래 메세지를 전송해줘 \n message : Lambda 에서 구축한 MCP 서버 테스트 \n title : MCP 서버 테스트")
    print(result)

 

 

 

B) Cursor MCP 툴 연동하여 테스트 발송

 

 

결론

  • lambda 의 장점은 서버리스, 즉 물리 서버를 운용하지 않으면서 서비스를 배포하는데 있다. 그렇다면 MCP 서버 역시 이런 장점을 누릴 수 있다.
  • 내가 테스트한 부분은 운영 버전용으로 활용할 수 는 없다. 운영 버전까지 고려할 때는 보안을 위해 VPC 안에서 배포를 한다던지, 앞단에 Gateway 의 셋팅, 동시성 테스트 등 다양한 준비가 필요하다.
  • 그리고 Dockerfile 을 보면 알겠지만, lambda web adapter 를 멀티스테이지 빌드를 사용하여 로드한 부분이 있는데, lambda 의 extension 기능으로 붙인 것이다. 즉 기능 추가, 의존성 등의 확장에 있어 제한이 있을 수 있음을 의미한다.

참고

[1] Welcome to FastMCP 2.0 - https://gofastmcp.com/getting-started/welcome 
[2] GitHub - awslabs/aws-lambda-web-adapter - https://github.com/awslabs/aws-lambda-web-adapter 
[3] Deploy MCP Server on AWS Lambda | Step-by-Step Guide - https://www.youtube.com/watch?v=JncV8hXuyRg&t=341s 
[4] 컨테이너 이미지로 Python Lambda 함수 배포 - Python용 AWS 기본 이미지 사용 - https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-image.html#python-image-instructions

반응형
반응형

 

최근 Cursor 업데이트로 PLAN 모드가 쓸만 한 것 같아, 소개하면서 룰과 메모리 뱅크에 대해서 비교한 내용을 포스팅 한다

 

커서의 룰과 메모리뱅크는 왜쓸까?

 

바로 일관된 규칙과 지시사항을 준수하게 하기 위해 사용한다.

 

cursor 에도 기본적으로 제공하는 .rule이라는 이름의 기능이 있지만, 별도의 써드파티 설치로 memory bank 를 활용하기도 하는데, 이 둘의 용도가 약간 햇갈린다.

1-1.Plan 모드 (신규 기능)

  • 코딩 작업 실행 전 사용자 요구사항을 확장하여 전략적 사고와 계획을 수립한다.
  • 큰 목적에 따라 작업을 분해하고, 계획을 세우며, 순차적 실행을 하기 위한 총 전략 파일을 markdown 포맷으로 생성한다.
  • 중간에 사용자와 interactive 하게 세부 계획에 대한 질문을 던져 수정하기도 한다.

1-2.Memroy bank 기능

  • 프로젝트 전반에 걸쳐 지속적으로 사용할 컨텍스트와 지식을 파일에 저장하여 관리한다.
  • cursor 에 내장된 기능인 rule 과 유사하지만, 룰은 단순 텍스트기반이지만 , memory bank 는 조금 더 구조화된 데이터를 갖는다
  • CLI 도구를 통해 컨텍스트와 프로그램 정보를 현행화한다.
  • 차이 점 비교 

구분기본 RulesMemory Bank

구분 Cursor Rules Memroy BAnk
관리 방식 수동 편집 CLI 도구
구조 단순 텍스트 구조화된 데이터
업데이트 파일 직접 수정 명령어로 관리
조직화 개발자가 직접 자동 카테고리화
학습 정적 AI 기반 학습 가능
검색 직접 찾기 검색 기능 제공

2. 2개의 기능 요약

기능주요 목적작동 방식 및 특징

Plan Mode (계획 모드) 코딩 작업 실행 전에 깊이 있는 전략적 사고와 계획 수립을 지원 - AI가 코드베이스를 분석하고, 관련 파일, 종속성, 문서를 연구하여 컨텍스트를 빠르게 파악
    - 실행 전에 명확한 질문을 던져 요구 사항을 정교화
    - 수립된 계획은 Markdown 파일로 저장되며, 파일 경로 및 코드 참조가 포함되어 수정 및 공유가 용이
    - 대규모 작업 시 코딩 전에 계획을 수립하도록 유도하여 버그를 줄이고 구조를 개선
Memory Bank
(npx cursor-bank)
AI가 세션 간에 프로젝트 컨텍스트를 유지하고 지속적인 기억을 갖도록 함. (Cursor의 기본 AI는 매 세션 컨텍스트가 초기화됨) - 구조화된 문서 파일 (예: [projectbrief.md](http://projectbrief.md), [activeContext.md](http://activeContext.md), [systemPatterns.md](http://systemPatterns.md) 등)을 생성하고 유지하여 장기 기억 역할 수행
    - AI는 새로운 세션이 시작될 때 이 문서들을 참조하여 프로젝트 이해도를 재구축
    - 커뮤니티에서 개발된 프로젝트이며, npx cursor-bank init과 같은 명령어로 설정
    - PLAN, ACT, update memory bank와 같은 커맨드를 통해 구조화된 워크플로우를 촉진

3. 결론

  • memory bank 는 숙지 하고 잘 쓰면, 서로의 컨텍스트를 공유하며 일관된 바이브 코딩의 결과를 기대할 수 있을 것 같지만, 사용하기 전에 CLI 를 숙지해야 하고 학습이 필요하고 , 프로젝트의 규모가 클 경우에 유리해 보인다.

끝.

반응형
반응형

 

AI Agent 시대다.

AI Agent를 쉽게 만들 수 있는 Framework 가 생겨났다.

AI Agent 엔지니어라는 직업이 나올 수도 있다.

많이 사용하는 AI Agent Framework 대표적인 것 4개를 선정하고, QuickStart 로 AI Agent 를 만들어본다.

그리고 지극히 주관적인 나의 느낌을 담아 장/단점을 정리하겠다.

후보1) Langchain - 너무 유명해서

후보2) AWS Strands SDK - 내가 AWS 클라우드 환경에 익숙해서

 

후보3) CrewAI - 비기너에게 적합하다고 해서  

후보4) Autogen - 멀티에이전트 구현에 집중됨.

후보5) LangGraph - 그래프 기반으로 stateful, multi-agent 시스템에 접근하는 확장성있는 Framework

 

 

Framework 를 고르는데 중요한 요소 (Key Factor)

  • 복잡한 워크플로우 까지 구현이 가능한가. - 복잡한 워크플로우도 구현 가능해?
  • 데이터 통합 - 다양한 데이터 소스로 부터 데이터를 연동하고 반환이 가능해?
  • 멀티에이전트 콜라보 - 여러 에이전트와의 협업 성능이 좋은가?
  • 노 코드 VS 코드 친화적이냐 - 노코드로 에이전트를 만들수 있는가? 반면에 개발자들에게 조금 더 친숙한 개발 환경을 제공하는가

 

LangChain - 생태계 광범위, 큰 커뮤니티
- 다양한 통합 도구 지원
- 성숙하고 안정적인 프레임워크
- LangSmith 제공
- Chain/Agent/RAG 등 유연한 패턴
- 과도한 추상화로 학습 곡선 높음
- 다중 레이어로 성능 저하
- 메모리 모듈 불안정
- 잦은 API 변경으로 호환성 문제
- 모듈식 아키텍처
- RAG/문서 처리 강점
- 엔터프라이즈급 기능 지원
- 복잡한 RAG 시스템
- 외부 서비스 통합 필요
- 엔터프라이즈 환경
AWS Strands SDK - 모델 중심 접근
- 간단한 코드로 에이전트 구축
- AWS 네이티브 통합
- 프로덕션 검증 (Amazon Q 등)
- A2A 멀티 에이전트 지원
- MCP 네이티브 지원
- 2024년 출시, 커뮤니티 작음
- AWS 환경 의존
- 문서 및 자료 부족
- Model-driven 아키텍처
- 세션 관리/상태 지속성
- 비동기 및 스트리밍 지원
- 에이전트 간 도구 공유
- AWS 클라우드 개발
- 빠른 프로토타이핑/배포
- 최신 LLM 활용
- 엔터프라이즈급 확장
CrewAI - 쉬운 학습 곡선
- 직관적 역할 기반 팀워크
- 빠른 멀티에이전트 개발
- 우수한 문서와 예제
- 자연스러운 협업
- 높은 추상화로 세부 제어 어려움
- 로깅/디버깅 제한적
- 맞춤화 한계
- LangChain 의존성
- 스트리밍 미지원
- Agent, Crew, Task 구조
- 순차/병렬 처리 지원
- 역할 기반 협업
- 메모리 관리 내장
- 입문자용 멀티에이전트
- 빠른 프로토타입
- 단순한 팀 기반 워크플로우
- 내부 자동화
Autogen - 자연스러운 대화 중심
- 유연한 에이전트 토폴로지
- Docker 내 안전한 코드 실행
- AutoGen Studio 제공
- Microsoft 지원, 확장성 높음
- 수동 설정 복잡
- 대화 오케스트레이션 학습 필요
- 문서 불일치
- 고성능 LLM 필요
- 디버깅 난이도 높음
- 비동기 이벤트 루프 구조
- 다양한 에이전트 역할
- 대화 기반 작업 조정
- RPC 기반 분산 처리
- 동적 에이전트 대화
- 개발/코딩 어시스턴트
- R&D 환경
- 복잡한 멀티에이전트 실험
LangGraph - 그래프 기반 정밀 제어
- 뛰어난 상태 지속성 및 복구
- DAG 기반 시각화
- LangChain 통합
- 조건부 분기/오류 처리 강력
- 그래프/상태 관리 학습 곡선 높음
- 초기 구조 정의 필요
- 단순 작업에 과도
- LangChain 이슈 상속
- 문서 업데이트 지연
- DAG 아키텍처
- 조건부 엣지/분기 로직
- 상태 기반 워크플로우
- LangSmith 모니터링
- 복잡한 다단계 워크플로우
- 상태 관리 필수 시스템
- 결정적 프로세스
- 금융/헬스케어 등 규제 환경

 

 

Framework 별 간단한 Agent 구현

1) Langchain

"""
LangChain 커스텀 툴 - 에러 수정 버전

설치:
pip install langchain langchain-ollama
"""

from langchain.tools import tool
from langchain_ollama import OllamaLLM  # 새로운 import
from langchain.agents import AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate
from langchain import hub


# 간단한 덧셈 툴
@tool
def add_numbers(a: int, b: int) -> int:
    """두 숫자를 더합니다. a와 b는 정수여야 합니다."""
    result = a + b
    print(f"[툴 실행] {a} + {b} = {result}")
    return result


@tool
def multiply_numbers(a: int, b: int) -> int:
    """두 숫자를 곱합니다. a와 b는 정수여야 합니다."""
    result = a * b
    print(f"[툴 실행] {a} × {b} = {result}")
    return result


def main():
    print("=" * 70)
    print("LangChain 툴 연동 예제 (수정 버전)")
    print("=" * 70)
    
    # 툴 목록
    my_tools = [add_numbers, multiply_numbers]
    
    print("\n등록된 툴:")
    for tool in my_tools:
        print(f"  • {tool.name}: {tool.description}")
    
    # 1. 새로운 Ollama 클래스 사용
    llm = OllamaLLM(
        model="llama3.1:8b",
        temperature=0
    )
    
    # 2. PromptTemplate을 올바르게 생성
    # 방법 A: 직접 PromptTemplate 생성
    prompt = PromptTemplate.from_template("""
Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action in JSON format, e.g. {{"arg1": value1, "arg2": value2}}
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

IMPORTANT: Action Input must be valid JSON format with double quotes.
Example: {{"a": 5, "b": 3}}

Begin!

Question: {input}
Thought: {agent_scratchpad}
""")
    
    # 방법 B: LangChain Hub에서 기본 ReAct 프롬프트 가져오기 (권장)
    # prompt = hub.pull("hwchase17/react")
    
    # 3. Agent 생성
    agent = create_react_agent(
        llm=llm,
        tools=my_tools,
        prompt=prompt  # PromptTemplate 객체를 전달
    )
    
    # 4. AgentExecutor 생성
    agent_executor = AgentExecutor(
        agent=agent,
        tools=my_tools,
        verbose=True,
        handle_parsing_errors=True,
        max_iterations=5
    )
    
    # 5. 테스트 질문들
    questions = [
        "5와 3을 더하면?",
        "10과 7을 곱하면?",
        "12 더하기 8은 얼마인가요?",
    ]
    
    print("\n" + "=" * 70)
    print("Agent 실행 테스트")
    print("=" * 70)
    
    for i, question in enumerate(questions, 1):
        print(f"\n[질문 {i}] {question}")
        print("-" * 70)
        try:
            result = agent_executor.invoke({"input": question})
            print(f"\n✅ 최종 답변: {result['output']}")
        except Exception as e:
            print(f"❌ 오류: {e}")
        print("=" * 70)


# 간단한 툴 직접 테스트 (Agent 없이)
def simple_test():
    print("\n" + "=" * 70)
    print("툴 직접 테스트 (Agent 없이)")
    print("=" * 70)
    
    # 직접 호출
    result1 = add_numbers.invoke({"a": 10, "b": 20})
    print(f"\n결과 1: {result1}")
    
    result2 = multiply_numbers.invoke({"a": 5, "b": 6})
    print(f"결과 2: {result2}")
    
    print("\n" + "=" * 70)


if __name__ == "__main__":
    # 1. 먼저 툴만 간단히 테스트
    simple_test()
    
    print("\n\n")
    
    # 2. Agent와 함께 사용
    try:
        main()
    except Exception as e:
        print(f"\n❌ Agent 실행 오류: {e}")
        print("\n💡 Ollama가 실행 중인지 확인하세요: ollama serve")
        print("💡 모델이 설치되어 있는지 확인하세요: ollama pull llama3.1:8b")

2) Strands

from strands import Agent
from strands.models.ollama import OllamaModel
from strands import tool


@tool
def add_numbers(a:int, b:int) -> int:
    """
    add two numbers together
    Args:
        a: first number
        b: second number

    Returns:
        sum of a and b
    """
    return a+b

model = OllamaModel(
    model_id="llama3.1:8b",
    host="http://localhost:11434",
    temperature=0.0)

agent = Agent(model=model, tools=[add_numbers])

response = agent("1+2")
print(response)

 

3) crawAI

"""
CrewAI + Ollama를 사용한 커스텀 툴 예제

설치:
pip install crewai crewai-tools langchain-ollama
"""

from crewai import Agent, Task, Crew, Process
from crewai.tools import BaseTool  
from crewai import LLM
from typing import Type
from pydantic import BaseModel, Field


# 1. 덧셈 툴 생성
class AddNumbersInput(BaseModel):
    """덧셈 툴의 입력 스키마"""
    a: int = Field(..., description="첫 번째 숫자")
    b: int = Field(..., description="두 번째 숫자")


class AddNumbersTool(BaseTool):
    name: str = "add_numbers"
    description: str = "두 개의 숫자를 더합니다. 덧셈 계산이 필요할 때 사용하세요."
    args_schema: Type[BaseModel] = AddNumbersInput
    
    def _run(self, a: int, b: int) -> str:
        """덧셈 실행"""
        result = a + b
        return f"{a} + {b} = {result}"


def main():
    print("=" * 70)
    print("CrewAI + Ollama 3.1:8b + 커스텀 툴 예제")
    print("=" * 70)
    
    # Ollama 3.1:8b 모델 설정
    llm = LLM(
        model="ollama/llama3.1:8b",  # ⚠️ "ollama/" prefix 필수!
        base_url="http://localhost:11434"
    )
    # 툴 인스턴스 생성
    add_tool = AddNumbersTool()
    
    print("\n✅ 등록된 툴:")
    print(f"  • {add_tool.name}: {add_tool.description}")
    
    # Agent 생성 - 수학 전문가
    math_expert = Agent(
        role="수학 계산 전문가",
        goal="사용자의 계산 요청을 정확하게 처리합니다",
        backstory="""당신은 숫자 계산에 능숙한 수학 전문가입니다. 
        주어진 툴을 사용하여 정확한 계산 결과를 제공합니다.""",
        tools=[add_tool],
        llm=llm,
        verbose=True,
        allow_delegation=False
    )
    
    # Task 1: 덧셈 작업
    task1 = Task(
        description="15와 27을 더한 결과를 계산하세요.",
        expected_output="두 숫자의 덧셈 결과를 명확하게 제시",
        agent=math_expert
    )

    
    # Crew 생성 및 실행
        agents=[math_expert],
        tasks=[task1],
        process=Process.sequential,  # 순차적 실행
        verbose=True
    )
    
    print("\n" + "=" * 70)
    print("🚀 Crew 실행 시작")
    print("=" * 70)
    
    try:
        result = crew.kickoff()
        
        print("\n" + "=" * 70)
        print("✅ 최종 결과")
        print("=" * 70)
        print(result)
        
    except Exception as e:
        print(f"\n❌ 오류 발생: {e}")
        import traceback
        traceback.print_exc()


# 간단한 툴 테스트
def simple_tool_test():
    print("\n" + "=" * 70)
    print("툴 직접 테스트")
    print("=" * 70)
    
    add_tool = AddNumbersTool()
    
    # 덧셈 테스트
    result1 = add_tool._run(a=10, b=20)
    print(f"\n덧셈 결과: {result1}")

 

 

결론

  • 학습 난이도는 strands 가 가장 쉬웠고, 이해해야 할 요소가 적어서 빠르게 테스트 코드를 만들 수 있었지만 레퍼런스가 부족하고, 공식 도큐먼트의 양과 질이 아쉽다.
  • LangCahin 이 테스트 코드를 실행하는데 가장 많은 시간이 소요됐고, Agent 의 프롬프트를 하나 실행하는데도 더 많은 객체가 필요했다. (prompt 를 만들때도 PromptTemplate 객체를 만들어야 하는 점.)
  • crewAI 도 비교적 학습이 간단했지만, Task들을 객체로 만들어 요소에 넣어야 하는 특징이 있었다. Tasks 를 모아서 순차적으로 실행할 수 있고, 병렬로 실행할 수도 있으며, Crew 라는 객체 안에서 Multi Agent 의 다양한 패턴을 정의할 수 있는것이 특징이다.
  • 생산성 면에서는 Strands 가 가장 좋을 것 같다는 결론이다.
반응형
반응형

 

별 내용 필요 없이 바로 빠른 사용 부터 !!


#  세션 생성
$ tmux new -s [세션 이름]


# 세션 리스트
$ tmux ls

# 세션 접속
$ tmux a -t [세션 이름]

# 세션 나가기
$ 컨트롤 + b , 그리고 d (detach)

# 세션 삭제
$ tmux kill-session -t [세션 이름]

# 세션 전체 삭제
$ tmux kill-server



# 세션 진입 후 창 분할
$ 컨트롤 + b , 그리고 % 또는 "

# 세션 진입 후 창 이동
$ 컨트롤 + b , 그리고 방향키 

1. 설치

Ubuntu/Debian:

sudo apt update
sudo apt install tmux

CentOS/RHEL:

sudo yum install tmux

macOS:

brew install tmux

설치 확인:

tmux -V

2. 기본 개념

  • Session: 하나의 작업 환경 (여러 개를 만들 수 있음)
  • Window: 세션 안의 탭 (브라우저 탭과 비슷)
  • Pane: 윈도우를 분할한 화면

3. 필수 명령어

세션 관리:

tmux                    # 새 세션 시작
tmux new -s mysession   # 이름 지정해서 세션 시작
tmux ls                 # 세션 목록 보기
tmux attach -t mysession  # 세션에 다시 접속
tmux kill-session -t mysession  # 세션 종료

4. 핵심 단축키

모든 tmux 명령은 프리픽스 키 (Ctrl+b) 를 먼저 누른 후 사용합니다.

세션 제어:

  • Ctrl+b 그 다음 d - 세션에서 분리 (detach, 세션은 백그라운드에서 계속 실행)
  • Ctrl+b 그 다음 $ - 세션 이름 변경

윈도우(탭) 관리:

  • Ctrl+b 그 다음 c - 새 윈도우 생성 (create)
  • Ctrl+b 그 다음 n - 다음 윈도우로 이동 (next)
  • Ctrl+b 그 다음 p - 이전 윈도우로 이동 (previous)
  • Ctrl+b 그 다음 0-9 - 특정 번호 윈도우로 이동
  • Ctrl+b 그 다음 , - 윈도우 이름 변경
  • Ctrl+b 그 다음 & - 윈도우 닫기

패널(화면 분할) 관리:

  • Ctrl+b 그 다음 % - 수직 분할 (좌우로 나누기)
  • Ctrl+b 그 다음 " - 수평 분할 (위아래로 나누기)
  • Ctrl+b 그 다음 방향키 - 패널 간 이동
  • Ctrl+b 그 다음 x - 현재 패널 닫기
  • Ctrl+b 그 다음 z - 현재 패널 확대/축소 토글
  • Ctrl+b 그 다음 { 또는 } - 패널 위치 바꾸기

기타 유용한 명령:

  • Ctrl+b 그 다음 ? - 모든 단축키 목록 보기
  • Ctrl+b 그 다음 t - 시계 표시
  • Ctrl+b 그 다음 [ - 스크롤 모드 (q로 종료)

5. 실전 연습 시나리오

시나리오 1: 기본 사용법

# 1. tmux 시작
tmux

# 2. 현재 윈도우에서 작업 (예: vim으로 파일 편집)
vim test.txt

# 3. Ctrl+b 그 다음 c 로 새 윈도우 생성
# 4. 새 윈도우에서 다른 작업 (예: 로그 모니터링)
tail -f /var/log/syslog

# 5. Ctrl+b 그 다음 n 또는 p로 윈도우 간 이동
# 6. Ctrl+b 그 다음 d로 세션에서 분리
# 7. 터미널 종료해도 작업은 계속 실행됨

시나리오 2: 화면 분할 활용

tmux

# 수직 분할 (Ctrl+b %)
# 왼쪽: 코드 편집
vim app.py

# 오른쪽 패널로 이동 (Ctrl+b 방향키)
# 오른쪽: 테스트 실행
python app.py

# 오른쪽을 다시 수평 분할 (Ctrl+b ")
# 아래: 로그 확인
tail -f app.log

6. 설정 파일로 더 편하게 사용하기

~/.tmux.conf 파일을 만들어 설정을 커스터마이징할 수 있어요:

# 마우스 사용 활성화
set -g mouse on

# 윈도우 번호를 1부터 시작
set -g base-index 1

# 패널 번호를 1부터 시작
setw -g pane-base-index 1

# 더 많은 히스토리 저장
set -g history-limit 10000

# 상태바 스타일
set -g status-style 'bg=blue fg=white'

설정 적용:

tmux source-file ~/.tmux.conf

7. 자주 사용하는 패턴

원격 서버 작업:

ssh user@server
tmux new -s work
# 작업 시작...
# Ctrl+b d로 분리
# 연결 끊김

# 나중에 다시 접속
ssh user@server
tmux attach -t work  # 작업이 그대로 있음!

여러 프로젝트 동시 관리:

tmux new -s project1
# 프로젝트1 작업
# Ctrl+b d

tmux new -s project2
# 프로젝트2 작업
# Ctrl+b d

tmux ls  # 모든 세션 확인
tmux attach -t project1  # 원하는 세션으로 복귀
반응형

'System' 카테고리의 다른 글

네트워크 통신 확인하기  (0) 2024.03.14
ssh로 서버 접속하기  (1) 2024.03.04
반응형

 

 

오늘은 Strands SDK 로 개발하는 Agent 의 도구에 MCP tool 을 붙여보겠다.

 

일단 MCP 도 Agent 의 하나의 tool 에 불과하다는 개념을 다시 기억하자.

 

MCP 구성은 Server 와 Client 두 가지 구현이 필요하다.

 

MCP Server 는 필요에 따라 직접 구현보다는 IDE 에서 시켜서 구현할 수 있고 ( 필자의 경우 Cursor 를 이용해서 쉽게 mcp 서버를 구현할 수 있었다.) 외부에 공개된 MCP 서버를 직접 사용할 수 있다. 다만 remote 서버의 경우 datasource 에 대한 정보들이 네트워크 트래픽을 타고 외부에 넘어갈 수 있으니 보안에 유의하자.

 

일단 아래 aws 에서도 aws 서비스에 access 할 수 있는 도구들을 mcp 서버로 제공하고 있다. 

- https://awslabs.github.io/mcp/

 

Welcome to AWS MCP Servers | AWS MCP Servers

Get started with AWS MCP Servers and learn core features.

awslabs.github.io

 

그리고 내가 일반적으로 Smithery 에서도 설치가 가능하지만, 설치가 잘 안될 때는 아래 cursor 의 mcp 페이지에서도 설치해보면 된다.

https://docs.cursor.com/ko/tools/mcp

 

Cursor: The best way to code with AI

Built to make you extraordinarily productive, Cursor is the best way to code with AI.

cursor.com

 

Strands SDK 에서의 MCP Client 요약

  • Strands SDK 에서 쉽게 MCP Client 를 구현해줌
  • MCP 클라이언트를 생성할 때 어떤 방식으로 연결할 것인지 선택해야 함
    • Standard I/O (stdio) : 로컬 프로세스를 구현한다면 적합
    • Streamable HTTP : HTTP 이벤트를 호출해서 스트림 방식으로 구현할 때
    • Server-Sent Events (SSE) : HTTP 기반의 서버를 직접 구축했을 때
    • Custom Transport with MCP Client - 그외 직접 프로토콜을 구현할 때

 

많이 활용하는 stdio , streamableHTTP 방식의 예제 코드를 아래에 첨부한다.

 

예제 코드 내용

  • Agent 모델을 만듦.
  • sequential-thingking  mcp 도구 등록.
  • strands tools 에서 기본으로 제공하는 http_request 를 이용해 openweather api 의 도큐먼트 페이지를 분석시킴.

 

from mcp import stdio_client, StdioServerParameters
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from strands_tools import http_request, retrieve

base_model = BedrockModel(
    model_id="apac.anthropic.claude-3-7-sonnet-20250219-v1:0",
    region_name="ap-northeast-2",
    temperature=0.3,
)

stdio_mcp_client = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="uvx",
            args=["awslabs.aws-documentation-mcp-server@latest"]
        )
    )
)

# HTTPStreamable 방식으로 붙일 경우 
# sequential_thinking = MCPClient(
#     lambda: streamablehttp_client("https://smithery.ai/server/@smithery-ai/server-sequential-thinking")
# )


# StdIO 방식으로 붙일 경우
sequential_thinking = MCPClient(
    lambda: stdio_client(
        StdioServerParameters(
            command="npx",
            args=[
                "-y",
                "@smithery/cli@latest",
                "run",
                "@smithery-ai/server-sequential-thinking",
                "--key",
                "생성된 사용자 키",
                "--profile",
                "생성된 사용자 프로필"
            ]
        )
    )
)
# 예제 테스트 코드 
with sequential_thinking:
    tools = sequential_thinking.list_tools_sync()
    tools += [http_request, retrieve]

    agent = Agent(tools=tools, model=base_model)
    result = agent("https://openweathermap.org/forecast5 에서 어떤 날씨 데이터 테이블을 수집할 수 있는지 분석해줘")
    print(result)

# with stdio_mcp_client:
#     tools = stdio_mcp_client.list_tools_sync()
#     agent = Agent(tools = tools, model = base_model)
#     result = agent("What is AWS Lambda?")
반응형
반응형

1.Langfuse 란?

Langfuse는 오픈소스 LLM 옵저버빌리티(Observability) 플랫폼이에요.

AI 애플리케이션의 성능 모니터링, 추적(Tracing), 평가(Evaluation), 그리고 프로덕션 환경에서의 LLM 사용량 분석을 위한 종합적인 도구를 제공합니다.

 

Langfuse 의 주요 기능들.

추적 및 모니터링 - LLM API 호출의 전체 lifecycle 추적- 토큰 사용량, 응답 시간, 비용 분석- 멀티 에이전트 워크플로우의 단계별 추적- 실시간 성능 메트릭 대시보드
데이터 분석 - 프롬프트 성능 분석 및 최적화- 사용자 세션 및 대화 추적- A/B 테스트를 위한 프롬프트 버전 관리- 데이터 내보내기 및 외부 분석 도구 연동
품질 관리 - LLM 응답 품질 평가 및 점수화- 사용자 피드백 수집 및 분석- 모델 성능 회귀 탐지- 프로덕션 환경에서의 품질 모니터링

 

 

2.왜 Langfuse ?

구분문제점Langfuse 해결책

1. 프로덕션 환경에서의 가시성 확보 - LLM API 호출이 블랙박스처럼 작동하여 문제 원인 파악이 어려움- 토큰 사용량과 비용을 실시간으로 추적하기 어려움- 성능 병목지점을 식별하기 어려움 - 모든 LLM 호출의 상세 로그와 메트릭 제공- 실시간 비용 추적 및 예산 관리- 응답 시간, 처리량 등 성능 지표 모니터링
2. 멀티 에이전트 시스템의 복잡성 관리 - 여러 에이전트가 연동하는 워크플로우에서 각 단계별 성능 파악 어려움- 에러 발생 시 정확한 원인 위치 추적 어려움- 에이전트 간 데이터 흐름 파악 어려움 - 워크플로우의 전체 실행 경로 시각화- 각 에이전트별 성능 메트릭 분리 측정- 계층적 추적으로 복잡한 호출 체인 관리
3. 데이터 기반 최적화 - 프롬프트 성능을 정량적으로 평가하기 어려움- 사용자 만족도와 모델 응답 품질 상관관계 파악 어려움- 개선 효과를 객관적으로 측정하기 어려움 - 프롬프트별 성능 지표 자동 수집- 사용자 피드백과 모델 메트릭 연동 분석- A/B 테스트를 통한 개선 효과 검증
4. 비용 최적화 - LLM API 비용이 예상보다 높게 발생- 어떤 기능이 비용을 많이 소모하는지 파악 어려움- 비용 효율적인 모델 선택 기준 부재 - 기능별, 사용자별 세분화된 비용 분석- 모델별 cost-per-performance 비교- 비용 임계값 알림 및 예산 관리

3.Langfuse 아키텍쳐

 

4.구축해보기

환경

  • 쿠버네티스가 설치되어있는 환경
  • Kubectl 설치 및 연동이 되어있고, 배포 권한이 있어야 함

순서

#!/bin/bash

  

helm repo add langfuse-k8s https://langfuse.github.io/langfuse-k8s/

echo "repository added.."

  

helm repo update

echo "repository updated.."

  

LANGFUSE_VERSION="1.5.2"

SERVICE_NAME="langfuse"

VALUES_FILE="langfuse-value.yaml"

NAMESPACE="langfuse"

  

echo "install langfuse..."

helm install $SERVICE_NAME langfuse-k8s/langfuse -n $NAMESPACE \

--values $VALUES_FILE \

--version $LANGFUSE_VERSION

  

helm uninstall langfuse -n langfuse

 

 

처음 웹서버 파드를 띄우고 진입하면 회원가입을 해야 한다. 

 

Setting 부분에서 API 키를 발급받아 아래 연결을 위한 테스트 코드를 작성하고 실행한다.

연결 테스트를 위한 코드

```
from langfuse import Langfuse

from langfuse import observe, get_client

  

langfuse = Langfuse(

secret_key="secret-key",

public_key="pub-key",

host="http://000000000.ap-northeast-2.elb.amazonaws.com"

)

  

@observe

def my_function():

return "Hello, world!" # Input/output and timings are automatically captured

my_function()

# Flush events in short-lived applications

langfuse = get_client()

langfuse.flush()
```

 

그리고 아래 연동된 모습이다.

5.트러블 슈팅

- 에러 요약
    - worker pod 에서 redis 통신 에러 발생
    - web pod 에서 CrashLoopBackOff 발생
    - minIO 오류 발생
    - 설치 시 오류 발생
- 해결방법
    - chart value 값 내에 postgresql, clickhouse 의 password 지정
    - 워커파드의 수동 재시작 ( 아마 잘 working 하는 파드의 순서가 있는 모양인데, worker 파드가 먼저 띄워지면서 에러가 발생하는것으로 보임)
    - host 는 수동으로 셋팅하지 말것
    - minIO 값에 글로벌 설정 통일 및 ID, password 설정

반응형
반응형

 

 


AI Agent 가 최근 개발 업계에서 화두인것 같다.
Agent 를 쉽게 개발하기 위한 도구들이 많이 생겨 나오고 있고, 우연히 웨비나를 통해 알게 된 Strands 에 대해서 사용법을 포함해 간단히 소개하고자 한다.

Strands 는 AWS 에서 개발한 AI Agent 를 개발하기 위한 오픈 SDK 다.



strand 는 가닥이라는 의미인데, Agent 와 tool 두 가닥으로 쉽게 Agent 를 구현할 수 있다는 의미.. 일 거다.  

아무튼 예제가 너무 쉽게 눈에 들어와서, 최근 AI Agent 과제를 진행하는데 이 SDK 를 선택하기로 했다. 잘 알려진  Langchain 은 너무 거대해서 공부할게 많아 보인다.

준비는 python 개발 환경과 LLM 을 사용할 수 있는 준비가 되겠다.


Strands package 설치는 아래 내용을 진행한다. (가상환경 셋팅 포함)

 

- 준비사항
- python 3.10 이상 설치
- 가상 환경 설치
- strands-agents SDK 패키지 설치

 

$ sudo yum install python3.11 -y
$ python3.11 -m venv .venv

$ pip install strands-agents strands-agents-tools

 

 


LLM 을 사용할 수 있는 준비가 되어야 하는데, 2가지를 소개하겠다.


1) 오픈 소스 Ollama 활용
- Ollama 설치 와 LLM 모델 다운로드 및 실행 (https://ollama.com/)

 

2) AWS 관리형 서비스 활용
- AWS Bedrock 의 LLM 모델활성화

  • AWS Bedrock 토큰 설정
    • 프로그램을 실행할 때 API 를 사용할 수 있는 토큰이 코드 또는 환경변수에  등록되어있어야 한다
    • Discover -> API Keys 진입 > API 키 생성
  • 트러블 슈팅
    • 모델 아이디를 조회하는 방법은 Discover -> Model catalog -> LLM 모델 선택
    • 모델 아이디앞에 지역 코드를 추가 [1]

예제 코드

from strands import Agent, tool
from strands.models import BedrockModel
from strands_tools import calculator
from strands.models.ollama import OllamaModel

  
  

base_model = BedrockModel(
model_id="apac.anthropic.claude-3-7-sonnet-20250219-v1:0",
region_name="ap-northeast-2",
)

base_model = OllamaModel(
host="http://localhost:11434",
model_id = "gpt-oss:20b",,
temperature=0.7
)

@tool
def weather():
""" Get weather """

return "흐림"

  
  
  

agent = Agent(model=base_model,
tools = [weather, calculator],
system_prompt="너는 간단한 수학계산과 날씨를 알려줄수 있는 어이스턴트야.")

response = agent("오늘 날씨는 어때?")

print(response)

 

위 코드 몇줄로 AI Agent 를 만들었다.


Tool 은 AI Agent 가 외부 세계와 상호작용하고 작업을 수행할 수 있게 해주는 기능. 또는 인터페이스를 의미한다.

기본적으로 strands 에서 제공하는 tool 들이 있고 직접 구현할 수 도 있다.
예를 들어 위 예제 코드에서 나는 weather() 라는 툴에 "흐림"을 하드코딩해서 return 했다. AI Agent 는 날씨를 매번 흐림으로 답변 할 것이다.

요즘 많이들 구현하는 MCP 도 Tool의 종류다.


오늘은 여기까지.

 

 

참고

[1] https://repost.aws/questions/QUEU82wbYVQk2oU4eNwyiong/bedrock-api-invocation-error-on-demand-throughput-isn-s-supported
[2] https://github.com/strands-agents/sdk-python

반응형
반응형

목차

  • MCP 란 무엇인가 ?
  • MCP 로 뭘 할 수 있는가?
  • MCP 는 어떻게 동작하는가?
  • MCP 의 실체를 알아보고 직접 server 를 만들어보자

1.MCP 란 무엇인가?


  • MCP = Model Context Protocol, 대형 언어 모델 LLM 이 외부 데이터 및 기능과 상호작용할 수 있도록 돕는 공개 프로토콜
  • 부연 설명 !! 2024년 11월 calude 로 유명한 Anthropic 에서 발표한, LLM Agent 가 다른 프로그램과 연동하기 위한 프로토콜이다.
  • 사람들이 쉽게 예를 들어 설명할때 AI USB 로 표현하기도 함
  • AI 가 더 똑똑해지려면 단순한 훈련 데이터에 의존하지 않도록 해야 하는게 필수. 외부데이터를 안전하게 가져와야 하는 방법이 필요했다 그래서 탄생하게 된게 MCP

 

2.MCP로 뭘 할 수 있는가?


 

  • 주요 역할
    • LLM 이 파일, 데이터베이스, API, 시스템 정보 등에 접근 가능
    • 사용자 허가를 통해 안전한 데이터 이동
    • 다양한 LLM 에서 공통적으로 사용할 수 있는 표준 인터페이스 제공
  • 기존에는 AI 가 사전에 학습된 정보에만 의존해야 했지만, MCP 를 사용하면 실시간으로 외부와 연동하여 최신 데이터를 가져와 활용할 수 있다.

3.그래서 MCP는 어떻게 동작하는가?

A. 주요 아키텍쳐 컴포넌트

  • MCP Hosts
    • LLM 모델을 이용하는 AI Tool
    • IDE, Claude Desktop,
  • MCP Client
    • 서버와 1:1 로 연결하는 프로그램 클라이언트
    • LLM 이 실제 실행되는 주체. 다양한 도구나 서비스를 사용할 수 있또록 요청을 보내는 쪽
  • MCP Server
    • 모델의 문맥을 교환할 수 있는 프로토콜로 통신하는 경량화된 프로그램
  • Local Data Sources
    • 컴퓨터 파일, DB 등의 서비스
  • Remote Services
    • 인터넷 API 를 통해 연결 가능한 외부 서비스들

 

B. 동작흐름

  • 클라이언트는 서버에 연결 요청을 보내고, 서버는 승인하여 통신 채널을 설정한다.
  • 사용자의 요청을 MCP Client 가 MCP Server 를 통해 요구하는 사양에 맞는 내용의 데이터를 반환 받고, LLM 제공자( sonnet, grok, gemini, gpt 등) 에게 전달하여 분석과 처리를 한다.

4. MCP 서버의 실체를 알아보고, 직접 만들어보기

A.개발환경 설정

  • MCP SDK 설치
  • MCP 서버 코드 작성
  • MCP 서버 실행 & Inspector 로 테스트
  • Cursor에서 연동해보기

B.시나리오 설정

  • AI Tool (MCP Host) 가 MCP 에 연결하여 회의록 내용을 읽고 회의록의 내용을 요약하거나 질문에 답한다.
  • 빠른 시연을 위해 회의록 데이터는 로컬에 txt 형태로 저장한다.
  • 샘플 데이터
$ mkdir my-mcp
$ cd my-mcp

$ npm init -y

$ npm install @modelcontextprotocol/sdk zod 
$ npm install -D @types/node typescript


# 모듈에 빌드스크립트 내용 추가
$ vi package.json
-> 아래 추가 
"type": "module",
iimport { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from "zod";

// MCP 서버를 생성합니다.
const server = new McpServer({
    name: "jssvs-server",
    version: "1.0.0"
    }
);

// registerTool 메서드: 서버에 새로운 툴(기능)을 등록합니다.
// 첫 번째 인자는 툴의 이름, 두 번째는 툴의 메타데이터(설명, 입력값 스키마 등),
// 세 번째는 실제로 동작하는 함수(입력값을 받아 결과를 반환)입니다.
// 아래는 두 숫자를 더하는 add 툴을 등록하는 예시입니다.
server.registerTool("add",
  {
    title: "Addition Tool",
    description: "Add two numbers",
    inputSchema: { a: z.number(), b: z.number() }
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

// registerTool 메서드: 회의록 읽기 툴을 등록합니다.
// 사용자가 날짜(date)를 입력하면, 해당 날짜의 회의록 파일을 읽어 반환합니다.
server.registerTool("readMeeting",
  {
    title: "Meeting Notes Reader",
    description: "Read meeting notes from a specific date",
    inputSchema: { date: z.string() }
  },
  async ({ date }) => {
    try {
      // 파일 시스템과 경로 모듈을 동적으로 import
      const fs = await import('fs/promises');
      const path = await import('path');
      // 회의록 파일 경로를 생성합니다.
      const filePath = path.join('/Users/jssvs/workspace/mcp-demo/mydata', `${date}-meeting.txt`);
      // 파일을 읽어서 내용을 반환합니다.
      const content = await fs.readFile(filePath, 'utf8');
      return {
        content: [{ type: "text", text: content }]
      };
    } catch (error) {
      // 파일 읽기 실패 시 에러 메시지를 반환합니다.
      return {
        content: [{ type: "text", text: `Error reading meeting notes: ${error.message}` }]
      };
    }
  }
);

// registerResource 메서드: 서버에 동적 리소스를 등록합니다.
// 첫 번째 인자는 리소스 이름, 두 번째는 리소스 URI 템플릿,
// 세 번째는 리소스의 메타데이터(설명 등), 네 번째는 실제 동작 함수입니다.
// 아래는 이름에 따라 인사말을 생성하는 greeting 리소스를 등록하는 예시입니다.
server.registerResource(
  "greeting",
  new ResourceTemplate("greeting://{name}", { list: undefined }),
  { 
    title: "Greeting Resource",      // Display name for UI
    description: "Dynamic greeting generator"
  },
  async (uri, { name }) => ({
    contents: [{
      uri: uri.href,
      text: `Hello, ${name}!`
    }]
  })
);

// connect 메서드: 서버와 트랜스포트(통신 채널)를 연결합니다.
// 여기서는 StdioServerTransport를 사용하여 표준 입출력(터미널) 기반으로 외부와 통신할 수 있게 합니다.
const transport = new StdioServerTransport();
await server.connect(transport);

D. MCP Client 와 연결 ( Cursor IDE )

# mcp.json
{
"my-demo-mcp-server": {
"type": "studio",
"command": "npx",
    "args": [
    "-y",
    "tsx",
    "/Users/jssvs/workspace/mcp-demo/main.ts"
    ],
"env": {}
}

** MCP 서버는 오픈 소스 커뮤니티에서 공유가 활발히 일어나고 있음

https://smithery.ai/

 

Smithery - Model Context Protocol Registry

Your Agent's Gateway to the World Integrate your AI with 7983 skills and extensions built by the community.

smithery.ai

느낀점

  • MCP 를 개인의 수준에서 활용하는 것은 바로 가능할 것 같다. 하지만 서비스 수준에서 활용하는 것은 아래의 부분들과 사이드 이펙트를 고려해야 하고, 경험이 필요 해보인다.
    • 연동 시스템에 대한 보안 ( DB 접속 정보가 Host의 학습 데이터로 넘어간다거나, 매출, 사내 보안 데이터가 유출 되는 것 )
    • 권한에 대한 문제 ( Drop, Delete, Rm등 권한을 갖게된 MCP Client 는 잘못된 명령으로 시스템을 망가뜨릴 수 있는 힘을 갖게 됨 )
    • 네트워크 구성에 대한 문제 ( 서비스 수준에서의 MCP 서버는 원격 서버 구성을 해야 하는데, 현재는 레퍼런스가 많아 보이지 않음 )
    • 어떤 LLM 모델을 연결할 것이고, 어떤 서비스를 사용할 것인가 ? ( AI 서비스는 다양하고, 서비스의 장단점을 아직 알지 못함. Bedrock? Azure OpenAIService? )

6. 참고

https://zdnet.co.kr/view/?no=20250630161811

https://techblog.woowahan.com/22342/

https://www.claudemcp.com/ko/blog/mcp-vs-api

https://medium.com/snowflake/using-mcp-with-snowflake-cortex-ai-f04e0b840958

https://aws.amazon.com/ko/blogs/tech/amazon-bedrock-agents-mcp-model-context-protocol/

반응형
반응형



 

aws summit 행사는, IT를 사용하는 산업의 기술 동향을 볼 수 있다. 왜냐면 여러 기업에서 aws 클라우드 환경으로 서비스를 운영 하기 때문이다.

최근에는 AI Agent 활용이 추세가 되면서, Google, MS Azure 를 멀티 클라우드 환경으로 혼용해서 쓰는 것 처럼보이지만, 서버, 스토리지 사용은 국내에서만큼은 여전히 aws 가 독보적인듯..

  

올해 행사는 2025.05.14~15 일 이틀간 이뤄졌고, 인상깊었던 세션 중 하나인 LG 전자의 발표를 소개하겠다.

  

주제는 "생성형 AI 기반 BI 플랫폼 인사이트 구현" 이다.

  

내용의 결론부터 말해보면 2가지 일 것 같다.

1) "Amazon Q in Quicksight 로 기존 대시보드의 한계를 극복했다".

2) "Multi AI Agent를 융합하여 구성한 Reporting 시스템 개발로 데이터를 보는 사용자의 관심을 끌었다"

  

그럼 배경과 함게 조금 더 자세한 결론을 얘기해보겠다.

  

일단 LG 전자에는 CDP(Customer Data Platform) 라는 데이터 플랫폼을 운영하고 있고, 셀프 BI 대시보드를 지향하는 환경을 사용자에게 제공하고 있었다.

LG 전자도 CDP 에 모인 데이터 기반으로 분석하는 과정은 어느 회사와 다르지 않다.

  

(1)문제 정의 -> (2)데이터 수집/추출 -> (3)데이터 정제/가공 -> (4)집계 /탐색 -> (5)데이터 해석 / 인사이트 도출 이 5가지 과정으로 분류 할 수 있다.

  

하지만 이 프로세스는 모두가 공감하는 문제를 갖고 있다. 비지니스 환경에서는 저 문제가 반복된다. 2번이 잘못되면 2~5번이 다시 수행되야 되는거지

그리고 현업이 요청 하면 대시보드를 제공하기 까지 리드 타임이 길다.

현업이 요청하면 한 번만 보고, 더 이상 대시보드를 보지 않는 경우도 있다. 만든 이후에는 관심도가 떨어지기 때문이다.

그리고 추가 분석항목들이 발생하면 제작자와 일정 줄다리기가 발생한다.

  

기능적으로도 문제가 있다. 고정된 차트 형태의 시각화를 제공해야 하는 제약이 있고 솔루션 자체의 기능 요청을 하면 답답함만 늘어난다. 글로벌 우선순위에 따라 진행되기 때문이지.

(실제로 LG 전자는 CDP 대시보드를 자체 구축했지만, 유지보수에 한계가 존재했다고 한다.)

  

그래서 Q in QuickSight 를 도입했고, BI 를 자연어 질의 기반으로 처리할 수 있는 환경을 갖추게 되었고, 현재는 전체 조직원이 활용하고 있다고 한다.

데이터 기반의 의사결정을 위한 생각의 시간을 줄여줬다 라는 표현을 했다.

  

플랫폼 아키텍쳐 측면에서 보면, ETL 한 DataMart 를 Amazon Redshift 로 이관하고, Quicksight 가 Datasource 로 연동되면서 SPICE 를 활용하는 구조다.

  

Quicksight 에 Amazon Q를 붙이기 전에, Amazon Bedrock으로 만든 자체 LLM 모델이 있었는데, 리소스가 많이 들고 복잡한 과정과 시간이 들어가서 Q를 사용했다고 했다.

생성형 AI 도 직접 만드는것보다 managed 서비스로 활용해보니 성능이 크게 다르지 않았다고 한다.

  

Q in Quicksight 와 태블로 대시보드의 성능 비교를 짧은 영상으로 보여줬는데 인상 적이었다.

  

그 다음에는 Multi Agent Report 시스템을 소개했다.

LG 전자가 생각하는 "대시보드 기반의 분석의 한계" 는 다음과 같다

(1) 분석에 시간이 많이 든다.

(2) 너무 많은 차트는 복잡도를 높인다. 그렇다고 차트를 줄이기도 그렇고

(3) 단순한 질문 & 답변으로는 종합적인 인사이트 도출이 어렵다.

(4) 현업들은 정형 데이터에서 정확성을 얻기 쉽지 않다.

  

다각도의 데이터 자동 분석이 필요했고, 그래서 만든게 Multi Agent Report 시스템이라고 한다.

제공된 서비스를 보면 Prompt 로 데이터셋과, 스키마 정보와 함께, 비지니스 요구사항을 자세히 작성해서 주문하면, 컨텍스트를 분석하는 Agent, 보고서 목차를 정리하는 Agent, 분석 코드를 만들고 실행하는 Agent , 결과를 취합하고 시각화하는 Agent, 보고서를 발송하는 Agent 이렇게 여러 Agent 가 유기적으로 동작하는 모습을 보였다.

  

머니테터 같은 10장 내외의 레포트가 자동으로 만들어지고, 이메일로 발송된다. LG 전자는 이 서비스로 보고서 작성 시간을 단축하고 의사결정을 빨리 할 수 있기를 기대하고 있다고 했다.

 

여기까지가 LG 전자 서밋의 내용 요약인데, 발표 자료 안에 있던 모든 기획과 구성들을 어느 기간안에 진행했는지, 그리고 데이터 처리 규모와 배치/스트리밍 데이터의 흐름 등이 궁금했다.

 

LG 전자... 데이터도 잘 하네..

 

끝.

 

 

 

 

반응형

+ Recent posts