728x90

안녕하세요. 20년 가까이 개발 현장에서 일하며 개발자와 PL·PM 역할을 경험하고 있는 개발자입니다.

 

Java와 Spring을 비롯해 다양한 데이터베이스와 웹·모바일 기술을 사용해 왔고,

 

최근에는 AI, LLM, RAG, VLM, AI Agent 같은 기술을 실제 프로젝트에 어떻게 활용할 수 있을지 관심을 가지고

 

관련 기술들을 이용해서 서비스를 직접 만들어보기도 하고 프로젝트를 리딩하고 있습니다.

 

새로운 기술을 배우는 것도 좋아하지만, 단순히 기술을 익히는 것보다

 

직접 적용해 보고, 잘 되는 방법과 실패하는 방법을 찾아보는 과정을 더 중요하게 생각합니다.

 

그래서 이곳에는 개발하면서 만난 문제와 해결 과정, AI를 활용한 개발 경험,

 

프로젝트에서 얻은 생각들을 편하게 기록하려고 합니다.

 

오랫동안 개발을 하다 보니 기술뿐 아니라 프로젝트를 함께 만들어가는 과정과

 

사람들과의 협업에도 많은 관심을 갖게 되었습니다.

 

앞으로도 개발, AI, 데이터, 프로젝트 경험을 하나씩 연결해 보려고 합니다.

 

배우고, 직접 만들어 보고, 다시 기록하는 것.

 

이 공간은 그 과정을 함께 나누는 편안한 공간이 되었으면 합니다.

728x90
728x90

2026.08.12 - [AI와 교육/AI Literacy] - 1차도전_헤르메스를 이용한 AI (ugreen nas with docker) 설치(2026.08.11)

 

1차도전_헤르메스를 이용한 AI (ugreen nas with docker) 설치(2026.08.11)

# 이글을 쓰는 이유..정말 열심히 AI 관련 프로젝트를 리딩해봤지만 전혀 AI를 사용할 수 없는 환경인 폐쇄망에서 작업이라... 개발자에게 가이드할 방법이 없고, 내가 사용하기에도 제한이 너무

jsi0.tistory.com

콜솔에서 hermes 메시지 서비스 연결 확인.

provider는 맥미니의 llm mcp url 등록

 

hermes dashboard 실행시 접속이 안되는 케이스 확인.

접속 설정 관련해서 설정 추가

호스트 미 등록시 외부에서 접속이 안됩니다.

config.yam

dashboard:
  basic_auth:
    username: rain
    password_hash: (scriypt$패스워드해시값)

plugins.dashboard_auth.basic를 이용해서 생성... claude code를 이용해서 설정을 하고 대시보드 접속확인했네요. 

 

사용방법을 조금더 익히고 도커 날리고 다시 설정해 봐야 겠네요. 

일단 헤르메스 대시보드까지 연결확인했고. 이어서 텔레그램 연결하고 멀티 에이전트로 일을 시켜보는 것까지 해봐야겠네요.

 

이번 광복절에 과연 독립을 이룰수 있을지 모르겠네요. 

독립 업무를 하는 헤르메스와 오픈클로를 볼 수 있으면 좋겠네요. (

둘이 좀 만들어봐봐... 안되면 클로드 코드로 다듬어서 돌려 보겠습니다. )

 

폐쇄망에서 사용가능한 개발 툴이 될지 잘 모르겠네요. 

될것도 같은데... 흠음.. 일단 개발  IDE까지 연결하고 온라인에서는 텔레그램으로 오프라인에서는 쳇봇화면에서 일감 던저보는 것까지 광복절에 이루어 볼 생각입니다.


gemma4:E4B 를 이용해서 다음 작업을 위한 모델 검토

더보기

로컬에 모델서빙 프로그램을 통해서 모델을 제공해주려고 하는데 허깅페이스를 조회해서 sLLM 중에 어떤 모델을 어떤 용도로 사용할 
   수 있도록 준비하면 좋을지 알려줘.                                                                                                    

 └─ ▾ Tool calls (1)
   └─ ● Skill View("huggingface-hub") (0.2s)  
                                                                                                                                       
 └─ Response           
                                                                                                                                       
 ┊  Hugging Face Hub에서 sLLM(Small Language Models)을 찾고 어떤 용도로 사용할지 결정하는 것은 모델의 크기, 성능(benchmark), 그리고
    사용 목적(Task)에 따라 달라집니다.

    huggingface-hub 스킬을 통해 직접 검색하고 정보를 얻을 수 있지만, 어떤 용도로 사용할지 결정하는 것은 모델의 특성(성능, 라이선스)에  
    대한 판단이 필요합니다.                                                                                                            
                                                                                                                                       
    따라서, 제가 먼저 일반적인 LLM/sLLM의 용도 분류와 검색 전략을 안내해 드리고, 그 후에 특정 모델을 검색하여 구체적인 정보를 제공해   
    드리겠습니다.                                                                                                                      
                       
    1. 모델 용도 분류 (Use Case Categorization)                                                                                        
      depending on how the skill exposes its functionality. Since I only see the skill name, I should first view it to understand its  
      methods, or if I assume a standard search capability, I'll proceed with a targeted call if one is apparent from the skill        
      description.                                                                                                                     
      Given the skill is `huggingface-hub`, I'll start by viewing it to understand its functions.                                      
 ├─ ▾ Tool calls (1)  ~7 tokens                                                                                                        
 │ └─ ● Skill View("huggingface-hub") (0.2s)                                                                                           
 └─ Σ ~360 total                                                                                                                       
                                                                                                                                       
 ┊  Hugging Face Hub에서 sLLM(Small Language Models)을 찾고 어떤 용도로 사용할지 결정하는 것은 모델의 크기, 성능(benchmark), 그리고     
    사용 목적(Task)에 따라 달라집니다.                                                                                
                                                                                                                                       
    huggingface-hub 스킬을 통해 직접 검색하고 정보를 얻을 수 있지만, 어떤 용도로 사용할지 결정하는 것은 모델의 특성(성능, 라이선스)에  
    대한 판단이 필요합니다.                                                                                                            
                                                                                                                                       
    따라서, 제가 먼저 일반적인 LLM/sLLM의 용도 분류와 검색 전략을 안내해 드리고, 그 후에 특정 모델을 검색하여 구체적인 정보를 제공해   
    드리겠습니다.                                                                                                                      
                                                                                                                                       
    1. 모델 용도 분류 (Use Case Categorization)                                                                                        
                                                                                                                                       
    sLLM을 로컬에서 서빙할 때, 용도별로 고려할 사항은 다음과 같습니다.                                                              
                                                                                                                                       
      용도 (Use Case)                    요구 성능             고려할 sLLM 특성                         추천 모델 유형                 
                                         (Performance Need)                                                                            
      ─────────────────────────────────  ────────────────────  ───────────────────────────────────────  ─────────────────────────      
      일반 대화/챗봇                     적절한 응답력,        대화 튜닝(Instruction-tuned) 모델        Llama-3-8B, Mistral-7B,        
      (Chat/Conversation)                일관성                                                         Gemma-2B/7B 등                 
      코드 생성/검토 (Code               문법 이해력, 논리적   Code-specific 파인튜닝 모델 (e.g.,       CodeLlama, StarCoder,           
      Generation/Review)                 사고                  StarCoder, CodeLlama)                    DeepSeek-Coder                 
      요약/정보 추출                     문맥 이해력, 응집성   강력한 문맥 이해 능력을 갖춘 모델        Mistral, Llama-3, Flan-T5      
      (Summarization/Extraction)                                                                        계열                           
      분류/이진 판단                     빠르고 정확한 패턴    작은 크기, 빠른 추론 속도 (Latency)      TinyLlama, Phi-3 Mini 등       
      (Classification/Triage)            인식                                                           경량 모델                      
                                                                                                                                       
    2. sLLM 선택 시 핵심 고려사항                                                                                                      
                                                                                                                                        
    로컬 서빙을 목표로 하신다면, 다음 두 가지가 가장 중요합니다.                                                                       
                                                                                                                                       
    1. 양자화 (Quantization) 가능 여부: 모델의 파일 크기를 줄여 로컬 GPU/CPU에서 구동하기 쉽게 만드는 과정입니다. GGUF (llama.cpp      
    포맷)가 현재 가장 널리 쓰이며 호환성이 좋습니다.                                                      
    2. 추론 속도 (Inference Speed) vs. 정확도 (Accuracy): 더 작은 모델(예: 3B/7B)은 빠르지만 정확도가 떨어질 수 있고, 더 큰 모델은     
    느리지만 더 정확할 수 있습니다.                                                                                                    
                                                                                                                                       
    3. 검색 전략 (Search Strategy)                                                                                                     
                                                                                                                                       
    Hugging Face Hub에서 원하는 모델을 찾으려면, 검색창에 다음과 같은 키워드를 조합하여 검색하는 것이 가장 효율적입니다.                
                                                                                                                                        
    • sllm GGUF (가장 쉽게 로컬 서빙 가능한 모델을 찾기 위함)                                                                           
    • 7B instruction tuned (성능과 크기의 균형을 찾기 위함)                                                                            
    • code generation 8bit (코딩 전용 모델을 찾기 위함)

    ────────────────────────────────────

728x90
728x90

# 이글을 쓰는 이유..

정말 열심히 AI 관련 프로젝트를 리딩해봤지만 전혀 AI를 사용할 수 없는 환경인 폐쇄망에서 작업이라... 

개발자에게 가이드할 방법이 없고, 내가 사용하기에도 제한이 너무 많아서 고민만 하는 몇개월을 보냈습니다.

결국 커서와 클로드코드는 내돈으로 일년 구독하고 프로젝트는 겨우 끝내고 개발자 바쁘다고 하자보수가서 프로그램 보수해주고 왔네요.

프로젝트 끝나고 회사에서 ChatGpt를 제공해주었지만 결국 사용자가 해본 만큼도 사용할 환경도 아니라고 말하고 싶고 뭔가 불편함은 어쩔수 없네요. 

그중 보안과 내가 뭘하는지 로그가 다 남아서 남들에게 또는 chatgpt에 이력이 남아서 기존 프로젝트 관련해서 반영하여 답변 주는 AI 서비스를 보면서 나의 보안도 프로젝트의 내용도 노출이 되는 것이 아닐까 걱정하는 부분이 커서 더욱 그런거 같습니다.

 

그래서 이번에 나스를 조금 더 좋은 것을 사서 헤르메스를 사용해보려 합니다.

유그린 나스에 썬더볼트로 그래픽카드를 연동해서 헤르메스를 설정한 것을 보아서 해보고 싶었달까....

우선 이 글을 쓸 2026.08.11 한글로 검색이 되는 것이 별로 없어서 정리하면서 설치를 해볼까 합니다. 


# 구성

LLM과 RAG는 맥미니로 이미 구성을 해본 상태에서 유그린 나스를 구매했기 때문에 ...  아래와 같이 설정해 보려 합니다.

 

 

유그린 나스

더보기

헤르메스를 도커로 설치

 

  도커 폴더 아래에 LLM wiki (  안드레 카파시가 제안한 ), 옵시디언 

맥 미니

더보기

LLM , embedding model, reranking model,

( stt, tts, vlm 추후 검토 )


# 유그린 나스에 도커로 헤르메스 에이전트 실행하기

사전준비

더보기

1. 일단 유그린 나스에서 도커설치

2. docker 설치하면 생기는 폴더 아래에 헤르메스 사용 폴더 생성 ( 권한 문제가 없도록 예하도 설정했음 )

3. 나스에 ssh로 접속 할 수 있도록 설정

4. 텔레그렘 연동을 사용을 위한 텔레그램 봇 파더를 이용한 봇생성

 

헤르메스용 컨테이너 생성

더보기

이미지 조회 설치

nousresearch/hermes-agent:latest

-- 포트 설정

port 8642 → 8642

-- 폴더 설정

/docker/hermes
        ↓
/opt/data

nousresearch/hermes-agent:latest 검색

 

https://hub.docker.com/r/nousresearch/hermes-agent
이미지 다운로드

컨테이너를 탭으로 가서 생성... 아까 받은 이미지를 선택 후 확인

 

생성 시 옵션 입력창

 

 

컨테이너 실행후 터미널 텝에서 add

/bin/bassh 로 컨테이너 접속해서

./setup-hermes.sh

 

일단 실행명령어 제거 후 재 생성.

헤르메스 셋업

이제 설정 들어왔네요

일단 해보고 지운다 생각하고 그냥 고고싱. 로컬모델을 설치하고 연결 예정이라 설정은 다음에 자세하는 것으로 선택하고 텔레그램선택.


헤르메스 설치 후 표시되는 명령어 

📁 All your files are in /opt/data/:

   Settings:  /opt/data/config.yaml
   API Keys:  /opt/data/.env
   Data:      /opt/data/cron/, sessions/, logs/

────────────────────────────────────────────────────────────

📝 To edit your configuration:

   hermes setup          Re-run the full wizard
   hermes setup model    Change model/provider
   hermes setup terminal Change terminal backend
   hermes setup gateway  Configure messaging
   hermes setup tools    Configure tool providers

   hermes config         View current settings
   hermes config edit    Open config in your editor
   hermes config set <key> <value>
                          Set a specific value

   Or edit the files directly:
   nano /opt/data/config.yaml
   nano /opt/data/.env

────────────────────────────────────────────────────────────

🚀 Ready to go!

   hermes              Start chatting
   hermes gateway      Start messaging gateway
   hermes doctor       Check for issues

오... 헤르메스... 실행하니 llm url를 설정해 달라고 하네요. 

모델 서빙하는 url 전달하고 각종 스킬을 셋팅하라고 하니 인터넷을 검색해서 만드네요.

문서 관련해서 다운받아서 확인하고 파일을 만든다고 해서 /opt/herm

728x90
728x90

가끔씩 필요해서 찾게 되는 것이라 정리해 봅니다.

 

1. sftp-config.sh (설정 파일)
용량 비교 및 상세 체크를 위해 SSH 접속 정보를 그대로 사용합니다.

더보기
# sftp-config.sh
# SFTP 서버 접속 정보 및 경로 설정

# [필수] SFTP 서버 정보
SFTP_HOST="sftp.example.com"
SFTP_PORT="22"
SFTP_USER="root"

# [선택] RSA 개인키 경로 (기본: /root/.ssh/id_rsa)
IDENTITY_FILE="/root/.ssh/id_rsa"

# 재시도 설정
MAX_RETRIES=2
BACKOFF_BASE=2

# 로그 및 경로 설정
LOG_BASE_DIR="/root/sftp_logs"       # 상세 실행 로그
RESULT_LOG_DIR="/root/sftp_results" # 운영용 요약 로그
DB_READY_LOG_DIR="/root/sftp_db_logs" # DB 적재용 정제 로그 (JSONL)
TARGET_LIST_DIR="/root/sftp_targets"

# 동일 용량 파일 스킵 여부 (YES: 스킵 및 로그 남김, NO: 덮어쓰기)
SKIP_SAME_SIZE="YES"

2. sftp_download.sh (고도화된 전체 소스)
이 스크립트는 각 파일의 상태를 4가지 상태(SUCCESS, NOT_FOUND, SKIPPED_SAME, FAILED)로 구분하여 로그를 남깁니다.

더보기
#!/usr/bin/env bash
# sftp_download.sh - DB 연동 최적화 및 상세 로깅 버전

set -uo pipefail

BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${BASE_DIR}/sftp-config.sh"
MODE=""          
REMOTE_BASE=""   
PATTERN=""       
TARGET_LIST=""   
LOCAL_DIR=""     

usage() {
  cat <<EOF
Usage: $(basename "$0") -M <mode> -r <remote_base> -d <local_abs_path> [options]
Modes: tree | list
EOF
}

# 1. 텍스트 로그 (운영자 확인용)
log() {
  local level="$1"; shift
  local msg="$*"
  echo "$(date '+%Y-%m-%d %H:%M:%S') [${level}] ${msg}" | tee -a "${LOG_FILE}"
}

# 2. 정제 로그 (DB 적재 프로그램용 - JSONL)
record_db_log() {
  local status="$1"
  local file_path="$2"
  local size="${3:-0}"
  local msg="${4:-""}"
  local ts
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ") # ISO 8601 표준

  # DB 프로그램에서 파싱하기 좋게 한 줄의 JSON으로 기록
  printf '{"ts":"%s", "status":"%s", "file":"%s", "size":%d, "msg":"%s"}\n' \
    "$ts" "$status" "$file_path" "$size" "$msg" >> "${DB_LOG}"
}

while getopts "M:r:p:f:d:c:h" opt; do
  case "${opt}" in
    M) MODE="${OPTARG}" ;;
    r) REMOTE_BASE="${OPTARG}" ;;
    p) PATTERN="${OPTARG}" ;;
    f) TARGET_LIST="${OPTARG}" ;;
    d) LOCAL_DIR="${OPTARG}" ;;
    c) CONFIG_FILE="${OPTARG}" ;;
    h) usage; exit 0 ;;
    *) usage; exit 1 ;;
  esac
done

if [[ ! -f "${CONFIG_FILE}" ]]; then
  echo "설정 파일을 찾을 수 없습니다: ${CONFIG_FILE}" >&2
  exit 1
fi

source "${CONFIG_FILE}"

# 디렉토리 및 로그 파일 준비
mkdir -p "${LOG_BASE_DIR}" "${RESULT_LOG_DIR}" "${DB_READY_LOG_DIR}" "${LOCAL_DIR}"
LOG_FILE="${LOG_BASE_DIR}/sftp_detail_$(date +%Y%m%d).log"
DB_LOG="${DB_READY_LOG_DIR}/transfer_data_$(date +%Y%m%d).jsonl"
SSH_OPTS="-i ${IDENTITY_FILE} -p ${SFTP_PORT} -o BatchMode=yes -o StrictHostKeyChecking=yes -o ServerAliveInterval=60"

download_file() {
  local rel_path="$1"
  local remote_p="${REMOTE_BASE}/${rel_path}"
  local local_p="${LOCAL_DIR}/${rel_path}"
  
  # 1. 원격 파일 정보 획득
  local r_size
  r_size=$(ssh ${SSH_OPTS} "${SFTP_USER}@${SFTP_HOST}" "stat -c%s '${remote_p}'" 2>/dev/null || echo "NOT_FOUND")

  if [[ "${r_size}" == "NOT_FOUND" ]]; then
    log WARN "파일 없음: ${rel_path}"
    record_db_log "NOT_FOUND" "${rel_path}" 0 "Remote file not found"
    return 1
  fi

  # 2. 로컬 파일과 용량 비교 (중복 체크)
  if [[ -f "${local_p}" ]]; then
    local l_size
    l_size=$(stat -c%s "${local_p}")
    if [[ "${r_size}" == "${l_size}" && "${SKIP_SAME_SIZE}" == "YES" ]]; then
      log INFO "스킵(동일 용량): ${rel_path}"
      record_db_log "SKIPPED" "${rel_path}" "${r_size}" "Same size skip"
      return 0
    fi
  fi

  # 3. 실제 SFTP 전송
  mkdir -p "$(dirname "${local_p}")"
  if sftp -b - -P "${SFTP_PORT}" -i "${IDENTITY_FILE}" -C -o BatchMode=yes "${SFTP_USER}@${SFTP_HOST}" <<EOF >> "${LOG_FILE}" 2>&1
get "${remote_p}" "${local_p}"
quit
EOF
  then
    log INFO "성공: ${rel_path}"
    record_db_log "SUCCESS" "${rel_path}" "${r_size}" "Transfer complete"
  else
    log ERROR "실패: ${rel_path}"
    record_db_log "FAILED" "${rel_path}" "${r_size}" "SFTP error during transfer"
    return 1
  fi
}

log INFO "=== 작업 시작 (Mode: ${MODE}) ==="

if [[ "${MODE}" == "list" ]]; then
  # 리스트 파일이 지정되지 않았다면 TARGET_LIST_DIR의 첫 번째 파일 사용
  [[ -z "${TARGET_LIST}" ]] && TARGET_LIST=$(find "${TARGET_LIST_DIR}" -type f | head -n 1)
  
  if [[ -f "${TARGET_LIST}" ]]; then
    while IFS= read -r line || [[ -n "$line" ]]; do
      [[ -z "$line" || "$line" =~ ^# ]] && continue
      download_file "${line}"
    done < "${TARGET_LIST}"
  else
    log ERROR "리스트 파일을 찾을 수 없습니다."
  fi

elif [[ "${MODE}" == "tree" ]]; then
  # 원격지 패턴 검색 후 목록 기반 다운로드
  log INFO "원격지 목록 조회 중..."
  file_list=$(ssh ${SSH_OPTS} "${SFTP_USER}@${SFTP_HOST}" "find '${REMOTE_BASE}' -type f -name '${PATTERN}'" | sed "s|${REMOTE_BASE}/||")
  
  for f in ${file_list}; do
    download_file "$f"
  done
fi

log INFO "=== 작업 종료 (결과: ${DB_LOG}) ==="

3. 스크립트의 특징 (운영 강점)
상세한 상태 분류:
SUCCESS: 파일이 성공적으로 전송됨.
NOT_FOUND: 원격 서버에 대상 파일이 아예 없음 (경로 오타 등).
SKIPPED: 이미 로컬에 같은 용량의 파일이 있어 전송을 생략함 (네트워크 자원 절약).
FAILED: 파일은 있으나 권한 문제, 네트워크 끊김 등으로 가져오지 못함.
용량 기반 검증: 단순히 파일명만 보는 게 아니라 stat 명령으로 용량을 비교하여 '정상적으로 가져온 상태'를 확신할 수 있습니다.
가독성 높은 요약 로그: RESULT_LOG 파일만 열어보면 어떤 파일이 어떤 이유로 처리되었는지 한눈에 알 수 있어 운영이 편리합니다.

더보기

 

  • DB 적재용 로그 샘플 (transfer_data_YYYYMMDD.jsonl):
  • JSON
     
    {"ts":"2026-03-18T00:50:01Z", "status":"SUCCESS", "file":"daily/data.csv", "size":1048, "msg":"Transfer complete"}
    {"ts":"2026-03-18T00:50:03Z", "status":"SKIPPED", "file":"backup/db.sql", "size":50020, "msg":"Same size skip"}
    
  • 후속 프로그램과의 연계:
    • 후속 프로그램(Java/Python 등)이 실행될 때 DB_READY_LOG_DIR 내의 파일을 한 줄씩 읽습니다.
    • status가 SUCCESS면 DB의 전송 완료 테이블에 INSERT 합니다.
    • status가 FAILED면 관리자 알림 테이블에 넣거나 재시도 큐에 넣습니다.
  • 성능 최적화:
    • -C 옵션을 통해 다량의 파일 전송 시 데이터를 압축하여 대역폭을 절약합니다.
    • ServerAliveInterval을 설정하여 대용량 파일 전송 중 연결 끊김을 방지합니다.

 

 

4. 사용 예제
Tree 모드 (패턴)

./sftp_download.sh -M tree -r /data/logs -p "*.csv" -d /root/downloads

동작: /data/logs에서 .csv를 찾아 리스트를 만든 후, 하나씩 용량 체크하며 가져옵니다.

List 모드 (파일명 나열)

./sftp_download.sh -M list -r /data/origin -f targets.txt -d /root/downloads

 

급하게 정리해봅니다.

728x90

+ Recent posts